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