github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/file.go (about)

     1  // Portions 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  // This file was originally copied from Go, see:
     6  // https://github.com/golang/go/blob/master/src/os/file.go
     7  //
     8  // Some of the code inherited from Go is not used anymore in Tinygo, but we keep
     9  // changes to a minimum to help simplify bringing changes (e.g. the lstat global
    10  // is not used here anymore, but we might need it if we add tests from Go in
    11  // this package).
    12  
    13  // Package os implements a subset of the Go "os" package. See
    14  // https://godoc.org/os for details.
    15  //
    16  // Note that the current implementation is blocking. This limitation should be
    17  // removed in a future version.
    18  package os
    19  
    20  import (
    21  	"errors"
    22  	"io"
    23  	"io/fs"
    24  	"runtime"
    25  	"syscall"
    26  )
    27  
    28  // Seek whence values.
    29  //
    30  // Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
    31  const (
    32  	SEEK_SET int = io.SeekStart
    33  	SEEK_CUR int = io.SeekCurrent
    34  	SEEK_END int = io.SeekEnd
    35  )
    36  
    37  // lstat is overridden in tests.
    38  var lstat = Lstat
    39  
    40  // Mkdir creates a directory. If the operation fails, it will return an error of
    41  // type *PathError.
    42  func Mkdir(path string, perm FileMode) error {
    43  	fs, suffix := findMount(path)
    44  	if fs == nil {
    45  		return &PathError{Op: "mkdir", Path: path, Err: ErrNotExist}
    46  	}
    47  	err := fs.Mkdir(suffix, perm)
    48  	if err != nil {
    49  		return &PathError{Op: "mkdir", Path: path, Err: err}
    50  	}
    51  	return nil
    52  }
    53  
    54  // Many functions in package syscall return a count of -1 instead of 0.
    55  // Using fixCount(call()) instead of call() corrects the count.
    56  func fixCount(n int, err error) (int, error) {
    57  	if n < 0 {
    58  		n = 0
    59  	}
    60  	return n, err
    61  }
    62  
    63  // Remove removes a file or (empty) directory. If the operation fails, it will
    64  // return an error of type *PathError.
    65  func Remove(path string) error {
    66  	fs, suffix := findMount(path)
    67  	if fs == nil {
    68  		return &PathError{Op: "remove", Path: path, Err: ErrNotExist}
    69  	}
    70  	err := fs.Remove(suffix)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	return nil
    75  }
    76  
    77  // Name returns the name of the file with which it was opened.
    78  func (f *File) Name() string {
    79  	return f.name
    80  }
    81  
    82  // OpenFile opens the named file. If the operation fails, the returned error
    83  // will be of type *PathError.
    84  func OpenFile(name string, flag int, perm FileMode) (*File, error) {
    85  	fs, suffix := findMount(name)
    86  	if fs == nil {
    87  		return nil, &PathError{Op: "open", Path: name, Err: ErrNotExist}
    88  	}
    89  	handle, err := fs.OpenFile(suffix, flag, perm)
    90  	if err != nil {
    91  		return nil, &PathError{Op: "open", Path: name, Err: err}
    92  	}
    93  	f := NewFile(handle, name)
    94  	f.appendMode = (flag & O_APPEND) != 0
    95  	return f, nil
    96  }
    97  
    98  // Open opens the file named for reading.
    99  func Open(name string) (*File, error) {
   100  	return OpenFile(name, O_RDONLY, 0)
   101  }
   102  
   103  // Create creates the named file, overwriting it if it already exists.
   104  func Create(name string) (*File, error) {
   105  	return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
   106  }
   107  
   108  // Read reads up to len(b) bytes from the File. It returns the number of bytes
   109  // read and any error encountered. At end of file, Read returns 0, io.EOF.
   110  func (f *File) Read(b []byte) (n int, err error) {
   111  	if f.handle == nil {
   112  		err = ErrClosed
   113  	} else {
   114  		n, err = f.handle.Read(b)
   115  	}
   116  	// TODO: want to always wrap, like upstream, but ReadFile() compares against exactly io.EOF?
   117  	if err != nil && err != io.EOF {
   118  		err = &PathError{Op: "read", Path: f.name, Err: err}
   119  	}
   120  	return
   121  }
   122  
   123  var errNegativeOffset = errors.New("negative offset")
   124  
   125  // ReadAt reads up to len(b) bytes from the File at the given absolute offset.
   126  // It returns the number of bytes read and any error encountered, possible io.EOF.
   127  // At end of file, Read returns 0, io.EOF.
   128  func (f *File) ReadAt(b []byte, offset int64) (n int, err error) {
   129  	if offset < 0 {
   130  		return 0, &PathError{Op: "readat", Path: f.name, Err: errNegativeOffset}
   131  	}
   132  	if f.handle == nil {
   133  		return 0, &PathError{Op: "readat", Path: f.name, Err: ErrClosed}
   134  	}
   135  
   136  	for len(b) > 0 {
   137  		m, e := f.handle.ReadAt(b, offset)
   138  		if e != nil {
   139  			// TODO: want to always wrap, like upstream, but TestReadAtEOF compares against exactly io.EOF?
   140  			if e != io.EOF {
   141  				err = &PathError{Op: "readat", Path: f.name, Err: e}
   142  			} else {
   143  				err = e
   144  			}
   145  			break
   146  		}
   147  		n += m
   148  		b = b[m:]
   149  		offset += int64(m)
   150  	}
   151  
   152  	return
   153  }
   154  
   155  // Write writes len(b) bytes to the File. It returns the number of bytes written
   156  // and an error, if any. Write returns a non-nil error when n != len(b).
   157  func (f *File) Write(b []byte) (n int, err error) {
   158  	if f.handle == nil {
   159  		err = ErrClosed
   160  	} else {
   161  		n, err = f.handle.Write(b)
   162  	}
   163  	if err != nil {
   164  		err = &PathError{Op: "write", Path: f.name, Err: err}
   165  	}
   166  	return
   167  }
   168  
   169  // WriteString is like Write, but writes the contents of string s rather than a
   170  // slice of bytes.
   171  func (f *File) WriteString(s string) (n int, err error) {
   172  	return f.Write([]byte(s))
   173  }
   174  
   175  var errWriteAtInAppendMode = errors.New("os: invalid use of WriteAt on file opened with O_APPEND")
   176  
   177  // WriteAt writes len(b) bytes to the File starting at byte offset off.
   178  // It returns the number of bytes written and an error, if any.
   179  // WriteAt returns a non-nil error when n != len(b).
   180  //
   181  // If file was opened with the O_APPEND flag, WriteAt returns an error.
   182  func (f *File) WriteAt(b []byte, offset int64) (n int, err error) {
   183  	switch {
   184  	case offset < 0:
   185  		return 0, &PathError{Op: "writeat", Path: f.name, Err: errNegativeOffset}
   186  	case f.handle == nil:
   187  		return 0, &PathError{Op: "writeat", Path: f.name, Err: ErrClosed}
   188  	case f.appendMode:
   189  		// Go does not wrap this error but it would be more consistent
   190  		// if it did.
   191  		return 0, errWriteAtInAppendMode
   192  	}
   193  	for len(b) > 0 {
   194  		m, e := f.handle.WriteAt(b, offset)
   195  		if e != nil {
   196  			err = &PathError{Op: "writeat", Path: f.name, Err: e}
   197  			break
   198  		}
   199  		n += m
   200  		b = b[m:]
   201  		offset += int64(m)
   202  	}
   203  	return
   204  }
   205  
   206  // Close closes the File, rendering it unusable for I/O.
   207  func (f *File) Close() (err error) {
   208  	if f.handle == nil {
   209  		err = ErrClosed
   210  	} else {
   211  		// Some platforms manage extra state other than the system handle which
   212  		// needs to be released when the file is closed. For example, darwin
   213  		// files have a DIR object holding a dup of the file descriptor, and
   214  		// linux files hold a buffer which needs to be released to a pool.
   215  		//
   216  		// These platform-specific logic is provided by the (*file).close method
   217  		// which is why we do not call the handle's Close method directly.
   218  		err = f.file.close()
   219  		if err == nil {
   220  			f.handle = nil
   221  		}
   222  	}
   223  	if err != nil {
   224  		err = &PathError{Op: "close", Path: f.name, Err: err}
   225  	}
   226  	return
   227  }
   228  
   229  // Seek sets the offset for the next Read or Write on file to offset, interpreted
   230  // according to whence: 0 means relative to the origin of the file, 1 means
   231  // relative to the current offset, and 2 means relative to the end.
   232  // It returns the new offset and an error, if any.
   233  // The behavior of Seek on a file opened with O_APPEND is not specified.
   234  //
   235  // If f is a directory, the behavior of Seek varies by operating
   236  // system; you can seek to the beginning of the directory on Unix-like
   237  // operating systems, but not on Windows.
   238  func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
   239  	if f.handle == nil {
   240  		err = ErrClosed
   241  	} else {
   242  		ret, err = f.handle.Seek(offset, whence)
   243  	}
   244  	if err != nil {
   245  		err = &PathError{Op: "seek", Path: f.name, Err: err}
   246  	}
   247  	return
   248  }
   249  
   250  func (f *File) SyscallConn() (conn syscall.RawConn, err error) {
   251  	if f.handle == nil {
   252  		err = ErrClosed
   253  	} else {
   254  		err = ErrNotImplemented
   255  	}
   256  	return
   257  }
   258  
   259  // fd is an internal interface that is used to try a type assertion in order to
   260  // call the Fd() method of the underlying file handle if it is implemented.
   261  type fd interface {
   262  	Fd() uintptr
   263  }
   264  
   265  // Fd returns the file handle referencing the open file.
   266  func (f *File) Fd() uintptr {
   267  	handle, ok := f.handle.(fd)
   268  	if ok {
   269  		return handle.Fd()
   270  	}
   271  	return ^uintptr(0)
   272  }
   273  
   274  // Sync commits the current contents of the file to stable storage.
   275  // Typically, this means flushing the file system's in-memory copy of recently
   276  // written data to disk.
   277  func (f *File) Sync() (err error) {
   278  	if f.handle == nil {
   279  		err = ErrClosed
   280  	} else {
   281  		err = f.handle.Sync()
   282  	}
   283  	return
   284  }
   285  
   286  // Truncate is a stub, not yet implemented
   287  func (f *File) Truncate(size int64) (err error) {
   288  	if f.handle == nil {
   289  		err = ErrClosed
   290  	} else {
   291  		err = ErrNotImplemented
   292  	}
   293  	return &PathError{Op: "truncate", Path: f.name, Err: err}
   294  }
   295  
   296  // LinkError records an error during a link or symlink or rename system call and
   297  // the paths that caused it.
   298  type LinkError struct {
   299  	Op  string
   300  	Old string
   301  	New string
   302  	Err error
   303  }
   304  
   305  func (e *LinkError) Error() string {
   306  	return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
   307  }
   308  
   309  func (e *LinkError) Unwrap() error {
   310  	return e.Err
   311  }
   312  
   313  const (
   314  	O_RDONLY int = syscall.O_RDONLY
   315  	O_WRONLY int = syscall.O_WRONLY
   316  	O_RDWR   int = syscall.O_RDWR
   317  	O_APPEND int = syscall.O_APPEND
   318  	O_CREATE int = syscall.O_CREAT
   319  	O_EXCL   int = syscall.O_EXCL
   320  	O_SYNC   int = syscall.O_SYNC
   321  	O_TRUNC  int = syscall.O_TRUNC
   322  )
   323  
   324  func Getwd() (string, error) {
   325  	return syscall.Getwd()
   326  }
   327  
   328  // TempDir returns the default directory to use for temporary files.
   329  //
   330  // On Unix systems, it returns $TMPDIR if non-empty, else /tmp.
   331  // On Windows, it uses GetTempPath, returning the first non-empty
   332  // value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory.
   333  //
   334  // The directory is neither guaranteed to exist nor have accessible
   335  // permissions.
   336  func TempDir() string {
   337  	return tempDir()
   338  }
   339  
   340  // UserHomeDir returns the current user's home directory.
   341  //
   342  // On Unix, including macOS, it returns the $HOME environment variable.
   343  // On Windows, it returns %USERPROFILE%.
   344  // On Plan 9, it returns the $home environment variable.
   345  func UserHomeDir() (string, error) {
   346  	env, enverr := "HOME", "$HOME"
   347  	switch runtime.GOOS {
   348  	case "windows":
   349  		env, enverr = "USERPROFILE", "%userprofile%"
   350  	case "plan9":
   351  		env, enverr = "home", "$home"
   352  	}
   353  	if v := Getenv(env); v != "" {
   354  		return v, nil
   355  	}
   356  	// On some geese the home directory is not always defined.
   357  	switch runtime.GOOS {
   358  	case "android":
   359  		return "/sdcard", nil
   360  	case "ios":
   361  		return "/", nil
   362  	}
   363  	return "", errors.New(enverr + " is not defined")
   364  }
   365  
   366  type (
   367  	FileMode = fs.FileMode
   368  	FileInfo = fs.FileInfo
   369  )
   370  
   371  // The followings are copied from Go 1.16 or 1.17 official implementation:
   372  // https://github.com/golang/go/blob/go1.16/src/os/file.go
   373  
   374  // DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir.
   375  //
   376  // Note that DirFS("/prefix") only guarantees that the Open calls it makes to the
   377  // operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the
   378  // same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside
   379  // the /prefix tree, then using DirFS does not stop the access any more than using
   380  // os.Open does. DirFS is therefore not a general substitute for a chroot-style security
   381  // mechanism when the directory tree contains arbitrary content.
   382  func DirFS(dir string) fs.FS {
   383  	return dirFS(dir)
   384  }
   385  
   386  func containsAny(s, chars string) bool {
   387  	for i := 0; i < len(s); i++ {
   388  		for j := 0; j < len(chars); j++ {
   389  			if s[i] == chars[j] {
   390  				return true
   391  			}
   392  		}
   393  	}
   394  	return false
   395  }
   396  
   397  type dirFS string
   398  
   399  func (dir dirFS) Open(name string) (fs.File, error) {
   400  	if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
   401  		return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
   402  	}
   403  	f, err := Open(string(dir) + "/" + name)
   404  	if err != nil {
   405  		return nil, err // nil fs.File
   406  	}
   407  	return f, nil
   408  }
   409  
   410  func (dir dirFS) Stat(name string) (fs.FileInfo, error) {
   411  	if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
   412  		return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid}
   413  	}
   414  	f, err := Stat(string(dir) + "/" + name)
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  	return f, nil
   419  }
   420  
   421  // ReadFile reads the named file and returns the contents.
   422  // A successful call returns err == nil, not err == EOF.
   423  // Because ReadFile reads the whole file, it does not treat an EOF from Read
   424  // as an error to be reported.
   425  func ReadFile(name string) ([]byte, error) {
   426  	f, err := Open(name)
   427  	if err != nil {
   428  		return nil, err
   429  	}
   430  	defer f.Close()
   431  
   432  	var size int
   433  	if info, err := f.Stat(); err == nil {
   434  		size64 := info.Size()
   435  		if int64(int(size64)) == size64 {
   436  			size = int(size64)
   437  		}
   438  	}
   439  	size++ // one byte for final read at EOF
   440  
   441  	// If a file claims a small size, read at least 512 bytes.
   442  	// In particular, files in Linux's /proc claim size 0 but
   443  	// then do not work right if read in small pieces,
   444  	// so an initial read of 1 byte would not work correctly.
   445  	if size < 512 {
   446  		size = 512
   447  	}
   448  
   449  	data := make([]byte, 0, size)
   450  	for {
   451  		if len(data) >= cap(data) {
   452  			d := append(data[:cap(data)], 0)
   453  			data = d[:len(data)]
   454  		}
   455  		n, err := f.Read(data[len(data):cap(data)])
   456  		data = data[:len(data)+n]
   457  		if err != nil {
   458  			if err == io.EOF {
   459  				err = nil
   460  			}
   461  			return data, err
   462  		}
   463  	}
   464  }
   465  
   466  // WriteFile writes data to the named file, creating it if necessary.
   467  // If the file does not exist, WriteFile creates it with permissions perm (before umask);
   468  // otherwise WriteFile truncates it before writing, without changing permissions.
   469  func WriteFile(name string, data []byte, perm FileMode) error {
   470  	f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
   471  	if err != nil {
   472  		return err
   473  	}
   474  	_, err = f.Write(data)
   475  	if err1 := f.Close(); err1 != nil && err == nil {
   476  		err = err1
   477  	}
   478  	return err
   479  }
   480  
   481  // The defined file mode bits are the most significant bits of the FileMode.
   482  // The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
   483  // The values of these bits should be considered part of the public API and
   484  // may be used in wire protocols or disk representations: they must not be
   485  // changed, although new bits might be added.
   486  const (
   487  	// The single letters are the abbreviations
   488  	// used by the String method's formatting.
   489  	ModeDir        = fs.ModeDir        // d: is a directory
   490  	ModeAppend     = fs.ModeAppend     // a: append-only
   491  	ModeExclusive  = fs.ModeExclusive  // l: exclusive use
   492  	ModeTemporary  = fs.ModeTemporary  // T: temporary file; Plan 9 only
   493  	ModeSymlink    = fs.ModeSymlink    // L: symbolic link
   494  	ModeDevice     = fs.ModeDevice     // D: device file
   495  	ModeNamedPipe  = fs.ModeNamedPipe  // p: named pipe (FIFO)
   496  	ModeSocket     = fs.ModeSocket     // S: Unix domain socket
   497  	ModeSetuid     = fs.ModeSetuid     // u: setuid
   498  	ModeSetgid     = fs.ModeSetgid     // g: setgid
   499  	ModeCharDevice = fs.ModeCharDevice // c: Unix character device, when ModeDevice is set
   500  	ModeSticky     = fs.ModeSticky     // t: sticky
   501  	ModeIrregular  = fs.ModeIrregular  // ?: non-regular file; nothing else is known about this file
   502  
   503  	// Mask for the type bits. For regular files, none will be set.
   504  	ModeType = fs.ModeType
   505  
   506  	ModePerm = fs.ModePerm // Unix permission bits, 0o777
   507  )