Commit 2a74f436 authored by David du Colombier's avatar David du Colombier

os: emulate plan 9 libc in stat

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/3281Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent e24e299f
...@@ -9,6 +9,8 @@ import ( ...@@ -9,6 +9,8 @@ import (
"time" "time"
) )
const _BIT16SZ = 2
func sameFile(fs1, fs2 *fileStat) bool { func sameFile(fs1, fs2 *fileStat) bool {
a := fs1.sys.(*syscall.Dir) a := fs1.sys.(*syscall.Dir)
b := fs2.sys.(*syscall.Dir) b := fs2.sys.(*syscall.Dir)
...@@ -41,16 +43,14 @@ func fileInfoFromStat(d *syscall.Dir) FileInfo { ...@@ -41,16 +43,14 @@ func fileInfoFromStat(d *syscall.Dir) FileInfo {
// arg is an open *File or a path string. // arg is an open *File or a path string.
func dirstat(arg interface{}) (*syscall.Dir, error) { func dirstat(arg interface{}) (*syscall.Dir, error) {
var name string var name string
var err error
// This is big enough for most stat messages size := syscall.STATFIXLEN + 16*4
// and rounded to a multiple of 128 bytes.
size := (syscall.STATFIXLEN + 16*4 + 128) &^ 128
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
buf := make([]byte, size) buf := make([]byte, _BIT16SZ+size)
var n int var n int
var err error
switch a := arg.(type) { switch a := arg.(type) {
case *File: case *File:
name = a.name name = a.name
...@@ -61,10 +61,8 @@ func dirstat(arg interface{}) (*syscall.Dir, error) { ...@@ -61,10 +61,8 @@ func dirstat(arg interface{}) (*syscall.Dir, error) {
default: default:
panic("phase error in dirstat") panic("phase error in dirstat")
} }
if err != nil {
return nil, &PathError{"stat", name, err} if n < _BIT16SZ {
}
if n < syscall.STATFIXLEN {
return nil, &PathError{"stat", name, syscall.ErrShortStat} return nil, &PathError{"stat", name, syscall.ErrShortStat}
} }
...@@ -73,17 +71,21 @@ func dirstat(arg interface{}) (*syscall.Dir, error) { ...@@ -73,17 +71,21 @@ func dirstat(arg interface{}) (*syscall.Dir, error) {
// If the stat message is larger than our buffer we will // If the stat message is larger than our buffer we will
// go around the loop and allocate one that is big enough. // go around the loop and allocate one that is big enough.
if size > n { if size <= n {
continue 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. // Stat returns a FileInfo describing the named file.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment