2fd401c8f1
From-SVN: r181964
303 lines
7.7 KiB
Go
303 lines
7.7 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package os
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"syscall"
|
|
)
|
|
|
|
// Readdir reads the contents of the directory associated with file and
|
|
// returns an array of up to n FileInfo structures, as would be returned
|
|
// by Lstat, in directory order. Subsequent calls on the same file will yield
|
|
// further FileInfos.
|
|
//
|
|
// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
|
|
// Readdirnames returns an empty slice, it will return a non-nil error
|
|
// explaining why. At the end of a directory, the error is io.EOF.
|
|
//
|
|
// If n <= 0, Readdir returns all the FileInfo from the directory in
|
|
// a single slice. In this case, if Readdir succeeds (reads all
|
|
// the way to the end of the directory), it returns the slice and a
|
|
// nil error. If it encounters an error before the end of the
|
|
// directory, Readdir returns the FileInfo read until that point
|
|
// and a non-nil error.
|
|
func (file *File) Readdir(n int) (fi []FileInfo, err error) {
|
|
// If this file has no dirinfo, create one.
|
|
if file.dirinfo == nil {
|
|
file.dirinfo = new(dirInfo)
|
|
}
|
|
d := file.dirinfo
|
|
size := n
|
|
if size <= 0 {
|
|
size = 100
|
|
n = -1
|
|
}
|
|
result := make([]FileInfo, 0, size) // Empty with room to grow.
|
|
for n != 0 {
|
|
// Refill the buffer if necessary
|
|
if d.bufp >= d.nbuf {
|
|
d.bufp = 0
|
|
var e error
|
|
d.nbuf, e = file.Read(d.buf[:])
|
|
if e != nil && e != io.EOF {
|
|
return result, &PathError{"readdir", file.name, e}
|
|
}
|
|
if e == io.EOF {
|
|
break
|
|
}
|
|
if d.nbuf < syscall.STATFIXLEN {
|
|
return result, &PathError{"readdir", file.name, Eshortstat}
|
|
}
|
|
}
|
|
|
|
// Get a record from buffer
|
|
m, _ := gbit16(d.buf[d.bufp:])
|
|
m += 2
|
|
if m < syscall.STATFIXLEN {
|
|
return result, &PathError{"readdir", file.name, Eshortstat}
|
|
}
|
|
dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
|
|
if e != nil {
|
|
return result, &PathError{"readdir", file.name, e}
|
|
}
|
|
var f FileInfo
|
|
fileInfoFromStat(&f, dir)
|
|
result = append(result, f)
|
|
|
|
d.bufp += int(m)
|
|
n--
|
|
}
|
|
|
|
if n >= 0 && len(result) == 0 {
|
|
return result, io.EOF
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Readdirnames reads and returns a slice of names from the directory f.
|
|
//
|
|
// If n > 0, Readdirnames returns at most n names. In this case, if
|
|
// Readdirnames returns an empty slice, it will return a non-nil error
|
|
// explaining why. At the end of a directory, the error is io.EOF.
|
|
//
|
|
// If n <= 0, Readdirnames returns all the names from the directory in
|
|
// a single slice. In this case, if Readdirnames succeeds (reads all
|
|
// the way to the end of the directory), it returns the slice and a
|
|
// nil error. If it encounters an error before the end of the
|
|
// directory, Readdirnames returns the names read until that point and
|
|
// a non-nil error.
|
|
func (file *File) Readdirnames(n int) (names []string, err error) {
|
|
fi, err := file.Readdir(n)
|
|
names = make([]string, len(fi))
|
|
for i := range fi {
|
|
names[i] = fi[i].Name
|
|
}
|
|
return
|
|
}
|
|
|
|
type Dir struct {
|
|
// system-modified data
|
|
Type uint16 // server type
|
|
Dev uint32 // server subtype
|
|
// file data
|
|
Qid Qid // unique id from server
|
|
Mode uint32 // permissions
|
|
Atime uint32 // last read time
|
|
Mtime uint32 // last write time
|
|
Length uint64 // file length
|
|
Name string // last element of path
|
|
Uid string // owner name
|
|
Gid string // group name
|
|
Muid string // last modifier name
|
|
}
|
|
|
|
type Qid struct {
|
|
Path uint64 // the file server's unique identification for the file
|
|
Vers uint32 // version number for given Path
|
|
Type uint8 // the type of the file (syscall.QTDIR for example)
|
|
}
|
|
|
|
var nullDir = Dir{
|
|
^uint16(0),
|
|
^uint32(0),
|
|
Qid{^uint64(0), ^uint32(0), ^uint8(0)},
|
|
^uint32(0),
|
|
^uint32(0),
|
|
^uint32(0),
|
|
^uint64(0),
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
}
|
|
|
|
// Null assigns members of d with special "don't care" values indicating
|
|
// they should not be written by syscall.Wstat.
|
|
func (d *Dir) Null() {
|
|
*d = nullDir
|
|
}
|
|
|
|
// pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b.
|
|
func pdir(b []byte, d *Dir) []byte {
|
|
n := len(b)
|
|
b = pbit16(b, 0) // length, filled in later
|
|
b = pbit16(b, d.Type)
|
|
b = pbit32(b, d.Dev)
|
|
b = pqid(b, d.Qid)
|
|
b = pbit32(b, d.Mode)
|
|
b = pbit32(b, d.Atime)
|
|
b = pbit32(b, d.Mtime)
|
|
b = pbit64(b, d.Length)
|
|
b = pstring(b, d.Name)
|
|
b = pstring(b, d.Uid)
|
|
b = pstring(b, d.Gid)
|
|
b = pstring(b, d.Muid)
|
|
pbit16(b[0:n], uint16(len(b)-(n+2)))
|
|
return b
|
|
}
|
|
|
|
// UnmarshalDir reads a 9P Stat message from a 9P protocol message stored in b,
|
|
// returning the corresponding Dir struct.
|
|
func UnmarshalDir(b []byte) (d *Dir, err error) {
|
|
n := uint16(0)
|
|
n, b = gbit16(b)
|
|
|
|
if int(n) != len(b) {
|
|
return nil, Ebadstat
|
|
}
|
|
|
|
d = new(Dir)
|
|
d.Type, b = gbit16(b)
|
|
d.Dev, b = gbit32(b)
|
|
d.Qid, b = gqid(b)
|
|
d.Mode, b = gbit32(b)
|
|
d.Atime, b = gbit32(b)
|
|
d.Mtime, b = gbit32(b)
|
|
d.Length, b = gbit64(b)
|
|
d.Name, b = gstring(b)
|
|
d.Uid, b = gstring(b)
|
|
d.Gid, b = gstring(b)
|
|
d.Muid, b = gstring(b)
|
|
|
|
if len(b) != 0 {
|
|
return nil, Ebadstat
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
// gqid reads the qid part of a 9P Stat message from a 9P protocol message stored in b,
|
|
// returning the corresponding Qid struct and the remaining slice of b.
|
|
func gqid(b []byte) (Qid, []byte) {
|
|
var q Qid
|
|
q.Path, b = gbit64(b)
|
|
q.Vers, b = gbit32(b)
|
|
q.Type, b = gbit8(b)
|
|
return q, b
|
|
}
|
|
|
|
// pqid appends a Qid struct q to a 9P message b.
|
|
func pqid(b []byte, q Qid) []byte {
|
|
b = pbit64(b, q.Path)
|
|
b = pbit32(b, q.Vers)
|
|
b = pbit8(b, q.Type)
|
|
return b
|
|
}
|
|
|
|
// gbit8 reads a byte-sized numeric value from a 9P protocol message stored in b,
|
|
// returning the value and the remaining slice of b.
|
|
func gbit8(b []byte) (uint8, []byte) {
|
|
return uint8(b[0]), b[1:]
|
|
}
|
|
|
|
// gbit16 reads a 16-bit numeric value from a 9P protocol message stored in b,
|
|
// returning the value and the remaining slice of b.
|
|
func gbit16(b []byte) (uint16, []byte) {
|
|
return uint16(b[0]) | uint16(b[1])<<8, b[2:]
|
|
}
|
|
|
|
// gbit32 reads a 32-bit numeric value from a 9P protocol message stored in b,
|
|
// returning the value and the remaining slice of b.
|
|
func gbit32(b []byte) (uint32, []byte) {
|
|
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
|
|
}
|
|
|
|
// gbit64 reads a 64-bit numeric value from a 9P protocol message stored in b,
|
|
// returning the value and the remaining slice of b.
|
|
func gbit64(b []byte) (uint64, []byte) {
|
|
lo, b := gbit32(b)
|
|
hi, b := gbit32(b)
|
|
return uint64(hi)<<32 | uint64(lo), b
|
|
}
|
|
|
|
// gstring reads a string from a 9P protocol message stored in b,
|
|
// returning the value as a Go string and the remaining slice of b.
|
|
func gstring(b []byte) (string, []byte) {
|
|
n, b := gbit16(b)
|
|
return string(b[0:n]), b[n:]
|
|
}
|
|
|
|
// pbit8 appends a byte-sized numeric value x to a 9P message b.
|
|
func pbit8(b []byte, x uint8) []byte {
|
|
n := len(b)
|
|
if n+1 > cap(b) {
|
|
nb := make([]byte, n, 100+2*cap(b))
|
|
copy(nb, b)
|
|
b = nb
|
|
}
|
|
b = b[0 : n+1]
|
|
b[n] = x
|
|
return b
|
|
}
|
|
|
|
// pbit16 appends a 16-bit numeric value x to a 9P message b.
|
|
func pbit16(b []byte, x uint16) []byte {
|
|
n := len(b)
|
|
if n+2 > cap(b) {
|
|
nb := make([]byte, n, 100+2*cap(b))
|
|
copy(nb, b)
|
|
b = nb
|
|
}
|
|
b = b[0 : n+2]
|
|
b[n] = byte(x)
|
|
b[n+1] = byte(x >> 8)
|
|
return b
|
|
}
|
|
|
|
// pbit32 appends a 32-bit numeric value x to a 9P message b.
|
|
func pbit32(b []byte, x uint32) []byte {
|
|
n := len(b)
|
|
if n+4 > cap(b) {
|
|
nb := make([]byte, n, 100+2*cap(b))
|
|
copy(nb, b)
|
|
b = nb
|
|
}
|
|
b = b[0 : n+4]
|
|
b[n] = byte(x)
|
|
b[n+1] = byte(x >> 8)
|
|
b[n+2] = byte(x >> 16)
|
|
b[n+3] = byte(x >> 24)
|
|
return b
|
|
}
|
|
|
|
// pbit64 appends a 64-bit numeric value x to a 9P message b.
|
|
func pbit64(b []byte, x uint64) []byte {
|
|
b = pbit32(b, uint32(x))
|
|
b = pbit32(b, uint32(x>>32))
|
|
return b
|
|
}
|
|
|
|
// pstring appends a Go string s to a 9P message b.
|
|
func pstring(b []byte, s string) []byte {
|
|
if len(s) >= 1<<16 {
|
|
panic(errors.New("string too long"))
|
|
}
|
|
b = pbit16(b, uint16(len(s)))
|
|
b = append(b, s...)
|
|
return b
|
|
}
|