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