Source file
src/os/file_windows.go
1
2
3
4
5 package os
6
7 import (
8 "errors"
9 "internal/filepathlite"
10 "internal/godebug"
11 "internal/poll"
12 "internal/syscall/windows"
13 "runtime"
14 "sync"
15 "sync/atomic"
16 "syscall"
17 "unsafe"
18 )
19
20 var errInvalidPath = errors.New("invalid path: cannot end with a space or period")
21
22
23 const _UTIME_OMIT = -1
24
25
26
27
28
29 type file struct {
30 pfd poll.FD
31 name string
32 dirinfo atomic.Pointer[dirInfo]
33 appendMode bool
34 }
35
36
37
38
39
40
41
42 func (file *File) Fd() uintptr {
43 if file == nil {
44 return uintptr(syscall.InvalidHandle)
45 }
46 return uintptr(file.pfd.Sysfd)
47 }
48
49
50
51 func newFile(h syscall.Handle, name string, kind string) *File {
52 if kind == "file" {
53 var m uint32
54 if syscall.GetConsoleMode(h, &m) == nil {
55 kind = "console"
56 }
57 if t, err := syscall.GetFileType(h); err == nil && t == syscall.FILE_TYPE_PIPE {
58 kind = "pipe"
59 }
60 }
61
62 f := &File{&file{
63 pfd: poll.FD{
64 Sysfd: h,
65 IsStream: true,
66 ZeroReadIsEOF: true,
67 },
68 name: name,
69 }}
70 runtime.SetFinalizer(f.file, (*file).close)
71
72
73
74 f.pfd.Init(kind, false)
75
76 return f
77 }
78
79
80 func newConsoleFile(h syscall.Handle, name string) *File {
81 return newFile(h, name, "console")
82 }
83
84
85
86
87 func NewFile(fd uintptr, name string) *File {
88 h := syscall.Handle(fd)
89 if h == syscall.InvalidHandle {
90 return nil
91 }
92 return newFile(h, name, "file")
93 }
94
95 func epipecheck(file *File, e error) {
96 }
97
98
99
100 const DevNull = "NUL"
101
102
103 func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
104 if name == "" {
105 return nil, &PathError{Op: "open", Path: name, Err: syscall.ENOENT}
106 }
107 if flag&O_CREATE != 0 && !validatePathForCreate(name) {
108 return nil, &PathError{Op: "open", Path: name, Err: errInvalidPath}
109 }
110 path := fixLongPath(name)
111 r, err := syscall.Open(path, flag|syscall.O_CLOEXEC, syscallMode(perm))
112 if err != nil {
113 return nil, &PathError{Op: "open", Path: name, Err: err}
114 }
115 return newFile(r, name, "file"), nil
116 }
117
118 func openDirNolog(name string) (*File, error) {
119 return openFileNolog(name, O_RDONLY, 0)
120 }
121
122 func mkdir(name string, perm FileMode) error {
123 if !validatePathForCreate(name) {
124 return errInvalidPath
125 }
126 longName := fixLongPath(name)
127 return syscall.Mkdir(longName, syscallMode(perm))
128 }
129
130 func (file *file) close() error {
131 if file == nil {
132 return syscall.EINVAL
133 }
134 if info := file.dirinfo.Swap(nil); info != nil {
135 info.close()
136 }
137 var err error
138 if e := file.pfd.Close(); e != nil {
139 if e == poll.ErrFileClosing {
140 e = ErrClosed
141 }
142 err = &PathError{Op: "close", Path: file.name, Err: e}
143 }
144
145
146 runtime.SetFinalizer(file, nil)
147 return err
148 }
149
150
151
152
153
154 func (f *File) seek(offset int64, whence int) (ret int64, err error) {
155 if info := f.dirinfo.Swap(nil); info != nil {
156
157
158 info.close()
159 }
160 ret, err = f.pfd.Seek(offset, whence)
161 runtime.KeepAlive(f)
162 return ret, err
163 }
164
165
166
167 func Truncate(name string, size int64) error {
168 f, e := OpenFile(name, O_WRONLY, 0666)
169 if e != nil {
170 return e
171 }
172 defer f.Close()
173 e1 := f.Truncate(size)
174 if e1 != nil {
175 return e1
176 }
177 return nil
178 }
179
180
181
182 func Remove(name string) error {
183 p, e := syscall.UTF16PtrFromString(fixLongPath(name))
184 if e != nil {
185 return &PathError{Op: "remove", Path: name, Err: e}
186 }
187
188
189
190 e = syscall.DeleteFile(p)
191 if e == nil {
192 return nil
193 }
194 e1 := syscall.RemoveDirectory(p)
195 if e1 == nil {
196 return nil
197 }
198
199
200 if e1 != e {
201 a, e2 := syscall.GetFileAttributes(p)
202 if e2 != nil {
203 e = e2
204 } else {
205 if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
206 e = e1
207 } else if a&syscall.FILE_ATTRIBUTE_READONLY != 0 {
208 if e1 = syscall.SetFileAttributes(p, a&^syscall.FILE_ATTRIBUTE_READONLY); e1 == nil {
209 if e = syscall.DeleteFile(p); e == nil {
210 return nil
211 }
212 }
213 }
214 }
215 }
216 return &PathError{Op: "remove", Path: name, Err: e}
217 }
218
219 func rename(oldname, newname string) error {
220 if !validatePathForCreate(newname) {
221 return &LinkError{"rename", oldname, newname, errInvalidPath}
222 }
223 e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
224 if e != nil {
225 return &LinkError{"rename", oldname, newname, e}
226 }
227 return nil
228 }
229
230
231
232
233 func Pipe() (r *File, w *File, err error) {
234 var p [2]syscall.Handle
235 e := syscall.Pipe(p[:])
236 if e != nil {
237 return nil, nil, NewSyscallError("pipe", e)
238 }
239 return newFile(p[0], "|0", "pipe"), newFile(p[1], "|1", "pipe"), nil
240 }
241
242 var useGetTempPath2 = sync.OnceValue(func() bool {
243 return windows.ErrorLoadingGetTempPath2() == nil
244 })
245
246 func tempDir() string {
247 getTempPath := syscall.GetTempPath
248 if useGetTempPath2() {
249 getTempPath = windows.GetTempPath2
250 }
251 n := uint32(syscall.MAX_PATH)
252 for {
253 b := make([]uint16, n)
254 n, _ = getTempPath(uint32(len(b)), &b[0])
255 if n > uint32(len(b)) {
256 continue
257 }
258 if n == 3 && b[1] == ':' && b[2] == '\\' {
259
260 } else if n > 0 && b[n-1] == '\\' {
261
262 n--
263 }
264 return syscall.UTF16ToString(b[:n])
265 }
266 }
267
268
269
270 func Link(oldname, newname string) error {
271 if !validatePathForCreate(newname) {
272 return &LinkError{"link", oldname, newname, errInvalidPath}
273 }
274 n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
275 if err != nil {
276 return &LinkError{"link", oldname, newname, err}
277 }
278 o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
279 if err != nil {
280 return &LinkError{"link", oldname, newname, err}
281 }
282 err = syscall.CreateHardLink(n, o, 0)
283 if err != nil {
284 return &LinkError{"link", oldname, newname, err}
285 }
286 return nil
287 }
288
289
290
291
292
293 func Symlink(oldname, newname string) error {
294 if !validatePathForCreate(newname) {
295 return &LinkError{"symlink", oldname, newname, errInvalidPath}
296 }
297
298 oldname = filepathlite.FromSlash(oldname)
299
300
301 destpath := oldname
302 if v := filepathlite.VolumeName(oldname); v == "" {
303 if len(oldname) > 0 && IsPathSeparator(oldname[0]) {
304
305 if v = filepathlite.VolumeName(newname); v != "" {
306
307
308 destpath = v + oldname
309 }
310 } else {
311
312 destpath = dirname(newname) + `\` + oldname
313 }
314 }
315
316 fi, err := Stat(destpath)
317 isdir := err == nil && fi.IsDir()
318
319 n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
320 if err != nil {
321 return &LinkError{"symlink", oldname, newname, err}
322 }
323 var o *uint16
324 if filepathlite.IsAbs(oldname) {
325 o, err = syscall.UTF16PtrFromString(fixLongPath(oldname))
326 } else {
327
328
329
330
331
332
333 o, err = syscall.UTF16PtrFromString(oldname)
334 }
335 if err != nil {
336 return &LinkError{"symlink", oldname, newname, err}
337 }
338
339 var flags uint32 = windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
340 if isdir {
341 flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY
342 }
343 err = syscall.CreateSymbolicLink(n, o, flags)
344 if err != nil {
345
346
347 flags &^= windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
348 err = syscall.CreateSymbolicLink(n, o, flags)
349 if err != nil {
350 return &LinkError{"symlink", oldname, newname, err}
351 }
352 }
353 return nil
354 }
355
356
357
358
359 func openSymlink(path string) (syscall.Handle, error) {
360 p, err := syscall.UTF16PtrFromString(path)
361 if err != nil {
362 return 0, err
363 }
364 attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
365
366
367 attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
368 h, err := syscall.CreateFile(p, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0)
369 if err != nil {
370 return 0, err
371 }
372 return h, nil
373 }
374
375 var winreadlinkvolume = godebug.New("winreadlinkvolume")
376
377
378
379
380
381
382
383
384
385 func normaliseLinkPath(path string) (string, error) {
386 if len(path) < 4 || path[:4] != `\??\` {
387
388 return path, nil
389 }
390
391 s := path[4:]
392 switch {
393 case len(s) >= 2 && s[1] == ':':
394 return s, nil
395 case len(s) >= 4 && s[:4] == `UNC\`:
396 return `\\` + s[4:], nil
397 }
398
399
400 if winreadlinkvolume.Value() != "0" {
401 return `\\?\` + path[4:], nil
402 }
403 winreadlinkvolume.IncNonDefault()
404
405 h, err := openSymlink(path)
406 if err != nil {
407 return "", err
408 }
409 defer syscall.CloseHandle(h)
410
411 buf := make([]uint16, 100)
412 for {
413 n, err := windows.GetFinalPathNameByHandle(h, &buf[0], uint32(len(buf)), windows.VOLUME_NAME_DOS)
414 if err != nil {
415 return "", err
416 }
417 if n < uint32(len(buf)) {
418 break
419 }
420 buf = make([]uint16, n)
421 }
422 s = syscall.UTF16ToString(buf)
423 if len(s) > 4 && s[:4] == `\\?\` {
424 s = s[4:]
425 if len(s) > 3 && s[:3] == `UNC` {
426
427 return `\` + s[3:], nil
428 }
429 return s, nil
430 }
431 return "", errors.New("GetFinalPathNameByHandle returned unexpected path: " + s)
432 }
433
434 func readReparseLink(path string) (string, error) {
435 h, err := openSymlink(path)
436 if err != nil {
437 return "", err
438 }
439 defer syscall.CloseHandle(h)
440
441 rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
442 var bytesReturned uint32
443 err = syscall.DeviceIoControl(h, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
444 if err != nil {
445 return "", err
446 }
447
448 rdb := (*windows.REPARSE_DATA_BUFFER)(unsafe.Pointer(&rdbbuf[0]))
449 switch rdb.ReparseTag {
450 case syscall.IO_REPARSE_TAG_SYMLINK:
451 rb := (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME))
452 s := rb.Path()
453 if rb.Flags&windows.SYMLINK_FLAG_RELATIVE != 0 {
454 return s, nil
455 }
456 return normaliseLinkPath(s)
457 case windows.IO_REPARSE_TAG_MOUNT_POINT:
458 return normaliseLinkPath((*windows.MountPointReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME)).Path())
459 default:
460
461
462 return "", syscall.ENOENT
463 }
464 }
465
466 func readlink(name string) (string, error) {
467 s, err := readReparseLink(fixLongPath(name))
468 if err != nil {
469 return "", &PathError{Op: "readlink", Path: name, Err: err}
470 }
471 return s, nil
472 }
473
View as plain text