github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/fastwalk/fastwalk_darwin.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build darwin && cgo 6 // +build darwin,cgo 7 8 package fastwalk 9 10 /* 11 #include <dirent.h> 12 13 // fastwalk_readdir_r wraps readdir_r so that we don't have to pass a dirent** 14 // result pointer which triggers CGO's "Go pointer to Go pointer" check unless 15 // we allocat the result dirent* with malloc. 16 // 17 // fastwalk_readdir_r returns 0 on success, -1 upon reaching the end of the 18 // directory, or a positive error number to indicate failure. 19 static int fastwalk_readdir_r(DIR *fd, struct dirent *entry) { 20 struct dirent *result; 21 int ret = readdir_r(fd, entry, &result); 22 if (ret == 0 && result == NULL) { 23 ret = -1; // EOF 24 } 25 return ret; 26 } 27 */ 28 import "C" 29 30 import ( 31 "os" 32 "syscall" 33 "unsafe" 34 ) 35 36 func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { 37 fd, err := openDir(dirName) 38 if err != nil { 39 return &os.PathError{Op: "opendir", Path: dirName, Err: err} 40 } 41 defer C.closedir(fd) 42 43 skipFiles := false 44 var dirent syscall.Dirent 45 for { 46 ret := int(C.fastwalk_readdir_r(fd, (*C.struct_dirent)(unsafe.Pointer(&dirent)))) 47 if ret != 0 { 48 if ret == -1 { 49 break // EOF 50 } 51 if ret == int(syscall.EINTR) { 52 continue 53 } 54 return &os.PathError{Op: "readdir", Path: dirName, Err: syscall.Errno(ret)} 55 } 56 if dirent.Ino == 0 { 57 continue 58 } 59 typ := dtToType(dirent.Type) 60 if skipFiles && typ.IsRegular() { 61 continue 62 } 63 name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:] 64 name = name[:dirent.Namlen] 65 for i, c := range name { 66 if c == 0 { 67 name = name[:i] 68 break 69 } 70 } 71 // Check for useless names before allocating a string. 72 if string(name) == "." || string(name) == ".." { 73 continue 74 } 75 if err := fn(dirName, string(name), typ); err != nil { 76 if err != ErrSkipFiles { 77 return err 78 } 79 skipFiles = true 80 } 81 } 82 83 return nil 84 } 85 86 func dtToType(typ uint8) os.FileMode { 87 switch typ { 88 case syscall.DT_BLK: 89 return os.ModeDevice 90 case syscall.DT_CHR: 91 return os.ModeDevice | os.ModeCharDevice 92 case syscall.DT_DIR: 93 return os.ModeDir 94 case syscall.DT_FIFO: 95 return os.ModeNamedPipe 96 case syscall.DT_LNK: 97 return os.ModeSymlink 98 case syscall.DT_REG: 99 return 0 100 case syscall.DT_SOCK: 101 return os.ModeSocket 102 } 103 return ^os.FileMode(0) 104 } 105 106 // openDir wraps opendir(3) and handles any EINTR errors. The returned *DIR 107 // needs to be closed with closedir(3). 108 func openDir(path string) (*C.DIR, error) { 109 name, err := syscall.BytePtrFromString(path) 110 if err != nil { 111 return nil, err 112 } 113 for { 114 fd, err := C.opendir((*C.char)(unsafe.Pointer(name))) 115 if err != syscall.EINTR { 116 return fd, err 117 } 118 } 119 }