github.com/akaros/go-akaros@v0.0.0-20181004170632-85005d477eab/src/os/file_akaros.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 "runtime" 9 "sync/atomic" 10 "syscall" 11 "time" 12 ) 13 14 // File represents an open file descriptor. 15 type File struct { 16 *file 17 } 18 19 // file is the real representation of *File. 20 // The extra level of indirection ensures that no clients of os 21 // can overwrite this data, which could cause the finalizer 22 // to close the wrong file descriptor. 23 type file struct { 24 fd int 25 name string 26 dirinfo *dirInfo // nil unless directory being read 27 nepipe int32 // number of consecutive EPIPE in Write 28 iocount int32 // Count of outstanding I/O on this file 29 } 30 31 // Fd returns the integer Unix file descriptor referencing the open file. 32 func (f *File) Fd() uintptr { 33 if f == nil { 34 return ^(uintptr(0)) 35 } 36 return uintptr(f.fd) 37 } 38 39 // NewFile returns a new File with the given file descriptor and name. 40 func NewFile(fd uintptr, name string) *File { 41 fdi := int(fd) 42 if fdi < 0 { 43 return nil 44 } 45 f := &File{&file{fd: fdi, name: name}} 46 runtime.SetFinalizer(f.file, (*file).close) 47 return f 48 } 49 50 // Auxiliary information if the File describes a directory 51 type dirInfo struct { 52 buf []byte // buffer for directory I/O 53 nbuf int // length of buf; return value from Getdirentries 54 bufp int // location of next record in buf. 55 } 56 57 func sigpipe() // implemented in package runtime 58 func epipecheck(file *File, e error) { 59 if e == syscall.EPIPE { 60 if atomic.AddInt32(&file.nepipe, 1) >= 10 { 61 sigpipe() 62 } 63 } else { 64 atomic.StoreInt32(&file.nepipe, 0) 65 } 66 } 67 68 // DevNull is the name of the operating system's ``null device.'' 69 // On Unix-like systems, it is "/dev/null"; on Windows, "NUL". 70 const DevNull = "/dev/null" 71 72 // syscallMode returns the syscall-specific mode bits from Go's portable mode bits. 73 func syscallMode(i FileMode) (o uint32) { 74 o |= uint32(i.Perm()) 75 if i&ModeSetuid != 0 { 76 o |= syscall.S_ISUID 77 } 78 if i&ModeSetgid != 0 { 79 o |= syscall.S_ISGID 80 } 81 if i&ModeSticky != 0 { 82 o |= syscall.S_ISVTX 83 } 84 // No mapping for Go's ModeTemporary (plan9 only). 85 return 86 } 87 88 // OpenFile is the generalized open call; most users will use Open 89 // or Create instead. It opens the named file with specified flag 90 // (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, 91 // methods on the returned File can be used for I/O. 92 // If there is an error, it will be of type *PathError. 93 func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { 94 r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm)) 95 if e != nil { 96 return nil, &PathError{"open", name, e} 97 } 98 99 // There's a race here with fork/exec, which we are 100 // content to live with. See ../syscall/exec_unix.go. 101 if !supportsCloseOnExec { 102 syscall.CloseOnExec(r) 103 } 104 105 return NewFile(uintptr(r), name), nil 106 } 107 108 // Close closes the File, rendering it unusable for I/O. 109 // It returns an error, if any. 110 func (f *File) Close() error { 111 if f == nil { 112 return ErrInvalid 113 } 114 return f.file.close() 115 } 116 117 func (file *file) close() error { 118 if file == nil || file.fd < 0 { 119 return syscall.EINVAL 120 } 121 var err error 122 if e := syscall.Close(file.fd); e != nil { 123 err = &PathError{"close", file.name, e} 124 } 125 file.fd = -1 // so it can't be closed again 126 127 // no need for a finalizer anymore 128 runtime.SetFinalizer(file, nil) 129 return err 130 } 131 132 // Stat returns the FileInfo structure describing file. 133 // If there is an error, it will be of type *PathError. 134 func (f *File) Stat() (fi FileInfo, err error) { 135 if f == nil { 136 return nil, ErrInvalid 137 } 138 var stat syscall.Stat_t 139 atomic.AddInt32(&f.file.iocount, 1) 140 err = syscall.Fstat(f.fd, &stat) 141 atomic.AddInt32(&f.file.iocount, -1) 142 if err != nil { 143 return nil, &PathError{"stat", f.name, err} 144 } 145 return fileInfoFromStat(&stat, f.name), nil 146 } 147 148 // Stat returns a FileInfo describing the named file. 149 // If there is an error, it will be of type *PathError. 150 func Stat(name string) (fi FileInfo, err error) { 151 var stat syscall.Stat_t 152 err = syscall.Stat(name, &stat) 153 if err != nil { 154 return nil, &PathError{"stat", name, err} 155 } 156 return fileInfoFromStat(&stat, name), nil 157 } 158 159 // Lstat returns a FileInfo describing the named file. 160 // If the file is a symbolic link, the returned FileInfo 161 // describes the symbolic link. Lstat makes no attempt to follow the link. 162 // If there is an error, it will be of type *PathError. 163 func Lstat(name string) (fi FileInfo, err error) { 164 var stat syscall.Stat_t 165 err = syscall.Lstat(name, &stat) 166 if err != nil { 167 return nil, &PathError{"lstat", name, err} 168 } 169 return fileInfoFromStat(&stat, name), nil 170 } 171 172 func (f *File) readdir(n int) (fi []FileInfo, err error) { 173 dirname := f.name 174 if dirname == "" { 175 dirname = "." 176 } 177 names, err := f.Readdirnames(n) 178 fi = make([]FileInfo, 0, len(names)) 179 for _, filename := range names { 180 fip, lerr := lstat(dirname + "/" + filename) 181 if IsNotExist(lerr) { 182 // File disappeared between readdir + stat. 183 // Just treat it as if it didn't exist. 184 continue 185 } 186 if lerr != nil { 187 return fi, lerr 188 } 189 fi = append(fi, fip) 190 } 191 return fi, err 192 } 193 194 // Darwin and FreeBSD can't read or write 2GB+ at a time, 195 // even on 64-bit systems. See golang.org/issue/7812. 196 // Use 1GB instead of, say, 2GB-1, to keep subsequent 197 // reads aligned. 198 const ( 199 needsMaxRW = runtime.GOOS == "darwin" || runtime.GOOS == "freebsd" 200 maxRW = 1 << 30 201 ) 202 203 // read reads up to len(b) bytes from the File. 204 // It returns the number of bytes read and an error, if any. 205 func (f *File) read(b []byte) (n int, err error) { 206 if needsMaxRW && len(b) > maxRW { 207 b = b[:maxRW] 208 } 209 atomic.AddInt32(&f.file.iocount, 1) 210 n, err = syscall.Read(f.fd, b) 211 atomic.AddInt32(&f.file.iocount, -1) 212 return 213 } 214 215 // pread reads len(b) bytes from the File starting at byte offset off. 216 // It returns the number of bytes read and the error, if any. 217 // EOF is signaled by a zero count with err set to nil. 218 func (f *File) pread(b []byte, off int64) (n int, err error) { 219 if needsMaxRW && len(b) > maxRW { 220 b = b[:maxRW] 221 } 222 atomic.AddInt32(&f.file.iocount, 1) 223 n, err = syscall.Pread(f.fd, b, off) 224 atomic.AddInt32(&f.file.iocount, -1) 225 return 226 } 227 228 // write writes len(b) bytes to the File. 229 // It returns the number of bytes written and an error, if any. 230 func (f *File) write(b []byte) (n int, err error) { 231 for { 232 bcap := b 233 if needsMaxRW && len(bcap) > maxRW { 234 bcap = bcap[:maxRW] 235 } 236 atomic.AddInt32(&f.file.iocount, 1) 237 m, err := syscall.Write(f.fd, bcap) 238 atomic.AddInt32(&f.file.iocount, -1) 239 n += m 240 241 // If the syscall wrote some data but not all (short write) 242 // or it returned EINTR, then assume it stopped early for 243 // reasons that are uninteresting to the caller, and try again. 244 if 0 < m && m < len(bcap) || err == syscall.EINTR { 245 b = b[m:] 246 continue 247 } 248 249 if needsMaxRW && len(bcap) != len(b) && err == nil { 250 b = b[m:] 251 continue 252 } 253 254 return n, err 255 } 256 } 257 258 // pwrite writes len(b) bytes to the File starting at byte offset off. 259 // It returns the number of bytes written and an error, if any. 260 func (f *File) pwrite(b []byte, off int64) (n int, err error) { 261 if needsMaxRW && len(b) > maxRW { 262 b = b[:maxRW] 263 } 264 atomic.AddInt32(&f.file.iocount, 1) 265 n, err = syscall.Pwrite(f.fd, b, off) 266 atomic.AddInt32(&f.file.iocount, -1) 267 return 268 } 269 270 // seek sets the offset for the next Read or Write on file to offset, interpreted 271 // according to whence: 0 means relative to the origin of the file, 1 means 272 // relative to the current offset, and 2 means relative to the end. 273 // It returns the new offset and an error, if any. 274 func (f *File) seek(offset int64, whence int) (ret int64, err error) { 275 atomic.AddInt32(&f.file.iocount, 1) 276 ret, err = syscall.Seek(f.fd, offset, whence) 277 atomic.AddInt32(&f.file.iocount, -1) 278 return 279 } 280 281 // Truncate changes the size of the named file. 282 // If the file is a symbolic link, it changes the size of the link's target. 283 // If there is an error, it will be of type *PathError. 284 func Truncate(name string, size int64) error { 285 var d syscall.Dir 286 287 d.Null() 288 d.Length = size 289 290 var buf [syscall.STATFIXLEN]byte 291 n, err := d.Marshal(buf[:]) 292 if err != nil { 293 return &PathError{"truncate", name, err} 294 } 295 if err = syscall.Wstat(name, buf[:n], syscall.WSTAT_LENGTH); err != nil { 296 return &PathError{"truncate", name, err} 297 } 298 return nil 299 } 300 301 // Truncate changes the size of the named file. 302 // If the file is a symbolic link, it changes the size of the link's target. 303 // If there is an error, it will be of type *PathError. 304 func (f *File) Truncate(size int64) error { 305 if f == nil { 306 return ErrInvalid 307 } 308 309 var d syscall.Dir 310 d.Null() 311 d.Length = size 312 313 var buf [syscall.STATFIXLEN]byte 314 n, err := d.Marshal(buf[:]) 315 if err != nil { 316 return &PathError{"truncate", f.name, err} 317 } 318 if err = syscall.Fwstat(f.fd, buf[:n], syscall.WSTAT_LENGTH); err != nil { 319 return &PathError{"truncate", f.name, err} 320 } 321 return nil 322 } 323 324 // Remove removes the named file or directory. 325 // If there is an error, it will be of type *PathError. 326 func Remove(name string) error { 327 // System call interface forces us to know 328 // whether name is a file or directory. 329 // Try both: it is cheaper on average than 330 // doing a Stat plus the right one. 331 e := syscall.Unlink(name) 332 if e == nil { 333 return nil 334 } 335 e1 := syscall.Rmdir(name) 336 if e1 == nil { 337 return nil 338 } 339 340 // Both failed: figure out which error to return. 341 // OS X and Linux differ on whether unlink(dir) 342 // returns EISDIR, so can't use that. However, 343 // both agree that rmdir(file) returns ENOTDIR, 344 // so we can use that to decide which error is real. 345 // Rmdir might also return ENOTDIR if given a bad 346 // file path, like /etc/passwd/foo, but in that case, 347 // both errors will be ENOTDIR, so it's okay to 348 // use the error from unlink. 349 if e1 != syscall.ENOTDIR { 350 e = e1 351 } 352 return &PathError{"remove", name, e} 353 } 354 355 // Link creates newname as a hard link to the oldname file. 356 // If there is an error, it will be of type *LinkError. 357 func Link(oldname, newname string) error { 358 e := syscall.Link(oldname, newname) 359 if e != nil { 360 return &LinkError{"link", oldname, newname, e} 361 } 362 return nil 363 } 364 365 // Symlink creates newname as a symbolic link to oldname. 366 // If there is an error, it will be of type *LinkError. 367 func Symlink(oldname, newname string) error { 368 e := syscall.Symlink(oldname, newname) 369 if e != nil { 370 return &LinkError{"symlink", oldname, newname, e} 371 } 372 return nil 373 } 374 375 // Readlink returns the destination of the named symbolic link. 376 // If there is an error, it will be of type *PathError. 377 func Readlink(name string) (string, error) { 378 for len := 128; ; len *= 2 { 379 b := make([]byte, len) 380 n, e := syscall.Readlink(name, b) 381 if e != nil { 382 return "", &PathError{"readlink", name, e} 383 } 384 if n < len { 385 return string(b[0:n]), nil 386 } 387 } 388 } 389 390 // HasPrefix from the strings package. 391 func hasPrefix(s, prefix string) bool { 392 return len(s) >= len(prefix) && s[0:len(prefix)] == prefix 393 } 394 395 // Variant of LastIndex from the strings package. 396 func lastIndex(s string, sep byte) int { 397 for i := len(s) - 1; i >= 0; i-- { 398 if s[i] == sep { 399 return i 400 } 401 } 402 return -1 403 } 404 405 func rename(oldname, newname string) error { 406 e := syscall.Rename(oldname, newname) 407 if e != nil { 408 return &LinkError{"rename", oldname, newname, e} 409 } 410 return nil 411 } 412 413 const chmodMask = uint32(syscall.S_ISUID | syscall.S_ISGID | syscall.S_ISVTX | ModePerm) 414 415 // Chmod changes the mode of the named file to mode. 416 // If the file is a symbolic link, it changes the mode of the link's target. 417 // If there is an error, it will be of type *PathError. 418 func Chmod(name string, mode FileMode) error { 419 var d syscall.Dir 420 421 d.Null() 422 d.Mode = syscallMode(mode) & chmodMask 423 424 var buf [syscall.STATFIXLEN]byte 425 n, err := d.Marshal(buf[:]) 426 if err != nil { 427 return &PathError{"chmod", name, err} 428 } 429 if err = syscall.Wstat(name, buf[:n], syscall.WSTAT_MODE); err != nil { 430 return &PathError{"chmod", name, err} 431 } 432 return nil 433 } 434 435 // Chmod changes the mode of the file to mode. 436 // If there is an error, it will be of type *PathError. 437 func (f *File) Chmod(mode FileMode) error { 438 if f == nil { 439 return ErrInvalid 440 } 441 var d syscall.Dir 442 443 d.Null() 444 d.Mode = syscallMode(mode) & chmodMask 445 446 var buf [syscall.STATFIXLEN]byte 447 n, err := d.Marshal(buf[:]) 448 if err != nil { 449 return &PathError{"chmod", f.name, err} 450 } 451 if err = syscall.Fwstat(f.fd, buf[:n], syscall.WSTAT_MODE); err != nil { 452 return &PathError{"chmod", f.name, err} 453 } 454 return nil 455 } 456 457 // Sync commits the current contents of the file to stable storage. 458 // Typically, this means flushing the file system's in-memory copy 459 // of recently written data to disk. 460 func (f *File) Sync() (err error) { 461 if f == nil { 462 return ErrInvalid 463 } 464 if e := syscall.Fsync(f.fd); e != nil { 465 return NewSyscallError("fsync", e) 466 } 467 return nil 468 } 469 470 // Chown changes the numeric uid and gid of the named file. 471 // If the file is a symbolic link, it changes the uid and gid of the link's target. 472 // If there is an error, it will be of type *PathError. 473 func Chown(name string, uid, gid int) error { 474 if e := syscall.Chown(name, uid, gid); e != nil { 475 return &PathError{"chown", name, e} 476 } 477 return nil 478 } 479 480 // Lchown changes the numeric uid and gid of the named file. 481 // If the file is a symbolic link, it changes the uid and gid of the link itself. 482 // If there is an error, it will be of type *PathError. 483 func Lchown(name string, uid, gid int) error { 484 if e := syscall.Lchown(name, uid, gid); e != nil { 485 return &PathError{"lchown", name, e} 486 } 487 return nil 488 } 489 490 // Chown changes the numeric uid and gid of the named file. 491 // If there is an error, it will be of type *PathError. 492 func (f *File) Chown(uid, gid int) error { 493 if f == nil { 494 return ErrInvalid 495 } 496 if e := syscall.Fchown(f.fd, uid, gid); e != nil { 497 return &PathError{"chown", f.name, e} 498 } 499 return nil 500 } 501 502 // Chtimes changes the access and modification times of the named 503 // file, similar to the Unix utime() or utimes() functions. 504 // 505 // The underlying filesystem may truncate or round the values to a 506 // less precise time unit. 507 // If there is an error, it will be of type *PathError. 508 func Chtimes(name string, atime time.Time, mtime time.Time) error { 509 var d syscall.Dir 510 511 d.Null() 512 d.Atime = uint32(atime.Unix()) 513 d.Mtime = uint32(mtime.Unix()) 514 515 var buf [syscall.STATFIXLEN]byte 516 n, err := d.Marshal(buf[:]) 517 if err != nil { 518 return &PathError{"chtimes", name, err} 519 } 520 if err = syscall.Wstat(name, buf[:n], syscall.WSTAT_MTIME|syscall.WSTAT_ATIME); err != nil { 521 return &PathError{"chtimes", name, err} 522 } 523 return nil 524 } 525 526 // basename removes trailing slashes and the leading directory name from path name 527 func basename(name string) string { 528 i := len(name) - 1 529 // Remove trailing slashes 530 for ; i > 0 && name[i] == '/'; i-- { 531 name = name[:i] 532 } 533 // Remove leading directory name 534 for i--; i >= 0; i-- { 535 if name[i] == '/' { 536 name = name[i+1:] 537 break 538 } 539 } 540 541 return name 542 } 543 544 // TempDir returns the default directory to use for temporary files. 545 func TempDir() string { 546 dir := Getenv("TMPDIR") 547 if dir == "" { 548 dir = "/tmp" 549 } 550 return dir 551 } 552 553 func (file *File) AbortOutstandingSyscalls() { 554 for file.file.iocount > 0 { 555 syscall.AbortSyscFd(file.file.fd) 556 time.Sleep(100 * time.Millisecond) 557 } 558 }