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

View as plain text