]> Cypherpunks repositories - gostls13.git/commitdiff
Make Readdirnames work properly on Linux.
authorRob Pike <r@golang.org>
Tue, 10 Feb 2009 19:55:48 +0000 (11:55 -0800)
committerRob Pike <r@golang.org>
Tue, 10 Feb 2009 19:55:48 +0000 (11:55 -0800)
Refactor so Readdir is portable code.

R=rsc
DELTA=192  (50 added, 130 deleted, 12 changed)
OCL=24770
CL=24772

src/lib/os/dir_amd64_darwin.go
src/lib/os/dir_amd64_linux.go
src/lib/os/os_file.go

index e66f540c858dd1457b9c4773fedf142f594d9da2..04d3a90c6f8e1a18e2455cd79bd3d4fb5dd061dc 100644 (file)
@@ -64,63 +64,3 @@ func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {
        }
        return names, nil
 }
-
-// TODO(r): see comment in dir_amd64_linux.go
-
-// Negative count means read until EOF.
-func Readdir(fd *FD, count int) (dirs []Dir, err *os.Error) {
-       dirname := fd.name;
-       if dirname == "" {
-               dirname = ".";
-       }
-       dirname += "/";
-       // Getdirentries needs the file offset - it's too hard for the kernel to remember
-       // a number it already has written down.
-       base, err1 := syscall.Seek(fd.fd, 0, 1);
-       if err1 != 0 {
-               return nil, os.ErrnoToError(err1)
-       }
-       // The buffer must be at least a block long.
-       // TODO(r): use fstatfs to find fs block size.
-       var buf = make([]byte, blockSize);
-       dirs = make([]Dir, 0, 100);     // TODO: could be smarter about size
-       for {
-               if count == 0 {
-                       break
-               }
-               ret, err2 := syscall.Getdirentries(fd.fd, &buf[0], int64(len(buf)), &base);
-               if ret < 0 || err2 != 0 {
-                       return dirs, os.ErrnoToError(err2)
-               }
-               if ret == 0 {
-                       break
-               }
-               for w, i := uintptr(0),uintptr(0); i < uintptr(ret); i += w {
-                       if count == 0 {
-                               break
-                       }
-                       dirent := unsafe.Pointer((uintptr(unsafe.Pointer(&buf[0])) + i)).(*syscall.Dirent);
-                       w = uintptr(dirent.Reclen);
-                       if dirent.Ino == 0 {
-                               continue
-                       }
-                       count--;
-                       if len(dirs) == cap(dirs) {
-                               ndirs := make([]Dir, len(dirs), 2*len(dirs));
-                               for i := 0; i < len(dirs); i++ {
-                                       ndirs[i] = dirs[i]
-                               }
-                               dirs = ndirs;
-                       }
-                       dirs = dirs[0:len(dirs)+1];
-                       filename := string(dirent.Name[0:dirent.Namlen]);
-                       dirp, err := Lstat(dirname + filename);
-                       if dirp == nil || err != nil {
-                               dirs[len(dirs)-1].Name = filename;      // rest will be zeroed out
-                       } else {
-                               dirs[len(dirs)-1] = *dirp;
-                       }
-               }
-       }
-       return dirs, nil;
-}
index bd81146aeaf17997b35e16f9d17da51e66c878e2..a5b8a7ba2e1a68477ce8bc06b7b26a45c6583304 100644 (file)
@@ -10,6 +10,10 @@ import (
        "unsafe";
 )
 
