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 runtime.SetFinalizer(r.root, (*root).Close)
117 return r, nil
118 }
119
120
121 func openRootInRoot(r *Root, name string) (*Root, error) {
122 fd, err := doInRoot(r, name, 0, nil, func(parent syscall.Handle, name string, endsInSlash bool) (syscall.Handle, error) {
123 return rootOpenDir(parent, name)
124 })
125 if err != nil {
126 return nil, &PathError{Op: "openat", Path: name, Err: err}
127 }
128 return newRoot(fd, joinPath(r.Name(), name))
129 }
130
131
132 func rootOpenFileNolog(root *Root, name string, flag int, perm FileMode) (*File, error) {
133 fd, err := doInRoot(root, name, doInRootNoHandleTerminalSlash, nil, func(parent syscall.Handle, name string, endsInSlash bool) (syscall.Handle, error) {
134 if endsInSlash {
135 flag |= windows.O_DIRECTORY
136 }
137 return openat(parent, name, uint64(flag), perm)
138 })
139 if err != nil {
140 return nil, &PathError{Op: "openat", Path: name, Err: err}
141 }
142 nonblocking := flag&windows.O_FILE_FLAG_OVERLAPPED != 0
143 return newFile(fd, joinPath(root.Name(), name), kindOpenFile, nonblocking), nil
144 }
145
146 func openat(dirfd syscall.Handle, name string, flag uint64, perm FileMode) (syscall.Handle, error) {
147 h, err := windows.Openat(dirfd, name, flag|syscall.O_CLOEXEC|windows.O_NOFOLLOW_ANY, syscallMode(perm))
148 if err == syscall.ELOOP || err == syscall.ENOTDIR {
149 if link, err := readReparseLinkAt(dirfd, name); err == nil {
150 return syscall.InvalidHandle, errSymlink(link)
151 }
152 }
153 return h, err
154 }
155
156 func readReparseLinkAt(dirfd syscall.Handle, name string) (string, error) {
157 objectName, err := windows.NewNTUnicodeString(name)
158 if err != nil {
159 return "", err
160 }
161 objAttrs := &windows.OBJECT_ATTRIBUTES{
162 ObjectName: objectName,
163 }
164 if dirfd != syscall.InvalidHandle {
165 objAttrs.RootDirectory = dirfd
166 }
167 objAttrs.Length = uint32(unsafe.Sizeof(*objAttrs))
168 var h syscall.Handle
169 err = windows.NtCreateFile(
170 &h,
171 windows.FILE_GENERIC_READ,
172 objAttrs,
173 &windows.IO_STATUS_BLOCK{},
174 nil,
175 uint32(syscall.FILE_ATTRIBUTE_NORMAL),
176 syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
177 windows.FILE_OPEN,
178 windows.FILE_SYNCHRONOUS_IO_NONALERT|windows.FILE_OPEN_REPARSE_POINT,
179 nil,
180 0,
181 )
182 if err != nil {
183 return "", err
184 }
185 defer syscall.CloseHandle(h)
186 return readReparseLinkHandle(h)
187 }
188
189 func rootOpenDir(parent syscall.Handle, name string) (syscall.Handle, error) {
190 h, err := openat(parent, name, syscall.O_RDONLY|syscall.O_CLOEXEC|windows.O_DIRECTORY, 0)
191 if err == syscall.ERROR_FILE_NOT_FOUND {
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206 err = syscall.ERROR_PATH_NOT_FOUND
207 }
208 return h, err
209 }
210
211 func rootStat(r *Root, name string, lstat bool) (FileInfo, error) {
212 var flags uint
213 if lstat {
214
215
216
217
218
219 flags = doInRootAlwaysResolveTerminalSlash
220 }
221 fi, err := doInRoot(r, name, flags, nil, func(parent syscall.Handle, n string, endsInSlash bool) (FileInfo, error) {
222 fd, err := openat(parent, n, windows.O_FILE_FLAG_OPEN_REPARSE_POINT, 0)
223 if err != nil {
224 return nil, err
225 }
226 defer syscall.CloseHandle(fd)
227 fi, err := statHandle(name, fd)
228 if err != nil {
229 return nil, err
230 }
231 if !lstat && fi.(*fileStat).isReparseTagNameSurrogate() {
232 link, err := readReparseLinkHandle(fd)
233 if err != nil {
234 return nil, err
235 }
236 return nil, errSymlink(link)
237 }
238 return fi, nil
239 })
240 if err != nil {
241 return nil, &PathError{Op: "statat", Path: name, Err: err}
242 }
243 return fi, nil
244 }
245
246 func rootSymlink(r *Root, oldname, newname string) error {
247 if oldname == "" {
248 return syscall.EINVAL
249 }
250
251
252
253 if filepathlite.VolumeNameLen(oldname) > 0 && !filepathlite.IsAbs(oldname) {
254 p, err := syscall.FullPath(oldname)
255 if err == nil {
256 oldname = p
257 }
258 }
259
260
261
262 var flags windows.SymlinkatFlags
263 if filepathlite.VolumeNameLen(oldname) == 0 && !IsPathSeparator(oldname[0]) {
264
265
266
267 destPath := oldname
268 if dir := dirname(newname); dir != "." {
269 destPath = dir + `\` + oldname
270 }
271 fi, err := r.Stat(destPath)
272 if err == nil && fi.IsDir() {
273 flags |= windows.SYMLINKAT_DIRECTORY
274 }
275 }
276
277
278
279 if filepathlite.VolumeNameLen(oldname) == 0 {
280 flags |= windows.SYMLINKAT_RELATIVE
281 }
282
283 _, err := doInRoot(r, newname, 0, nil, func(parent sysfdType, name string, endsInSlash bool) (struct{}, error) {
284 return struct{}{}, windows.Symlinkat(oldname, parent, name, flags)
285 })
286 if err != nil {
287 return &LinkError{"symlinkat", oldname, newname, err}
288 }
289 return nil
290 }
291
292 func chmodat(parent syscall.Handle, name string, mode FileMode) error {
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310 h, err := windows.Openat(parent, name, syscall.O_CLOEXEC|windows.O_FILE_FLAG_OPEN_REPARSE_POINT|windows.O_WRITE_ATTRS, 0)
311 if err != nil {
312 return err
313 }
314 defer syscall.CloseHandle(h)
315
316 var d syscall.ByHandleFileInformation
317 if err := syscall.GetFileInformationByHandle(h, &d); err != nil {
318 return err
319 }
320 attrs := d.FileAttributes
321
322 if mode&syscall.S_IWRITE != 0 {
323 attrs &^= syscall.FILE_ATTRIBUTE_READONLY
324 } else {
325 attrs |= syscall.FILE_ATTRIBUTE_READONLY
326 }
327 if attrs == d.FileAttributes {
328 return nil
329 }
330
331 var fbi windows.FILE_BASIC_INFO
332 fbi.FileAttributes = attrs
333 return windows.SetFileInformationByHandle(h, windows.FileBasicInfo, unsafe.Pointer(&fbi), uint32(unsafe.Sizeof(fbi)))
334 }
335
336 func chownat(parent syscall.Handle, name string, uid, gid int) error {
337 return syscall.EWINDOWS
338 }
339
340 func lchownat(parent syscall.Handle, name string, uid, gid int) error {
341 return syscall.EWINDOWS
342 }
343
344 func mkdirat(dirfd syscall.Handle, name string, perm FileMode) error {
345 return windows.Mkdirat(dirfd, name, syscallMode(perm))
346 }
347
348 func removeat(dirfd syscall.Handle, name string) error {
349 return windows.Deleteat(dirfd, name, 0)
350 }
351
352 func removefileat(dirfd syscall.Handle, name string) error {
353 return windows.Deleteat(dirfd, name, windows.FILE_NON_DIRECTORY_FILE)
354 }
355
356 func removedirat(dirfd syscall.Handle, name string) error {
357 return windows.Deleteat(dirfd, name, windows.FILE_DIRECTORY_FILE)
358 }
359
360 func chtimesat(dirfd syscall.Handle, name string, atime time.Time, mtime time.Time) error {
361 h, err := windows.Openat(dirfd, name, syscall.O_CLOEXEC|windows.O_NOFOLLOW_ANY|windows.O_WRITE_ATTRS, 0)
362 if err == syscall.ELOOP || err == syscall.ENOTDIR {
363 if link, err := readReparseLinkAt(dirfd, name); err == nil {
364 return errSymlink(link)
365 }
366 }
367 if err != nil {
368 return err
369 }
370 defer syscall.CloseHandle(h)
371 a := syscall.Filetime{}
372 w := syscall.Filetime{}
373 if !atime.IsZero() {
374 a = syscall.NsecToFiletime(atime.UnixNano())
375 }
376 if !mtime.IsZero() {
377 w = syscall.NsecToFiletime(mtime.UnixNano())
378 }
379 return syscall.SetFileTime(h, nil, &a, &w)
380 }
381
382 func renameat(oldfd syscall.Handle, oldname string, newfd syscall.Handle, newname string) error {
383 return windows.Renameat(oldfd, oldname, newfd, newname)
384 }
385
386 func linkat(oldfd syscall.Handle, oldname string, newfd syscall.Handle, newname string) error {
387 return windows.Linkat(oldfd, oldname, newfd, newname)
388 }
389
390
391
392
393
394 func checkSymlink(parent syscall.Handle, name string, origError error) error {
395 link, err := readlinkat(parent, name)
396 if err != nil {
397 return origError
398 }
399 return errSymlink(link)
400 }
401
402 func readlinkat(dirfd syscall.Handle, name string) (string, error) {
403 fd, err := openat(dirfd, name, windows.O_FILE_FLAG_OPEN_REPARSE_POINT, 0)
404 if err != nil {
405 return "", err
406 }
407 defer syscall.CloseHandle(fd)
408 return readReparseLinkHandle(fd)
409 }
410
411 func lstatat(parent syscall.Handle, name string) (FileInfo, error) {
412 fd, err := openat(parent, name, windows.O_FILE_FLAG_OPEN_REPARSE_POINT, 0)
413 if err != nil {
414 return nil, err
415 }
416 defer syscall.CloseHandle(fd)
417 fi, err := statHandle(name, fd)
418 if err != nil {
419 return nil, err
420 }
421 return fi, nil
422 }
423
424
425
426
427 func isDirectoryLink(fi FileInfo) bool {
428 fs, ok := fi.(*fileStat)
429 return ok && fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
430 }
431
View as plain text