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