+const (
+       blockSize = 4096        // TODO(r): use statfs
+)
+
 func clen(n []byte) int {
        for i := 0; i < len(n); i++ {
                if n[i] == 0 {
@@ -21,28 +25,38 @@ func clen(n []byte) int {
 
 // Negative count means read until EOF.
 func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {
-       // The buffer should be at least a block long.
-       // TODO(r): use fstatfs to find fs block size.
-       var buf = make([]syscall.Dirent, 8192/unsafe.Sizeof(*new(syscall.Dirent)));
-       names = make([]string, 0, 100); // TODO: could be smarter about size
-       for {
-               if count == 0 {
-                       break
-               }
-               ret, err2 := syscall.Getdents(fd.fd, &buf[0], int64(len(buf) * unsafe.Sizeof(buf[0])));
-               if ret < 0 || err2 != 0 {
-                       return names, os.ErrnoToError(err2)
-               }
-               if ret == 0 {
-                       break
-               }
-               for w, i := uintptr(0),uintptr(0); i < uintptr(ret); i += w {
-                       if count == 0 {
-                               break
+       // If this fd has no dirinfo, create one.
+       if fd.dirinfo == nil {
+               fd.dirinfo = new(DirInfo);
+               // The buffer must be at least a block long.
+               // TODO(r): use fstatfs to find fs block size.
+               fd.dirinfo.buf = make([]byte, blockSize);
+       }
+       d := fd.dirinfo;
+       size := count;
+       if size < 0 {
+               size = 100
+       }
+       names = make([]string, 0, size);        // Empty with room to grow.
+       for count != 0 {
+               // Refill the buffer if necessary
+               if d.bufp == d.nbuf {
+                       var errno int64;
+                       dbuf := unsafe.Pointer(&d.buf[0]).(*syscall.Dirent);
+                       d.nbuf, errno = syscall.Getdents(fd.fd, dbuf, int64(len(d.buf)));
+                       if d.nbuf < 0 {
+                               return names, os.ErrnoToError(errno)
                        }
-                       dirent := unsafe.Pointer((uintptr(unsafe.Pointer(&buf[0])) + i)).(*syscall.Dirent);
-                       w = uintptr(dirent.Reclen);
-                       if dirent.Ino == 0 {
+                       if d.nbuf == 0 {
+                               break   // EOF
+                       }
+                       d.bufp = 0;
+               }
+               // Drain the buffer
+               for count != 0 && d.bufp < d.nbuf {
+                       dirent := unsafe.Pointer(&d.buf[d.bufp]).(*syscall.Dirent);
+                       d.bufp += int64(dirent.Reclen);
+                       if dirent.Ino == 0 {    // File absent in directory.
                                continue
                        }
                        count--;
@@ -59,64 +73,3 @@ func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {
        }
        return names, nil;
 }
-
-// TODO(r): Readdir duplicates a lot of Readdirnames. The other way would
-// be to have Readdir (which could then be portable) call Readdirnames and
-// then do the Stats.  The existing design was chosen to avoid allocating a
-// throwaway names array, but the issue should be revisited once we have
-// a better handle on what that overhead is with a strong garbage collector.
-// Also, it's possible given the nature of the Unix kernel that interleaving
-// reads of the directory with stats (as done here) would work better than
-// one big read of the directory followed by a long run of Stat calls.
-
-// Negative count means read until EOF.
-func Readdir(fd *FD, count int) (dirs []Dir, err *os.Error) {
-       dirname := fd.name;
-       if dirname == "" {
-               dirname = ".";
-       }
-       dirname += "/";
-       // The buffer must be at least a block long.
-       // TODO(r): use fstatfs to find fs block size.
-       var buf = make([]syscall.Dirent, 8192/unsafe.Sizeof(*new(syscall.Dirent)));
-       dirs = make([]Dir, 0, 100);     // TODO: could be smarter about size
-       for {
-               if count == 0 {
-                       break
-               }
-               ret, err2 := syscall.Getdents(fd.fd, &buf[0], int64(len(buf) * unsafe.Sizeof(buf[0])));
-               if ret < 0 || err2 != 0 {
-                       return dirs, os.ErrnoToError(err2)
-               }
-               if ret == 0 {
-                       break
-               }
-               for w, i := uintptr(0),uintptr(0); i < uintptr(ret); i += w {
-                       if count == 0 {
-                               break
-                       }
-                       dirent := unsafe.Pointer((uintptr(unsafe.Pointer(&buf[0])) + i)).(*syscall.Dirent);
-                       w = uintptr(dirent.Reclen);
-                       if dirent.Ino == 0 {
-                               continue
-                       }
-                       count--;
-                       if len(dirs) == cap(dirs) {
-                               ndirs := make([]Dir, len(dirs), 2*len(dirs));
-                               for i := 0; i < len(dirs); i++ {
-                                       ndirs[i] = dirs[i]
-                               }
-                               dirs = ndirs;
-                       }
-                       dirs = dirs[0:len(dirs)+1];
-                       filename := string(dirent.Name[0:clen(dirent.Name)]);
-                       dirp, err := Stat(dirname + filename);
-                       if dirp ==  nil || err != nil {
-                               dirs[len(dirs)-1].Name = filename;      // rest will be zeroed out
-                       } else {
-                               dirs[len(dirs)-1] = *dirp;
-                       }
-               }
-       }
-       return dirs, nil;
-}
index cd924bd205a36d6841732ed6cf7c37a6c228229f..2a4bc6723d7631626c81442e8a439d5de6a29092 100644 (file)
@@ -160,3 +160,30 @@ func Lstat(name string) (dir *Dir, err *Error) {
        }
        return dirFromStat(name, new(Dir), stat), nil
 }
+
+// Non-portable function defined in operating-system-dependent file.
+func Readdirnames(fd *FD, count int) (names []string, err *os.Error)
+
+// Negative count means read until EOF.
+func Readdir(fd *FD, count int) (dirs []Dir, err *os.Error) {
+       dirname := fd.name;
+       if dirname == "" {
+               dirname = ".";
+       }
+       dirname += "/";
+       names, err1 := Readdirnames(fd, count);
+       if err1 != nil {
+               return nil, err1
+       }
+       dirs = make([]Dir, len(names));
+       for i, filename := range names {
+               dirp, err := Stat(dirname + filename);
+               if dirp ==  nil || err != nil {
+                       dirs[i].Name = filename // rest is already zeroed out
+               } else {
+                       dirs[i] = *dirp
+               }
+       }
+       return
+}
+