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

View as plain text