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