github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/os/file_plan9.go (about)

     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/poll"
     9  	"io"
    10  	"runtime"
    11  	"syscall"
    12  	"time"
    13  )
    14  
    15  // fixLongPath is a noop on non-Windows platforms.
    16  func fixLongPath(path string) string {
    17  	return path
    18  }
    19  
    20  // file is the real representation of *File.
    21  // The extra level of indirection ensures that no clients of os
    22  // can overwrite this data, which could cause the finalizer
    23  // to close the wrong file descriptor.
    24  type file struct {
    25  	fdmu       poll.FDMutex
    26  	fd         int
    27  	name       string
    28  	dirinfo    *dirInfo // nil unless directory being read
    29  	appendMode bool     // whether file is opened for appending
    30  }
    31  
    32  // Fd returns the integer Plan 9 file descriptor referencing the open file.
    33  // If f is closed, the file descriptor becomes invalid.
    34  // If f is garbage collected, a finalizer may close the file descriptor,
    35  // making it invalid; see runtime.SetFinalizer for more information on when
    36  // a finalizer might be run. On Unix systems this will cause the SetDeadline
    37  // methods to stop working.
    38  //
    39  // As an alternative, see the f.SyscallConn method.
    40  func (f *File) Fd() uintptr {
    41  	if f == nil {
    42  		return ^(uintptr(0))
    43  	}
    44  	return uintptr(f.fd)
    45  }
    46  
    47  // NewFile returns a new File with the given file descriptor and
    48  // name. The returned value will be nil if fd is not a valid file
    49  // descriptor.
    50  func NewFile(fd uintptr, name string) *File {
    51  	fdi := int(fd)
    52  	if fdi < 0 {
    53  		return nil
    54  	}
    55  	f := &File{&file{fd: fdi, name: name}}
    56  	runtime.SetFinalizer(f.file, (*file).close)
    57  	return f
    58  }
    59  
    60  // Auxiliary information if the File describes a directory
    61  type dirInfo struct {
    62  	buf  [syscall.STATMAX]byte // buffer for directory I/O
    63  	nbuf int                   // length of buf; return value from Read
    64  	bufp int                   // location of next record in buf.
    65  }
    66  
    67  func epipecheck(file *File, e error) {
    68  }
    69  
    70  // DevNull is the name of the operating system's “null device.”
    71  // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
    72  const DevNull = "/dev/null"
    73  
    74  // syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
    75  func syscallMode(i FileMode) (o uint32) {
    76  	o |= uint32(i.Perm())
    77  	if i&ModeAppend != 0 {
    78  		o |= syscall.DMAPPEND
    79  	}
    80  	if i&ModeExclusive != 0 {
    81  		o |= syscall.DMEXCL
    82  	}
    83  	if i&ModeTemporary != 0 {
    84  		o |= syscall.DMTMP
    85  	}
    86  	return
    87  }
    88  
    89  // openFileNolog is the Plan 9 implementation of OpenFile.
    90  func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
    91  	var (
    92  		fd     int
    93  		e      error
    94  		create bool
    95  		excl   bool
    96  		trunc  bool
    97  		append bool
    98  	)
    99  
   100  	if flag&O_CREATE == O_CREATE {
   101  		flag = flag & ^O_CREATE
   102  		create = true
   103  	}
   104  	if flag&O_EXCL == O_EXCL {
   105  		excl = true
   106  	}
   107  	if flag&O_TRUNC == O_TRUNC {
   108  		trunc = true
   109  	}
   110  	// O_APPEND is emulated on Plan 9
   111  	if flag&O_APPEND == O_APPEND {
   112  		flag = flag &^ O_APPEND
   113  		append = true
   114  	}
   115  
   116  	if (create && trunc) || excl {
   117  		fd, e = syscall.Create(name, flag, syscallMode(perm))
   118  	} else {
   119  		fd, e = syscall.Open(name, flag)
   120  		if IsNotExist(e) && create {
   121  			fd, e = syscall.Create(name, flag, syscallMode(perm))
   122  			if e != nil {
   123  				return nil, &PathError{Op: "create", Path: name, Err: e}
   124  			}
   125  		}
   126  	}
   127  
   128  	if e != nil {
   129  		return nil, &PathError{Op: "open", Path: name, Err: e}
   130  	}
   131  
   132  	if append {
   133  		if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil {
   134  			return nil, &PathError{Op: "seek", Path: name, Err: e}
   135  		}
   136  	}
   137  
   138  	return NewFile(uintptr(fd), name), nil
   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.fd); 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.fd, 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.fd, 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.fd, 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.fd, 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.fd, 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.fd, 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.fd, 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  	if f.dirinfo != nil {
   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 = nil
   351  	}
   352  	return syscall.Seek(f.fd, offset, whence)
   353  }
   354  
   355  // Truncate changes the size of the named file.
   356  // If the file is a symbolic link, it changes the size of the link's target.
   357  // If there is an error, it will be of type *PathError.
   358  func Truncate(name string, size int64) error {
   359  	var d syscall.Dir
   360  
   361  	d.Null()
   362  	d.Length = size
   363  
   364  	var buf [syscall.STATFIXLEN]byte
   365  	n, err := d.Marshal(buf[:])
   366  	if err != nil {
   367  		return &PathError{Op: "truncate", Path: name, Err: err}
   368  	}
   369  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   370  		return &PathError{Op: "truncate", Path: name, Err: err}
   371  	}
   372  	return nil
   373  }
   374  
   375  // Remove removes the named file or directory.
   376  // If there is an error, it will be of type *PathError.
   377  func Remove(name string) error {
   378  	if e := syscall.Remove(name); e != nil {
   379  		return &PathError{Op: "remove", Path: name, Err: e}
   380  	}
   381  	return nil
   382  }
   383  
   384  // HasPrefix from the strings package.
   385  func hasPrefix(s, prefix string) bool {
   386  	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
   387  }
   388  
   389  func rename(oldname, newname string) error {
   390  	dirname := oldname[:lastIndex(oldname, '/')+1]
   391  	if hasPrefix(newname, dirname) {
   392  		newname = newname[len(dirname):]
   393  	} else {
   394  		return &LinkError{"rename", oldname, newname, ErrInvalid}
   395  	}
   396  
   397  	// If newname still contains slashes after removing the oldname
   398  	// prefix, the rename is cross-directory and must be rejected.
   399  	if lastIndex(newname, '/') >= 0 {
   400  		return &LinkError{"rename", oldname, newname, ErrInvalid}
   401  	}
   402  
   403  	var d syscall.Dir
   404  
   405  	d.Null()
   406  	d.Name = newname
   407  
   408  	buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
   409  	n, err := d.Marshal(buf[:])
   410  	if err != nil {
   411  		return &LinkError{"rename", oldname, newname, err}
   412  	}
   413  
   414  	// If newname already exists and is not a directory, rename replaces it.
   415  	f, err := Stat(dirname + newname)
   416  	if err == nil && !f.IsDir() {
   417  		Remove(dirname + newname)
   418  	}
   419  
   420  	if err = syscall.Wstat(oldname, buf[:n]); err != nil {
   421  		return &LinkError{"rename", oldname, newname, err}
   422  	}
   423  	return nil
   424  }
   425  
   426  // See docs in file.go:Chmod.
   427  func chmod(name string, mode FileMode) error {
   428  	var d syscall.Dir
   429  
   430  	odir, e := dirstat(name)
   431  	if e != nil {
   432  		return &PathError{Op: "chmod", Path: name, Err: e}
   433  	}
   434  	d.Null()
   435  	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
   436  
   437  	var buf [syscall.STATFIXLEN]byte
   438  	n, err := d.Marshal(buf[:])
   439  	if err != nil {
   440  		return &PathError{Op: "chmod", Path: name, Err: err}
   441  	}
   442  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   443  		return &PathError{Op: "chmod", Path: name, Err: err}
   444  	}
   445  	return nil
   446  }
   447  
   448  // Chtimes changes the access and modification times of the named
   449  // file, similar to the Unix utime() or utimes() functions.
   450  //
   451  // The underlying filesystem may truncate or round the values to a
   452  // less precise time unit.
   453  // If there is an error, it will be of type *PathError.
   454  func Chtimes(name string, atime time.Time, mtime time.Time) error {
   455  	var d syscall.Dir
   456  
   457  	d.Null()
   458  	d.Atime = uint32(atime.Unix())
   459  	d.Mtime = uint32(mtime.Unix())
   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  // Readlink returns the destination of the named symbolic link.
   501  // If there is an error, it will be of type *PathError.
   502  func Readlink(name string) (string, error) {
   503  	return "", &PathError{Op: "readlink", Path: name, Err: syscall.EPLAN9}
   504  }
   505  
   506  // Chown changes the numeric uid and gid of the named file.
   507  // If the file is a symbolic link, it changes the uid and gid of the link's target.
   508  // A uid or gid of -1 means to not change that value.
   509  // If there is an error, it will be of type *PathError.
   510  //
   511  // On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or
   512  // EPLAN9 error, wrapped in *PathError.
   513  func Chown(name string, uid, gid int) error {
   514  	return &PathError{Op: "chown", Path: name, Err: syscall.EPLAN9}
   515  }
   516  
   517  // Lchown changes the numeric uid and gid of the named file.
   518  // If the file is a symbolic link, it changes the uid and gid of the link itself.
   519  // If there is an error, it will be of type *PathError.
   520  func Lchown(name string, uid, gid int) error {
   521  	return &PathError{Op: "lchown", Path: name, Err: syscall.EPLAN9}
   522  }
   523  
   524  // Chown changes the numeric uid and gid of the named file.
   525  // If there is an error, it will be of type *PathError.
   526  func (f *File) Chown(uid, gid int) error {
   527  	if f == nil {
   528  		return ErrInvalid
   529  	}
   530  	return &PathError{Op: "chown", Path: f.name, Err: syscall.EPLAN9}
   531  }
   532  
   533  func tempDir() string {
   534  	dir := Getenv("TMPDIR")
   535  	if dir == "" {
   536  		dir = "/tmp"
   537  	}
   538  	return dir
   539  
   540  }
   541  
   542  // Chdir changes the current working directory to the file,
   543  // which must be a directory.
   544  // If there is an error, it will be of type *PathError.
   545  func (f *File) Chdir() error {
   546  	if err := f.incref("chdir"); err != nil {
   547  		return err
   548  	}
   549  	defer f.decref()
   550  	if e := syscall.Fchdir(f.fd); e != nil {
   551  		return &PathError{Op: "chdir", Path: f.name, Err: e}
   552  	}
   553  	return nil
   554  }
   555  
   556  // setDeadline sets the read and write deadline.
   557  func (f *File) setDeadline(time.Time) error {
   558  	if err := f.checkValid("SetDeadline"); err != nil {
   559  		return err
   560  	}
   561  	return poll.ErrNoDeadline
   562  }
   563  
   564  // setReadDeadline sets the read deadline.
   565  func (f *File) setReadDeadline(time.Time) error {
   566  	if err := f.checkValid("SetReadDeadline"); err != nil {
   567  		return err
   568  	}
   569  	return poll.ErrNoDeadline
   570  }
   571  
   572  // setWriteDeadline sets the write deadline.
   573  func (f *File) setWriteDeadline(time.Time) error {
   574  	if err := f.checkValid("SetWriteDeadline"); err != nil {
   575  		return err
   576  	}
   577  	return poll.ErrNoDeadline
   578  }
   579  
   580  // checkValid checks whether f is valid for use, but does not prepare
   581  // to actually use it. If f is not ready checkValid returns an appropriate
   582  // error, perhaps incorporating the operation name op.
   583  func (f *File) checkValid(op string) error {
   584  	if f == nil {
   585  		return ErrInvalid
   586  	}
   587  	if err := f.incref(op); err != nil {
   588  		return err
   589  	}
   590  	return f.decref()
   591  }
   592  
   593  type rawConn struct{}
   594  
   595  func (c *rawConn) Control(f func(uintptr)) error {
   596  	return syscall.EPLAN9
   597  }
   598  
   599  func (c *rawConn) Read(f func(uintptr) bool) error {
   600  	return syscall.EPLAN9
   601  }
   602  
   603  func (c *rawConn) Write(f func(uintptr) bool) error {
   604  	return syscall.EPLAN9
   605  }
   606  
   607  func newRawConn(file *File) (*rawConn, error) {
   608  	return nil, syscall.EPLAN9
   609  }
   610  
   611  func ignoringEINTR(fn func() error) error {
   612  	return fn()
   613  }