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 if ino == 0 && runtime.GOOS != "wasip1" {
116 continue
117 }
118 const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
119 namlen, ok := direntNamlen(rec)
120 if !ok || namoff+namlen > uint64(len(rec)) {
121 break
122 }
123 name := rec[namoff : namoff+namlen]
124 for i, c := range name {
125 if c == 0 {
126 name = name[:i]
127 break
128 }
129 }
130
131 if string(name) == "." || string(name) == ".." {
132 continue
133 }
134 if n > 0 {
135 n--
136 }
137 if mode == readdirName {
138 names = append(names, string(name))
139 } else if mode == readdirDirEntry {
140 de, err := newUnixDirent(f.name, string(name), direntType(rec))
141 if IsNotExist(err) {
142
143
144 continue
145 }
146 if err != nil {
147 return nil, dirents, nil, err
148 }
149 dirents = append(dirents, de)
150 } else {
151 info, err := lstat(f.name + "/" + string(name))
152 if IsNotExist(err) {
153
154
155 continue
156 }
157 if err != nil {
158 return nil, nil, infos, err
159 }
160 infos = append(infos, info)
161 }
162 }
163
164 if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
165 return nil, nil, nil, io.EOF
166 }
167 return names, dirents, infos, nil
168 }
169
170
171 func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
172 if len(b) < int(off+size) {
173 return 0, false
174 }
175 if goarch.BigEndian {
176 return readIntBE(b[off:], size), true
177 }
178 return readIntLE(b[off:], size), true
179 }
180
181 func readIntBE(b []byte, size uintptr) uint64 {
182 switch size {
183 case 1:
184 return uint64(b[0])
185 case 2:
186 return uint64(byteorder.BEUint16(b))
187 case 4:
188 return uint64(byteorder.BEUint32(b))
189 case 8:
190 return uint64(byteorder.BEUint64(b))
191 default:
192 panic("syscall: readInt with unsupported size")
193 }
194 }
195
196 func readIntLE(b []byte, size uintptr) uint64 {
197 switch size {
198 case 1:
199 return uint64(b[0])
200 case 2:
201 return uint64(byteorder.LEUint16(b))
202 case 4:
203 return uint64(byteorder.LEUint32(b))
204 case 8:
205 return uint64(byteorder.LEUint64(b))
206 default:
207 panic("syscall: readInt with unsupported size")
208 }
209 }
210
View as plain text