github.com/rohankumardubey/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/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 func (file *File) Fd() uintptr { 40 if file == nil { 41 return uintptr(syscall.InvalidHandle) 42 } 43 return uintptr(file.fd) 44 } 45 46 // newFile returns a new File with the given file handle and name. 47 // Unlike NewFile, it does not check that h is syscall.InvalidHandle. 48 func newFile(h syscall.Handle, name string) *File { 49 f := &File{&file{fd: h, name: name}} 50 var m uint32 51 if syscall.GetConsoleMode(f.fd, &m) == nil { 52 f.isConsole = true 53 } 54 runtime.SetFinalizer(f.file, (*file).close) 55 return f 56 } 57 58 // NewFile returns a new File with the given file descriptor and name. 59 func NewFile(fd uintptr, name string) *File { 60 h := syscall.Handle(fd) 61 if h == syscall.InvalidHandle { 62 return nil 63 } 64 return newFile(h, name) 65 } 66 67 // Auxiliary information if the File describes a directory 68 type dirInfo struct { 69 data syscall.Win32finddata 70 needdata bool 71 path string 72 isempty bool // set if FindFirstFile returns ERROR_FILE_NOT_FOUND 73 } 74 75 func epipecheck(file *File, e error) { 76 } 77 78 const DevNull = "NUL" 79 80 func (f *file) isdir() bool { return f != nil && f.dirinfo != nil } 81 82 func openFile(name string, flag int, perm FileMode) (file *File, err error) { 83 r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm)) 84 if e != nil { 85 return nil, e 86 } 87 return NewFile(uintptr(r), name), nil 88 } 89 90 func openDir(name string) (file *File, err error) { 91 maskp, e := syscall.UTF16PtrFromString(name + `\*`) 92 if e != nil { 93 return nil, e 94 } 95 d := new(dirInfo) 96 r, e := syscall.FindFirstFile(maskp, &d.data) 97 if e != nil { 98 // FindFirstFile returns ERROR_FILE_NOT_FOUND when 99 // no matching files can be found. Then, if directory 100 // exists, we should proceed. 101 if e != syscall.ERROR_FILE_NOT_FOUND { 102 return nil, e 103 } 104 var fa syscall.Win32FileAttributeData 105 namep, e := syscall.UTF16PtrFromString(name) 106 if e != nil { 107 return nil, e 108 } 109 e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) 110 if e != nil { 111 return nil, e 112 } 113 if fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 { 114 return nil, e 115 } 116 d.isempty = true 117 } 118 d.path = name 119 if !isAbs(d.path) { 120 cwd, _ := Getwd() 121 d.path = cwd + `\` + d.path 122 } 123 f := newFile(r, name) 124 f.dirinfo = d 125 return f, nil 126 } 127 128 // OpenFile is the generalized open call; most users will use Open 129 // or Create instead. It opens the named file with specified flag 130 // (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, 131 // methods on the returned File can be used for I/O. 132 // If there is an error, it will be of type *PathError. 133 func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { 134 if name == "" { 135 return nil, &PathError{"open", name, syscall.ENOENT} 136 } 137 // TODO(brainman): not sure about my logic of assuming it is dir first, then fall back to file 138 r, e := openDir(name) 139 if e == nil { 140 if flag&O_WRONLY != 0 || flag&O_RDWR != 0 { 141 r.Close() 142 return nil, &PathError{"open", name, syscall.EISDIR} 143 } 144 return r, nil 145 } 146 r, e = openFile(name, flag, perm) 147 if e == nil { 148 return r, nil 149 } 150 return nil, &PathError{"open", name, e} 151 } 152 153 // Close closes the File, rendering it unusable for I/O. 154 // It returns an error, if any. 155 func (file *File) Close() error { 156 if file == nil { 157 return ErrInvalid 158 } 159 return file.file.close() 160 } 161 162 func (file *file) close() error { 163 if file == nil { 164 return syscall.EINVAL 165 } 166 if file.isdir() && file.dirinfo.isempty { 167 // "special" empty directories 168 return nil 169 } 170 if file.fd == syscall.InvalidHandle { 171 return syscall.EINVAL 172 } 173 var e error 174 if file.isdir() { 175 e = syscall.FindClose(syscall.Handle(file.fd)) 176 } else { 177 e = syscall.CloseHandle(syscall.Handle(file.fd)) 178 } 179 var err error 180 if e != nil { 181 err = &PathError{"close", file.name, e} 182 } 183 file.fd = syscall.InvalidHandle // so it can't be closed again 184 185 // no need for a finalizer anymore 186 runtime.SetFinalizer(file, nil) 187 return err 188 } 189 190 func (file *File) readdir(n int) (fi []FileInfo, err error) { 191 if file == nil { 192 return nil, syscall.EINVAL 193 } 194 if !file.isdir() { 195 return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR} 196 } 197 if !file.dirinfo.isempty && file.fd == syscall.InvalidHandle { 198 return nil, syscall.EINVAL 199 } 200 wantAll := n <= 0 201 size := n 202 if wantAll { 203 n = -1 204 size = 100 205 } 206 fi = make([]FileInfo, 0, size) // Empty with room to grow. 207 d := &file.dirinfo.data 208 for n != 0 && !file.dirinfo.isempty { 209 if file.dirinfo.needdata { 210 e := syscall.FindNextFile(syscall.Handle(file.fd), d) 211 if e != nil { 212 if e == syscall.ERROR_NO_MORE_FILES { 213 break 214 } else { 215 err = &PathError{"FindNextFile", file.name, e} 216 if !wantAll { 217 fi = nil 218 } 219 return 220 } 221 } 222 } 223 file.dirinfo.needdata = true 224 name := string(syscall.UTF16ToString(d.FileName[0:])) 225 if name == "." || name == ".." { // Useless names 226 continue 227 } 228 f := &fileStat{ 229 name: name, 230 sys: syscall.Win32FileAttributeData{ 231 FileAttributes: d.FileAttributes, 232 CreationTime: d.CreationTime, 233 LastAccessTime: d.LastAccessTime, 234 LastWriteTime: d.LastWriteTime, 235 FileSizeHigh: d.FileSizeHigh, 236 FileSizeLow: d.FileSizeLow, 237 }, 238 path: file.dirinfo.path + `\` + name, 239 } 240 n-- 241 fi = append(fi, f) 242 } 243 if !wantAll && len(fi) == 0 { 244 return fi, io.EOF 245 } 246 return fi, nil 247 } 248 249 // readConsole reads utf16 characters from console File, 250 // encodes them into utf8 and stores them in buffer b. 251 // It returns the number of utf8 bytes read and an error, if any. 252 func (f *File) readConsole(b []byte) (n int, err error) { 253 if len(b) == 0 { 254 return 0, nil 255 } 256 if len(f.readbuf) == 0 { 257 // syscall.ReadConsole seems to fail, if given large buffer. 258 // So limit the buffer to 16000 characters. 259 numBytes := len(b) 260 if numBytes > 16000 { 261 numBytes = 16000 262 } 263 // get more input data from os 264 wchars := make([]uint16, numBytes) 265 var p *uint16 266 if len(b) > 0 { 267 p = &wchars[0] 268 } 269 var nw uint32 270 err := syscall.ReadConsole(f.fd, p, uint32(len(wchars)), &nw, nil) 271 if err != nil { 272 return 0, err 273 } 274 f.readbuf = utf16.Decode(wchars[:nw]) 275 } 276 for i, r := range f.readbuf { 277 if utf8.RuneLen(r) > len(b) { 278 f.readbuf = f.readbuf[i:] 279 return n, nil 280 } 281 nr := utf8.EncodeRune(b, r) 282 b = b[nr:] 283 n += nr 284 } 285 f.readbuf = nil 286 return n, nil 287 } 288 289 // read reads up to len(b) bytes from the File. 290 // It returns the number of bytes read and an error, if any. 291 func (f *File) read(b []byte) (n int, err error) { 292 f.l.Lock() 293 defer f.l.Unlock() 294 if f.isConsole { 295 return f.readConsole(b) 296 } 297 return syscall.Read(f.fd, b) 298 } 299 300 // pread reads len(b) bytes from the File starting at byte offset off. 301 // It returns the number of bytes read and the error, if any. 302 // EOF is signaled by a zero count with err set to 0. 303 func (f *File) pread(b []byte, off int64) (n int, err error) { 304 f.l.Lock() 305 defer f.l.Unlock() 306 curoffset, e := syscall.Seek(f.fd, 0, 1) 307 if e != nil { 308 return 0, e 309 } 310 defer syscall.Seek(f.fd, curoffset, 0) 311 o := syscall.Overlapped{ 312 OffsetHigh: uint32(off >> 32), 313 Offset: uint32(off), 314 } 315 var done uint32 316 e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o) 317 if e != nil { 318 if e == syscall.ERROR_HANDLE_EOF { 319 // end of file 320 return 0, nil 321 } 322 return 0, e 323 } 324 return int(done), nil 325 } 326 327 // writeConsole writes len(b) bytes to the console File. 328 // It returns the number of bytes written and an error, if any. 329 func (f *File) writeConsole(b []byte) (n int, err error) { 330 n = len(b) 331 runes := make([]rune, 0, 256) 332 if len(f.lastbits) > 0 { 333 b = append(f.lastbits, b...) 334 f.lastbits = nil 335 336 } 337 for len(b) >= utf8.UTFMax || utf8.FullRune(b) { 338 r, l := utf8.DecodeRune(b) 339 runes = append(runes, r) 340 b = b[l:] 341 } 342 if len(b) > 0 { 343 f.lastbits = make([]byte, len(b)) 344 copy(f.lastbits, b) 345 } 346 // syscall.WriteConsole seems to fail, if given large buffer. 347 // So limit the buffer to 16000 characters. This number was 348 // discovered by experimenting with syscall.WriteConsole. 349 const maxWrite = 16000 350 for len(runes) > 0 { 351 m := len(runes) 352 if m > maxWrite { 353 m = maxWrite 354 } 355 chunk := runes[:m] 356 runes = runes[m:] 357 uint16s := utf16.Encode(chunk) 358 for len(uint16s) > 0 { 359 var written uint32 360 err = syscall.WriteConsole(f.fd, &uint16s[0], uint32(len(uint16s)), &written, nil) 361 if err != nil { 362 return 0, nil 363 } 364 uint16s = uint16s[written:] 365 } 366 } 367 return n, nil 368 } 369 370 // write writes len(b) bytes to the File. 371 // It returns the number of bytes written and an error, if any. 372 func (f *File) write(b []byte) (n int, err error) { 373 f.l.Lock() 374 defer f.l.Unlock() 375 if f.isConsole { 376 return f.writeConsole(b) 377 } 378 return syscall.Write(f.fd, b) 379 } 380 381 // pwrite writes len(b) bytes to the File starting at byte offset off. 382 // It returns the number of bytes written and an error, if any. 383 func (f *File) pwrite(b []byte, off int64) (n int, err error) { 384 f.l.Lock() 385 defer f.l.Unlock() 386 curoffset, e := syscall.Seek(f.fd, 0, 1) 387 if e != nil { 388 return 0, e 389 } 390 defer syscall.Seek(f.fd, curoffset, 0) 391 o := syscall.Overlapped{ 392 OffsetHigh: uint32(off >> 32), 393 Offset: uint32(off), 394 } 395 var done uint32 396 e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o) 397 if e != nil { 398 return 0, e 399 } 400 return int(done), nil 401 } 402 403 // seek sets the offset for the next Read or Write on file to offset, interpreted 404 // according to whence: 0 means relative to the origin of the file, 1 means 405 // relative to the current offset, and 2 means relative to the end. 406 // It returns the new offset and an error, if any. 407 func (f *File) seek(offset int64, whence int) (ret int64, err error) { 408 f.l.Lock() 409 defer f.l.Unlock() 410 return syscall.Seek(f.fd, offset, whence) 411 } 412 413 // Truncate changes the size of the named file. 414 // If the file is a symbolic link, it changes the size of the link's target. 415 func Truncate(name string, size int64) error { 416 f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666) 417 if e != nil { 418 return e 419 } 420 defer f.Close() 421 e1 := f.Truncate(size) 422 if e1 != nil { 423 return e1 424 } 425 return nil 426 } 427 428 // Remove removes the named file or directory. 429 // If there is an error, it will be of type *PathError. 430 func Remove(name string) error { 431 p, e := syscall.UTF16PtrFromString(name) 432 if e != nil { 433 return &PathError{"remove", name, e} 434 } 435 436 // Go file interface forces us to know whether 437 // name is a file or directory. Try both. 438 e = syscall.DeleteFile(p) 439 if e == nil { 440 return nil 441 } 442 e1 := syscall.RemoveDirectory(p) 443 if e1 == nil { 444 return nil 445 } 446 447 // Both failed: figure out which error to return. 448 if e1 != e { 449 a, e2 := syscall.GetFileAttributes(p) 450 if e2 != nil { 451 e = e2 452 } else { 453 if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { 454 e = e1 455 } 456 } 457 } 458 return &PathError{"remove", name, e} 459 } 460 461 // Pipe returns a connected pair of Files; reads from r return bytes written to w. 462 // It returns the files and an error, if any. 463 func Pipe() (r *File, w *File, err error) { 464 var p [2]syscall.Handle 465 466 // See ../syscall/exec.go for description of lock. 467 syscall.ForkLock.RLock() 468 e := syscall.Pipe(p[0:]) 469 if e != nil { 470 syscall.ForkLock.RUnlock() 471 return nil, nil, NewSyscallError("pipe", e) 472 } 473 syscall.CloseOnExec(p[0]) 474 syscall.CloseOnExec(p[1]) 475 syscall.ForkLock.RUnlock() 476 477 return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil 478 } 479 480 // TempDir returns the default directory to use for temporary files. 481 func TempDir() string { 482 const pathSep = '\\' 483 dirw := make([]uint16, syscall.MAX_PATH) 484 n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0]) 485 if n > uint32(len(dirw)) { 486 dirw = make([]uint16, n) 487 n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0]) 488 if n > uint32(len(dirw)) { 489 n = 0 490 } 491 } 492 if n > 0 && dirw[n-1] == pathSep { 493 n-- 494 } 495 return string(utf16.Decode(dirw[0:n])) 496 }