aboutsummaryrefslogtreecommitdiff
path: root/src/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg')
-rw-r--r--src/pkg/syscall/syscall_bsd.go35
1 files changed, 34 insertions, 1 deletions
diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go
index af563910b1..2556fa8746 100644
--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -68,7 +68,40 @@ 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)))
- return Getdirentries(fd, buf, base)
+ 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
}
// Wait status is 7 bits at bottom, either 0 (exited),