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