Source file
src/os/dir_unix.go
1
2
3
4
5
6
7 package os
8
9 import (
10 "internal/byteorder"
11 "internal/goarch"
12 "io"
13 "runtime"
14 "sync"
15 "syscall"
16 "unsafe"
17 )
18
19
20 type dirInfo struct {
21 mu sync.Mutex
22 buf *[]byte
23 nbuf int
24 bufp int
25 }
26
27 const (
28
29 blockSize = 8192
30 )
31
32 var dirBufPool = sync.Pool{
33 New: func() any {
34
35 buf := make([]byte, blockSize)
36 return &buf
37 },
38 }
39
40 func (d *dirInfo) close() {
41 if d.buf != nil {
42 dirBufPool.Put(d.buf)
43 d.buf = nil
44 }
45 }
46
47 func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
48
49 var d *dirInfo
50 for {
51 d = f.dirinfo.Load()
52 if d != nil {
53 break
54 }
55 newD := new(dirInfo)
56 if f.dirinfo.CompareAndSwap(nil, newD) {
57 d = newD
58 break
59 }
60 }
61
62 d.mu.Lock()
63 defer d.mu.Unlock()
64 if d.buf == nil {
65 d.buf = dirBufPool.Get().(*[]byte)
66 }
67
68
69
70
71
72
73
74
75
76
77 if n == 0 {
78 n = -1
79 }
80
81 for n != 0 {
82
83 if d.bufp >= d.nbuf {
84 d.bufp = 0
85 var errno error
86 d.nbuf, errno = f.pfd.ReadDirent(*d.buf)
87 runtime.KeepAlive(f)
88 if errno != nil {
89 return names, dirents, infos, &PathError{Op: "readdirent", Path: f.name, Err: errno}
90 }
91 if d.nbuf <= 0 {
92
93 dirBufPool.Put(d.buf)
94 d.buf = nil
95 break
96 }
97 }
98
99
100 buf := (*d.buf)[d.bufp:d.nbuf]
101 reclen, ok := direntReclen(buf)
102 if !ok || reclen > uint64(len(buf)) {
103 break
104 }
105 rec := buf[:reclen]
106 d.bufp += int(reclen)
107 ino, ok := direntIno(rec)
108 if !ok {
109 break
110 }
111
112
113
114
115
116 if ino == 0 && runtime.GOOS != "linux" && runtime.GOOS != "wasip1" {
117 continue
118 }
119 const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
120 namlen, ok := direntNamlen(rec)
121 if !ok || namoff+namlen > uint64(len(rec)) {
122 break
123 }
124 name := rec[namoff : namoff+namlen]
125 for i, c := range name {
126 if c == 0 {
127 name = name[:i]
128 break
129 }
130 }
131
132 if string(name) == "." || string(name) == ".." {
133 continue
134 }
135 if n > 0 {
136 n--
137 }
138 if mode == readdirName {
139 names = append(names, string(name))
140 } else if mode == readdirDirEntry {
141 de, err := newUnixDirent(f.name, string(name), direntType(rec))
142 if IsNotExist(err) {
143
144
145 continue
146 }
147 if err != nil {
148 return nil, dirents, nil, err
149 }
150 dirents = append(dirents, de)
151 } else {
152 info, err := lstat(f.name + "/" + string(name))
153 if IsNotExist(err) {
154
155
156 continue
157 }
158 if err != nil {
159 return nil, nil, infos, err
160 }
161 infos = append(infos, info)
162 }
163 }
164
165 if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
166 return nil, nil, nil, io.EOF
167 }
168 return names, dirents, infos, nil
169 }
170
171
172 func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
173 if len(b) < int(off+size) {
174 return 0, false
175 }
176 if goarch.BigEndian {
177 return readIntBE(b[off:], size), true
178 }
179 return readIntLE(b[off:], size), true
180 }
181
182 func readIntBE(b []byte, size uintptr) uint64 {
183 switch size {
184 case 1:
185 return uint64(b[0])
186 case 2:
187 return uint64(byteorder.BEUint16(b))
188 case 4:
189 return uint64(byteorder.BEUint32(b))
190 case 8:
191 return uint64(byteorder.BEUint64(b))
192 default:
193 panic("syscall: readInt with unsupported size")
194 }
195 }
196
197 func readIntLE(b []byte, size uintptr) uint64 {
198 switch size {
199 case 1:
200 return uint64(b[0])
201 case 2:
202 return uint64(byteorder.LEUint16(b))
203 case 4:
204 return uint64(byteorder.LEUint32(b))
205 case 8:
206 return uint64(byteorder.LEUint64(b))
207 default:
208 panic("syscall: readInt with unsupported size")
209 }
210 }
211
View as plain text