github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/os/file_windows.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package os 6 7 import ( 8 "io" 9 "runtime" 10 "sync" 11 "syscall" 12 "unicode/utf16" 13 "unicode/utf8" 14 "unsafe" 15 ) 16 17 // File represents an open file descriptor. 18 type File struct { 19 *file 20 } 21 22 // file is the real representation of *File. 23 // The extra level of indirection ensures that no clients of os 24 // can overwrite this data, which could cause the finalizer 25 // to close the wrong file descriptor. 26 type file struct { 27 fd syscall.Handle 28 name string 29 dirinfo *dirInfo // nil unless directory being read 30 l sync.Mutex // used to implement windows pread/pwrite 31 32 // only for console io 33 isConsole bool 34 lastbits []byte // first few bytes of the last incomplete rune in last write 35 readbuf []rune // input console buffer 36 } 37 38 // Fd returns the Windows handle referencing the open file. 39 // The handle is valid only until f.Close is called or f is garbage collected. 40 func (file *File) Fd() uintptr { 41 if file == nil { 42 return uintptr(syscall.InvalidHandle) 43 } 44 return uintptr(file.fd) 45 } 46 47 // newFile returns a new File with the given file handle and name. 48 // Unlike NewFile, it does not check that h is syscall.InvalidHandle. 49 func newFile(h syscall.Handle, name string) *File { 50 f := &File{&file{fd: h, name: name}} 51 var m uint32 52 if syscall.GetConsoleMode(f.fd, &m) == nil { 53 f.isConsole = true 54 } 55 runtime.SetFinalizer(f.file, (*file).close) 56 return f 57 } 58 59 // NewFile returns a new File with the given file descriptor and name. 60 func NewFile(fd uintptr, name string) *File { 61 h := syscall.Handle(fd) 62 if h == syscall.InvalidHandle { 63 return nil 64 } 65 return newFile(h, name) 66 } 67 68 // Auxiliary information if the File describes a directory 69 type dirInfo struct { 70 data syscall.Win32finddata 71 needdata bool 72 path string 73 isempty bool // set if FindFirstFile returns ERROR_FILE_NOT_FOUND 74 } 75 76 func epipecheck(file *File, e error) { 77 } 78 79 const DevNull = "NUL" 80 81 func (f *file) isdir() bool { return f != nil && f.dirinfo != nil } 82 83 func openFile(name string, flag int, perm FileMode) (file *File, err error) { 84 r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm)) 85 if e != nil { 86 return nil, e 87 } 88 return NewFile(uintptr(r), name), nil 89 } 90 91 func openDir(name string) (file *File, err error) { 92 maskp, e := syscall.UTF16PtrFromString(name + `\*`) 93 if e != nil { 94 return nil, e 95 } 96 d := new(dirInfo) 97 r, e := syscall.FindFirstFile(maskp, &d.data) 98 if e != nil { 99 // FindFirstFile returns ERROR_FILE_NOT_FOUND when 100 // no matching files can be found. Then, if directory 101 // exists, we should proceed. 102 if e != syscall.ERROR_FILE_NOT_FOUND { 103 return nil, e 104 } 105 var fa syscall.Win32FileAttributeData 106 namep, e := syscall.UTF16PtrFromString(name) 107 if e != nil { 108 return nil, e 109 } 110 e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) 111 if e != nil { 112 return nil, e 113 } 114 if fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 { 115 return nil, e 116 } 117 d.isempty = true 118 } 119 d.path = name 120 if !isAbs(d.path) { 121 d.path, e = syscall.FullPath(d.path) 122 if e != nil { 123 return nil, e 124 } 125 } 126 f := newFile(r, name) 127 f.dirinfo = d 128 return f, nil 129 } 130 131 // OpenFile is the generalized open call; most users will use Open 132 // or Create instead. It opens the named file with specified flag 133 // (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, 134 // methods on the returned File can be used for I/O. 135 // If there is an error, it will be of type *PathError. 136 func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { 137 if name == "" { 138 return nil, &PathError{"open", name, syscall.ENOENT} 139 } 140 r, errf := openFile(name, flag, perm) 141 if errf == nil { 142 return r, nil 143 } 144 r, errd := openDir(name) 145 if errd == nil { 146 if flag&O_WRONLY != 0 || flag&O_RDWR != 0 { 147 r.Close() 148 return nil, &PathError{"open", name, syscall.EISDIR} 149 } 150 return r, nil 151 } 152 return nil, &PathError{"open", name, errf} 153 } 154 155 // Close closes the File, rendering it unusable for I/O. 156 // It returns an error, if any. 157 func (file *File) Close() error { 158 if file == nil { 159 return ErrInvalid 160 } 161 return file.file.close() 162 } 163 164 func (file *file) close() error { 165 if file == nil { 166 return syscall.EINVAL 167 } 168 if file.isdir() && file.dirinfo.isempty { 169 // "special" empty directories 170 return nil 171 } 172 if file.fd == syscall.InvalidHandle { 173 return syscall.EINVAL 174 } 175 var e error 176 if file.isdir() { 177 e = syscall.FindClose(syscall.Handle(file.fd)) 178 } else { 179 e = syscall.CloseHandle(syscall.Handle(file.fd)) 180 } 181 var err error 182 if e != nil { 183 err = &PathError{"close", file.name, e} 184 } 185 file.fd = syscall.InvalidHandle // so it can't be closed again 186 187 // no need for a finalizer anymore 188 runtime.SetFinalizer(file, nil) 189 return err 190 } 191 192 func (file *File) readdir(n int) (fi []FileInfo, err error) { 193 if file == nil { 194 return nil, syscall.EINVAL 195 } 196 if !file.isdir() { 197 return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR} 198 } 199 if !file.dirinfo.isempty && file.fd == syscall.InvalidHandle { 200 return nil, syscall.EINVAL 201 } 202 wantAll := n <= 0 203 size := n 204 if wantAll { 205 n = -1 206 size = 100 207 } 208 fi = make([]FileInfo, 0, size) // Empty with room to grow. 209 d := &file.dirinfo.data 210 for n != 0 && !file.dirinfo.isempty { 211 if file.dirinfo.needdata { 212 e := syscall.FindNextFile(syscall.Handle(file.fd), d) 213 if e != nil { 214 if e == syscall.ERROR_NO_MORE_FILES { 215 break 216 } else { 217 err = &PathError{"FindNextFile", file.name, e} 218 if !wantAll { 219 fi = nil 220 } 221 return 222 } 223 } 224 } 225 file.dirinfo.needdata = true 226 name := string(syscall.UTF16ToString(d.FileName[0:])) 227 if name == "." || name == ".." { // Useless names 228 continue 229 } 230 f := &fileStat{ 231 name: name, 232 sys: syscall.Win32FileAttributeData{ 233 FileAttributes: d.FileAttributes, 234 CreationTime: d.CreationTime, 235 LastAccessTime: d.LastAccessTime, 236 LastWriteTime: d.LastWriteTime, 237 FileSizeHigh: d.FileSizeHigh, 238 FileSizeLow: d.FileSizeLow, 239 }, 240 path: file.dirinfo.path + `\` + name, 241 } 242 n-- 243 fi = append(fi, f) 244 } 245 if !wantAll && len(fi) == 0 { 246 return fi, io.EOF 247 } 248 return fi, nil 249 } 250 251 // readConsole reads utf16 characters from console File, 252 // encodes them into utf8 and stores them in buffer b. 253 // It returns the number of utf8 bytes read and an error, if any. 254 func (f *File) readConsole(b []byte) (n int, err error) { 255 if len(b) == 0 { 256 return 0, nil 257 } 258 if len(f.readbuf) == 0 { 259 // syscall.ReadConsole seems to fail, if given large buffer. 260 // So limit the buffer to 16000 characters. 261 numBytes := len(b) 262 if numBytes > 16000 { 263 numBytes = 16000 264 } 265 // get more input data from os 266 wchars := make([]uint16, numBytes) 267 var p *uint16 268 if len(b) > 0 { 269 p = &wchars[0] 270 } 271 var nw uint32 272 err := syscall.ReadConsole(f.fd, p, uint32(len(wchars)), &nw, nil) 273 if err != nil { 274 return 0, err 275 } 276 f.readbuf = utf16.Decode(wchars[:nw]) 277 } 278 for i, r := range f.readbuf { 279 if utf8.RuneLen(r) > len(b) { 280 f.readbuf = f.readbuf[i:] 281 return n, nil 282 } 283 nr := utf8.EncodeRune(b, r) 284 b = b[nr:] 285 n += nr 286 } 287 f.readbuf = nil 288 return n, nil 289 } 290 291 // read reads up to len(b) bytes from the File. 292 // It returns the number of bytes read and an error, if any. 293 func (f *File) read(b []byte) (n int, err error) { 294 f.l.Lock() 295 defer f.l.Unlock() 296 if f.isConsole { 297 return f.readConsole(b) 298 } 299 return fixCount(syscall.Read(f.fd, b)) 300 } 301 302 // pread reads len(b) bytes from the File starting at byte offset off. 303 // It returns the number of bytes read and the error, if any. 304 // EOF is signaled by a zero count with err set to 0. 305 func (f *File) pread(b []byte, off int64) (n int, err error) { 306 f.l.Lock() 307 defer f.l.Unlock() 308 curoffset, e := syscall.Seek(f.fd, 0, 1) 309 if e != nil { 310 return 0, e 311 } 312 defer syscall.Seek(f.fd, curoffset, 0) 313 o := syscall.Overlapped{ 314 OffsetHigh: uint32(off >> 32), 315 Offset: uint32(off), 316 } 317 var done uint32 318 e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o) 319 if e != nil { 320 if e == syscall.ERROR_HANDLE_EOF { 321 // end of file 322 return 0, nil 323 } 324 return 0, e 325 } 326 return int(done), nil 327 } 328 329 // writeConsole writes len(b) bytes to the console File. 330 // It returns the number of bytes written and an error, if any. 331 func (f *File) writeConsole(b []byte) (n int, err error) { 332 n = len(b) 333 runes := make([]rune, 0, 256) 334 if len(f.lastbits) > 0 { 335 b = append(f.lastbits, b...) 336 f.lastbits = nil 337 338 } 339 for len(b) >= utf8.UTFMax || utf8.FullRune(b) { 340 r, l := utf8.DecodeRune(b) 341 runes = append(runes, r) 342 b = b[l:] 343 } 344 if len(b) > 0 { 345 f.lastbits = make([]byte, len(b)) 346 copy(f.lastbits, b) 347 } 348 // syscall.WriteConsole seems to fail, if given large buffer. 349 // So limit the buffer to 16000 characters. This number was 350 // discovered by experimenting with syscall.WriteConsole. 351 const maxWrite = 16000 352 for len(runes) > 0 { 353 m := len(runes) 354 if m > maxWrite { 355 m = maxWrite 356 } 357 chunk := runes[:m] 358 runes = runes[m:] 359 uint16s := utf16.Encode(chunk) 360 for len(uint16s) > 0 { 361 var written uint32 362 err = syscall.WriteConsole(f.fd, &uint16s[0], uint32(len(uint16s)), &written, nil) 363 if err != nil { 364 return 0, nil 365 } 366 uint16s = uint16s[written:] 367 } 368 } 369 return n, nil 370 } 371 372 // write writes len(b) bytes to the File. 373 // It returns the number of bytes written and an error, if any. 374 func (f *File) write(b []byte) (n int, err error) { 375 f.l.Lock() 376 defer f.l.Unlock() 377 if f.isConsole { 378 return f.writeConsole(b) 379 } 380 return fixCount(syscall.Write(f.fd, b)) 381 } 382 383 // pwrite writes len(b) bytes to the File starting at byte offset off. 384 // It returns the number of bytes written and an error, if any. 385 func (f *File) pwrite(b []byte, off int64) (n int, err error) { 386 f.l.Lock() 387 defer f.l.Unlock() 388 curoffset, e := syscall.Seek(f.fd, 0, 1) 389 if e != nil { 390 return 0, e 391 } 392 defer syscall.Seek(f.fd, curoffset, 0) 393 o := syscall.Overlapped{ 394 OffsetHigh: uint32(off >> 32), 395 Offset: uint32(off), 396 } 397 var done uint32 398 e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o) 399 if e != nil { 400 return 0, e 401 } 402 return int(done), nil 403 } 404 405 // seek sets the offset for the next Read or Write on file to offset, interpreted 406 // according to whence: 0 means relative to the origin of the file, 1 means 407 // relative to the current offset, and 2 means relative to the end. 408 // It returns the new offset and an error, if any. 409 func (f *File) seek(offset int64, whence int) (ret int64, err error) { 410 f.l.Lock() 411 defer f.l.Unlock() 412 return syscall.Seek(f.fd, offset, whence) 413 } 414 415 // Truncate changes the size of the named file. 416 // If the file is a symbolic link, it changes the size of the link's target. 417 func Truncate(name string, size int64) error { 418 f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666) 419 if e != nil { 420 return e 421 } 422 defer f.Close() 423 e1 := f.Truncate(size) 424 if e1 != nil { 425 return e1 426 } 427 return nil 428 } 429 430 // Remove removes the named file or directory. 431 // If there is an error, it will be of type *PathError. 432 func Remove(name string) error { 433 p, e := syscall.UTF16PtrFromString(name) 434 if e != nil { 435 return &PathError{"remove", name, e} 436 } 437 438 // Go file interface forces us to know whether 439 // name is a file or directory. Try both. 440 e = syscall.DeleteFile(p) 441 if e == nil { 442 return nil 443 } 444 e1 := syscall.RemoveDirectory(p) 445 if e1 == nil { 446 return nil 447 } 448 449 // Both failed: figure out which error to return. 450 if e1 != e { 451 a, e2 := syscall.GetFileAttributes(p) 452 if e2 != nil { 453 e = e2 454 } else { 455 if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { 456 e = e1 457 } 458 } 459 } 460 return &PathError{"remove", name, e} 461 } 462 463 // Pipe returns a connected pair of Files; reads from r return bytes written to w. 464 // It returns the files and an error, if any. 465 func Pipe() (r *File, w *File, err error) { 466 var p [2]syscall.Handle 467 468 // See ../syscall/exec.go for description of lock. 469 syscall.ForkLock.RLock() 470 e := syscall.Pipe(p[0:]) 471 if e != nil { 472 syscall.ForkLock.RUnlock() 473 return nil, nil, NewSyscallError("pipe", e) 474 } 475 syscall.CloseOnExec(p[0]) 476 syscall.CloseOnExec(p[1]) 477 syscall.ForkLock.RUnlock() 478 479 return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil 480 } 481 482 // TempDir returns the default directory to use for temporary files. 483 func TempDir() string { 484 const pathSep = '\\' 485 dirw := make([]uint16, syscall.MAX_PATH) 486 n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0]) 487 if n > uint32(len(dirw)) { 488 dirw = make([]uint16, n) 489 n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0]) 490 if n > uint32(len(dirw)) { 491 n = 0 492 } 493 } 494 if n > 0 && dirw[n-1] == pathSep { 495 n-- 496 } 497 return string(utf16.Decode(dirw[0:n])) 498 } 499 500 // Link creates newname as a hard link to the oldname file. 501 // If there is an error, it will be of type *LinkError. 502 func Link(oldname, newname string) error { 503 n, err := syscall.UTF16PtrFromString(newname) 504 if err != nil { 505 return &LinkError{"link", oldname, newname, err} 506 } 507 o, err := syscall.UTF16PtrFromString(oldname) 508 if err != nil { 509 return &LinkError{"link", oldname, newname, err} 510 } 511 512 e := syscall.CreateHardLink(n, o, 0) 513 if e != nil { 514 return &LinkError{"link", oldname, newname, err} 515 } 516 return nil 517 } 518 519 // Symlink creates newname as a symbolic link to oldname. 520 // If there is an error, it will be of type *LinkError. 521 func Symlink(oldname, newname string) error { 522 // CreateSymbolicLink is not supported before Windows Vista 523 if syscall.LoadCreateSymbolicLink() != nil { 524 return &LinkError{"symlink", oldname, newname, syscall.EWINDOWS} 525 } 526 527 // '/' does not work in link's content 528 oldname = fromSlash(oldname) 529 530 // need the exact location of the oldname when its relative to determine if its a directory 531 destpath := oldname 532 if !isAbs(oldname) { 533 destpath = dirname(newname) + `\` + oldname 534 } 535 536 fi, err := Lstat(destpath) 537 isdir := err == nil && fi.IsDir() 538 539 n, err := syscall.UTF16PtrFromString(newname) 540 if err != nil { 541 return &LinkError{"symlink", oldname, newname, err} 542 } 543 o, err := syscall.UTF16PtrFromString(oldname) 544 if err != nil { 545 return &LinkError{"symlink", oldname, newname, err} 546 } 547 548 var flags uint32 549 if isdir { 550 flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY 551 } 552 err = syscall.CreateSymbolicLink(n, o, flags) 553 if err != nil { 554 return &LinkError{"symlink", oldname, newname, err} 555 } 556 return nil 557 } 558 559 func fromSlash(path string) string { 560 // Replace each '/' with '\\' if present 561 var pathbuf []byte 562 var lastSlash int 563 for i, b := range path { 564 if b == '/' { 565 if pathbuf == nil { 566 pathbuf = make([]byte, len(path)) 567 } 568 copy(pathbuf[lastSlash:], path[lastSlash:i]) 569 pathbuf[i] = '\\' 570 lastSlash = i + 1 571 } 572 } 573 if pathbuf == nil { 574 return path 575 } 576 577 copy(pathbuf[lastSlash:], path[lastSlash:]) 578 return string(pathbuf) 579 } 580 581 func dirname(path string) string { 582 vol := volumeName(path) 583 i := len(path) - 1 584 for i >= len(vol) && !IsPathSeparator(path[i]) { 585 i-- 586 } 587 dir := path[len(vol) : i+1] 588 last := len(dir) - 1 589 if last > 0 && IsPathSeparator(dir[last]) { 590 dir = dir[:last] 591 } 592 if dir == "" { 593 dir = "." 594 } 595 return vol + dir 596 }