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

View as plain text