github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/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  	"runtime"
     9  	"syscall"
    10  	"time"
    11  )
    12  
    13  // File represents an open file descriptor.
    14  type File struct {
    15  	*file
    16  }
    17  
    18  // file is the real representation of *File.
    19  // The extra level of indirection ensures that no clients of os
    20  // can overwrite this data, which could cause the finalizer
    21  // to close the wrong file descriptor.
    22  type file struct {
    23  	fd      int
    24  	name    string
    25  	dirinfo *dirInfo // nil unless directory being read
    26  }
    27  
    28  // Fd returns the integer Unix file descriptor referencing the open file.
    29  func (f *File) Fd() uintptr {
    30  	if f == nil {
    31  		return ^(uintptr(0))
    32  	}
    33  	return uintptr(f.fd)
    34  }
    35  
    36  // NewFile returns a new File with the given file descriptor and name.
    37  func NewFile(fd uintptr, name string) *File {
    38  	fdi := int(fd)
    39  	if fdi < 0 {
    40  		return nil
    41  	}
    42  	f := &File{&file{fd: fdi, name: name}}
    43  	runtime.SetFinalizer(f.file, (*file).close)
    44  	return f
    45  }
    46  
    47  // Auxiliary information if the File describes a directory
    48  type dirInfo struct {
    49  	buf  [syscall.STATMAX]byte // buffer for directory I/O
    50  	nbuf int                   // length of buf; return value from Read
    51  	bufp int                   // location of next record in buf.
    52  }
    53  
    54  func epipecheck(file *File, e error) {
    55  }
    56  
    57  // DevNull is the name of the operating system's ``null device.''
    58  // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
    59  const DevNull = "/dev/null"
    60  
    61  // syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
    62  func syscallMode(i FileMode) (o uint32) {
    63  	o |= uint32(i.Perm())
    64  	if i&ModeAppend != 0 {
    65  		o |= syscall.DMAPPEND
    66  	}
    67  	if i&ModeExclusive != 0 {
    68  		o |= syscall.DMEXCL
    69  	}
    70  	if i&ModeTemporary != 0 {
    71  		o |= syscall.DMTMP
    72  	}
    73  	return
    74  }
    75  
    76  // OpenFile is the generalized open call; most users will use Open
    77  // or Create instead.  It opens the named file with specified flag
    78  // (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
    79  // methods on the returned File can be used for I/O.
    80  // If there is an error, it will be of type *PathError.
    81  func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
    82  	var (
    83  		fd     int
    84  		e      error
    85  		create bool
    86  		excl   bool
    87  		trunc  bool
    88  		append bool
    89  	)
    90  
    91  	if flag&O_CREATE == O_CREATE {
    92  		flag = flag & ^O_CREATE
    93  		create = true
    94  	}
    95  	if flag&O_EXCL == O_EXCL {
    96  		excl = true
    97  	}
    98  	if flag&O_TRUNC == O_TRUNC {
    99  		trunc = true
   100  	}
   101  	// O_APPEND is emulated on Plan 9
   102  	if flag&O_APPEND == O_APPEND {
   103  		flag = flag &^ O_APPEND
   104  		append = true
   105  	}
   106  
   107  	if (create && trunc) || excl {
   108  		fd, e = syscall.Create(name, flag, syscallMode(perm))
   109  	} else {
   110  		fd, e = syscall.Open(name, flag)
   111  		if e != nil && create {
   112  			var e1 error
   113  			fd, e1 = syscall.Create(name, flag, syscallMode(perm))
   114  			if e1 == nil {
   115  				e = nil
   116  			}
   117  		}
   118  	}
   119  
   120  	if e != nil {
   121  		return nil, &PathError{"open", name, e}
   122  	}
   123  
   124  	if append {
   125  		if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil {
   126  			return nil, &PathError{"seek", name, e}
   127  		}
   128  	}
   129  
   130  	return NewFile(uintptr(fd), name), nil
   131  }
   132  
   133  // Close closes the File, rendering it unusable for I/O.
   134  // It returns an error, if any.
   135  func (f *File) Close() error {
   136  	return f.file.close()
   137  }
   138  
   139  func (file *file) close() error {
   140  	if file == nil || file.fd < 0 {
   141  		return ErrInvalid
   142  	}
   143  	var err error
   144  	syscall.ForkLock.RLock()
   145  	if e := syscall.Close(file.fd); e != nil {
   146  		err = &PathError{"close", file.name, e}
   147  	}
   148  	syscall.ForkLock.RUnlock()
   149  	file.fd = -1 // so it can't be closed again
   150  
   151  	// no need for a finalizer anymore
   152  	runtime.SetFinalizer(file, nil)
   153  	return err
   154  }
   155  
   156  // Stat returns the FileInfo structure describing file.
   157  // If there is an error, it will be of type *PathError.
   158  func (f *File) Stat() (fi FileInfo, err error) {
   159  	d, err := dirstat(f)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	return fileInfoFromStat(d), nil
   164  }
   165  
   166  // Truncate changes the size of the file.
   167  // It does not change the I/O offset.
   168  // If there is an error, it will be of type *PathError.
   169  func (f *File) Truncate(size int64) error {
   170  	var d syscall.Dir
   171  
   172  	d.Null()
   173  	d.Length = size
   174  
   175  	var buf [syscall.STATFIXLEN]byte
   176  	n, err := d.Marshal(buf[:])
   177  	if err != nil {
   178  		return &PathError{"truncate", f.name, err}
   179  	}
   180  	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
   181  		return &PathError{"truncate", f.name, err}
   182  	}
   183  	return nil
   184  }
   185  
   186  const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm)
   187  
   188  // Chmod changes the mode of the file to mode.
   189  // If there is an error, it will be of type *PathError.
   190  func (f *File) Chmod(mode FileMode) error {
   191  	var d syscall.Dir
   192  
   193  	odir, e := dirstat(f)
   194  	if e != nil {
   195  		return &PathError{"chmod", f.name, e}
   196  	}
   197  	d.Null()
   198  	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
   199  
   200  	var buf [syscall.STATFIXLEN]byte
   201  	n, err := d.Marshal(buf[:])
   202  	if err != nil {
   203  		return &PathError{"chmod", f.name, err}
   204  	}
   205  	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
   206  		return &PathError{"chmod", f.name, err}
   207  	}
   208  	return nil
   209  }
   210  
   211  // Sync commits the current contents of the file to stable storage.
   212  // Typically, this means flushing the file system's in-memory copy
   213  // of recently written data to disk.
   214  func (f *File) Sync() (err error) {
   215  	if f == nil {
   216  		return ErrInvalid
   217  	}
   218  	var d syscall.Dir
   219  	d.Null()
   220  
   221  	var buf [syscall.STATFIXLEN]byte
   222  	n, err := d.Marshal(buf[:])
   223  	if err != nil {
   224  		return NewSyscallError("fsync", err)
   225  	}
   226  	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
   227  		return NewSyscallError("fsync", err)
   228  	}
   229  	return nil
   230  }
   231  
   232  // read reads up to len(b) bytes from the File.
   233  // It returns the number of bytes read and an error, if any.
   234  func (f *File) read(b []byte) (n int, err error) {
   235  	return syscall.Read(f.fd, b)
   236  }
   237  
   238  // pread reads len(b) bytes from the File starting at byte offset off.
   239  // It returns the number of bytes read and the error, if any.
   240  // EOF is signaled by a zero count with err set to nil.
   241  func (f *File) pread(b []byte, off int64) (n int, err error) {
   242  	return syscall.Pread(f.fd, b, off)
   243  }
   244  
   245  // write writes len(b) bytes to the File.
   246  // It returns the number of bytes written and an error, if any.
   247  // Since Plan 9 preserves message boundaries, never allow
   248  // a zero-byte write.
   249  func (f *File) write(b []byte) (n int, err error) {
   250  	if len(b) == 0 {
   251  		return 0, nil
   252  	}
   253  	return syscall.Write(f.fd, b)
   254  }
   255  
   256  // pwrite writes len(b) bytes to the File starting at byte offset off.
   257  // It returns the number of bytes written and an error, if any.
   258  // Since Plan 9 preserves message boundaries, never allow
   259  // a zero-byte write.
   260  func (f *File) pwrite(b []byte, off int64) (n int, err error) {
   261  	if len(b) == 0 {
   262  		return 0, nil
   263  	}
   264  	return syscall.Pwrite(f.fd, b, off)
   265  }
   266  
   267  // seek sets the offset for the next Read or Write on file to offset, interpreted
   268  // according to whence: 0 means relative to the origin of the file, 1 means
   269  // relative to the current offset, and 2 means relative to the end.
   270  // It returns the new offset and an error, if any.
   271  func (f *File) seek(offset int64, whence int) (ret int64, err error) {
   272  	return syscall.Seek(f.fd, offset, whence)
   273  }
   274  
   275  // Truncate changes the size of the named file.
   276  // If the file is a symbolic link, it changes the size of the link's target.
   277  // If there is an error, it will be of type *PathError.
   278  func Truncate(name string, size int64) error {
   279  	var d syscall.Dir
   280  
   281  	d.Null()
   282  	d.Length = size
   283  
   284  	var buf [syscall.STATFIXLEN]byte
   285  	n, err := d.Marshal(buf[:])
   286  	if err != nil {
   287  		return &PathError{"truncate", name, err}
   288  	}
   289  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   290  		return &PathError{"truncate", name, err}
   291  	}
   292  	return nil
   293  }
   294  
   295  // Remove removes the named file or directory.
   296  // If there is an error, it will be of type *PathError.
   297  func Remove(name string) error {
   298  	if e := syscall.Remove(name); e != nil {
   299  		return &PathError{"remove", name, e}
   300  	}
   301  	return nil
   302  }
   303  
   304  // Rename renames a file.
   305  func Rename(oldname, newname string) error {
   306  	var d syscall.Dir
   307  
   308  	d.Null()
   309  	d.Name = newname
   310  
   311  	buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
   312  	n, err := d.Marshal(buf[:])
   313  	if err != nil {
   314  		return &PathError{"rename", oldname, err}
   315  	}
   316  	if err = syscall.Wstat(oldname, buf[:n]); err != nil {
   317  		return &PathError{"rename", oldname, err}
   318  	}
   319  	return nil
   320  }
   321  
   322  // Chmod changes the mode of the named file to mode.
   323  // If the file is a symbolic link, it changes the mode of the link's target.
   324  // If there is an error, it will be of type *PathError.
   325  func Chmod(name string, mode FileMode) error {
   326  	var d syscall.Dir
   327  
   328  	odir, e := dirstat(name)
   329  	if e != nil {
   330  		return &PathError{"chmod", name, e}
   331  	}
   332  	d.Null()
   333  	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
   334  
   335  	var buf [syscall.STATFIXLEN]byte
   336  	n, err := d.Marshal(buf[:])
   337  	if err != nil {
   338  		return &PathError{"chmod", name, err}
   339  	}
   340  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   341  		return &PathError{"chmod", name, err}
   342  	}
   343  	return nil
   344  }
   345  
   346  // Chtimes changes the access and modification times of the named
   347  // file, similar to the Unix utime() or utimes() functions.
   348  //
   349  // The underlying filesystem may truncate or round the values to a
   350  // less precise time unit.
   351  // If there is an error, it will be of type *PathError.
   352  func Chtimes(name string, atime time.Time, mtime time.Time) error {
   353  	var d syscall.Dir
   354  
   355  	d.Null()
   356  	d.Atime = uint32(atime.Unix())
   357  	d.Mtime = uint32(mtime.Unix())
   358  
   359  	var buf [syscall.STATFIXLEN]byte
   360  	n, err := d.Marshal(buf[:])
   361  	if err != nil {
   362  		return &PathError{"chtimes", name, err}
   363  	}
   364  	if err = syscall.Wstat(name, buf[:n]); err != nil {
   365  		return &PathError{"chtimes", name, err}
   366  	}
   367  	return nil
   368  }
   369  
   370  // Pipe returns a connected pair of Files; reads from r return bytes
   371  // written to w. It returns the files and an error, if any.
   372  func Pipe() (r *File, w *File, err error) {
   373  	var p [2]int
   374  
   375  	syscall.ForkLock.RLock()
   376  	if e := syscall.Pipe(p[0:]); e != nil {
   377  		syscall.ForkLock.RUnlock()
   378  		return nil, nil, NewSyscallError("pipe", e)
   379  	}
   380  	syscall.ForkLock.RUnlock()
   381  
   382  	return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
   383  }
   384  
   385  // not supported on Plan 9
   386  
   387  // Link creates newname as a hard link to the oldname file.
   388  // If there is an error, it will be of type *LinkError.
   389  func Link(oldname, newname string) error {
   390  	return &LinkError{"link", oldname, newname, syscall.EPLAN9}
   391  }
   392  
   393  // Symlink creates newname as a symbolic link to oldname.
   394  // If there is an error, it will be of type *LinkError.
   395  func Symlink(oldname, newname string) error {
   396  	return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
   397  }
   398  
   399  // Readlink returns the destination of the named symbolic link.
   400  // If there is an error, it will be of type *PathError.
   401  func Readlink(name string) (string, error) {
   402  	return "", &PathError{"readlink", name, syscall.EPLAN9}
   403  }
   404  
   405  // Chown changes the numeric uid and gid of the named file.
   406  // If the file is a symbolic link, it changes the uid and gid of the link's target.
   407  // If there is an error, it will be of type *PathError.
   408  func Chown(name string, uid, gid int) error {
   409  	return &PathError{"chown", name, syscall.EPLAN9}
   410  }
   411  
   412  // Lchown changes the numeric uid and gid of the named file.
   413  // If the file is a symbolic link, it changes the uid and gid of the link itself.
   414  // If there is an error, it will be of type *PathError.
   415  func Lchown(name string, uid, gid int) error {
   416  	return &PathError{"lchown", name, syscall.EPLAN9}
   417  }
   418  
   419  // Chown changes the numeric uid and gid of the named file.
   420  // If there is an error, it will be of type *PathError.
   421  func (f *File) Chown(uid, gid int) error {
   422  	return &PathError{"chown", f.name, syscall.EPLAN9}
   423  }
   424  
   425  // TempDir returns the default directory to use for temporary files.
   426  func TempDir() string {
   427  	return "/tmp"
   428  }