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