github.com/brass-software/os@v0.0.0-20240129060254-960f457a5dea/file_plan9.go (about) 1 // Copyright 2011 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 "internal/bytealg" 9 "internal/poll" 10 "io" 11 "runtime" 12 "syscall" 13 "time" 14 ) 15 16 // fixLongPath is a noop on non-Windows platforms. 17 func fixLongPath(path string) string { 18 return path 19 } 20 21 // file is the real representation of *File. 22 // The extra level of indirection ensures that no clients of os 23 // can overwrite this data, which could cause the finalizer 24 // to close the wrong file descriptor. 25 type file struct { 26 fdmu poll.FDMutex 27 fd int 28 name string 29 dirinfo *dirInfo // nil unless directory being read 30 appendMode bool // whether file is opened for appending 31 } 32 33 // Fd returns the integer Plan 9 file descriptor referencing the open file. 34 // If f is closed, the file descriptor becomes invalid. 35 // If f is garbage collected, a finalizer may close the file descriptor, 36 // making it invalid; see runtime.SetFinalizer for more information on when 37 // a finalizer might be run. On Unix systems this will cause the SetDeadline 38 // methods to stop working. 39 // 40 // As an alternative, see the f.SyscallConn method. 41 func (f *File) Fd() uintptr { 42 if f == nil { 43 return ^(uintptr(0)) 44 } 45 return uintptr(f.fd) 46 } 47 48 // NewFile returns a new File with the given file descriptor and 49 // name. The returned value will be nil if fd is not a valid file 50 // descriptor. 51 func NewFile(fd uintptr, name string) *File { 52 fdi := int(fd) 53 if fdi < 0 { 54 return nil 55 } 56 f := &File{&file{fd: fdi, name: name}} 57 runtime.SetFinalizer(f.file, (*file).close) 58 return f 59 } 60 61 // Auxiliary information if the File describes a directory 62 type dirInfo struct { 63 buf [syscall.STATMAX]byte // buffer for directory I/O 64 nbuf int // length of buf; return value from Read 65 bufp int // location of next record in buf. 66 } 67 68 func epipecheck(file *File, e error) { 69 } 70 71 // DevNull is the name of the operating system's “null device.” 72 // On Unix-like systems, it is "/dev/null"; on Windows, "NUL". 73 const DevNull = "/dev/null" 74 75 // syscallMode returns the syscall-specific mode bits from Go's portable mode bits. 76 func syscallMode(i FileMode) (o uint32) { 77 o |= uint32(i.Perm()) 78 if i&ModeAppend != 0 { 79 o |= syscall.DMAPPEND 80 } 81 if i&ModeExclusive != 0 { 82 o |= syscall.DMEXCL 83 } 84 if i&ModeTemporary != 0 { 85 o |= syscall.DMTMP 86 } 87 return 88 } 89 90 // openFileNolog is the Plan 9 implementation of OpenFile. 91 func openFileNolog(name string, flag int, perm FileMode) (*File, error) { 92 var ( 93 fd int 94 e error 95 create bool 96 excl bool 97 trunc bool 98 append bool 99 ) 100 101 if flag&O_CREATE == O_CREATE { 102 flag = flag & ^O_CREATE 103 create = true 104 } 105 if flag&O_EXCL == O_EXCL { 106 excl = true 107 } 108 if flag&O_TRUNC == O_TRUNC { 109 trunc = true 110 } 111 // O_APPEND is emulated on Plan 9 112 if flag&O_APPEND == O_APPEND { 113 flag = flag &^ O_APPEND 114 append = true 115 } 116 117 if (create && trunc) || excl { 118 fd, e = syscall.Create(name, flag, syscallMode(perm)) 119 } else { 120 fd, e = syscall.Open(name, flag) 121 if IsNotExist(e) && create { 122 fd, e = syscall.Create(name, flag, syscallMode(perm)) 123 if e != nil { 124 return nil, &PathError{Op: "create", Path: name, Err: e} 125 } 126 } 127 } 128 129 if e != nil { 130 return nil, &PathError{Op: "open", Path: name, Err: e} 131 } 132 133 if append { 134 if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil { 135 return nil, &PathError{Op: "seek", Path: name, Err: e} 136 } 137 } 138 139 return NewFile(uintptr(fd), name), nil 140 } 141 142 // Close closes the File, rendering it unusable for I/O. 143 // On files that support SetDeadline, any pending I/O operations will 144 // be canceled and return immediately with an ErrClosed error. 145 // Close will return an error if it has already been called. 146 func (f *File) Close() error { 147 if f == nil { 148 return ErrInvalid 149 } 150 return f.file.close() 151 } 152 153 func (file *file) close() error { 154 if !file.fdmu.IncrefAndClose() { 155 return &PathError{Op: "close", Path: file.name, Err: ErrClosed} 156 } 157 158 // At this point we should cancel any pending I/O. 159 // How do we do that on Plan 9? 160 161 err := file.decref() 162 163 // no need for a finalizer anymore 164 runtime.SetFinalizer(file, nil) 165 return err 166 } 167 168 // destroy actually closes the descriptor. This is called when 169 // there are no remaining references, by the decref, readUnlock, 170 // and writeUnlock methods. 171 func (file *file) destroy() error { 172 var err error 173 if e := syscall.Close(file.fd); e != nil { 174 err = &PathError{Op: "close", Path: file.name, Err: e} 175 } 176 return err 177 } 178 179 // Stat returns the FileInfo structure describing file. 180 // If there is an error, it will be of type *PathError. 181 func (f *File) Stat() (FileInfo, error) { 182 if f == nil { 183 return nil, ErrInvalid 184 } 185 d, err := dirstat(f) 186 if err != nil { 187 return nil, err 188 } 189 return fileInfoFromStat(d), nil 190 } 191 192 // Truncate changes the size of the file. 193 // It does not change the I/O offset. 194 // If there is an error, it will be of type *PathError. 195 func (f *File) Truncate(size int64) error { 196 if f == nil { 197 return ErrInvalid 198 } 199 200 var d syscall.Dir 201 d.Null() 202 d.Length = size 203 204 var buf [syscall.STATFIXLEN]byte 205 n, err := d.Marshal(buf[:]) 206 if err != nil { 207 return &PathError{Op: "truncate", Path: f.name, Err: err} 208 } 209 210 if err := f.incref("truncate"); err != nil { 211 return err 212 } 213 defer f.decref() 214 215 if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { 216 return &PathError{Op: "truncate", Path: f.name, Err: err} 217 } 218 return nil 219 } 220 221 const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm) 222 223 func (f *File) chmod(mode FileMode) error { 224 if f == nil { 225 return ErrInvalid 226 } 227 var d syscall.Dir 228 229 odir, e := dirstat(f) 230 if e != nil { 231 return &PathError{Op: "chmod", Path: f.name, Err: e} 232 } 233 d.Null() 234 d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask 235 236 var buf [syscall.STATFIXLEN]byte 237 n, err := d.Marshal(buf[:]) 238 if err != nil { 239 return &PathError{Op: "chmod", Path: f.name, Err: err} 240 } 241 242 if err := f.incref("chmod"); err != nil { 243 return err 244 } 245 defer f.decref() 246 247 if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { 248 return &PathError{Op: "chmod", Path: f.name, Err: err} 249 } 250 return nil 251 } 252 253 // Sync commits the current contents of the file to stable storage. 254 // Typically, this means flushing the file system's in-memory copy 255 // of recently written data to disk. 256 func (f *File) Sync() error { 257 if f == nil { 258 return ErrInvalid 259 } 260 var d syscall.Dir 261 d.Null() 262 263 var buf [syscall.STATFIXLEN]byte 264 n, err := d.Marshal(buf[:]) 265 if err != nil { 266 return &PathError{Op: "sync", Path: f.name, Err: err} 267 } 268 269 if err := f.incref("sync"); err != nil { 270 return err 271 } 272 defer f.decref() 273 274 if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { 275 return &PathError{Op: "sync", Path: f.name, Err: err} 276 } 277 return nil 278 } 279 280 // read reads up to len(b) bytes from the File. 281 // It returns the number of bytes read and an error, if any. 282 func (f *File) read(b []byte) (n int, err error) { 283 if err := f.readLock(); err != nil { 284 return 0, err 285 } 286 defer f.readUnlock() 287 n, e := fixCount(syscall.Read(f.fd, b)) 288 if n == 0 && len(b) > 0 && e == nil { 289 return 0, io.EOF 290 } 291 return n, e 292 } 293 294 // pread reads len(b) bytes from the File starting at byte offset off. 295 // It returns the number of bytes read and the error, if any. 296 // EOF is signaled by a zero count with err set to nil. 297 func (f *File) pread(b []byte, off int64) (n int, err error) { 298 if err := f.readLock(); err != nil { 299 return 0, err 300 } 301 defer f.readUnlock() 302 n, e := fixCount(syscall.Pread(f.fd, b, off)) 303 if n == 0 && len(b) > 0 && e == nil { 304 return 0, io.EOF 305 } 306 return n, e 307 } 308 309 // write writes len(b) bytes to the File. 310 // It returns the number of bytes written and an error, if any. 311 // Since Plan 9 preserves message boundaries, never allow 312 // a zero-byte write. 313 func (f *File) write(b []byte) (n int, err error) { 314 if err := f.writeLock(); err != nil { 315 return 0, err 316 } 317 defer f.writeUnlock() 318 if len(b) == 0 { 319 return 0, nil 320 } 321 return fixCount(syscall.Write(f.fd, b)) 322 } 323 324 // pwrite writes len(b) bytes to the File starting at byte offset off. 325 // It returns the number of bytes written and an error, if any. 326 // Since Plan 9 preserves message boundaries, never allow 327 // a zero-byte write. 328 func (f *File) pwrite(b []byte, off int64) (n int, err error) { 329 if err := f.writeLock(); err != nil { 330 return 0, err 331 } 332 defer f.writeUnlock() 333 if len(b) == 0 { 334 return 0, nil 335 } 336 return fixCount(syscall.Pwrite(f.fd, b, off)) 337 } 338 339 // seek sets the offset for the next Read or Write on file to offset, interpreted 340 // according to whence: 0 means relative to the origin of the file, 1 means 341 // relative to the current offset, and 2 means relative to the end. 342 // It returns the new offset and an error, if any. 343 func (f *File) seek(offset int64, whence int) (ret int64, err error) { 344 if err := f.incref(""); err != nil { 345 return 0, err 346 } 347 defer f.decref() 348 if f.dirinfo != nil { 349 // Free cached dirinfo, so we allocate a new one if we 350 // access this file as a directory again. See #35767 and #37161. 351 f.dirinfo = nil 352 } 353 return syscall.Seek(f.fd, offset, whence) 354 } 355 356 // Truncate changes the size of the named file. 357 // If the file is a symbolic link, it changes the size of the link's target. 358 // If there is an error, it will be of type *PathError. 359 func Truncate(name string, size int64) error { 360 var d syscall.Dir 361 362 d.Null() 363 d.Length = size 364 365 var buf [syscall.STATFIXLEN]byte 366 n, err := d.Marshal(buf[:]) 367 if err != nil { 368 return &PathError{Op: "truncate", Path: name, Err: err} 369 } 370 if err = syscall.Wstat(name, buf[:n]); err != nil { 371 return &PathError{Op: "truncate", Path: name, Err: err} 372 } 373 return nil 374 } 375 376 // Remove removes the named file or directory. 377 // If there is an error, it will be of type *PathError. 378 func Remove(name string) error { 379 if e := syscall.Remove(name); e != nil { 380 return &PathError{Op: "remove", Path: name, Err: e} 381 } 382 return nil 383 } 384 385 // hasPrefix from the strings package. 386 func hasPrefix(s, prefix string) bool { 387 return len(s) >= len(prefix) && s[0:len(prefix)] == prefix 388 } 389 390 func rename(oldname, newname string) error { 391 dirname := oldname[:bytealg.LastIndexByteString(oldname, '/')+1] 392 if hasPrefix(newname, dirname) { 393 newname = newname[len(dirname):] 394 } else { 395 return &LinkError{"rename", oldname, newname, ErrInvalid} 396 } 397 398 // If newname still contains slashes after removing the oldname 399 // prefix, the rename is cross-directory and must be rejected. 400 if bytealg.LastIndexByteString(newname, '/') >= 0 { 401 return &LinkError{"rename", oldname, newname, ErrInvalid} 402 } 403 404 var d syscall.Dir 405 406 d.Null() 407 d.Name = newname 408 409 buf := make([]byte, syscall.STATFIXLEN+len(d.Name)) 410 n, err := d.Marshal(buf[:]) 411 if err != nil { 412 return &LinkError{"rename", oldname, newname, err} 413 } 414 415 // If newname already exists and is not a directory, rename replaces it. 416 f, err := Stat(dirname + newname) 417 if err == nil && !f.IsDir() { 418 Remove(dirname + newname) 419 } 420 421 if err = syscall.Wstat(oldname, buf[:n]); err != nil { 422 return &LinkError{"rename", oldname, newname, err} 423 } 424 return nil 425 } 426 427 // See docs in file.go:Chmod. 428 func chmod(name string, mode FileMode) error { 429 var d syscall.Dir 430 431 odir, e := dirstat(name) 432 if e != nil { 433 return &PathError{Op: "chmod", Path: name, Err: e} 434 } 435 d.Null() 436 d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask 437 438 var buf [syscall.STATFIXLEN]byte 439 n, err := d.Marshal(buf[:]) 440 if err != nil { 441 return &PathError{Op: "chmod", Path: name, Err: err} 442 } 443 if err = syscall.Wstat(name, buf[:n]); err != nil { 444 return &PathError{Op: "chmod", Path: name, Err: err} 445 } 446 return nil 447 } 448 449 // Chtimes changes the access and modification times of the named 450 // file, similar to the Unix utime() or utimes() functions. 451 // A zero time.Time value will leave the corresponding file time unchanged. 452 // 453 // The underlying filesystem may truncate or round the values to a 454 // less precise time unit. 455 // If there is an error, it will be of type *PathError. 456 func Chtimes(name string, atime time.Time, mtime time.Time) error { 457 var d syscall.Dir 458 459 d.Null() 460 d.Atime = uint32(atime.Unix()) 461 d.Mtime = uint32(mtime.Unix()) 462 if atime.IsZero() { 463 d.Atime = 0xFFFFFFFF 464 } 465 if mtime.IsZero() { 466 d.Mtime = 0xFFFFFFFF 467 } 468 469 var buf [syscall.STATFIXLEN]byte 470 n, err := d.Marshal(buf[:]) 471 if err != nil { 472 return &PathError{Op: "chtimes", Path: name, Err: err} 473 } 474 if err = syscall.Wstat(name, buf[:n]); err != nil { 475 return &PathError{Op: "chtimes", Path: name, Err: err} 476 } 477 return nil 478 } 479 480 // Pipe returns a connected pair of Files; reads from r return bytes 481 // written to w. It returns the files and an error, if any. 482 func Pipe() (r *File, w *File, err error) { 483 var p [2]int 484 485 if e := syscall.Pipe(p[0:]); e != nil { 486 return nil, nil, NewSyscallError("pipe", e) 487 } 488 489 return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil 490 } 491 492 // not supported on Plan 9 493 494 // Link creates newname as a hard link to the oldname file. 495 // If there is an error, it will be of type *LinkError. 496 func Link(oldname, newname string) error { 497 return &LinkError{"link", oldname, newname, syscall.EPLAN9} 498 } 499 500 // Symlink creates newname as a symbolic link to oldname. 501 // On Windows, a symlink to a non-existent oldname creates a file symlink; 502 // if oldname is later created as a directory the symlink will not work. 503 // If there is an error, it will be of type *LinkError. 504 func Symlink(oldname, newname string) error { 505 return &LinkError{"symlink", oldname, newname, syscall.EPLAN9} 506 } 507 508 func readlink(name string) (string, error) { 509 return "", &PathError{Op: "readlink", Path: name, Err: syscall.EPLAN9} 510 } 511 512 // Chown changes the numeric uid and gid of the named file. 513 // If the file is a symbolic link, it changes the uid and gid of the link's target. 514 // A uid or gid of -1 means to not change that value. 515 // If there is an error, it will be of type *PathError. 516 // 517 // On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or 518 // EPLAN9 error, wrapped in *PathError. 519 func Chown(name string, uid, gid int) error { 520 return &PathError{Op: "chown", Path: name, Err: syscall.EPLAN9} 521 } 522 523 // Lchown changes the numeric uid and gid of the named file. 524 // If the file is a symbolic link, it changes the uid and gid of the link itself. 525 // If there is an error, it will be of type *PathError. 526 func Lchown(name string, uid, gid int) error { 527 return &PathError{Op: "lchown", Path: name, Err: syscall.EPLAN9} 528 } 529 530 // Chown changes the numeric uid and gid of the named file. 531 // If there is an error, it will be of type *PathError. 532 func (f *File) Chown(uid, gid int) error { 533 if f == nil { 534 return ErrInvalid 535 } 536 return &PathError{Op: "chown", Path: f.name, Err: syscall.EPLAN9} 537 } 538 539 func tempDir() string { 540 dir := Getenv("TMPDIR") 541 if dir == "" { 542 dir = "/tmp" 543 } 544 return dir 545 } 546 547 // Chdir changes the current working directory to the file, 548 // which must be a directory. 549 // If there is an error, it will be of type *PathError. 550 func (f *File) Chdir() error { 551 if err := f.incref("chdir"); err != nil { 552 return err 553 } 554 defer f.decref() 555 if e := syscall.Fchdir(f.fd); e != nil { 556 return &PathError{Op: "chdir", Path: f.name, Err: e} 557 } 558 return nil 559 } 560 561 // setDeadline sets the read and write deadline. 562 func (f *File) setDeadline(time.Time) error { 563 if err := f.checkValid("SetDeadline"); err != nil { 564 return err 565 } 566 return poll.ErrNoDeadline 567 } 568 569 // setReadDeadline sets the read deadline. 570 func (f *File) setReadDeadline(time.Time) error { 571 if err := f.checkValid("SetReadDeadline"); err != nil { 572 return err 573 } 574 return poll.ErrNoDeadline 575 } 576 577 // setWriteDeadline sets the write deadline. 578 func (f *File) setWriteDeadline(time.Time) error { 579 if err := f.checkValid("SetWriteDeadline"); err != nil { 580 return err 581 } 582 return poll.ErrNoDeadline 583 } 584 585 // checkValid checks whether f is valid for use, but does not prepare 586 // to actually use it. If f is not ready checkValid returns an appropriate 587 // error, perhaps incorporating the operation name op. 588 func (f *File) checkValid(op string) error { 589 if f == nil { 590 return ErrInvalid 591 } 592 if err := f.incref(op); err != nil { 593 return err 594 } 595 return f.decref() 596 } 597 598 type rawConn struct{} 599 600 func (c *rawConn) Control(f func(uintptr)) error { 601 return syscall.EPLAN9 602 } 603 604 func (c *rawConn) Read(f func(uintptr) bool) error { 605 return syscall.EPLAN9 606 } 607 608 func (c *rawConn) Write(f func(uintptr) bool) error { 609 return syscall.EPLAN9 610 } 611 612 func newRawConn(file *File) (*rawConn, error) { 613 return nil, syscall.EPLAN9 614 } 615 616 func ignoringEINTR(fn func() error) error { 617 return fn() 618 }