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 d := f.dirinfo.Load()
50 if d == nil {
51 d = new(dirInfo)
52 f.dirinfo.Store(d)
53 }
54 d.mu.Lock()
55 defer d.mu.Unlock()
56 if d.buf == nil {
57 d.buf = dirBufPool.Get().(*[]byte)
58 }
59
60
61
62
63
64
65
66
67
68
69 if n == 0 {
70 n = -1
71 }
72
73 for n != 0 {
74
75 if d.bufp >= d.nbuf {
76 d.bufp = 0
77 var errno error
78 d.nbuf, errno = f.pfd.ReadDirent(*d.buf)
79 runtime.KeepAlive(f)
80 if errno != nil {
81 return names, dirents, infos, &PathError{Op: "readdirent", Path: f.name, Err: errno}
82 }
83 if d.nbuf <= 0 {
84
85 dirBufPool.Put(d.buf)
86 d.buf = nil
87 break
88 }
89 }
90
91
92 buf := (*d.buf)[d.bufp:d.nbuf]
93 reclen, ok := direntReclen(buf)
94 if !ok || reclen > uint64(len(buf)) {
95 break
96 }
97 rec := buf[:reclen]
98 d.bufp += int(reclen)
99 ino, ok := direntIno(rec)
100 if !ok {
101 break
102 }
103
104
105
106
107 if ino == 0 && runtime.GOOS != "wasip1" {
108 continue
109 }
110 const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
111 namlen, ok := direntNamlen(rec)
112 if !ok || namoff+namlen > uint64(len(rec)) {
113 break
114 }
115 name := rec[namoff : namoff+namlen]
116 for i, c := range name {
117 if c == 0 {
118 name = name[:i]
119 break
120 }
121 }
122
123 if string(name) == "." || string(name) == ".." {
124 continue
125 }
126 if n > 0 {
127 n--
128 }
129 if mode == readdirName {
130 names = append(names, string(name))
131 } else if mode == readdirDirEntry {
132 de, err := newUnixDirent(f.name, string(name), direntType(rec))
133 if IsNotExist(err) {
134
135
136 continue
137 }
138 if err != nil {
139 return nil, dirents, nil, err
140 }
141 dirents = append(dirents, de)
142 } else {
143 info, err := lstat(f.name + "/" + string(name))
144 if IsNotExist(err) {
145
146
147 continue
148 }
149 if err != nil {
150 return nil, nil, infos, err
151 }
152 infos = append(infos, info)
153 }
154 }
155
156 if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
157 return nil, nil, nil, io.EOF
158 }
159 return names, dirents, infos, nil
160 }
161
162
163 func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
164 if len(b) < int(off+size) {
165 return 0, false
166 }
167 if goarch.BigEndian {
168 return readIntBE(b[off:], size), true
169 }
170 return readIntLE(b[off:], size), true
171 }
172
173 func readIntBE(b []byte, size uintptr) uint64 {
174 switch size {
175 case 1:
176 return uint64(b[0])
177 case 2:
178 return uint64(byteorder.BeUint16(b))
179 case 4:
180 return uint64(byteorder.BeUint32(b))
181 case 8:
182 return uint64(byteorder.BeUint64(b))
183 default:
184 panic("syscall: readInt with unsupported size")
185 }
186 }
187
188 func readIntLE(b []byte, size uintptr) uint64 {
189 switch size {
190 case 1:
191 return uint64(b[0])
192 case 2:
193 return uint64(byteorder.LeUint16(b))
194 case 4:
195 return uint64(byteorder.LeUint32(b))
196 case 8:
197 return uint64(byteorder.LeUint64(b))
198 default:
199 panic("syscall: readInt with unsupported size")
200 }
201 }
202
View as plain text