]> Cypherpunks repositories - gostls13.git/commitdiff
os: don't let File.Readdir return an empty slice and nil error
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 29 Aug 2016 21:27:40 +0000 (21:27 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 29 Aug 2016 23:59:42 +0000 (23:59 +0000)
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>

src/os/dir_unix.go
src/os/os_unix_test.go

index cfa70a141e5f8ea87c0e967674068048c0488fe5..9337144c3ac0e154254c217cd0a2951e499afff7 100644 (file)
@@ -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
 }
 
index 5c10154760cbe756b7548854eb33529cdb4d9ce7..e239835c6ac0d41d8e45e521d1ae6426d24994ea 100644 (file)
@@ -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()
+       }
+}