Source file
src/os/dir_windows.go
1
2
3
4
5 package os
6
7 import (
8 "internal/syscall/windows"
9 "io"
10 "io/fs"
11 "runtime"
12 "sync"
13 "syscall"
14 "unsafe"
15 )
16
17
18 type dirInfo struct {
19 mu sync.Mutex
20
21
22
23 buf *[]byte
24 bufp int
25 h syscall.Handle
26 vol uint32
27 class uint32
28 path string
29 }
30
31 const (
32
33
34
35
36
37
38 dirBufSize = 64 * 1024
39 )
40
41 var dirBufPool = sync.Pool{
42 New: func() any {
43
44 buf := make([]byte, dirBufSize)
45 return &buf
46 },
47 }
48
49 func (d *dirInfo) close() {
50 d.h = 0
51 if d.buf != nil {
52 dirBufPool.Put(d.buf)
53 d.buf = nil
54 }
55 }
56
57
58
59
60 var allowReadDirFileID = true
61
62 func (d *dirInfo) init(h syscall.Handle) {
63 d.h = h
64 d.class = windows.FileFullDirectoryRestartInfo
65
66
67
68
69
70
71
72 var flags uint32
73 err := windows.GetVolumeInformationByHandle(h, nil, 0, &d.vol, nil, &flags, nil, 0)
74 if err != nil {
75 d.vol = 0
76
77
78 return
79 }
80 if flags&windows.FILE_SUPPORTS_OBJECT_IDS == 0 {
81
82 return
83 }
84 if allowReadDirFileID && flags&windows.FILE_SUPPORTS_OPEN_BY_FILE_ID != 0 {
85
86
87 d.class = windows.FileIdBothDirectoryRestartInfo
88 } else {
89
90
91
92 d.path, _ = windows.FinalPath(h, windows.FILE_NAME_OPENED)
93 }
94 }
95
96 func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
97
98 var d *dirInfo
99 for {
100 d = file.dirinfo.Load()
101 if d != nil {
102 break
103 }
104 d = new(dirInfo)
105 d.init(file.pfd.Sysfd)
106 if file.dirinfo.CompareAndSwap(nil, d) {
107 break
108 }
109
110 d.close()
111 }
112 d.mu.Lock()
113 defer d.mu.Unlock()
114 if d.buf == nil {
115 d.buf = dirBufPool.Get().(*[]byte)
116 }
117
118 wantAll := n <= 0
119 if wantAll {
120 n = -1
121 }
122 for n != 0 {
123
124 if d.bufp == 0 {
125 err = windows.GetFileInformationByHandleEx(file.pfd.Sysfd, d.class, (*byte)(unsafe.Pointer(&(*d.buf)[0])), uint32(len(*d.buf)))
126 runtime.KeepAlive(file)
127 if err != nil {
128 if err == syscall.ERROR_NO_MORE_FILES {
129
130 dirBufPool.Put(d.buf)
131 d.buf = nil
132 break
133 }
134 if err == syscall.ERROR_FILE_NOT_FOUND &&
135 (d.class == windows.FileIdBothDirectoryRestartInfo || d.class == windows.FileFullDirectoryRestartInfo) {
136
137
138
139
140
141
142
143
144
145 break
146 }
147 if s, _ := file.Stat(); s != nil && !s.IsDir() {
148 err = &PathError{Op: "readdir", Path: file.name, Err: syscall.ENOTDIR}
149 } else {
150 err = &PathError{Op: "GetFileInformationByHandleEx", Path: file.name, Err: err}
151 }
152 return
153 }
154 if d.class == windows.FileIdBothDirectoryRestartInfo {
155 d.class = windows.FileIdBothDirectoryInfo
156 } else if d.class == windows.FileFullDirectoryRestartInfo {
157 d.class = windows.FileFullDirectoryInfo
158 }
159 }
160
161 var islast bool
162 for n != 0 && !islast {
163 var nextEntryOffset uint32
164 var nameslice []uint16
165 entry := unsafe.Pointer(&(*d.buf)[d.bufp])
166 if d.class == windows.FileIdBothDirectoryInfo {
167 info := (*windows.FILE_ID_BOTH_DIR_INFO)(entry)
168 nextEntryOffset = info.NextEntryOffset
169 nameslice = unsafe.Slice(&info.FileName[0], info.FileNameLength/2)
170 } else {
171 info := (*windows.FILE_FULL_DIR_INFO)(entry)
172 nextEntryOffset = info.NextEntryOffset
173 nameslice = unsafe.Slice(&info.FileName[0], info.FileNameLength/2)
174 }
175 d.bufp += int(nextEntryOffset)
176 islast = nextEntryOffset == 0
177 if islast {
178 d.bufp = 0
179 }
180 if (len(nameslice) == 1 && nameslice[0] == '.') ||
181 (len(nameslice) == 2 && nameslice[0] == '.' && nameslice[1] == '.') {
182
183 continue
184 }
185 name := syscall.UTF16ToString(nameslice)
186 if mode == readdirName {
187 names = append(names, name)
188 } else {
189 var f *fileStat
190 if d.class == windows.FileIdBothDirectoryInfo {
191 f = newFileStatFromFileIDBothDirInfo((*windows.FILE_ID_BOTH_DIR_INFO)(entry))
192 } else {
193 f = newFileStatFromFileFullDirInfo((*windows.FILE_FULL_DIR_INFO)(entry))
194 if d.path != "" {
195
196
197
198 f.appendNameToPath = true
199 f.path = d.path
200 }
201 }
202 f.name = name
203 f.vol = d.vol
204 if mode == readdirDirEntry {
205 dirents = append(dirents, dirEntry{f})
206 } else {
207 infos = append(infos, f)
208 }
209 }
210 n--
211 }
212 }
213 if !wantAll && len(names)+len(dirents)+len(infos) == 0 {
214 return nil, nil, nil, io.EOF
215 }
216 return names, dirents, infos, nil
217 }
218
219 type dirEntry struct {
220 fs *fileStat
221 }
222
223 func (de dirEntry) Name() string { return de.fs.Name() }
224 func (de dirEntry) IsDir() bool { return de.fs.IsDir() }
225 func (de dirEntry) Type() FileMode { return de.fs.Mode().Type() }
226 func (de dirEntry) Info() (FileInfo, error) { return de.fs, nil }
227
228 func (de dirEntry) String() string {
229 return fs.FormatDirEntry(de)
230 }
231
View as plain text