github.com/charlievieth/fastwalk@v1.0.3/fastwalk_getdirentries_darwin.go (about)

     1  //go:build darwin && go1.13 && !appengine && !nogetdirentries
     2  // +build darwin,go1.13,!appengine,!nogetdirentries
     3  
     4  package fastwalk
     5  
     6  import (
     7  	"io/fs"
     8  	"os"
     9  	"sync"
    10  	"syscall"
    11  	"unsafe"
    12  )
    13  
    14  const direntBufSize = 32 * 1024
    15  
    16  var direntBufPool = sync.Pool{
    17  	New: func() interface{} {
    18  		b := make([]byte, direntBufSize)
    19  		return &b
    20  	},
    21  }
    22  
    23  func readDir(dirName string, fn func(dirName, entName string, de fs.DirEntry) error) error {
    24  	fd, err := syscall.Open(dirName, syscall.O_RDONLY, 0)
    25  	if err != nil {
    26  		return &os.PathError{Op: "open", Path: dirName, Err: err}
    27  	}
    28  	defer syscall.Close(fd)
    29  
    30  	p := direntBufPool.Get().(*[]byte)
    31  	defer direntBufPool.Put(p)
    32  	dbuf := *p
    33  
    34  	var skipFiles bool
    35  	var basep uintptr
    36  	for {
    37  		length, err := getdirentries(fd, dbuf, &basep)
    38  		if err != nil {
    39  			return &os.PathError{Op: "getdirentries64", Path: dirName, Err: err}
    40  		}
    41  		if length == 0 {
    42  			break
    43  		}
    44  		buf := dbuf[:length]
    45  
    46  		for i := 0; len(buf) > 0; i++ {
    47  			reclen, ok := direntReclen(buf)
    48  			if !ok || reclen > uint64(len(buf)) {
    49  				break
    50  			}
    51  			rec := buf[:reclen]
    52  			buf = buf[reclen:]
    53  			typ := direntType(rec)
    54  			if skipFiles && typ.IsRegular() {
    55  				continue
    56  			}
    57  			const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
    58  			namlen, ok := direntNamlen(rec)
    59  			if !ok || namoff+namlen > uint64(len(rec)) {
    60  				break
    61  			}
    62  			name := rec[namoff : namoff+namlen]
    63  			for i, c := range name {
    64  				if c == 0 {
    65  					name = name[:i]
    66  					break
    67  				}
    68  			}
    69  			if string(name) == "." || string(name) == ".." {
    70  				continue
    71  			}
    72  			nm := string(name)
    73  			if err := fn(dirName, nm, newUnixDirent(dirName, nm, typ)); err != nil {
    74  				if err != ErrSkipFiles {
    75  					return err
    76  				}
    77  				skipFiles = true
    78  			}
    79  		}
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  // readInt returns the size-bytes unsigned integer in native byte order at offset off.
    86  func readInt(b []byte, off, size uintptr) (uint64, bool) {
    87  	if len(b) >= int(off+size) {
    88  		p := b[off:]
    89  		_ = p[1] // bounds check hint to compiler; see golang.org/issue/14808
    90  		return uint64(p[0]) | uint64(p[1])<<8, true
    91  	}
    92  	return 0, false
    93  }
    94  
    95  // Statically assert that the size of Reclen and Namlen is 2.
    96  var _ = ([2]int{})[unsafe.Sizeof(syscall.Dirent{}.Reclen)-1]
    97  var _ = ([2]int{})[unsafe.Sizeof(syscall.Dirent{}.Namlen)-1]
    98  
    99  func direntReclen(buf []byte) (uint64, bool) {
   100  	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
   101  }
   102  
   103  func direntNamlen(buf []byte) (uint64, bool) {
   104  	return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Namlen), unsafe.Sizeof(syscall.Dirent{}.Namlen))
   105  }
   106  
   107  func direntType(buf []byte) os.FileMode {
   108  	off := unsafe.Offsetof(syscall.Dirent{}.Type)
   109  	if off >= uintptr(len(buf)) {
   110  		return ^os.FileMode(0) // unknown
   111  	}
   112  	typ := buf[off]
   113  	switch typ {
   114  	case syscall.DT_BLK:
   115  		return os.ModeDevice
   116  	case syscall.DT_CHR:
   117  		return os.ModeDevice | os.ModeCharDevice
   118  	case syscall.DT_DIR:
   119  		return os.ModeDir
   120  	case syscall.DT_FIFO:
   121  		return os.ModeNamedPipe
   122  	case syscall.DT_LNK:
   123  		return os.ModeSymlink
   124  	case syscall.DT_REG:
   125  		return 0
   126  	case syscall.DT_SOCK:
   127  		return os.ModeSocket
   128  	}
   129  	return ^os.FileMode(0)
   130  }