123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- // +build darwin freebsd linux netbsd openbsd
- package godirwalk
- import (
- "os"
- "path/filepath"
- "syscall"
- "unsafe"
- "github.com/pkg/errors"
- )
- func readdirents(osDirname string, scratchBuffer []byte) (Dirents, error) {
- dh, err := os.Open(osDirname)
- if err != nil {
- return nil, errors.Wrap(err, "cannot Open")
- }
- var entries Dirents
- fd := int(dh.Fd())
- if len(scratchBuffer) < MinimumScratchBufferSize {
- scratchBuffer = make([]byte, DefaultScratchBufferSize)
- }
- var de *syscall.Dirent
- for {
- n, err := syscall.ReadDirent(fd, scratchBuffer)
- if err != nil {
- _ = dh.Close() // ignore potential error returned by Close
- return nil, errors.Wrap(err, "cannot ReadDirent")
- }
- if n <= 0 {
- break // end of directory reached
- }
- // Loop over the bytes returned by reading the directory entries.
- buf := scratchBuffer[:n]
- for len(buf) > 0 {
- de = (*syscall.Dirent)(unsafe.Pointer(&buf[0])) // point entry to first syscall.Dirent in buffer
- buf = buf[de.Reclen:] // advance buffer
- if inoFromDirent(de) == 0 {
- continue // this item has been deleted, but not yet removed from directory
- }
- nameSlice := nameFromDirent(de)
- namlen := len(nameSlice)
- if (namlen == 0) || (namlen == 1 && nameSlice[0] == '.') || (namlen == 2 && nameSlice[0] == '.' && nameSlice[1] == '.') {
- continue // skip unimportant entries
- }
- osChildname := string(nameSlice)
- // Convert syscall constant, which is in purview of OS, to a
- // constant defined by Go, assumed by this project to be stable.
- var mode os.FileMode
- switch de.Type {
- case syscall.DT_REG:
- // regular file
- case syscall.DT_DIR:
- mode = os.ModeDir
- case syscall.DT_LNK:
- mode = os.ModeSymlink
- case syscall.DT_CHR:
- mode = os.ModeDevice | os.ModeCharDevice
- case syscall.DT_BLK:
- mode = os.ModeDevice
- case syscall.DT_FIFO:
- mode = os.ModeNamedPipe
- case syscall.DT_SOCK:
- mode = os.ModeSocket
- default:
- // If syscall returned unknown type (e.g., DT_UNKNOWN, DT_WHT),
- // then resolve actual mode by getting stat.
- fi, err := os.Lstat(filepath.Join(osDirname, osChildname))
- if err != nil {
- _ = dh.Close() // ignore potential error returned by Close
- return nil, errors.Wrap(err, "cannot Stat")
- }
- // We only care about the bits that identify the type of a file
- // system node, and can ignore append, exclusive, temporary,
- // setuid, setgid, permission bits, and sticky bits, which are
- // coincident to the bits that declare type of the file system
- // node.
- mode = fi.Mode() & os.ModeType
- }
- entries = append(entries, &Dirent{name: osChildname, modeType: mode})
- }
- }
- if err = dh.Close(); err != nil {
- return nil, err
- }
- return entries, nil
- }
- func readdirnames(osDirname string, scratchBuffer []byte) ([]string, error) {
- des, err := readdirents(osDirname, scratchBuffer)
- if err != nil {
- return nil, err
- }
- names := make([]string, len(des))
- for i, v := range des {
- names[i] = v.name
- }
- return names, nil
- }
|