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