os: fix Readdir in Plan 9

'TestReaddir.*' tests now passes.

R=golang-dev, lucio, r
CC=golang-dev
https://golang.org/cl/4381048
This commit is contained in:
Fazlul Shahriar 2011-04-12 16:58:56 -07:00 committed by Rob Pike
parent 36b6d1aaf2
commit 08b0927771
4 changed files with 57 additions and 49 deletions

View File

@ -8,72 +8,56 @@ import (
"syscall" "syscall"
) )
type dirInfo int
var markDirectory dirInfo = ^0
// Readdir reads the contents of the directory associated with file and // Readdir reads the contents of the directory associated with file and
// returns an array of up to count FileInfo structures, as would be returned // returns an array of up to count FileInfo structures, in directory order.
// by Lstat, in directory order. Subsequent calls on the same file will yield // Subsequent calls on the same file will yield further FileInfos.
// further FileInfos. A negative count means to read the entire directory. // A negative count means to read until EOF.
// Readdir returns the array and an Error, if any. // Readdir returns the array and an Error, if any.
func (file *File) Readdir(count int) (fi []FileInfo, err Error) { func (file *File) Readdir(count int) (fi []FileInfo, err Error) {
// If this file has no dirinfo, create one. // If this file has no dirinfo, create one.
if file.dirinfo == nil { if file.dirinfo == nil {
file.dirinfo = &markDirectory file.dirinfo = new(dirInfo)
} }
d := file.dirinfo
size := count size := count
if size < 0 { if size < 0 {
size = 100 size = 100
} }
result := make([]FileInfo, 0, size) // Empty with room to grow.
result := make([]FileInfo, 0, size) for count != 0 {
var buf [syscall.STATMAX]byte // Refill the buffer if necessary
if d.bufp >= d.nbuf {
for { d.bufp = 0
n, e := file.Read(buf[:]) var e Error
d.nbuf, e = file.Read(d.buf[:])
if e != nil { if e != nil && e != EOF {
return nil, &PathError{"readdir", file.name, e}
}
if e == EOF { if e == EOF {
break break
} }
if d.nbuf < syscall.STATFIXLEN {
return []FileInfo{}, &PathError{"readdir", file.name, e} return nil, &PathError{"readdir", file.name, Eshortstat}
}
} }
if n < syscall.STATFIXLEN { // Get a record from buffer
return []FileInfo{}, &PathError{"readdir", file.name, Eshortstat} m, _ := gbit16(d.buf[d.bufp:])
m += 2
if m < syscall.STATFIXLEN {
return nil, &PathError{"readdir", file.name, Eshortstat}
} }
dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
for i := 0; i < n; { if e != nil {
m, _ := gbit16(buf[i:]) return nil, &PathError{"readdir", file.name, e}
m += 2
if m < syscall.STATFIXLEN {
return []FileInfo{}, &PathError{"readdir", file.name, Eshortstat}
}
d, e := UnmarshalDir(buf[i : i+int(m)])
if e != nil {
return []FileInfo{}, &PathError{"readdir", file.name, e}
}
var f FileInfo
fileInfoFromStat(&f, d)
result = append(result, f)
// a negative count means to read until EOF.
if count > 0 && len(result) >= count {
break
}
i += int(m)
} }
var f FileInfo
fileInfoFromStat(&f, dir)
result = append(result, f)
d.bufp += int(m)
count--
} }
return result, nil return result, nil
} }

View File

@ -37,12 +37,15 @@ var (
Enonexist = NewError("file does not exist") Enonexist = NewError("file does not exist")
Eexist = NewError("file already exists") Eexist = NewError("file already exists")
Eio = NewError("i/o error") Eio = NewError("i/o error")
Eperm = NewError("permission denied")
EINVAL = Ebadarg EINVAL = Ebadarg
ENOTDIR = Enotdir ENOTDIR = Enotdir
ENOENT = Enonexist ENOENT = Enonexist
EEXIST = Eexist EEXIST = Eexist
EIO = Eio EIO = Eio
EACCES = Eperm
EISDIR = syscall.EISDIR
ENAMETOOLONG = NewError("file name too long") ENAMETOOLONG = NewError("file name too long")
ERANGE = NewError("math result not representable") ERANGE = NewError("math result not representable")

View File

@ -9,6 +9,13 @@ import (
"syscall" "syscall"
) )
// Auxiliary information if the File describes a directory
type dirInfo struct {
buf [syscall.STATMAX]byte // buffer for directory I/O
nbuf int // length of buf; return value from Read
bufp int // location of next record in buf.
}
func epipecheck(file *File, e syscall.Error) { func epipecheck(file *File, e syscall.Error) {
} }

View File

@ -45,6 +45,14 @@ var sysdir = func() (sd *sysDir) {
"services", "services",
}, },
} }
case "plan9":
sd = &sysDir{
"/lib/ndb",
[]string{
"common",
"local",
},
}
default: default:
sd = &sysDir{ sd = &sysDir{
"/etc", "/etc",
@ -245,8 +253,11 @@ func smallReaddirnames(file *File, length int, t *testing.T) []string {
func TestReaddirnamesOneAtATime(t *testing.T) { func TestReaddirnamesOneAtATime(t *testing.T) {
// big directory that doesn't change often. // big directory that doesn't change often.
dir := "/usr/bin" dir := "/usr/bin"
if syscall.OS == "windows" { switch syscall.OS {
case "windows":
dir = Getenv("SystemRoot") + "\\system32" dir = Getenv("SystemRoot") + "\\system32"
case "plan9":
dir = "/bin"
} }
file, err := Open(dir) file, err := Open(dir)
defer file.Close() defer file.Close()
@ -262,6 +273,9 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
t.Fatalf("open %q failed: %v", dir, err2) t.Fatalf("open %q failed: %v", dir, err2)
} }
small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up
if len(small) < len(all) {
t.Fatalf("len(small) is %d, less than %d", len(small), len(all))
}
for i, n := range all { for i, n := range all {
if small[i] != n { if small[i] != n {
t.Errorf("small read %q mismatch: %v", small[i], n) t.Errorf("small read %q mismatch: %v", small[i], n)