Commit 9f8335b7 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

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/28056Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 11e3955e
...@@ -34,6 +34,11 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) { ...@@ -34,6 +34,11 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) {
} }
fi = append(fi, fip) 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 return fi, err
} }
......
...@@ -7,8 +7,12 @@ ...@@ -7,8 +7,12 @@
package os_test package os_test
import ( import (
"io"
"io/ioutil"
. "os" . "os"
"path/filepath"
"runtime" "runtime"
"strings"
"syscall" "syscall"
"testing" "testing"
) )
...@@ -178,3 +182,38 @@ func TestLchown(t *testing.T) { ...@@ -178,3 +182,38 @@ func TestLchown(t *testing.T) {
checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid)) 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()
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment