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