mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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 <iant@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
11e3955e10
commit
9f8335b7e7
2 changed files with 44 additions and 0 deletions
|
|
@ -34,6 +34,11 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) {
|
|||
}
|
||||
fi = append(fi, fip)
|
||||
}
|
||||
if len(fi) == 0 && err == nil && n > 0 {
|
||||
// Per File.Readir, the slice must be non-empty or err
|
||||
// must be non-nil if n > 0.
|
||||
err = io.EOF
|
||||
}
|
||||
return fi, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,12 @@
|
|||
package os_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
. "os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
|
@ -178,3 +182,38 @@ func TestLchown(t *testing.T) {
|
|||
checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 16919: Readdir must return a non-empty slice or an error.
|
||||
func TestReaddirRemoveRace(t *testing.T) {
|
||||
oldStat := *LstatP
|
||||
defer func() { *LstatP = oldStat }()
|
||||
*LstatP = func(name string) (FileInfo, error) {
|
||||
if strings.HasSuffix(name, "some-file") {
|
||||
// Act like it's been deleted.
|
||||
return nil, ErrNotExist
|
||||
}
|
||||
return oldStat(name)
|
||||
}
|
||||
dir := newDir("TestReaddirRemoveRace", t)
|
||||
defer RemoveAll(dir)
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d, err := Open(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer d.Close()
|
||||
fis, err := d.Readdir(2) // notably, greater than zero
|
||||
if len(fis) == 0 && err == nil {
|
||||
// This is what used to happen (Issue 16919)
|
||||
t.Fatal("Readdir = empty slice & err == nil")
|
||||
}
|
||||
if len(fis) != 0 || err != io.EOF {
|
||||
t.Errorf("Readdir = %d entries: %v; want 0, io.EOF", len(fis), err)
|
||||
for i, fi := range fis {
|
||||
t.Errorf(" entry[%d]: %q, %v", i, fi.Name(), fi.Mode())
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue