Source file src/os/file_plan9.go

     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  	"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.
    20  func 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.
    28  type 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.
    44  func (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.
    54  func 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
    65  type 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  
    72  func 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".
    77  const DevNull = "/dev/null"
    78  
    79  // syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
    80  func 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.
    95  func 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  
   146  func 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.
   154  func (f *File) Close() error {
   155  	if f == nil {
   156  		return ErrInvalid
   157  	}
   158  	return f.file.close()
   159  }
   160  
   161  func (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.
   179  func (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.
   189  func (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.
   203  func (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  
   229  const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm)
   230  
   231  func (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.
   264  func (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.
   290  func (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.
   305  func (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.
   321  func (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.
   336  func (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.
   351  func (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.
   365  func 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.
   384  func 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  
   391  func 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.
   429  func 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.
   457  func 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.
   483  func 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.
   497  func 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.
   505  func Symlink(oldname, newname string) error {
   506  	return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
   507  }
   508  
   509  func 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.
   520  func 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.
   527  func 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.
   533  func (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  
   540  func 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.
   551  func (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.
   563  func (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.
   571  func (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.
   579  func (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.
   589  func (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  
   599  type rawConn struct{}
   600  
   601  func (c *rawConn) Control(f func(uintptr)) error {
   602  	return syscall.EPLAN9
   603  }
   604  
   605  func (c *rawConn) Read(f func(uintptr) bool) error {
   606  	return syscall.EPLAN9
   607  }
   608  
   609  func (c *rawConn) Write(f func(uintptr) bool) error {
   610  	return syscall.EPLAN9
   611  }
   612  
   613  func newRawConn(file *File) (*rawConn, error) {
   614  	return nil, syscall.EPLAN9
   615  }
   616  
   617  func ignoringEINTR(fn func() error) error {
   618  	return fn()
   619  }
   620  

View as plain text