]> Cypherpunks repositories - gostls13.git/commitdiff
os: emulate plan 9 libc in stat
authorDavid du Colombier <0intro@gmail.com>
Sun, 25 Jan 2015 01:19:39 +0000 (02:19 +0100)
committerDavid du Colombier <0intro@gmail.com>
Mon, 26 Jan 2015 06:34:07 +0000 (06:34 +0000)
This change is a recreation of the CL written
by Nick Owens on http://golang.org/cl/150730043.

If the stat buffer is too short, the kernel
informs us by putting the 2-byte size in the
buffer, so we read that and try again.

This follows the same algorithm as /sys/src/libc/9sys/dirfstat.c.

Fixes #8781.

Change-Id: I01b4ad3a5e705dd4cab6673c7a119f8bef9bbd7c
Reviewed-on: https://go-review.googlesource.com/3281
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/os/stat_plan9.go

index 25c9a8c14b3ddc61352f18c28fe9afce6557bd20..57227876f1c6fe7777e81f73c7ac120d1421d12e 100644 (file)
@@ -9,6 +9,8 @@ import (
        "time"
 )
 
+const _BIT16SZ = 2
+
 func sameFile(fs1, fs2 *fileStat) bool {
        a := fs1.sys.(*syscall.Dir)
        b := fs2.sys.(*syscall.Dir)
@@ -41,16 +43,14 @@ func fileInfoFromStat(d *syscall.Dir) FileInfo {
 // arg is an open *File or a path string.
 func dirstat(arg interface{}) (*syscall.Dir, error) {
        var name string
+       var err error
 
-       // This is big enough for most stat messages
-       // and rounded to a multiple of 128 bytes.
-       size := (syscall.STATFIXLEN + 16*4 + 128) &^ 128
+       size := syscall.STATFIXLEN + 16*4
 
        for i := 0; i < 2; i++ {
-               buf := make([]byte, size)
+               buf := make([]byte, _BIT16SZ+size)
 
                var n int
-               var err error
                switch a := arg.(type) {
                case *File:
                        name = a.name
@@ -61,10 +61,8 @@ func dirstat(arg interface{}) (*syscall.Dir, error) {
                default:
                        panic("phase error in dirstat")
                }
-               if err != nil {
-                       return nil, &PathError{"stat", name, err}
-               }
-               if n < syscall.STATFIXLEN {
+
+               if n < _BIT16SZ {
                        return nil, &PathError{"stat", name, syscall.ErrShortStat}
                }
 
@@ -73,17 +71,21 @@ func dirstat(arg interface{}) (*syscall.Dir, error) {
 
                // If the stat message is larger than our buffer we will
                // go around the loop and allocate one that is big enough.
-               if size > n {
-                       continue
+               if size <= n {
+                       d, err := syscall.UnmarshalDir(buf[:n])
+                       if err != nil {
+                               return nil, &PathError{"stat", name, err}
+                       }
+                       return d, nil
                }
 
-               d, err := syscall.UnmarshalDir(buf[:n])
-               if err != nil {
-                       return nil, &PathError{"stat", name, err}
-               }
-               return d, nil
        }
-       return nil, &PathError{"stat", name, syscall.ErrBadStat}
+
+       if err == nil {
+               err = syscall.ErrBadStat
+       }
+
+       return nil, &PathError{"stat", name, err}
 }
 
 // Stat returns a FileInfo describing the named file.