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 oldname = filepathlite.FromSlash(oldname)
254
255
256
257 if filepathlite.VolumeNameLen(oldname) > 0 && !filepathlite.IsAbs(oldname) {
258 p, err := syscall.FullPath(oldname)
259 if err == nil {
260 oldname = p
261 }
262 }
263
264
265
266 var flags windows.SymlinkatFlags
267 if filepathlite.VolumeNameLen(oldname) == 0 && !IsPathSeparator(oldname[0]) {
268
269
270
271 destPath := oldname
272 if dir := dirname(newname); dir != "." {
273 destPath = dir + `\` + oldname
274 }
275 fi, err := r.Stat(destPath)
276 if err == nil && fi.IsDir() {
277 flags |= windows.SYMLINKAT_DIRECTORY
278 }
279 }
280
281
282
283 if filepathlite.VolumeNameLen(oldname) == 0 {
284 flags |= windows.SYMLINKAT_RELATIVE
285 }
286
287 _, err := doInRoot(r, newname, 0, nil, func(parent sysfdType, name string, endsInSlash bool) (struct{}, error) {
288 return struct{}{}, windows.Symlinkat(oldname, parent, name, flags)
289 })
290 if err != nil {
291 return &LinkError{"symlinkat", oldname, newname, err}
292 }
293 return nil
294 }
295
296 func chmodat(parent syscall.Handle, name string, mode FileMode) error {
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314 h, err := windows.Openat(parent, name, syscall.O_CLOEXEC|windows.O_FILE_FLAG_OPEN_REPARSE_POINT|windows.O_WRITE_ATTRS, 0)
315 if err != nil {
316 return err
317 }
318 defer syscall.CloseHandle(h)
319
320 var d syscall.ByHandleFileInformation
321 if err := syscall.GetFileInformationByHandle(h, &d); err != nil {
322 return err
323 }
324 attrs := d.FileAttributes
325
326 if mode&syscall.S_IWRITE != 0 {
327 attrs &^= syscall.FILE_ATTRIBUTE_READONLY
328 } else {
329 attrs |= syscall.FILE_ATTRIBUTE_READONLY
330 }
331 if attrs == d.FileAttributes {
332 return nil
333 }
334
335 var fbi windows.FILE_BASIC_INFO
336 fbi.FileAttributes = attrs
337 return windows.SetFileInformationByHandle(h, windows.FileBasicInfo, unsafe.Pointer(&fbi), uint32(unsafe.Sizeof(fbi)))
338 }
339
340 func chownat(parent syscall.Handle, name string, uid, gid int) error {
341 return syscall.EWINDOWS
342 }
343
344 func lchownat(parent syscall.Handle, name string, uid, gid int) error {
345 return syscall.EWINDOWS
346 }
347
348 func mkdirat(dirfd syscall.Handle, name string, perm FileMode) error {
349 return windows.Mkdirat(dirfd, name, syscallMode(perm))
350 }
351
352 func removeat(dirfd syscall.Handle, name string) error {
353 return windows.Deleteat(dirfd, name, 0)
354 }
355
356 func removefileat(dirfd syscall.Handle, name string) error {
357 return windows.Deleteat(dirfd, name, windows.FILE_NON_DIRECTORY_FILE)
358 }
359
360 func removedirat(dirfd syscall.Handle, name string) error {
361 return windows.Deleteat(dirfd, name, windows.FILE_DIRECTORY_FILE)
362 }
363
364 func chtimesat(dirfd syscall.Handle, name string, atime time.Time, mtime time.Time) error {
365 h, err := windows.Openat(dirfd, name, syscall.O_CLOEXEC|windows.O_NOFOLLOW_ANY|windows.O_WRITE_ATTRS, 0)
366 if err == syscall.ELOOP || err == syscall.ENOTDIR {
367 if link, err := readReparseLinkAt(dirfd, name); err == nil {
368 return errSymlink(link)
369 }
370 }
371 if err != nil {
372 return err
373 }
374 defer syscall.CloseHandle(h)
375 a := syscall.Filetime{}
376 w := syscall.Filetime{}
377 if !atime.IsZero() {
378 a = syscall.NsecToFiletime(atime.UnixNano())
379 }
380 if !mtime.IsZero() {
381 w = syscall.NsecToFiletime(mtime.UnixNano())
382 }
383 return syscall.SetFileTime(h, nil, &a, &w)
384 }
385
386 func renameat(oldfd syscall.Handle, oldname string, newfd syscall.Handle, newname string) error {
387 return windows.Renameat(oldfd, oldname, newfd, newname)
388 }
389
390 func linkat(oldfd syscall.Handle, oldname string, newfd syscall.Handle, newname string) error {
391 return windows.Linkat(oldfd, oldname, newfd, newname)
392 }
393
394
395
396
397
398 func checkSymlink(parent syscall.Handle, name string, origError error) error {
399 link, err := readlinkat(parent, name)
400 if err != nil {
401 return origError
402 }
403 return errSymlink(link)
404 }
405
406 func readlinkat(dirfd syscall.Handle, name string) (string, error) {
407 fd, err := openat(dirfd, name, windows.O_FILE_FLAG_OPEN_REPARSE_POINT, 0)
408 if err != nil {
409 return "", err
410 }
411 defer syscall.CloseHandle(fd)
412 return readReparseLinkHandle(fd)
413 }
414
415 func lstatat(parent syscall.Handle, name string) (FileInfo, error) {
416 fd, err := openat(parent, name, windows.O_FILE_FLAG_OPEN_REPARSE_POINT, 0)
417 if err != nil {
418 return nil, err
419 }
420 defer syscall.CloseHandle(fd)
421 fi, err := statHandle(name, fd)
422 if err != nil {
423 return nil, err
424 }
425 return fi, nil
426 }
427
428
429
430
431 func isDirectoryLink(fi FileInfo) bool {
432 fs, ok := fi.(*fileStat)
433 return ok && fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
434 }
435
View as plain text