func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error { d, err := os.Open(path) if err != nil { return err } defer d.Close() fd := int(d.Fd()) buf := make([]byte, 4096) for { nbytes, err := syscall.ReadDirent(fd, buf) if err != nil { return err } if nbytes == 0 { break } for off := 0; off < nbytes; { ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off])) if stop := fn(ent); stop { return nil } off += int(ent.Reclen) } } return nil }
// Return all the entries at the directory dirPath. func readDir(dirPath string) (entries []string, err error) { buf := make([]byte, readDirentBufSize) d, err := os.Open(dirPath) if err != nil { // File is really not found. if os.IsNotExist(err) { return nil, errFileNotFound } // File path cannot be verified since one of the parents is a file. if strings.Contains(err.Error(), "not a directory") { return nil, errFileNotFound } return nil, err } defer d.Close() fd := int(d.Fd()) for { nbuf, err := syscall.ReadDirent(fd, buf) if err != nil { return nil, err } if nbuf <= 0 { break } var tmpEntries []string if tmpEntries, err = parseDirents(dirPath, buf[:nbuf]); err != nil { return nil, err } entries = append(entries, tmpEntries...) } return }
func Readdir(f *os.File, n int) (dirents []*Dirent, err error) { d := new(dirInfo) d.buf = make([]byte, 4096) if n <= 0 { n = 100 } fd := int(f.Fd()) dirents = make([]*Dirent, 0, n) // Empty with room to grow. for { // Refill the buffer if necessary if d.bufp >= d.nbuf { d.bufp = 0 var errno error d.nbuf, errno = syscall.ReadDirent(fd, d.buf) if errno != nil { return dirents, os.NewSyscallError("readdirent", errno) } if d.nbuf <= 0 { break // EOF } } // Drain the buffer var nb int nb, _, dirents = ParseDirent(d.buf[d.bufp:d.nbuf], -1, dirents) d.bufp += nb } return dirents, nil }
// Read through the directory calling ParseDir() to write to the DirentBuf // specified by 'db' // Returns error EOF when the directory has been walked; returns "readdirent" // error if unable to perform a ReadDirent() syscall // It's a modified version of (os) readdirnames() in Go's standard library func ReadDir(fd int, n int64, db *[]syscall.Dirent) error { d := new(dirInfo) d.buf = make([]byte, 4096) size := n if size <= 0 { size = 100 n = -1 } for n != 0 { // Refill the buffer if necessary if d.bufp >= d.nbuf { d.bufp = 0 var errno error d.nbuf, errno = general.FixCount(syscall.ReadDirent(fd, d.buf)) if errno != nil { return os.NewSyscallError("readdirent", errno) } if d.nbuf <= 0 { break // EOF } } // Drain the buffer var nb, nc int nb, nc = ParseDir(d.buf[d.bufp:d.nbuf], n, db) d.bufp += nb n -= int64(nc) } if n >= 0 { return io.EOF } return nil }
func ext۰syscall۰ReadDirent(fr *Frame, args []Value) Value { // func ReadDirent(fd int, buf []byte) (n int, err error) fd := args[0].(int) p := args[1].([]Value) b := make([]byte, len(p)) n, err := syscall.ReadDirent(fd, b) for i := 0; i < n; i++ { p[i] = b[i] } return tuple{n, wrapError(err)} }
// PathSupportsOverlay checks whether the given path is compatible with OverlayFS. // This method also calls SupportsOverlay(). // // It returns an instance of ErrOverlayUnsupported if OverlayFS is not supported // or any other error if determining overlay support failed. func PathSupportsOverlay(path string) error { if err := SupportsOverlay(); err != nil { // don't wrap since SupportsOverlay already returns ErrOverlayUnsupported return err } var data syscall.Statfs_t if err := syscall.Statfs(path, &data); err != nil { return errwrap.Wrap(fmt.Errorf("cannot statfs %q", path), err) } switch data.Type { case FsMagicAUFS: return ErrOverlayUnsupported("unsupported filesystem: aufs") case FsMagicZFS: return ErrOverlayUnsupported("unsupported filesystem: zfs") } dir, err := os.OpenFile(path, syscall.O_RDONLY|syscall.O_DIRECTORY, 0755) if err != nil { return errwrap.Wrap(fmt.Errorf("cannot open %q", dir), err) } defer dir.Close() buf := make([]byte, 4096) // ReadDirent forwards to the raw syscall getdents(3), // passing the buffer size. n, err := syscall.ReadDirent(int(dir.Fd()), buf) if err != nil { return errwrap.Wrap(fmt.Errorf("cannot read directory %q", dir), err) } offset := 0 for offset < n { // offset overflow cannot happen, because Reclen // is being maintained by getdents(3), considering the buffer size. dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[offset])) offset += int(dirent.Reclen) if dirent.Ino == 0 { // File absent in directory. continue } if dirent.Type == syscall.DT_UNKNOWN { return ErrOverlayUnsupported("unsupported filesystem: missing d_type support") } } return nil }
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { fd, err := syscall.Open(dirName, 0, 0) if err != nil { return err } defer syscall.Close(fd) // The buffer must be at least a block long. buf := make([]byte, blockSize) // stack-allocated; doesn't escape bufp := 0 // starting read position in buf nbuf := 0 // end valid data in buf for { if bufp >= nbuf { bufp = 0 nbuf, err = syscall.ReadDirent(fd, buf) if err != nil { return os.NewSyscallError("readdirent", err) } if nbuf <= 0 { return nil } } consumed, name, typ := parseDirEnt(buf[bufp:nbuf]) bufp += consumed if name == "" || name == "." || name == ".." { continue } // Fallback for filesystems (like old XFS) that don't // support Dirent.Type and have DT_UNKNOWN (0) there // instead. if typ == unknownFileMode { fi, err := os.Lstat(dirName + "/" + name) if err != nil { // It got deleted in the meantime. if os.IsNotExist(err) { continue } return err } typ = fi.Mode() & os.ModeType } if err := fn(dirName, name, typ); err != nil { return err } } }
func readDir(dir string) []string { fd, err := syscall.Open(dir, syscall.O_CLOEXEC, 0644) if err != nil { return []string{} } defer syscall.Close(fd) var size = 100 var n = -1 var nbuf int var bufp int var buf = make([]byte, 4096) var names = make([]string, 0, size) for n != 0 { if bufp >= nbuf { bufp = 0 var errno error nbuf, errno = fixCount(syscall.ReadDirent(fd, buf)) if errno != nil { return names } if nbuf <= 0 { break } } var nb, nc int nb, nc, names = syscall.ParseDirent(buf[bufp:nbuf], n, names) bufp += nb n -= nc } return names }
// readdirnames is a hacked-apart version of the Go stdlib code, exposing inode // numbers further up the stack when reading directory contents. Unlike // os.Readdirnames, which returns a list of filenames, this function returns a // list of {filename,inode} pairs. func readdirnames(dirname string) (names []nameIno, err error) { var ( size = 100 buf = make([]byte, 4096) nbuf int bufp int nb int ) f, err := os.Open(dirname) if err != nil { return nil, err } defer f.Close() names = make([]nameIno, 0, size) // Empty with room to grow. for { // Refill the buffer if necessary if bufp >= nbuf { bufp = 0 nbuf, err = syscall.ReadDirent(int(f.Fd()), buf) // getdents on linux if nbuf < 0 { nbuf = 0 } if err != nil { return nil, os.NewSyscallError("readdirent", err) } if nbuf <= 0 { break // EOF } } // Drain the buffer nb, names = parseDirent(buf[bufp:nbuf], names) bufp += nb } sl := nameInoSlice(names) sort.Sort(sl) return sl, nil }
func (f *File) readdirnames(n int) (names []string, err error) { // If this file has no dirinfo, create one. if f.dirinfo == nil { f.dirinfo = new(dirInfo) // The buffer must be at least a block long. f.dirinfo.buf = make([]byte, blockSize) } d := f.dirinfo size := n if size <= 0 { size = 100 n = -1 } names = make([]string, 0, size) // Empty with room to grow. for n != 0 { // Refill the buffer if necessary if d.bufp >= d.nbuf { d.bufp = 0 var errno error d.nbuf, errno = syscall.ReadDirent(f.fd, d.buf) if errno != nil { return names, NewSyscallError("readdirent", errno) } if d.nbuf <= 0 { break // EOF } } // Drain the buffer var nb, nc int nb, nc, names = syscall.ParseDirent(d.buf[d.bufp:d.nbuf], n, names) d.bufp += nb n -= nc } if n >= 0 && len(names) == 0 { return names, io.EOF } return names, nil }
// IsEmptyDir check if directory empty or not func IsEmptyDir(path string) bool { if path == "" { return false } path = PATH.Clean(path) fd, err := syscall.Open(path, syscall.O_RDONLY, 0) if err != nil { return false } defer syscall.Close(fd) n, err := syscall.ReadDirent(fd, make([]byte, 4096)) if n == 0x30 || err != nil { return true } return false }
// This test is inspired on xfstest, t_dir_offset2.c func TestReaddirPlusSeek(t *testing.T) { tc := NewTestCase(t) defer tc.Cleanup() var names []string for i := 0; i < 20; i++ { names = append(names, fmt.Sprintf("abcd%d", i)) } for _, n := range names { if err := os.MkdirAll(filepath.Join(tc.origSubdir, n), 0755); err != nil { t.Fatalf("Mkdir failed: %v", err) } } fd, err := syscall.Open(tc.mountSubdir, syscall.O_RDONLY, 0755) if err != nil { t.Fatalf("Open(%q): %v", tc.mountSubdir, err) } defer syscall.Close(int(fd)) // store offsets type entryOff struct { ino uint64 off int64 } previous := map[int]entryOff{} var bufdata [1024]byte for { buf := bufdata[:] n, err := syscall.ReadDirent(fd, buf) if err != nil { t.Fatalf("ReadDirent: %v", err) } if n == 0 { break } buf = buf[:n] for _, d := range parseDirents(buf) { if d.name == "." || d.name == ".." { continue } i := len(previous) previous[i] = entryOff{d.ino, d.off} } } for i := len(previous) - 1; i >= 0; i-- { var off int64 if i > 0 { off = previous[i-1].off } if _, err := syscall.Seek(fd, off, 0); err != nil { t.Fatalf("Seek %v", err) } buf := bufdata[:] n, err := syscall.ReadDirent(fd, buf) if err != nil { t.Fatalf("readdir after seek %d: %v", i, err) } if n == 0 { t.Fatalf("no dirent after seek to %d", i) } ds := parseDirents(buf[:n]) if ds[0].ino != previous[i].ino { t.Errorf("got ino %d, want %d", ds[0].ino, previous[i].ino) } } // Delete has a forget as side-effect: make sure we get the lookup counts correct. for _, n := range names { full := filepath.Join(tc.mountSubdir, n) if err := syscall.Rmdir(full); err != nil { t.Fatalf("Rmdir(%q): %v", n, err) } } }
func Walk(_path string, _filter Filter, _collector Collector, _errors Errors) { var _cursor *cursor var _error Error var _errno int _cursor, _error = open(nil, _path) if _error != nil { _errors(_error) } for _cursor != nil { if !_cursor.filtered { _cursor.recurse, _cursor.collect = _filter(_cursor.entity) _cursor.filtered = true } if _cursor.collect { _entity := new(Entity) *_entity = *_cursor.entity if _entity.Descriptor >= 0 { _entity.Descriptor, _errno = syscall.Dup(_entity.Descriptor) } else { _errno = 0 } if _errno == 0 { _collector(_entity) } else { _errors(newError("dup", _cursor.entity.Path, _cursor.entity.Descriptor, _errno)) } _cursor.collect = false } if _cursor.recurse && _cursor.entity.IsDirectory() && _cursor.entity.IsOpen() { if _cursor.dirent == nil { _cursor.dirent = new(dirent) _cursor.dirent.nameBuffer = make([]string, 0, _nameBufferSize) _cursor.dirent.dataBuffer = make([]byte, _dataBufferSize) } if _nameBufferLen := len(_cursor.dirent.nameBuffer); _nameBufferLen > 0 { _childName := _cursor.dirent.nameBuffer[_nameBufferLen-1] _cursor.dirent.nameBuffer[_nameBufferLen-1] = "" _cursor.dirent.nameBuffer = _cursor.dirent.nameBuffer[:_nameBufferLen-1] var _childCursor, _error = open(_cursor, _childName) if _error == nil { _cursor = _childCursor } else { _errors(_error) } continue } if !_cursor.dirent.dataEnded && (_cursor.dirent.dataOffset == _cursor.dirent.dataLimit) { _cursor.dirent.dataOffset = 0 _cursor.dirent.dataLimit, _errno = syscall.ReadDirent(_cursor.entity.Descriptor, _cursor.dirent.dataBuffer[:]) if _errno == 0 { if _cursor.dirent.dataLimit == 0 { _cursor.dirent.dataEnded = true } } else { _errors(newError("readdirent", _cursor.entity.Path, _cursor.entity.Descriptor, _errno)) _cursor.dirent.dataLimit = 0 _cursor.dirent.dataEnded = true } } if !_cursor.dirent.dataEnded { _dataParsed, _, _nameBuffer := syscall.ParseDirent(_cursor.dirent.dataBuffer[_cursor.dirent.dataOffset:_cursor.dirent.dataLimit], cap(_cursor.dirent.nameBuffer), _cursor.dirent.nameBuffer) if _dataParsed == 0 { _errors(newError("parsedirent", _cursor.entity.Path, _cursor.entity.Descriptor, syscall.EINVAL)) _cursor.dirent.dataLimit = 0 _cursor.dirent.dataOffset = 0 _cursor.dirent.dataEnded = true } else { _cursor.dirent.dataOffset += _dataParsed _cursor.dirent.nameBuffer = _nameBuffer continue } } } if _cursor.entity.IsOpen() { _cursor.entity.Close() } _cursor = _cursor.parent } }