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