Black Lives Matter. Support the Equal Justice Initiative.

Source file src/os/file_plan9.go

Documentation: os

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

View as plain text