1
0
mirror of https://github.com/golang/go synced 2024-11-17 17:54:48 -07:00

os: seek should invalidate any cached directory reads

When we seek on the underlying FD, discard any directory entries
we've already read and cached. This makes sure we won't return
the same entry twice.

We already fixed this for Darwin in CL 209961.

Fixes #37161

Change-Id: I20e1ac8d751443135e67fb4c43c18d69befb643b
Reviewed-on: https://go-review.googlesource.com/c/go/+/219143
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Keith Randall 2020-02-11 17:49:52 -08:00
parent a4f7b0879c
commit 529988d62c
7 changed files with 40 additions and 13 deletions

View File

@ -24,16 +24,6 @@ func (d *dirInfo) close() {
d.dir = 0
}
func (f *File) seekInvalidate() {
if f.dirinfo == nil {
return
}
// Free cached dirinfo, so we allocate a new one if we
// access this file as a directory again. See #35767.
f.dirinfo.close()
f.dirinfo = nil
}
func (f *File) readdirnames(n int) (names []string, err error) {
if f.dirinfo == nil {
dir, call, errno := f.pfd.OpenDir()

View File

@ -26,8 +26,6 @@ const (
func (d *dirInfo) close() {}
func (f *File) seekInvalidate() {}
func (f *File) readdirnames(n int) (names []string, err error) {
// If this file has no dirinfo, create one.
if f.dirinfo == nil {

View File

@ -295,7 +295,12 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) {
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
f.seekInvalidate()
if f.dirinfo != nil {
// Free cached dirinfo, so we allocate a new one if we
// access this file as a directory again. See #35767 and #37161.
f.dirinfo.close()
f.dirinfo = nil
}
ret, err = f.pfd.Seek(offset, whence)
runtime.KeepAlive(f)
return ret, err

View File

@ -2496,3 +2496,34 @@ func TestDirSeek(t *testing.T) {
}
}
}
func TestReaddirSmallSeek(t *testing.T) {
// See issue 37161. Read only one entry from a directory,
// seek to the beginning, and read again. We should not see
// duplicate entries.
if runtime.GOOS == "windows" {
testenv.SkipFlaky(t, 36019)
}
wd, err := Getwd()
if err != nil {
t.Fatal(err)
}
df, err := Open(filepath.Join(wd, "testdata", "issue37161"))
if err != nil {
t.Fatal(err)
}
names1, err := df.Readdirnames(1)
if err != nil {
t.Fatal(err)
}
if _, err = df.Seek(0, 0); err != nil {
t.Fatal(err)
}
names2, err := df.Readdirnames(0)
if err != nil {
t.Fatal(err)
}
if len(names2) != 3 {
t.Fatalf("first names: %v, second names: %v", names1, names2)
}
}

1
src/os/testdata/issue37161/a vendored Normal file
View File

@ -0,0 +1 @@
a

1
src/os/testdata/issue37161/b vendored Normal file
View File

@ -0,0 +1 @@
b

1
src/os/testdata/issue37161/c vendored Normal file
View File

@ -0,0 +1 @@
c