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