github.com/akaros/go-akaros@v0.0.0-20181004170632-85005d477eab/src/os/file_akaros.go (about)

     1  // Copyright 2009 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  	"sync/atomic"
    10  	"syscall"
    11  	"time"
    12  )
    13  
    14  // File represents an open file descriptor.
    15  type File struct {
    16  	*file
    17  }
    18  
    19  // file is the real representation of *File.
    20  // The extra level of indirection ensures that no clients of os
    21  // can overwrite this data, which could cause the finalizer
    22  // to close the wrong file descriptor.
    23  type file struct {
    24  	fd      int
    25  	name    string
    26  	dirinfo *dirInfo // nil unless directory being read
    27  	nepipe  int32    // number of consecutive EPIPE in Write
    28  	iocount int32    // Count of outstanding I/O on this file
    29  }
    30  
    31  // Fd returns the integer Unix file descriptor referencing the open file.
    32  func (f *File) Fd() uintptr {
    33  	if f == nil {
    34  		return ^(uintptr(0))
    35  	}
    36  	return uintptr(f.fd)
    37  }
    38  
    39  // NewFile returns a new File with the given file descriptor and name.
    40  func NewFile(fd uintptr, name string) *File {
    41  	fdi := int(fd)
    42  	if fdi < 0 {
    43  		return nil
    44  	}
    45  	f := &File{&file{fd: fdi, name: name}}
    46  	runtime.SetFinalizer(f.file, (*file).close)
    47  	return f
    48  }
    49  
    50  // Auxiliary information if the File describes a directory
    51  type dirInfo struct {
    52  	buf  []byte // buffer for directory I/O
    53  	nbuf int    // length of buf; return value from Getdirentries
    54  	bufp int    // location of next record in buf.
    55  }
    56  
    57  func sigpipe() // implemented in package runtime
    58  func epipecheck(file *File, e error) {
    59  	if e == syscall.EPIPE {
    60  		if atomic.AddInt32(&file.nepipe, 1) >= 10 {
    61  			sigpipe()
    62  		}
    63  	} else {
    64  		atomic.StoreInt32(&file.nepipe, 0)
    65  	}
    66  }
    67  
    68  // DevNull is the name of the operating system's ``null device.''
    69  // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
    70  const DevNull = "/dev/null"
    71  
    72  // syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
    73  func syscallMode(i FileMode) (o uint32) {
    74  	o |= uint32(i.Perm())
    75  	if i&ModeSetuid != 0 {
    76  		o |= syscall.S_ISUID
    77  	}
    78  	if i&ModeSetgid != 0 {
    79  		o |= syscall.S_ISGID
    80  	}
    81  	if i&ModeSticky != 0 {
    82  		o |= syscall.S_ISVTX
    83  	}
    84  	// No mapping for Go's ModeTemporary (plan9 only).
    85  	return
    86  }
    87  
    88  // OpenFile is the generalized open call; most users will use Open
    89  // or Create instead.  It opens the named file with specified flag
    90  // (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
    91  // methods on the returned File can be used for I/O.
    92  // If there is an error, it will be of type *PathError.
    93  func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
    94  	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
    95  	if e != nil {
    96  		return nil, &PathError{"open", name, e}
    97  	}
    98  
    99  	// There's a race here with fork/exec, which we are
   100  	// content to live with.  See ../syscall/exec_unix.go.
   101  	if !supportsCloseOnExec {
   102  		syscall.CloseOnExec(r)
   103  	}
   104  
   105  	return NewFile(uintptr(r), name), nil
   106  }
   107  
   108  // Close closes the File, rendering it unusable for I/O.
   109  // It returns an error, if any.
   110  func (f *File) Close() error {
   111  	if f == nil {
   112  		return ErrInvalid
   113  	}
   114  	return f.file.close()
   115  }
   116  
   117  func (file *file) close() error {
   118  	if file == nil || file.fd < 0 {
   119  		return syscall.EINVAL
   120  	}
   121  	var err error
   122  	if e := syscall.Close(file.fd); e != nil {
   123  		err = &PathError{"close", file.name, e}
   124  	}
   125  	file.fd = -1 // so it can't be closed again
   126  
   127  	// no need for a finalizer anymore
   128  	runtime.SetFinalizer(file, nil)
   129  	return err
   130  }
   131  
   132  // Stat returns the FileInfo structure describing file.
   133  // If there is an error, it will be of type *PathError.
   134  func (f *File) Stat() (fi FileInfo, err error) {
   135  	if f == nil {
   136  		return nil, ErrInvalid
   137  	}
   138  	var stat syscall.Stat_t
   139  	atomic.AddInt32(&f.file.iocount, 1)
   140  	err = syscall.Fstat(f.fd, &stat)
   141  	atomic.AddInt32(&f.file.iocount, -1)
   142  	if err != nil {
   143  		return nil, &PathError{"stat", f.name, err}
   144  	}
   145  	return fileInfoFromStat(&stat, f.name), nil
   146  }
   147  
   148  // Stat returns a FileInfo describing the named file.
   149  // If there is an error, it will be of type *PathError.
   150  func Stat(name string) (fi FileInfo, err error) {
   151  	var stat syscall.Stat_t
   152  	err = syscall.Stat(name, &stat)
   153  	if err != nil {
   154  		return nil, &PathError{"stat", name, err}
   155  	}
   156  	return fileInfoFromStat(&stat, name), nil
   157  }
   158  
   159  // Lstat returns a FileInfo describing the named file.
   160  // If the file is a symbolic link, the returned FileInfo
   161  // describes the symbolic link.  Lstat makes no attempt to follow the link.
   162  // If there is an error, it will be of type *PathError.
   163  func Lstat(name string) (fi FileInfo, err error) {
   164  	var stat syscall.Stat_t
   165  	err = syscall.Lstat(name, &stat)
   166  	if err != nil {
   167  		return nil, &PathError{"lstat", name, err}
   168  	}
   169  	return fileInfoFromStat(&stat, name), nil
   170  }
   171  
   172  func (f *File) readdir(n int) (fi []FileInfo, err error) {
   173  	dirname := f.name
   174  	if dirname == "" {
   175  		dirname = "."
   176  	}
   177  	names, err := f.Readdirnames(n)
   178  	fi = make([]FileInfo, 0, len(names))
   179  	for _, filename := range names {
   180  		fip, lerr := lstat(dirname + "/" + filename)
   181  		if IsNotExist(lerr) {
   182  			// File disappeared between readdir + stat.
   183  			// Just treat it as if it didn't exist.
   184  			continue
   185  		}
   186  		if lerr != nil {
   187  			return fi, lerr
   188  		}
   189  		fi = append(fi, fip)
   190  	}
   191  	return fi, err
   192  }
   193  
   194  // Darwin and FreeBSD can't read or write 2GB+ at a time,
   195  // even on 64-bit systems. See golang.org/issue/7812.
   196  // Use 1GB instead of, say, 2GB-1, to keep subsequent
   197  // reads aligned.
   198  const (
   199  	needsMaxRW = runtime.GOOS == "darwin" || runtime.GOOS == "freebsd"
   200  	maxRW      = 1 << 30
   201  )
   202  
   203  // read reads up to len(b) bytes from the File.
   204  // It returns the number of bytes read and an error, if any.
   205  func (f *File) read(b []byte) (n int, err error) {
   206  	if needsMaxRW && len(b) > maxRW {
   207  		b = b[:maxRW]
   208  	}
   209  	atomic.AddInt32(&f.file.iocount, 1)
   210  	n, err = syscall.Read(f.fd, b)
   211  	atomic.AddInt32(&f.file.iocount, -1)
   212  	return
   213  }
   214  
   215  // pread reads len(b) bytes from the File starting at byte offset off.
   216  // It returns the number of bytes read and the error, if any.
   217  // EOF is signaled by a zero count with err set to nil.
   218  func (f *File) pread(b []byte, off int64) (n int, err error) {
   219  	if needsMaxRW && len(b) > maxRW {
   220  		b = b[:maxRW]
   221  	}
   222  	atomic.AddInt32(&f.file.iocount, 1)
   223  	n, err = syscall.Pread(f.fd, b, off)
   224  	atomic.AddInt32(&f.file.iocount, -1)
   225  	return
   226  }
   227  
   228  // write writes len(b) bytes to the File.
   229  // It returns the number of bytes written and an error, if any.
   230  func (f *File) write(b []byte) (n int, err error) {
   231  	for {
   232  		bcap := b
   233  		if needsMaxRW && len(bcap) > maxRW {
   234  			bcap = bcap[:maxRW]
   235  		}
   236  		atomic.AddInt32(&f.file.iocount, 1)
   237  		m, err := syscall.Write(f.fd, bcap)
   238  		atomic.AddInt32(&f.file.iocount, -1)
   239  		n += m
   240  
   241  		// If the syscall wrote some data but not all (short write)
   242  		// or it returned EINTR, then assume it stopped early for
   243  		// reasons that are uninteresting to the caller, and try again.
   244  		if 0 < m && m < len(bcap) || err == syscall.EINTR {
   245  			b = b[m:]
   246  			continue
   247  		}
   248  
   249  		if needsMaxRW && len(bcap) != len(b) && err == nil {
   250  			b = b[m:]
   251  			continue
   252  		}
   253  
   254  		return n, err
   255  	}
   256  }
   257  
   258  // pwrite writes len(b) bytes to the File starting at byte offset off.
   259  // It returns the number of bytes written and an error, if any.
   260  func (f *File) pwrite(b []byte, off int64) (n int, err error) {
   261  	if needsMaxRW && len(b) > maxRW {
   262  		b = b[:maxRW]
   263  	}
   264  	atomic.AddInt32(&f.file.iocount, 1)
   265  	n, err = syscall.Pwrite(f.fd, b, off)
   266  	atomic.AddInt32(&f.file.iocount, -1)
   267  	return
   268  }
   269  
   270  // seek sets the offset for the next Read or Write on file to offset, interpreted
   271  // according to whence: 0 means relative to the origin of the file, 1 means
   272  // relative to the current offset, and 2 means relative to the end.
   273  // It returns the new offset and an error, if any.
   274  func (f *File) seek(offset int64, whence int) (ret int64, err error) {
   275  	atomic.AddInt32(&f.file.iocount, 1)
   276  	ret, err = syscall.Seek(f.fd, offset, whence)
   277  	atomic.AddInt32(&f.file.iocount, -1)
   278  	return
   279  }
   280  
   281  // Truncate changes the size of the named file.
   282  // If the file is a symbolic link, it changes the size of the link's target.
   283  // If there is an error, it will be of type *PathError.
   284  func Truncate(name string, size int64) error {
   285  	var d syscall.Dir
   286  
   287  	d.Null()
   288  	d.Length = size
   289  
   290  	var buf [syscall.STATFIXLEN]byte
   291  	n, err := d.Marshal(buf[:])
   292  	if err != nil {
   293  		return &PathError{"truncate", name, err}
   294  	}
   295  	if err = syscall.Wstat(name, buf[:n], syscall.WSTAT_LENGTH); err != nil {
   296  		return &PathError{"truncate", name, err}
   297  	}
   298  	return nil
   299  }
   300  
   301  // Truncate changes the size of the named file.
   302  // If the file is a symbolic link, it changes the size of the link's target.
   303  // If there is an error, it will be of type *PathError.
   304  func (f *File) Truncate(size int64) error {
   305  	if f == nil {
   306  		return ErrInvalid
   307  	}
   308  
   309  	var d syscall.Dir
   310  	d.Null()
   311  	d.Length = size
   312  
   313  	var buf [syscall.STATFIXLEN]byte
   314  	n, err := d.Marshal(buf[:])
   315  	if err != nil {
   316  		return &PathError{"truncate", f.name, err}
   317  	}
   318  	if err = syscall.Fwstat(f.fd, buf[:n], syscall.WSTAT_LENGTH); err != nil {
   319  		return &PathError{"truncate", f.name, err}
   320  	}
   321  	return nil
   322  }
   323  
   324  // Remove removes the named file or directory.
   325  // If there is an error, it will be of type *PathError.
   326  func Remove(name string) error {
   327  	// System call interface forces us to know
   328  	// whether name is a file or directory.
   329  	// Try both: it is cheaper on average than
   330  	// doing a Stat plus the right one.
   331  	e := syscall.Unlink(name)
   332  	if e == nil {
   333  		return nil
   334  	}
   335  	e1 := syscall.Rmdir(name)
   336  	if e1 == nil {
   337  		return nil
   338  	}
   339  
   340  	// Both failed: figure out which error to return.
   341  	// OS X and Linux differ on whether unlink(dir)
   342  	// returns EISDIR, so can't use that.  However,
   343  	// both agree that rmdir(file) returns ENOTDIR,
   344  	// so we can use that to decide which error is real.
   345  	// Rmdir might also return ENOTDIR if given a bad
   346  	// file path, like /etc/passwd/foo, but in that case,
   347  	// both errors will be ENOTDIR, so it's okay to
   348  	// use the error from unlink.
   349  	if e1 != syscall.ENOTDIR {
   350  		e = e1
   351  	}
   352  	return &PathError{"remove", name, e}
   353  }
   354  
   355  // Link creates newname as a hard link to the oldname file.
   356  // If there is an error, it will be of type *LinkError.
   357  func Link(oldname, newname string) error {
   358  	e := syscall.Link(oldname, newname)
   359  	if e != nil {
   360  		return &LinkError{"link", oldname, newname, e}
   361  	}
   362  	return nil
   363  }
   364  
   365  // Symlink creates newname as a symbolic link to oldname.
   366  // If there is an error, it will be of type *LinkError.
   367  func Symlink(oldname, newname string) error {
   368  	e := syscall.Symlink(oldname, newname)
   369  	if e != nil {
   370  		return &LinkError{"symlink", oldname, newname, e}
   371  	}
   372  	return nil
   373  }
   374  
   375  // Readlink returns the destination of the named symbolic link.
   376  // If there is an error, it will be of type *PathError.
   377  func Readlink(name string) (string, error) {
   378  	for len := 128; ; len *= 2 {
   379  		b := make([]byte, len)
   380  		n, e := syscall.Readlink(name, b)
   381  		if e != nil {
   382  			return "", &PathError{"readlink", name, e}
   383  		}
   384  		if n < len {
   385  			return string(b[0:n]), nil
   386  		}
   387  	}
   388  }
   389  
   390  // HasPrefix from the strings package.
   391  func hasPrefix(s, prefix string) bool {
   392  	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
   393  }
   394  
   395  // Variant of LastIndex from the strings package.
   396  func lastIndex(s string, sep byte) int {
   397  	for i := len(s) - 1; i >= 0; i-- {
   398  		if s[i] == sep {
   399  			return i
   400  		}
   401  	}
   402  	return -1
   403  }
   404  
   405  func rename(oldname, newname string) error {
   406  	e := syscall.Rename(oldname, newname)
   407  	if e != nil {
   408  		return &LinkError{"rename", oldname, newname, e}
   409  	}
   410  	return nil
   411  }
   412  
   413  const chmodMask = uint32(syscall.S_ISUID | syscall.S_ISGID | syscall.S_ISVTX | ModePerm)
   414  
   415  // Chmod changes the mode of the named file to mode.
   416  // If the file is a symbolic link, it changes the mode of the link's target.
   417  // If there is an error, it will be of type *PathError.
   418  func Chmod(name string, mode FileMode) error {
   419  	var d syscall.Dir
   420  
   421  	d.Null()
   422  	d.Mode = syscallMode(mode) & chmodMask
   423  
   424  	var buf [syscall.STATFIXLEN]byte
   425  	n, err := d.Marshal(buf[:])
   426  	if err != nil {
   427  		return &PathError{"chmod", name, err}
   428  	}
   429  	if err = syscall.Wstat(name, buf[:n], syscall.WSTAT_MODE); err != nil {
   430  		return &PathError{"chmod", name, err}
   431  	}
   432  	return nil
   433  }
   434  
   435  // Chmod changes the mode of the file to mode.
   436  // If there is an error, it will be of type *PathError.
   437  func (f *File) Chmod(mode FileMode) error {
   438  	if f == nil {
   439  		return ErrInvalid
   440  	}
   441  	var d syscall.Dir
   442  
   443  	d.Null()
   444  	d.Mode = syscallMode(mode) & chmodMask
   445  
   446  	var buf [syscall.STATFIXLEN]byte
   447  	n, err := d.Marshal(buf[:])
   448  	if err != nil {
   449  		return &PathError{"chmod", f.name, err}
   450  	}
   451  	if err = syscall.Fwstat(f.fd, buf[:n], syscall.WSTAT_MODE); err != nil {
   452  		return &PathError{"chmod", f.name, err}
   453  	}
   454  	return nil
   455  }
   456  
   457  // Sync commits the current contents of the file to stable storage.
   458  // Typically, this means flushing the file system's in-memory copy
   459  // of recently written data to disk.
   460  func (f *File) Sync() (err error) {
   461  	if f == nil {
   462  		return ErrInvalid
   463  	}
   464  	if e := syscall.Fsync(f.fd); e != nil {
   465  		return NewSyscallError("fsync", e)
   466  	}
   467  	return nil
   468  }
   469  
   470  // Chown changes the numeric uid and gid of the named file.
   471  // If the file is a symbolic link, it changes the uid and gid of the link's target.
   472  // If there is an error, it will be of type *PathError.
   473  func Chown(name string, uid, gid int) error {
   474  	if e := syscall.Chown(name, uid, gid); e != nil {
   475  		return &PathError{"chown", name, e}
   476  	}
   477  	return nil
   478  }
   479  
   480  // Lchown changes the numeric uid and gid of the named file.
   481  // If the file is a symbolic link, it changes the uid and gid of the link itself.
   482  // If there is an error, it will be of type *PathError.
   483  func Lchown(name string, uid, gid int) error {
   484  	if e := syscall.Lchown(name, uid, gid); e != nil {
   485  		return &PathError{"lchown", name, e}
   486  	}
   487  	return nil
   488  }
   489  
   490  // Chown changes the numeric uid and gid of the named file.
   491  // If there is an error, it will be of type *PathError.
   492  func (f *File) Chown(uid, gid int) error {
   493  	if f == nil {
   494  		return ErrInvalid
   495  	}
   496  	if e := syscall.Fchown(f.fd, uid, gid); e != nil {
   497  		return &PathError{"chown", f.name, e}
   498  	}
   499  	return nil
   500  }
   501  
   502  // Chtimes changes the access and modification times of the named
   503  // file, similar to the Unix utime() or utimes() functions.
   504  //
   505  // The underlying filesystem may truncate or round the values to a
   506  // less precise time unit.
   507  // If there is an error, it will be of type *PathError.
   508  func Chtimes(name string, atime time.Time, mtime time.Time) error {
   509  	var d syscall.Dir
   510  
   511  	d.Null()
   512  	d.Atime = uint32(atime.Unix())
   513  	d.Mtime = uint32(mtime.Unix())
   514  
   515  	var buf [syscall.STATFIXLEN]byte
   516  	n, err := d.Marshal(buf[:])
   517  	if err != nil {
   518  		return &PathError{"chtimes", name, err}
   519  	}
   520  	if err = syscall.Wstat(name, buf[:n], syscall.WSTAT_MTIME|syscall.WSTAT_ATIME); err != nil {
   521  		return &PathError{"chtimes", name, err}
   522  	}
   523  	return nil
   524  }
   525  
   526  // basename removes trailing slashes and the leading directory name from path name
   527  func basename(name string) string {
   528  	i := len(name) - 1
   529  	// Remove trailing slashes
   530  	for ; i > 0 && name[i] == '/'; i-- {
   531  		name = name[:i]
   532  	}
   533  	// Remove leading directory name
   534  	for i--; i >= 0; i-- {
   535  		if name[i] == '/' {
   536  			name = name[i+1:]
   537  			break
   538  		}
   539  	}
   540  
   541  	return name
   542  }
   543  
   544  // TempDir returns the default directory to use for temporary files.
   545  func TempDir() string {
   546  	dir := Getenv("TMPDIR")
   547  	if dir == "" {
   548  		dir = "/tmp"
   549  	}
   550  	return dir
   551  }
   552  
   553  func (file *File) AbortOutstandingSyscalls() {
   554  	for file.file.iocount > 0 {
   555  		syscall.AbortSyscFd(file.file.fd)
   556  		time.Sleep(100 * time.Millisecond)
   557  	}
   558  }