Skip to content

Commit 9f8335b

Browse files
committed
os: don't let File.Readdir return an empty slice and nil error
In the case of a file being deleted while Readdir was running, it was possible for File.Readdir to return an empty slice and a nil error, counter to its documentation. Fixes #16919 Change-Id: If0e42882eea52fbf5530317a1895f3829ea8e67b Reviewed-on: https://go-review.googlesource.com/28056 Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 11e3955 commit 9f8335b

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

src/os/dir_unix.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) {
3434
}
3535
fi = append(fi, fip)
3636
}
37+
if len(fi) == 0 && err == nil && n > 0 {
38+
// Per File.Readir, the slice must be non-empty or err
39+
// must be non-nil if n > 0.
40+
err = io.EOF
41+
}
3742
return fi, err
3843
}
3944

src/os/os_unix_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77
package os_test
88

99
import (
10+
"io"
11+
"io/ioutil"
1012
. "os"
13+
"path/filepath"
1114
"runtime"
15+
"strings"
1216
"syscall"
1317
"testing"
1418
)
@@ -178,3 +182,38 @@ func TestLchown(t *testing.T) {
178182
checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
179183
}
180184
}
185+
186+
// Issue 16919: Readdir must return a non-empty slice or an error.
187+
func TestReaddirRemoveRace(t *testing.T) {
188+
oldStat := *LstatP
189+
defer func() { *LstatP = oldStat }()
190+
*LstatP = func(name string) (FileInfo, error) {
191+
if strings.HasSuffix(name, "some-file") {
192+
// Act like it's been deleted.
193+
return nil, ErrNotExist
194+
}
195+
return oldStat(name)
196+
}
197+
dir := newDir("TestReaddirRemoveRace", t)
198+
defer RemoveAll(dir)
199+
if err := ioutil.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
200+
t.Fatal(err)
201+
}
202+
d, err := Open(dir)
203+
if err != nil {
204+
t.Fatal(err)
205+
}
206+
defer d.Close()
207+
fis, err := d.Readdir(2) // notably, greater than zero
208+
if len(fis) == 0 && err == nil {
209+
// This is what used to happen (Issue 16919)
210+
t.Fatal("Readdir = empty slice & err == nil")
211+
}
212+
if len(fis) != 0 || err != io.EOF {
213+
t.Errorf("Readdir = %d entries: %v; want 0, io.EOF", len(fis), err)
214+
for i, fi := range fis {
215+
t.Errorf(" entry[%d]: %q, %v", i, fi.Name(), fi.Mode())
216+
}
217+
t.FailNow()
218+
}
219+
}

0 commit comments

Comments
 (0)