github.com/dara-project/godist@v0.0.0-20200823115410-e0c80c8f0c78/src/os/file_unix.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  // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
     6  
     7  package os
     8  
     9  import (
    10  	"dara"
    11  	"internal/poll"
    12  	"runtime"
    13  	"syscall"
    14  )
    15  
    16  // fixLongPath is a noop on non-Windows platforms.
    17  func fixLongPath(path string) string {
    18  	return path
    19  }
    20  
    21  func rename(oldname, newname string) error {
    22  	fi, err := Lstat(newname)
    23  	if err == nil && fi.IsDir() {
    24  		// There are two independent errors this function can return:
    25  		// one for a bad oldname, and one for a bad newname.
    26  		// At this point we've determined the newname is bad.
    27  		// But just in case oldname is also bad, prioritize returning
    28  		// the oldname error because that's what we did historically.
    29  		if _, err := Lstat(oldname); err != nil {
    30  			if pe, ok := err.(*PathError); ok {
    31  				err = pe.Err
    32  			}
    33  			return &LinkError{"rename", oldname, newname, err}
    34  		}
    35  		return &LinkError{"rename", oldname, newname, syscall.EEXIST}
    36  	}
    37  	// DARA Instrumentation
    38  	if runtime.Is_dara_profiling_on() {
    39          runtime.Dara_Debug_Print(func() {
    40  		    print("[RENAME] : ")
    41  		    print(oldname)
    42  		    print(" ")
    43  		    println(newname)
    44          })
    45  		argInfo1 := dara.GeneralType{Type: dara.STRING}
    46          copy(argInfo1.String[:], oldname)
    47  		argInfo2 := dara.GeneralType{Type: dara.STRING}
    48          copy(argInfo2.String[:], newname)
    49  		retInfo := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
    50  		syscallInfo := dara.GeneralSyscall{dara.DSYS_RENAME, 2, 1, [10]dara.GeneralType{argInfo1, argInfo2}, [10]dara.GeneralType{retInfo}}
    51  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_RENAME, syscallInfo)
    52  	}
    53  	err = syscall.Rename(oldname, newname)
    54  	if err != nil {
    55  		return &LinkError{"rename", oldname, newname, err}
    56  	}
    57  	return nil
    58  }
    59  
    60  // file is the real representation of *File.
    61  // The extra level of indirection ensures that no clients of os
    62  // can overwrite this data, which could cause the finalizer
    63  // to close the wrong file descriptor.
    64  type file struct {
    65  	pfd         poll.FD
    66  	name        string
    67  	dirinfo     *dirInfo // nil unless directory being read
    68  	nonblock    bool     // whether we set nonblocking mode
    69  	stdoutOrErr bool     // whether this is stdout or stderr
    70  }
    71  
    72  // Fd returns the integer Unix file descriptor referencing the open file.
    73  // The file descriptor is valid only until f.Close is called or f is garbage collected.
    74  // On Unix systems this will cause the SetDeadline methods to stop working.
    75  func (f *File) Fd() uintptr {
    76  	if f == nil {
    77  		return ^(uintptr(0))
    78  	}
    79  
    80  	// If we put the file descriptor into nonblocking mode,
    81  	// then set it to blocking mode before we return it,
    82  	// because historically we have always returned a descriptor
    83  	// opened in blocking mode. The File will continue to work,
    84  	// but any blocking operation will tie up a thread.
    85  	if f.nonblock {
    86  		f.pfd.SetBlocking()
    87  	}
    88  
    89  	return uintptr(f.pfd.Sysfd)
    90  }
    91  
    92  // NewFile returns a new File with the given file descriptor and
    93  // name. The returned value will be nil if fd is not a valid file
    94  // descriptor.
    95  func NewFile(fd uintptr, name string) *File {
    96  	return newFile(fd, name, kindNewFile)
    97  }
    98  
    99  // newFileKind describes the kind of file to newFile.
   100  type newFileKind int
   101  
   102  const (
   103  	kindNewFile newFileKind = iota
   104  	kindOpenFile
   105  	kindPipe
   106  )
   107  
   108  // newFile is like NewFile, but if called from OpenFile or Pipe
   109  // (as passed in the kind parameter) it tries to add the file to
   110  // the runtime poller.
   111  func newFile(fd uintptr, name string, kind newFileKind) *File {
   112  	fdi := int(fd)
   113  	if fdi < 0 {
   114  		return nil
   115  	}
   116  	f := &File{&file{
   117  		pfd: poll.FD{
   118  			Sysfd:         fdi,
   119  			IsStream:      true,
   120  			ZeroReadIsEOF: true,
   121  		},
   122  		name:        name,
   123  		stdoutOrErr: fdi == 1 || fdi == 2,
   124  	}}
   125  
   126  	// Don't try to use kqueue with regular files on FreeBSD.
   127  	// It crashes the system unpredictably while running all.bash.
   128  	// Issue 19093.
   129  	if runtime.GOOS == "freebsd" && kind == kindOpenFile {
   130  		kind = kindNewFile
   131  	}
   132  
   133  	pollable := kind == kindOpenFile || kind == kindPipe
   134  	if err := f.pfd.Init("file", pollable); err != nil {
   135  		// An error here indicates a failure to register
   136  		// with the netpoll system. That can happen for
   137  		// a file descriptor that is not supported by
   138  		// epoll/kqueue; for example, disk files on
   139  		// GNU/Linux systems. We assume that any real error
   140  		// will show up in later I/O.
   141  	} else if pollable {
   142  		// We successfully registered with netpoll, so put
   143  		// the file into nonblocking mode.
   144  		if err := syscall.SetNonblock(fdi, true); err == nil {
   145  			f.nonblock = true
   146  		}
   147  	}
   148  
   149  	runtime.SetFinalizer(f.file, (*file).close)
   150  	return f
   151  }
   152  
   153  // Auxiliary information if the File describes a directory
   154  type dirInfo struct {
   155  	buf  []byte // buffer for directory I/O
   156  	nbuf int    // length of buf; return value from Getdirentries
   157  	bufp int    // location of next record in buf.
   158  }
   159  
   160  // epipecheck raises SIGPIPE if we get an EPIPE error on standard
   161  // output or standard error. See the SIGPIPE docs in os/signal, and
   162  // issue 11845.
   163  func epipecheck(file *File, e error) {
   164  	if e == syscall.EPIPE && file.stdoutOrErr {
   165  		sigpipe()
   166  	}
   167  }
   168  
   169  // DevNull is the name of the operating system's ``null device.''
   170  // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
   171  const DevNull = "/dev/null"
   172  
   173  // openFileNolog is the Unix implementation of OpenFile.
   174  func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
   175  	chmod := false
   176  	if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
   177  		if _, err := Stat(name); IsNotExist(err) {
   178  			chmod = true
   179  		}
   180  	}
   181  
   182  	// DARA Instrumentation
   183  	if runtime.Is_dara_profiling_on() {
   184          runtime.Dara_Debug_Print(func() {
   185  		    print("[OPEN] : ")
   186  		    print(name + " ")
   187  		    print(flag)
   188  		    print(" ")
   189  		    println(perm)
   190          })
   191  		argInfo1 := dara.GeneralType{Type: dara.STRING}
   192          copy(argInfo1.String[:], name)
   193  		argInfo2 := dara.GeneralType{Type: dara.INTEGER, Integer: flag}
   194  		argInfo3 := dara.GeneralType{Type: dara.INTEGER, Integer: int(perm)}
   195  		retInfo1 := dara.GeneralType{Type: dara.POINTER, Unsupported: dara.UNSUPPORTEDVAL}
   196  		retInfo2 := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   197  		syscallInfo := dara.GeneralSyscall{dara.DSYS_OPEN, 3, 2, [10]dara.GeneralType{argInfo1, argInfo2, argInfo3}, [10]dara.GeneralType{retInfo1, retInfo2}}
   198  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_OPEN, syscallInfo)
   199  	}
   200  	var r int
   201  	for {
   202  		var e error
   203  		r, e = syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
   204  		if e == nil {
   205  			break
   206  		}
   207  
   208  		// On OS X, sigaction(2) doesn't guarantee that SA_RESTART will cause
   209  		// open(2) to be restarted for regular files. This is easy to reproduce on
   210  		// fuse file systems (see http://golang.org/issue/11180).
   211  		if runtime.GOOS == "darwin" && e == syscall.EINTR {
   212  			continue
   213  		}
   214  
   215  		return nil, &PathError{"open", name, e}
   216  	}
   217  
   218  	// open(2) itself won't handle the sticky bit on *BSD and Solaris
   219  	if chmod {
   220  		Chmod(name, perm)
   221  	}
   222  
   223  	// There's a race here with fork/exec, which we are
   224  	// content to live with. See ../syscall/exec_unix.go.
   225  	if !supportsCloseOnExec {
   226  		syscall.CloseOnExec(r)
   227  	}
   228  
   229  	return newFile(uintptr(r), name, kindOpenFile), nil
   230  }
   231  
   232  // Close closes the File, rendering it unusable for I/O.
   233  // It returns an error, if any.
   234  func (f *File) Close() error {
   235  	if f == nil {
   236  		return ErrInvalid
   237  	}
   238  	return f.file.close()
   239  }
   240  
   241  func (file *file) close() error {
   242  	if file == nil {
   243  		return syscall.EINVAL
   244  	}
   245  	// DARA Instrumentation
   246  	if runtime.Is_dara_profiling_on() {
   247          runtime.Dara_Debug_Print(func() {
   248  		    print("[CLOSE] : ")
   249  		    println(file.name)
   250          })
   251  		argInfo := dara.GeneralType{Type: dara.FILE}
   252          copy(argInfo.String[:], file.name)
   253  		retInfo := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   254  		syscallInfo := dara.GeneralSyscall{dara.DSYS_CLOSE, 1, 1, [10]dara.GeneralType{argInfo}, [10]dara.GeneralType{retInfo}}
   255  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_CLOSE, syscallInfo)
   256  	}
   257  	var err error
   258  	if e := file.pfd.Close(); e != nil {
   259  		if e == poll.ErrFileClosing {
   260  			e = ErrClosed
   261  		}
   262  		err = &PathError{"close", file.name, e}
   263  	}
   264  
   265  	// no need for a finalizer anymore
   266  	runtime.SetFinalizer(file, nil)
   267  	return err
   268  }
   269  
   270  // read reads up to len(b) bytes from the File.
   271  // It returns the number of bytes read and an error, if any.
   272  func (f *File) read(b []byte) (n int, err error) {
   273  	n, err = f.pfd.Read(b)
   274  	runtime.KeepAlive(f)
   275  	// DARA Instrumentation
   276  	if runtime.Is_dara_profiling_on() {
   277          runtime.Dara_Debug_Print(func() {
   278  		    print("[READ] : ")
   279  		    println(f.file.name)
   280          })
   281  		argInfo1 := dara.GeneralType{Type: dara.FILE}
   282          copy(argInfo1.String[:], f.name)
   283  		argInfo2 := dara.GeneralType{Type: dara.ARRAY, Integer: len(b)}
   284  		retInfo1 := dara.GeneralType{Type: dara.INTEGER, Integer: n}
   285  		retInfo2 := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   286  		syscallInfo := dara.GeneralSyscall{dara.DSYS_READ, 2, 2, [10]dara.GeneralType{argInfo1, argInfo2}, [10]dara.GeneralType{retInfo1, retInfo2}}
   287  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_READ, syscallInfo)
   288  	}
   289  	return n, err
   290  }
   291  
   292  // pread reads len(b) bytes from the File starting at byte offset off.
   293  // It returns the number of bytes read and the error, if any.
   294  // EOF is signaled by a zero count with err set to nil.
   295  func (f *File) pread(b []byte, off int64) (n int, err error) {
   296  	n, err = f.pfd.Pread(b, off)
   297  	runtime.KeepAlive(f)
   298  	// DARA Instrumentation
   299  	if runtime.Is_dara_profiling_on() {
   300          runtime.Dara_Debug_Print(func() {
   301  		    print("[PREAD] : ")
   302  		    print(f.file.name)
   303  		    print(" ")
   304  		    println(off)
   305          })
   306  		argInfo1 := dara.GeneralType{Type: dara.FILE}
   307          copy(argInfo1.String[:], f.name)
   308  		argInfo2 := dara.GeneralType{Type: dara.ARRAY, Integer: len(b)}
   309  		argInfo3 := dara.GeneralType{Type: dara.INTEGER64, Integer64: off}
   310  		retInfo1 := dara.GeneralType{Type: dara.INTEGER, Integer: n}
   311  		retInfo2 := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   312  		syscallInfo := dara.GeneralSyscall{dara.DSYS_PREAD64, 3, 2, [10]dara.GeneralType{argInfo1, argInfo2, argInfo3}, [10]dara.GeneralType{retInfo1, retInfo2}}
   313  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_PREAD64, syscallInfo)
   314  	}
   315  	return n, err
   316  }
   317  
   318  // write writes len(b) bytes to the File.
   319  // It returns the number of bytes written and an error, if any.
   320  func (f *File) write(b []byte) (n int, err error) {
   321  	n, err = f.pfd.Write(b)
   322  	runtime.KeepAlive(f)
   323  	// DARA Instrumentation
   324  	if runtime.Is_dara_profiling_on() {
   325          runtime.Dara_Debug_Print(func() {
   326  		    print("[WRITE] : ")
   327  		    print(f.file.name)
   328  		    print(" ")
   329  		    println(string(b[:len(b)]))
   330          })
   331  		argInfo1 := dara.GeneralType{Type: dara.FILE}
   332          copy(argInfo1.String[:], f.name)
   333  		argInfo2 := dara.GeneralType{Type: dara.ARRAY, Integer: len(b)}
   334  		retInfo1 := dara.GeneralType{Type: dara.INTEGER, Integer: n}
   335  		retInfo2 := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   336  		syscallInfo := dara.GeneralSyscall{dara.DSYS_WRITE, 2, 2, [10]dara.GeneralType{argInfo1, argInfo2}, [10]dara.GeneralType{retInfo1, retInfo2}}
   337  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_WRITE, syscallInfo)
   338  	}
   339  	return n, err
   340  }
   341  
   342  // pwrite writes len(b) bytes to the File starting at byte offset off.
   343  // It returns the number of bytes written and an error, if any.
   344  func (f *File) pwrite(b []byte, off int64) (n int, err error) {
   345      // DARA Instrumentation
   346  	if runtime.Is_dara_profiling_on() {
   347          runtime.Dara_Debug_Print(func() {
   348  		    print("[PWRITE] : ")
   349  		    print(f.file.name)
   350  		    print(" ")
   351  		    print(string(b[:len(b)]))
   352  		    print(" ")
   353  		    println(off)
   354          })
   355  		argInfo1 := dara.GeneralType{Type: dara.FILE}
   356          copy(argInfo1.String[:], f.name)
   357  		argInfo2 := dara.GeneralType{Type: dara.ARRAY, Integer: len(b)}
   358  		argInfo3 := dara.GeneralType{Type: dara.INTEGER64, Integer64: off}
   359  		retInfo1 := dara.GeneralType{Type: dara.INTEGER, Integer: n}
   360  		retInfo2 := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   361  		syscallInfo := dara.GeneralSyscall{dara.DSYS_PWRITE64, 3, 2, [10]dara.GeneralType{argInfo1, argInfo2, argInfo3}, [10]dara.GeneralType{retInfo1, retInfo2}}
   362  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_PWRITE64, syscallInfo)
   363  	}
   364  	n, err = f.pfd.Pwrite(b, off)
   365  	runtime.KeepAlive(f)
   366  	return n, err
   367  }
   368  
   369  // seek sets the offset for the next Read or Write on file to offset, interpreted
   370  // according to whence: 0 means relative to the origin of the file, 1 means
   371  // relative to the current offset, and 2 means relative to the end.
   372  // It returns the new offset and an error, if any.
   373  func (f *File) seek(offset int64, whence int) (ret int64, err error) {
   374  	ret, err = f.pfd.Seek(offset, whence)
   375  	runtime.KeepAlive(f)
   376  	// DARA Instrumentation
   377  	if runtime.Is_dara_profiling_on() {
   378          runtime.Dara_Debug_Print(func() {
   379  		    print("[SEEK] : ")
   380  		    print(f.file.name)
   381  		    print(" ")
   382  		    print(offset)
   383  		    print(" ")
   384  		    println(whence)
   385          })
   386  		argInfo1 := dara.GeneralType{Type: dara.FILE}
   387          copy(argInfo1.String[:], f.name)
   388  		argInfo2 := dara.GeneralType{Type: dara.INTEGER64, Integer64: offset}
   389  		argInfo3 := dara.GeneralType{Type: dara.INTEGER, Integer: whence}
   390  		retInfo1 := dara.GeneralType{Type: dara.INTEGER64, Integer64: ret}
   391  		retInfo2 := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   392  		syscallInfo := dara.GeneralSyscall{dara.DSYS_LSEEK, 3, 2, [10]dara.GeneralType{argInfo1, argInfo2, argInfo3}, [10]dara.GeneralType{retInfo1, retInfo2}}
   393  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_LSEEK, syscallInfo)
   394  	}
   395  	return ret, err
   396  }
   397  
   398  // Truncate changes the size of the named file.
   399  // If the file is a symbolic link, it changes the size of the link's target.
   400  // If there is an error, it will be of type *PathError.
   401  func Truncate(name string, size int64) error {
   402      // DARA Instrumentation
   403  	if runtime.Is_dara_profiling_on() {
   404          runtime.Dara_Debug_Print(func() {
   405  		    print("[TRUNCATE] : ")
   406  		    print(name)
   407  		    print(" ")
   408  		    println(size)
   409          })
   410  		argInfo1 := dara.GeneralType{Type: dara.STRING}
   411          copy(argInfo1.String[:], name)
   412  		argInfo2 := dara.GeneralType{Type: dara.INTEGER64, Integer64: size}
   413  		retInfo := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   414  		syscallInfo := dara.GeneralSyscall{dara.DSYS_TRUNCATE, 2, 1, [10]dara.GeneralType{argInfo1, argInfo2}, [10]dara.GeneralType{retInfo}}
   415  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_TRUNCATE, syscallInfo)
   416  	}
   417  	if e := syscall.Truncate(name, size); e != nil {
   418  		return &PathError{"truncate", name, e}
   419  	}
   420  	return nil
   421  }
   422  
   423  // Remove removes the named file or directory.
   424  // If there is an error, it will be of type *PathError.
   425  func Remove(name string) error {
   426  	// System call interface forces us to know
   427  	// whether name is a file or directory.
   428  	// Try both: it is cheaper on average than
   429  	// doing a Stat plus the right one
   430  	e := syscall.Unlink(name)
   431  	if e == nil {
   432  		return nil
   433  	}
   434  	e1 := syscall.Rmdir(name)
   435  	if e1 == nil {
   436  		return nil
   437  	}
   438  
   439  	// Both failed: figure out which error to return.
   440  	// OS X and Linux differ on whether unlink(dir)
   441  	// returns EISDIR, so can't use that. However,
   442  	// both agree that rmdir(file) returns ENOTDIR,
   443  	// so we can use that to decide which error is real.
   444  	// Rmdir might also return ENOTDIR if given a bad
   445  	// file path, like /etc/passwd/foo, but in that case,
   446  	// both errors will be ENOTDIR, so it's okay to
   447  	// use the error from unlink.
   448  	if e1 != syscall.ENOTDIR {
   449  		e = e1
   450  	}
   451  	return &PathError{"remove", name, e}
   452  }
   453  
   454  func tempDir() string {
   455  	dir := Getenv("TMPDIR")
   456  	if dir == "" {
   457  		if runtime.GOOS == "android" {
   458  			dir = "/data/local/tmp"
   459  		} else {
   460  			dir = "/tmp"
   461  		}
   462  	}
   463  	return dir
   464  }
   465  
   466  // Link creates newname as a hard link to the oldname file.
   467  // If there is an error, it will be of type *LinkError.
   468  func Link(oldname, newname string) error {
   469  	e := syscall.Link(oldname, newname)
   470  	// DARA Instrumentation
   471  	if runtime.Is_dara_profiling_on() {
   472          runtime.Dara_Debug_Print(func() {
   473  		    print("[LINK] : ")
   474  		    print(oldname)
   475  		    print(" ")
   476  		    println(newname)
   477          })
   478  		argInfo1 := dara.GeneralType{Type: dara.STRING}
   479          copy(argInfo1.String[:], oldname)
   480  		argInfo2 := dara.GeneralType{Type: dara.STRING}
   481          copy(argInfo2.String[:], newname)
   482  		retInfo := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   483  		syscallInfo := dara.GeneralSyscall{dara.DSYS_LINK, 2, 1, [10]dara.GeneralType{argInfo1, argInfo2}, [10]dara.GeneralType{retInfo}}
   484  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_LINK, syscallInfo)
   485  	}
   486  	if e != nil {
   487  		return &LinkError{"link", oldname, newname, e}
   488  	}
   489  	return nil
   490  }
   491  
   492  // Symlink creates newname as a symbolic link to oldname.
   493  // If there is an error, it will be of type *LinkError.
   494  func Symlink(oldname, newname string) error {
   495  	e := syscall.Symlink(oldname, newname)
   496  	// DARA Instrumentation
   497  	if runtime.Is_dara_profiling_on() {
   498          runtime.Dara_Debug_Print(func() {
   499  		    print("[LINK] : ")
   500  		    print(oldname)
   501  		    print(" ")
   502  		    println(newname)
   503          })
   504  		argInfo1 := dara.GeneralType{Type: dara.STRING}
   505          copy(argInfo1.String[:], oldname)
   506  		argInfo2 := dara.GeneralType{Type: dara.STRING}
   507          copy(argInfo2.String[:], newname)
   508  		retInfo := dara.GeneralType{Type: dara.ERROR, Unsupported: dara.UNSUPPORTEDVAL}
   509  		syscallInfo := dara.GeneralSyscall{dara.DSYS_SYMLINK, 2, 1, [10]dara.GeneralType{argInfo1, argInfo2}, [10]dara.GeneralType{retInfo}}
   510  		runtime.Report_Syscall_To_Scheduler(dara.DSYS_SYMLINK, syscallInfo)
   511  	}
   512  	if e != nil {
   513  		return &LinkError{"symlink", oldname, newname, e}
   514  	}
   515  	return nil
   516  }