1//go:build darwin && go1.13
2
3package fastwalk
4
5import (
6 "os"
7 "syscall"
8 "unsafe"
9)
10
11func (w *walker) readDir(dirName string, depth int) (err error) {
12 var fd uintptr
13 for {
14 fd, err = opendir(dirName)
15 if err != syscall.EINTR {
16 break
17 }
18 }
19 if err != nil {
20 return &os.PathError{Op: "opendir", Path: dirName, Err: err}
21 }
22 defer closedir(fd) //nolint:errcheck
23
24 var p *[]*unixDirent
25 if w.sortMode != SortNone {
26 p = direntSlicePool.Get().(*[]*unixDirent)
27 }
28 defer putDirentSlice(p)
29
30 skipFiles := false
31 var dirent syscall.Dirent
32 var entptr *syscall.Dirent
33 for {
34 if errno := readdir_r(fd, &dirent, &entptr); errno != 0 {
35 if errno == syscall.EINTR {
36 continue
37 }
38 return &os.PathError{Op: "readdir", Path: dirName, Err: errno}
39 }
40 if entptr == nil { // EOF
41 break
42 }
43 // Darwin may return a zero inode when a directory entry has been
44 // deleted but not yet removed from the directory. The man page for
45 // getdirentries(2) states that programs are responsible for skipping
46 // those entries:
47 //
48 // Users of getdirentries() should skip entries with d_fileno = 0,
49 // as such entries represent files which have been deleted but not
50 // yet removed from the directory entry.
51 //
52 if dirent.Ino == 0 {
53 continue
54 }
55 typ := dtToType(dirent.Type)
56 if skipFiles && typ.IsRegular() {
57 continue
58 }
59 name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:]
60 for i, c := range name {
61 if c == 0 {
62 name = name[:i]
63 break
64 }
65 }
66 // Check for useless names before allocating a string.
67 if string(name) == "." || string(name) == ".." {
68 continue
69 }
70 nm := string(name)
71 de := newUnixDirent(dirName, nm, typ, depth)
72 if w.sortMode == SortNone {
73 if err := w.onDirEnt(dirName, nm, de); err != nil {
74 if err != ErrSkipFiles {
75 return err
76 }
77 skipFiles = true
78 }
79 } else {
80 *p = append(*p, de)
81 }
82 }
83 if w.sortMode == SortNone {
84 return nil
85 }
86
87 dents := *p
88 sortDirents(w.sortMode, dents)
89 for _, d := range dents {
90 d := d
91 if skipFiles && d.typ.IsRegular() {
92 continue
93 }
94 if err := w.onDirEnt(dirName, d.Name(), d); err != nil {
95 if err != ErrSkipFiles {
96 return err
97 }
98 skipFiles = true
99 }
100 }
101 return nil
102}
103
104func dtToType(typ uint8) os.FileMode {
105 switch typ {
106 case syscall.DT_BLK:
107 return os.ModeDevice
108 case syscall.DT_CHR:
109 return os.ModeDevice | os.ModeCharDevice
110 case syscall.DT_DIR:
111 return os.ModeDir
112 case syscall.DT_FIFO:
113 return os.ModeNamedPipe
114 case syscall.DT_LNK:
115 return os.ModeSymlink
116 case syscall.DT_REG:
117 return 0
118 case syscall.DT_SOCK:
119 return os.ModeSocket
120 }
121 return ^os.FileMode(0)
122}