github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/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  // A zero time.Time value will leave the corresponding file time unchanged.
   451  //
   452  // The underlying filesystem may truncate or round the values to a
   453  // less precise time unit.
   454  // If there is an error, it will be of type *PathError.
   455  func Chtimes(name string, atime time.Time, mtime time.Time) error {
   456  	var d syscall.Dir
   457  
   458  	d.Null()
   459  	d.Atime = uint32(atime.Unix())
   460  	d.Mtime = uint32(mtime.Unix())
   461  	if atime.IsZero() {
   462  		d.Atime = 0xFFFFFFFF
   463  	}
   464  	if mtime.IsZero() {
   465  		d.Mtime = 0xFFFFFFFF
   466  	}
   467  
   468  	var buf [syscall.STATFIXLEN]byte
   469  	n, err := d.Marshal(buf[:])
   470  	if err != nil {
   471  		return &PathError{Op: "chtimes", Path: name, Err: err}
   472  	}
   473  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   474  		return &PathError{Op: "chtimes", Path: name, Err: err}
   475  	}
   476  	return nil
   477  }
   478  
   479  // Pipe returns a connected pair of Files; reads from r return bytes
   480  // written to w. It returns the files and an error, if any.
   481  func Pipe() (r *File, w *File, err error) {
   482  	var p [2]int
   483  
   484  	if e := syscall.Pipe(p[0:]); e != nil {
   485  		return nil, nil, NewSyscallError("pipe", e)
   486  	}
   487  
   488  	return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
   489  }
   490  
   491  // not supported on Plan 9
   492  
   493  // Link creates newname as a hard link to the oldname file.
   494  // If there is an error, it will be of type *LinkError.
   495  func Link(oldname, newname string) error {
   496  	return &LinkError{"link", oldname, newname, syscall.EPLAN9}
   497  }
   498  
   499  // Symlink creates newname as a symbolic link to oldname.
   500  // On Windows, a symlink to a non-existent oldname creates a file symlink;
   501  // if oldname is later created as a directory the symlink will not work.
   502  // If there is an error, it will be of type *LinkError.
   503  func Symlink(oldname, newname string) error {
   504  	return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
   505  }
   506  
   507  // Readlink returns the destination of the named symbolic link.
   508  // If there is an error, it will be of type *PathError.
   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  
   549  // Chdir changes the current working directory to the file,
   550  // which must be a directory.
   551  // If there is an error, it will be of type *PathError.
   552  func (f *File) Chdir() error {
   553  	if err := f.incref("chdir"); err != nil {
   554  		return err
   555  	}
   556  	defer f.decref()
   557  	if e := syscall.Fchdir(f.fd); e != nil {
   558  		return &PathError{Op: "chdir", Path: f.name, Err: e}
   559  	}
   560  	return nil
   561  }
   562  
   563  // setDeadline sets the read and write deadline.
   564  func (f *File) setDeadline(time.Time) error {
   565  	if err := f.checkValid("SetDeadline"); err != nil {
   566  		return err
   567  	}
   568  	return poll.ErrNoDeadline
   569  }
   570  
   571  // setReadDeadline sets the read deadline.
   572  func (f *File) setReadDeadline(time.Time) error {
   573  	if err := f.checkValid("SetReadDeadline"); err != nil {
   574  		return err
   575  	}
   576  	return poll.ErrNoDeadline
   577  }
   578  
   579  // setWriteDeadline sets the write deadline.
   580  func (f *File) setWriteDeadline(time.Time) error {
   581  	if err := f.checkValid("SetWriteDeadline"); err != nil {
   582  		return err
   583  	}
   584  	return poll.ErrNoDeadline
   585  }
   586  
   587  // checkValid checks whether f is valid for use, but does not prepare
   588  // to actually use it. If f is not ready checkValid returns an appropriate
   589  // error, perhaps incorporating the operation name op.
   590  func (f *File) checkValid(op string) error {
   591  	if f == nil {
   592  		return ErrInvalid
   593  	}
   594  	if err := f.incref(op); err != nil {
   595  		return err
   596  	}
   597  	return f.decref()
   598  }
   599  
   600  type rawConn struct{}
   601  
   602  func (c *rawConn) Control(f func(uintptr)) error {
   603  	return syscall.EPLAN9
   604  }
   605  
   606  func (c *rawConn) Read(f func(uintptr) bool) error {
   607  	return syscall.EPLAN9
   608  }
   609  
   610  func (c *rawConn) Write(f func(uintptr) bool) error {
   611  	return syscall.EPLAN9
   612  }
   613  
   614  func newRawConn(file *File) (*rawConn, error) {
   615  	return nil, syscall.EPLAN9
   616  }
   617  
   618  func ignoringEINTR(fn func() error) error {
   619  	return fn()
   620  }