Source file
src/os/root_windows.go
1
2
3
4
5
6
7 package os
8
9 import (
10 "errors"
11 "internal/filepathlite"
12 "internal/stringslite"
13 "internal/syscall/windows"
14 "runtime"
15 "syscall"
16 "time"
17 "unsafe"
18 )
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 func rootCleanPath(s string, prefix, suffix []string) (string, error) {
40
41 if stringslite.IndexByte(s, '?') >= 0 {
42 return "", windows.ERROR_INVALID_NAME
43 }
44
45 const fixedPrefix = `\\?\?`
46 buf := []byte(fixedPrefix)
47 for _, p := range prefix {
48 buf = append(buf, '\\')
49 buf = append(buf, []byte(p)...)
50 }
51 buf = append(buf, '\\')
52 buf = append(buf, []byte(s)...)
53 for _, p := range suffix {
54 buf = append(buf, '\\')
55 buf = append(buf, []byte(p)...)
56 }
57 s = string(buf)
58
59 s, err := syscall.FullPath(s)
60 if err != nil {
61 return "", err
62 }
63
64 s, ok := stringslite.CutPrefix(s, fixedPrefix)
65 if !ok {
66 return "", errPathEscapes
67 }
68 s = stringslite.TrimPrefix(s, `\`)
69 if s == "" {
70 s = "."
71 }
72
73 if !filepathlite.IsLocal(s) {
74 return "", errPathEscapes
75 }
76
77 return s, nil
78 }
79
80
81
82
83 type sysfdType = syscall.Handle
84
85
86 func openRootNolog(name string) (*Root, error) {
87 if name == "" {
88 return nil, &PathError{Op: "open", Path: name, Err: syscall.ENOENT}
89 }
90 path := fixLongPath(name)
91 fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_CLOEXEC, 0)
92 if err != nil {
93 return nil, &PathError{Op: "open", Path: name, Err: err}
94 }
95 return newRoot(fd, name)
96 }
97
98
99
100 func newRoot(fd syscall.Handle, name string) (*Root, error) {
101
102
103
104
105 var fi syscall.ByHandleFileInformation
106 err := syscall.GetFileInformationByHandle(fd, &fi)
107 if err == nil && fi.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 {
108 syscall.CloseHandle(fd)
109 return nil, &PathError{Op: "open", Path: name, Err: errors.New("not a directory")}
110 }
111
112 r := &Root{&root{
113 fd: fd,
114 name: name,
115 }}
116 r.root.cleanup = runtime.AddCleanup(r, func(f *root) { f.Close() }, r.root)
117 return r, nil
118 }
119
120
121 func openRootInRoot(r *Root, name string) (*Root, error) {
122 fd, err := doInRoot(r, name, nil, rootOpenDir)
123 if err != nil {
124 return nil, &PathError{Op: "openat", Path: name, Err: err}
125 }
126 return newRoot(fd, name)
127 }
128
129
130 func rootOpenFileNolog(root *Root, name string, flag int, perm FileMode) (*File, error) {
131 fd, err := doInRoot(root, name, nil, func(parent syscall.Handle, name string) (syscall.Handle, error) {
132 return openat(parent, name, flag, perm)
133 })
134 if err != nil {
135 return nil, &PathError{Op: "openat", Path: name, Err: err}
136 }
137
138 return newFile(fd, joinPath(root.Name(), name), "file", false), nil
139 }
140
141 func openat(dirfd syscall.Handle, name string, flag int, perm FileMode) (syscall.Handle, error) {
142 h, err := windows.Openat(dirfd, name, uint64(flag)|syscall.O_CLOEXEC|windows.O_NOFOLLOW_ANY, syscallMode(perm))
143 if err == syscall.ELOOP || err == syscall.ENOTDIR {
144 if link, err := readReparseLinkAt(dirfd, name); err == nil {
145 return syscall.InvalidHandle, errSymlink(link)
146 }
147 }
148 return h, err
149 }
150
151 func readReparseLinkAt(dirfd syscall.Handle, name string) (string, error) {
152 objectName, err := windows.NewNTUnicodeString(name)
153 if err != nil {
154 return "", err
155 }
156 objAttrs := &windows.OBJECT_ATTRIBUTES{
157 ObjectName: objectName,
158 }
159 if dirfd != syscall.InvalidHandle {
160 objAttrs.RootDirectory = dirfd
161 }
162 objAttrs.Length = uint32(unsafe.Sizeof(*objAttrs))
163 var h syscall.Handle
164 err = windows.NtCreateFile(
165 &h,
166 windows.FILE_GENERIC_READ,
167 objAttrs,
168 &windows.IO_STATUS_BLOCK{},
169 nil,
170 uint32(syscall.FILE_ATTRIBUTE_NORMAL),
171 syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
172 windows.FILE_OPEN,
173 windows.FILE_SYNCHRONOUS_IO_NONALERT|windows.FILE_OPEN_REPARSE_POINT,
174 nil,
175 0,
176 )
177 if err != nil {
178 return "", err
179 }
180 defer syscall.CloseHandle(h)
181 return readReparseLinkHandle(h)
182 }
183
184 func rootOpenDir(parent syscall.Handle, name string) (syscall.Handle, error) {
185 h, err := openat(parent, name, syscall.O_RDONLY|syscall.O_CLOEXEC|windows.O_DIRECTORY, 0)
186 if err == syscall.ERROR_FILE_NOT_FOUND {
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201 err = syscall.ERROR_PATH_NOT_FOUND
202 }
203 return h, err
204 }
205
206 func rootStat(r *Root, name string, lstat bool) (FileInfo, error) {
207 if len(name) > 0 && IsPathSeparator(name[len(name)-1]) {
208
209
210
211
212
213 lstat = false
214 }
215 fi, err := doInRoot(r, name, nil, func(parent syscall.Handle, n string) (FileInfo, error) {
216 fd, err := openat(parent, n, windows.O_OPEN_REPARSE, 0)
217 if err != nil {
218 return nil, err
219 }
220 defer syscall.CloseHandle(fd)
221 fi, err := statHandle(name, fd)
222 if err != nil {
223 return nil, err
224 }
225 if !lstat && fi.(*fileStat).isReparseTagNameSurrogate() {
226 link, err := readReparseLinkHandle(fd)
227 if err != nil {
228 return nil, err
229 }
230 return nil, errSymlink(link)
231 }
232 return fi, nil
233 })
234 if err != nil {
235 return nil, &PathError{Op: "statat", Path: name, Err: err}
236 }
237 return fi, nil
238 }
239
240 func rootSymlink(r *Root, oldname, newname string) error {
241 if oldname == "" {
242 return syscall.EINVAL
243 }
244
245
246
247 if filepathlite.VolumeNameLen(oldname) > 0 && !filepathlite.IsAbs(oldname) {
248 p, err := syscall.FullPath(oldname)
249 if err == nil {
250 oldname = p
251 }
252 }
253
254
255
256 var flags windows.SymlinkatFlags
257 if filepathlite.VolumeNameLen(oldname) == 0 && !IsPathSeparator(oldname[0]) {
258
259
260
261 destPath := oldname
262 if dir := dirname(newname); dir != "." {
263 destPath = dir + `\` + oldname
264 }
265 fi, err := r.Stat(destPath)
266 if err == nil && fi.IsDir() {
267 flags |= windows.SYMLINKAT_DIRECTORY
268 }
269 }
270
271
272
273 if filepathlite.VolumeNameLen(oldname) == 0 {
274 flags |= windows.SYMLINKAT_RELATIVE
275 }
276
277 _, err := doInRoot(r, newname, nil, func(parent sysfdType, name string) (struct{}, error) {
278 return struct{}{}, windows.Symlinkat(oldname, parent, name, flags)
279 })
280 if err != nil {
281 return &LinkError{"symlinkat", oldname, newname, err}
282 }
283 return nil
284 }
285
286 func chmodat(parent syscall.Handle, name string, mode FileMode) error {
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304 h, err := windows.Openat(parent, name, syscall.O_CLOEXEC|windows.O_OPEN_REPARSE|windows.O_WRITE_ATTRS, 0)
305 if err != nil {
306 return err
307 }
308 defer syscall.CloseHandle(h)
309
310 var d syscall.ByHandleFileInformation
311 if err := syscall.GetFileInformationByHandle(h, &d); err != nil {
312 return err
313 }
314 attrs := d.FileAttributes
315
316 if mode&syscall.S_IWRITE != 0 {
317 attrs &^= syscall.FILE_ATTRIBUTE_READONLY
318 } else {
319 attrs |= syscall.FILE_ATTRIBUTE_READONLY
320 }
321 if attrs == d.FileAttributes {
322 return nil
323 }
324
325 var fbi windows.FILE_BASIC_INFO
326 fbi.FileAttributes = attrs
327 return windows.SetFileInformationByHandle(h, windows.FileBasicInfo, unsafe.Pointer(&fbi), uint32(unsafe.Sizeof(fbi)))
328 }
329
330 func chownat(parent syscall.Handle, name string, uid, gid int) error {
331 return syscall.EWINDOWS
332 }
333
334 func lchownat(parent syscall.Handle, name string, uid, gid int) error {
335 return syscall.EWINDOWS
336 }
337
338 func mkdirat(dirfd syscall.Handle, name string, perm FileMode) error {
339 return windows.Mkdirat(dirfd, name, syscallMode(perm))
340 }
341
342 func removeat(dirfd syscall.Handle, name string) error {
343 return windows.Deleteat(dirfd, name, 0)
344 }
345
346 func removefileat(dirfd syscall.Handle, name string) error {
347 return windows.Deleteat(dirfd, name, windows.FILE_NON_DIRECTORY_FILE)
348 }
349
350 func removedirat(dirfd syscall.Handle, name string) error {
351 return windows.Deleteat(dirfd, name, windows.FILE_DIRECTORY_FILE)
352 }
353
354 func chtimesat(dirfd syscall.Handle, name string, atime time.Time, mtime time.Time) error {
355 h, err := windows.Openat(dirfd, name, syscall.O_CLOEXEC|windows.O_NOFOLLOW_ANY|windows.O_WRITE_ATTRS, 0)
356 if err == syscall.ELOOP || err == syscall.ENOTDIR {
357 if link, err := readReparseLinkAt(dirfd, name); err == nil {
358 return errSymlink(link)
359 }
360 }
361 if err != nil {
362 return err
363 }
364 defer syscall.CloseHandle(h)
365 a := syscall.Filetime{}
366 w := syscall.Filetime{}
367 if !atime.IsZero() {
368 a = syscall.NsecToFiletime(atime.UnixNano())
369 }
370 if !mtime.IsZero() {
371 w = syscall.NsecToFiletime(mtime.UnixNano())
372 }
373 return syscall.SetFileTime(h, nil, &a, &w)
374 }
375
376 func renameat(oldfd syscall.Handle, oldname string, newfd syscall.Handle, newname string) error {
377 return windows.Renameat(oldfd, oldname, newfd, newname)
378 }
379
380 func linkat(oldfd syscall.Handle, oldname string, newfd syscall.Handle, newname string) error {
381 return windows.Linkat(oldfd, oldname, newfd, newname)
382 }
383
384 func readlinkat(dirfd syscall.Handle, name string) (string, error) {
385 fd, err := openat(dirfd, name, windows.O_OPEN_REPARSE, 0)
386 if err != nil {
387 return "", err
388 }
389 defer syscall.CloseHandle(fd)
390 return readReparseLinkHandle(fd)
391 }
392
393 func modeAt(parent syscall.Handle, name string) (FileMode, error) {
394 fd, err := openat(parent, name, windows.O_OPEN_REPARSE|windows.O_DIRECTORY, 0)
395 if err != nil {
396 return 0, err
397 }
398 defer syscall.CloseHandle(fd)
399 fi, err := statHandle(name, fd)
400 if err != nil {
401 return 0, err
402 }
403 return fi.Mode(), nil
404 }
405
View as plain text