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