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