func (file *File) readdir(n int) ([]FileInfo, 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 } fi := make([]FileInfo, 0, size) // Empty with room to grow. for n != 0 { // Refill the buffer if necessary. if d.bufp >= d.nbuf { nb, err := file.Read(d.buf[:]) // Update the buffer state before checking for errors. d.bufp, d.nbuf = 0, nb if err != nil { if err == io.EOF { break } return fi, &PathError{"readdir", file.name, err} } if nb < syscall.STATFIXLEN { return fi, &PathError{"readdir", file.name, syscall.ErrShortStat} } } // Get a record from the buffer. b := d.buf[d.bufp:] m := int(uint16(b[0])|uint16(b[1])<<8) + 2 if m < syscall.STATFIXLEN { return fi, &PathError{"readdir", file.name, syscall.ErrShortStat} } dir, err := syscall.UnmarshalDir(b[:m]) if err != nil { return fi, &PathError{"readdir", file.name, err} } fi = append(fi, fileInfoFromStat(dir)) d.bufp += m n-- } if n >= 0 && len(fi) == 0 { return fi, io.EOF } return fi, nil }
// arg is an open *File or a path string. func dirstat(arg interface{}) (*syscall.Dir, error) { var name string var err error size := syscall.STATFIXLEN + 16*4 for i := 0; i < 2; i++ { buf := make([]byte, _BIT16SZ+size) var n int switch a := arg.(type) { case *File: name = a.name n, err = syscall.Fstat(a.fd, buf) case string: name = a n, err = syscall.Stat(a, buf) default: panic("phase error in dirstat") } if n < _BIT16SZ { return nil, &PathError{"stat", name, err} } // Pull the real size out of the stat message. size = int(uint16(buf[0]) | uint16(buf[1])<<8) // 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 { d, err := syscall.UnmarshalDir(buf[:n]) if err != nil { return nil, &PathError{"stat", name, err} } return d, nil } } if err == nil { err = syscall.ErrBadStat } return nil, &PathError{"stat", name, err} }
// arg is an open *File or a path string. func dirstat(arg interface{}) (*syscall.Dir, error) { var name string // This is big enough for most stat messages // and rounded to a multiple of 128 bytes. size := (syscall.STATFIXLEN + 16*4 + 128) &^ 128 for i := 0; i < 2; i++ { buf := make([]byte, size) var n int var err error switch a := arg.(type) { case *File: name = a.name n, err = syscall.Fstat(a.fd, buf) case string: name = a n, err = syscall.Stat(a, buf) default: panic("phase error in dirstat") } if err != nil { return nil, &PathError{"stat", name, err} } if n < syscall.STATFIXLEN { return nil, &PathError{"stat", name, syscall.ErrShortStat} } // Pull the real size out of the stat message. size = int(uint16(buf[0]) | uint16(buf[1])<<8) // 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 } 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} }