From a35181ba7fcb9e62e3f867292501a49e5d1a8b0c Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 8 Feb 2015 12:19:20 -0800 Subject: [PATCH] os, syscall: revert Yosemite readdir workaround Reverts https://golang.org/cl/119530044 (OS X 10.10 Yosemite beta 14A299l workaround), since it was fixed in the final Yosemite release. I verified that the C program http://swtch.com/~rsc/readdirbug.c passes on Yosemite. Adds a new test to the os package too, to verify that reading a regular file as a directory fails. Fixes #9789 (ReadDir: no error if dirname is a file) Change-Id: I75286cef88fbb2ebccf045b479e33c810749dcbc Reviewed-on: https://go-review.googlesource.com/4164 Reviewed-by: Dave Cheney --- src/os/os_test.go | 24 ++++++++++++++++++++++++ src/syscall/syscall_bsd.go | 35 +---------------------------------- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/os/os_test.go b/src/os/os_test.go index d26eb9c181..b705e2d6d2 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -491,6 +491,30 @@ func TestReaddirStatFailures(t *testing.T) { } } +// Readdir on a regular file should fail. +func TestReaddirOfFile(t *testing.T) { + f, err := ioutil.TempFile("", "_Go_ReaddirOfFile") + if err != nil { + t.Fatal(err) + } + defer Remove(f.Name()) + f.Write([]byte("foo")) + f.Close() + reg, err := Open(f.Name()) + if err != nil { + t.Fatal(err) + } + defer reg.Close() + + names, err := reg.Readdirnames(-1) + if err == nil { + t.Error("Readdirnames succeeded; want non-nil error") + } + if len(names) > 0 { + t.Errorf("unexpected dir names in regular file: %q", names) + } +} + func TestHardLink(t *testing.T) { // Hardlinks are not supported under windows or Plan 9. if runtime.GOOS == "plan9" { diff --git a/src/syscall/syscall_bsd.go b/src/syscall/syscall_bsd.go index 2556fa8746..af563910b1 100644 --- a/src/syscall/syscall_bsd.go +++ b/src/syscall/syscall_bsd.go @@ -68,40 +68,7 @@ func ReadDirent(fd int, buf []byte) (n int, err error) { // actual system call is getdirentries64, 64 is a good guess. // TODO(rsc): Can we use a single global basep for all calls? var base = (*uintptr)(unsafe.Pointer(new(uint64))) - n, err = Getdirentries(fd, buf, base) - - // On OS X 10.10 Yosemite, if you have a directory that can be returned - // in a single getdirentries64 call (for example, a directory with one file), - // and you read from the directory at EOF twice, you get EOF both times: - // fd = open("dir") - // getdirentries64(fd) // returns data - // getdirentries64(fd) // returns 0 (EOF) - // getdirentries64(fd) // returns 0 (EOF) - // - // But if you remove the file in the middle between the two calls, the - // second call returns an error instead. - // fd = open("dir") - // getdirentries64(fd) // returns data - // getdirentries64(fd) // returns 0 (EOF) - // remove("dir/file") - // getdirentries64(fd) // returns ENOENT/EINVAL - // - // Whether you get ENOENT or EINVAL depends on exactly what was - // in the directory. It is deterministic, just data-dependent. - // - // This only happens in small directories. A directory containing more data - // than fits in a 4k getdirentries64 call will return EOF correctly. - // (It's not clear if the criteria is that the directory be split across multiple - // getdirentries64 calls or that it be split across multiple file system blocks.) - // - // We could change package os to avoid the second read at EOF, - // and maybe we should, but that's a bit involved. - // For now, treat the EINVAL/ENOENT as EOF. - if runtime.GOOS == "darwin" && (err == EINVAL || err == ENOENT) { - err = nil - } - - return + return Getdirentries(fd, buf, base) } // Wait status is 7 bits at bottom, either 0 (exited),