internal/lsp: check that a file handle is unmodified before read

Address a lingering TODO. A FileHandle read should return errors if the
file has been modified on disk while in use.

Change-Id: I540de9bef575a9ca838f49500665a24b05b4f54c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/215981
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rohan Challa <rohan@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Rebecca Stambler 2020-01-23 12:34:02 -05:00
parent 219d3418f5
commit e873952e15
1 changed files with 14 additions and 8 deletions

View File

@ -13,6 +13,7 @@ import (
"golang.org/x/tools/internal/lsp/telemetry"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors"
)
// ioLimit limits the number of parallel file reads per process.
@ -28,17 +29,12 @@ type nativeFileHandle struct {
}
func (fs *nativeFileSystem) GetFile(uri span.URI) source.FileHandle {
identifier := "DOES NOT EXIST"
if fi, err := os.Stat(uri.Filename()); err == nil {
identifier = fi.ModTime().String()
}
kind := source.DetectLanguage("", uri.Filename())
return &nativeFileHandle{
fs: fs,
identity: source.FileIdentity{
URI: uri,
Identifier: identifier,
Kind: kind,
Identifier: identifier(uri.Filename()),
Kind: source.DetectLanguage("", uri.Filename()),
},
}
}
@ -58,10 +54,20 @@ func (h *nativeFileHandle) Read(ctx context.Context) ([]byte, string, error) {
ioLimit <- struct{}{}
defer func() { <-ioLimit }()
// TODO: this should fail if the version is not the same as the handle
if id := identifier(h.identity.URI.Filename()); id != h.identity.Identifier {
return nil, "", errors.Errorf("%s: file has been modified", h.identity.URI.Filename())
}
data, err := ioutil.ReadFile(h.identity.URI.Filename())
if err != nil {
return nil, "", err
}
return data, hashContents(data), nil
}
func identifier(filename string) string {
if fi, err := os.Stat(filename); err == nil {
return fi.ModTime().String()
}
return "DOES NOT EXIST"
}