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