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  	return openFileNolog(name, O_RDONLY, 0)
   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.sysfd); 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.sysfd, 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.sysfd, 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.sysfd, 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.sysfd, 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.sysfd, 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.sysfd, 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.sysfd, 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  	// Free cached dirinfo, so we allocate a new one if we
   349  	// access this file as a directory again. See #35767 and #37161.
   350  	f.dirinfo.Store(nil)
   351  	return syscall.Seek(f.sysfd, offset, whence)
   352  }
   353  
   354  // Truncate changes the size of the named file.
   355  // If the file is a symbolic link, it changes the size of the link's target.
   356  // If there is an error, it will be of type [*PathError].
   357  func Truncate(name string, size int64) error {
   358  	var d syscall.Dir
   359  
   360  	d.Null()
   361  	d.Length = size
   362  
   363  	var buf [syscall.STATFIXLEN]byte
   364  	n, err := d.Marshal(buf[:])
   365  	if err != nil {
   366  		return &PathError{Op: "truncate", Path: name, Err: err}
   367  	}
   368  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   369  		return &PathError{Op: "truncate", Path: name, Err: err}
   370  	}
   371  	return nil
   372  }
   373  
   374  // Remove removes the named file or directory.
   375  // If there is an error, it will be of type [*PathError].
   376  func Remove(name string) error {
   377  	if e := syscall.Remove(name); e != nil {
   378  		return &PathError{Op: "remove", Path: name, Err: e}
   379  	}
   380  	return nil
   381  }
   382  
   383  func rename(oldname, newname string) error {
   384  	dirname := oldname[:bytealg.LastIndexByteString(oldname, '/')+1]
   385  	if stringslite.HasPrefix(newname, dirname) {
   386  		newname = newname[len(dirname):]
   387  	} else {
   388  		return &LinkError{"rename", oldname, newname, ErrInvalid}
   389  	}
   390  
   391  	// If newname still contains slashes after removing the oldname
   392  	// prefix, the rename is cross-directory and must be rejected.
   393  	if bytealg.LastIndexByteString(newname, '/') >= 0 {
   394  		return &LinkError{"rename", oldname, newname, ErrInvalid}
   395  	}
   396  
   397  	var d syscall.Dir
   398  
   399  	d.Null()
   400  	d.Name = newname
   401  
   402  	buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
   403  	n, err := d.Marshal(buf[:])
   404  	if err != nil {
   405  		return &LinkError{"rename", oldname, newname, err}
   406  	}
   407  
   408  	// If newname already exists and is not a directory, rename replaces it.
   409  	f, err := Stat(dirname + newname)
   410  	if err == nil && !f.IsDir() {
   411  		Remove(dirname + newname)
   412  	}
   413  
   414  	if err = syscall.Wstat(oldname, buf[:n]); err != nil {
   415  		return &LinkError{"rename", oldname, newname, err}
   416  	}
   417  	return nil
   418  }
   419  
   420  // See docs in file.go:Chmod.
   421  func chmod(name string, mode FileMode) error {
   422  	var d syscall.Dir
   423  
   424  	odir, e := dirstat(name)
   425  	if e != nil {
   426  		return &PathError{Op: "chmod", Path: name, Err: e}
   427  	}
   428  	d.Null()
   429  	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
   430  
   431  	var buf [syscall.STATFIXLEN]byte
   432  	n, err := d.Marshal(buf[:])
   433  	if err != nil {
   434  		return &PathError{Op: "chmod", Path: name, Err: err}
   435  	}
   436  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   437  		return &PathError{Op: "chmod", Path: name, Err: err}
   438  	}
   439  	return nil
   440  }
   441  
   442  // Chtimes changes the access and modification times of the named
   443  // file, similar to the Unix utime() or utimes() functions.
   444  // A zero time.Time value will leave the corresponding file time unchanged.
   445  //
   446  // The underlying filesystem may truncate or round the values to a
   447  // less precise time unit.
   448  // If there is an error, it will be of type [*PathError].
   449  func Chtimes(name string, atime time.Time, mtime time.Time) error {
   450  	var d syscall.Dir
   451  
   452  	d.Null()
   453  	d.Atime = uint32(atime.Unix())
   454  	d.Mtime = uint32(mtime.Unix())
   455  	if atime.IsZero() {
   456  		d.Atime = 0xFFFFFFFF
   457  	}
   458  	if mtime.IsZero() {
   459  		d.Mtime = 0xFFFFFFFF
   460  	}
   461  
   462  	var buf [syscall.STATFIXLEN]byte
   463  	n, err := d.Marshal(buf[:])
   464  	if err != nil {
   465  		return &PathError{Op: "chtimes", Path: name, Err: err}
   466  	}
   467  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   468  		return &PathError{Op: "chtimes", Path: name, Err: err}
   469  	}
   470  	return nil
   471  }
   472  
   473  // Pipe returns a connected pair of Files; reads from r return bytes
   474  // written to w. It returns the files and an error, if any.
   475  func Pipe() (r *File, w *File, err error) {
   476  	var p [2]int
   477  
   478  	if e := syscall.Pipe(p[0:]); e != nil {
   479  		return nil, nil, NewSyscallError("pipe", e)
   480  	}
   481  
   482  	return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
   483  }
   484  
   485  // not supported on Plan 9
   486  
   487  // Link creates newname as a hard link to the oldname file.
   488  // If there is an error, it will be of type *LinkError.
   489  func Link(oldname, newname string) error {
   490  	return &LinkError{"link", oldname, newname, syscall.EPLAN9}
   491  }
   492  
   493  // Symlink creates newname as a symbolic link to oldname.
   494  // On Windows, a symlink to a non-existent oldname creates a file symlink;
   495  // if oldname is later created as a directory the symlink will not work.
   496  // If there is an error, it will be of type *LinkError.
   497  func Symlink(oldname, newname string) error {
   498  	return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
   499  }
   500  
   501  func readlink(name string) (string, error) {
   502  	return "", &PathError{Op: "readlink", Path: name, Err: syscall.EPLAN9}
   503  }
   504  
   505  // Chown changes the numeric uid and gid of the named file.
   506  // If the file is a symbolic link, it changes the uid and gid of the link's target.
   507  // A uid or gid of -1 means to not change that value.
   508  // If there is an error, it will be of type [*PathError].
   509  //
   510  // On Windows or Plan 9, Chown always returns the [syscall.EWINDOWS] or
   511  // [syscall.EPLAN9] error, wrapped in [*PathError].
   512  func Chown(name string, uid, gid int) error {
   513  	return &PathError{Op: "chown", Path: name, Err: syscall.EPLAN9}
   514  }
   515  
   516  // Lchown changes the numeric uid and gid of the named file.
   517  // If the file is a symbolic link, it changes the uid and gid of the link itself.
   518  // If there is an error, it will be of type [*PathError].
   519  func Lchown(name string, uid, gid int) error {
   520  	return &PathError{Op: "lchown", Path: name, Err: syscall.EPLAN9}
   521  }
   522  
   523  // Chown changes the numeric uid and gid of the named file.
   524  // If there is an error, it will be of type [*PathError].
   525  func (f *File) Chown(uid, gid int) error {
   526  	if f == nil {
   527  		return ErrInvalid
   528  	}
   529  	return &PathError{Op: "chown", Path: f.name, Err: syscall.EPLAN9}
   530  }
   531  
   532  func tempDir() string {
   533  	dir := Getenv("TMPDIR")
   534  	if dir == "" {
   535  		dir = "/tmp"
   536  	}
   537  	return dir
   538  }
   539  
   540  // Chdir changes the current working directory to the file,
   541  // which must be a directory.
   542  // If there is an error, it will be of type [*PathError].
   543  func (f *File) Chdir() error {
   544  	if err := f.incref("chdir"); err != nil {
   545  		return err
   546  	}
   547  	defer f.decref()
   548  	if e := syscall.Fchdir(f.sysfd); e != nil {
   549  		return &PathError{Op: "chdir", Path: f.name, Err: e}
   550  	}
   551  	if log := testlog.Logger(); log != nil {
   552  		wd, err := Getwd()
   553  		if err == nil {
   554  			log.Chdir(wd)
   555  		}
   556  	}
   557  	return nil
   558  }
   559  
   560  // setDeadline sets the read and write deadline.
   561  func (f *File) setDeadline(time.Time) error {
   562  	if err := f.checkValid("SetDeadline"); err != nil {
   563  		return err
   564  	}
   565  	return poll.ErrNoDeadline
   566  }
   567  
   568  // setReadDeadline sets the read deadline.
   569  func (f *File) setReadDeadline(time.Time) error {
   570  	if err := f.checkValid("SetReadDeadline"); err != nil {
   571  		return err
   572  	}
   573  	return poll.ErrNoDeadline
   574  }
   575  
   576  // setWriteDeadline sets the write deadline.
   577  func (f *File) setWriteDeadline(time.Time) error {
   578  	if err := f.checkValid("SetWriteDeadline"); err != nil {
   579  		return err
   580  	}
   581  	return poll.ErrNoDeadline
   582  }
   583  
   584  // checkValid checks whether f is valid for use, but does not prepare
   585  // to actually use it. If f is not ready checkValid returns an appropriate
   586  // error, perhaps incorporating the operation name op.
   587  func (f *File) checkValid(op string) error {
   588  	if f == nil {
   589  		return ErrInvalid
   590  	}
   591  	if err := f.incref(op); err != nil {
   592  		return err
   593  	}
   594  	return f.decref()
   595  }
   596  
   597  type rawConn struct{}
   598  
   599  func (c *rawConn) Control(f func(uintptr)) error {
   600  	return syscall.EPLAN9
   601  }
   602  
   603  func (c *rawConn) Read(f func(uintptr) bool) error {
   604  	return syscall.EPLAN9
   605  }
   606  
   607  func (c *rawConn) Write(f func(uintptr) bool) error {
   608  	return syscall.EPLAN9
   609  }
   610  
   611  func newRawConn(file *File) (*rawConn, error) {
   612  	return nil, syscall.EPLAN9
   613  }
   614  
   615  func ignoringEINTR(fn func() error) error {
   616  	return fn()
   617  }
   618  
   619  func ignoringEINTR2[T any](fn func() (T, error)) (T, error) {
   620  	return fn()
   621  }
   622  

View as plain text