Source file
src/os/file_windows.go
1
2
3
4
5 package os
6
7 import (
8 "errors"
9 "internal/godebug"
10 "internal/poll"
11 "internal/syscall/windows"
12 "runtime"
13 "sync"
14 "syscall"
15 "unsafe"
16 )
17
18
19 const _UTIME_OMIT = -1
20
21
22
23
24
25 type file struct {
26 pfd poll.FD
27 name string
28 dirinfo *dirInfo
29 appendMode bool
30 }
31
32
33
34
35
36
37
38 func (file *File) Fd() uintptr {
39 if file == nil {
40 return uintptr(syscall.InvalidHandle)
41 }
42 return uintptr(file.pfd.Sysfd)
43 }
44
45
46
47 func newFile(h syscall.Handle, name string, kind string) *File {
48 if kind == "file" {
49 var m uint32
50 if syscall.GetConsoleMode(h, &m) == nil {
51 kind = "console"
52 }
53 if t, err := syscall.GetFileType(h); err == nil && t == syscall.FILE_TYPE_PIPE {
54 kind = "pipe"
55 }
56 }
57
58 f := &File{&file{
59 pfd: poll.FD{
60 Sysfd: h,
61 IsStream: true,
62 ZeroReadIsEOF: true,
63 },
64 name: name,
65 }}
66 runtime.SetFinalizer(f.file, (*file).close)
67
68
69
70 f.pfd.Init(kind, false)
71
72 return f
73 }
74
75
76 func newConsoleFile(h syscall.Handle, name string) *File {
77 return newFile(h, name, "console")
78 }
79
80
81
82
83 func NewFile(fd uintptr, name string) *File {
84 h := syscall.Handle(fd)
85 if h == syscall.InvalidHandle {
86 return nil
87 }
88 return newFile(h, name, "file")
89 }
90
91 func epipecheck(file *File, e error) {
92 }
93
94
95
96 const DevNull = "NUL"
97
98
99 func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
100 if name == "" {
101 return nil, &PathError{Op: "open", Path: name, Err: syscall.ENOENT}
102 }
103 path := fixLongPath(name)
104 r, e := syscall.Open(path, flag|syscall.O_CLOEXEC, syscallMode(perm))
105 if e != nil {
106
107 if e == syscall.ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {
108 pathp, e1 := syscall.UTF16PtrFromString(path)
109 if e1 == nil {
110 var fa syscall.Win32FileAttributeData
111 e1 = syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
112 if e1 == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
113 e = syscall.EISDIR
114 }
115 }
116 }
117 return nil, &PathError{Op: "open", Path: name, Err: e}
118 }
119 return newFile(r, name, "file"), nil
120 }
121
122 func openDirNolog(name string) (*File, error) {
123 return openFileNolog(name, O_RDONLY, 0)
124 }
125
126 func (file *file) close() error {
127 if file == nil {
128 return syscall.EINVAL
129 }
130 if file.dirinfo != nil {
131 file.dirinfo.close()
132 file.dirinfo = nil
133 }
134 var err error
135 if e := file.pfd.Close(); e != nil {
136 if e == poll.ErrFileClosing {
137 e = ErrClosed
138 }
139 err = &PathError{Op: "close", Path: file.name, Err: e}
140 }
141
142
143 runtime.SetFinalizer(file, nil)
144 return err
145 }
146
147
148
149
150
151 func (f *File) seek(offset int64, whence int) (ret int64, err error) {
152 if f.dirinfo != nil {
153
154
155 f.dirinfo.close()
156 f.dirinfo = nil
157 }
158 ret, err = f.pfd.Seek(offset, whence)
159 runtime.KeepAlive(f)
160 return ret, err
161 }
162
163
164
165 func Truncate(name string, size int64) error {
166 f, e := OpenFile(name, O_WRONLY, 0666)
167 if e != nil {
168 return e
169 }
170 defer f.Close()
171 e1 := f.Truncate(size)
172 if e1 != nil {
173 return e1
174 }
175 return nil
176 }
177
178
179
180 func Remove(name string) error {
181 p, e := syscall.UTF16PtrFromString(fixLongPath(name))
182 if e != nil {
183 return &PathError{Op: "remove", Path: name, Err: e}
184 }
185
186
187
188 e = syscall.DeleteFile(p)
189 if e == nil {
190 return nil
191 }
192 e1 := syscall.RemoveDirectory(p)
193 if e1 == nil {
194 return nil
195 }
196
197
198 if e1 != e {
199 a, e2 := syscall.GetFileAttributes(p)
200 if e2 != nil {
201 e = e2
202 } else {
203 if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
204 e = e1
205 } else if a&syscall.FILE_ATTRIBUTE_READONLY != 0 {
206 if e1 = syscall.SetFileAttributes(p, a&^syscall.FILE_ATTRIBUTE_READONLY); e1 == nil {
207 if e = syscall.DeleteFile(p); e == nil {
208 return nil
209 }
210 }
211 }
212 }
213 }
214 return &PathError{Op: "remove", Path: name, Err: e}
215 }
216
217 func rename(oldname, newname string) error {
218 e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
219 if e != nil {
220 return &LinkError{"rename", oldname, newname, e}
221 }
222 return nil
223 }
224
225
226
227
228 func Pipe() (r *File, w *File, err error) {
229 var p [2]syscall.Handle
230 e := syscall.Pipe(p[:])
231 if e != nil {
232 return nil, nil, NewSyscallError("pipe", e)
233 }
234 return newFile(p[0], "|0", "pipe"), newFile(p[1], "|1", "pipe"), nil
235 }
236
237 var (
238 useGetTempPath2Once sync.Once
239 useGetTempPath2 bool
240 )
241
242 func tempDir() string {
243 useGetTempPath2Once.Do(func() {
244 useGetTempPath2 = (windows.ErrorLoadingGetTempPath2() == nil)
245 })
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 = fromSlash(oldname)
292
293
294 destpath := oldname
295 if v := volumeName(oldname); v == "" {
296 if len(oldname) > 0 && IsPathSeparator(oldname[0]) {
297
298 if v = 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 o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
317 if err != nil {
318 return &LinkError{"symlink", oldname, newname, err}
319 }
320
321 var flags uint32 = windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
322 if isdir {
323 flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY
324 }
325 err = syscall.CreateSymbolicLink(n, o, flags)
326 if err != nil {
327
328
329 flags &^= windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
330 err = syscall.CreateSymbolicLink(n, o, flags)
331 if err != nil {
332 return &LinkError{"symlink", oldname, newname, err}
333 }
334 }
335 return nil
336 }
337
338
339
340
341 func openSymlink(path string) (syscall.Handle, error) {
342 p, err := syscall.UTF16PtrFromString(path)
343 if err != nil {
344 return 0, err
345 }
346 attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
347
348
349 attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
350 h, err := syscall.CreateFile(p, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0)
351 if err != nil {
352 return 0, err
353 }
354 return h, nil
355 }
356
357 var winreadlinkvolume = godebug.New("winreadlinkvolume")
358
359
360
361
362
363
364
365
366
367 func normaliseLinkPath(path string) (string, error) {
368 if len(path) < 4 || path[:4] != `\??\` {
369
370 return path, nil
371 }
372
373 s := path[4:]
374 switch {
375 case len(s) >= 2 && s[1] == ':':
376 return s, nil
377 case len(s) >= 4 && s[:4] == `UNC\`:
378 return `\\` + s[4:], nil
379 }
380
381
382 if winreadlinkvolume.Value() != "0" {
383 return `\\?\` + path[4:], nil
384 }
385 winreadlinkvolume.IncNonDefault()
386
387 h, err := openSymlink(path)
388 if err != nil {
389 return "", err
390 }
391 defer syscall.CloseHandle(h)
392
393 buf := make([]uint16, 100)
394 for {
395 n, err := windows.GetFinalPathNameByHandle(h, &buf[0], uint32(len(buf)), windows.VOLUME_NAME_DOS)
396 if err != nil {
397 return "", err
398 }
399 if n < uint32(len(buf)) {
400 break
401 }
402 buf = make([]uint16, n)
403 }
404 s = syscall.UTF16ToString(buf)
405 if len(s) > 4 && s[:4] == `\\?\` {
406 s = s[4:]
407 if len(s) > 3 && s[:3] == `UNC` {
408
409 return `\` + s[3:], nil
410 }
411 return s, nil
412 }
413 return "", errors.New("GetFinalPathNameByHandle returned unexpected path: " + s)
414 }
415
416 func readReparseLink(path string) (string, error) {
417 h, err := openSymlink(path)
418 if err != nil {
419 return "", err
420 }
421 defer syscall.CloseHandle(h)
422
423 rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
424 var bytesReturned uint32
425 err = syscall.DeviceIoControl(h, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
426 if err != nil {
427 return "", err
428 }
429
430 rdb := (*windows.REPARSE_DATA_BUFFER)(unsafe.Pointer(&rdbbuf[0]))
431 switch rdb.ReparseTag {
432 case syscall.IO_REPARSE_TAG_SYMLINK:
433 rb := (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME))
434 s := rb.Path()
435 if rb.Flags&windows.SYMLINK_FLAG_RELATIVE != 0 {
436 return s, nil
437 }
438 return normaliseLinkPath(s)
439 case windows.IO_REPARSE_TAG_MOUNT_POINT:
440 return normaliseLinkPath((*windows.MountPointReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME)).Path())
441 default:
442
443
444 return "", syscall.ENOENT
445 }
446 }
447
448 func readlink(name string) (string, error) {
449 s, err := readReparseLink(fixLongPath(name))
450 if err != nil {
451 return "", &PathError{Op: "readlink", Path: name, Err: err}
452 }
453 return s, nil
454 }
455
View as plain text