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 }