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

     1  //go:build !baremetal && !js
     2  
     3  // Portions copyright 2009 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package os
     8  
     9  import (
    10  	"io"
    11  	"syscall"
    12  )
    13  
    14  func init() {
    15  	// Mount the host filesystem at the root directory. This is what most
    16  	// programs will be expecting.
    17  	Mount("/", unixFilesystem{})
    18  }
    19  
    20  // Stdin, Stdout, and Stderr are open Files pointing to the standard input,
    21  // standard output, and standard error file descriptors.
    22  var (
    23  	Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
    24  	Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
    25  	Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
    26  )
    27  
    28  // isOS indicates whether we're running on a real operating system with
    29  // filesystem support.
    30  const isOS = true
    31  
    32  // Chdir changes the current working directory to the named directory.
    33  // If there is an error, it will be of type *PathError.
    34  func Chdir(dir string) error {
    35  	if e := syscall.Chdir(dir); e != nil {
    36  		return &PathError{Op: "chdir", Path: dir, Err: e}
    37  	}
    38  	return nil
    39  }
    40  
    41  // Rename renames (moves) oldpath to newpath.
    42  // If newpath already exists and is not a directory, Rename replaces it.
    43  // OS-specific restrictions may apply when oldpath and newpath are in different directories.
    44  // If there is an error, it will be of type *LinkError.
    45  func Rename(oldpath, newpath string) error {
    46  	return rename(oldpath, newpath)
    47  }
    48  
    49  // unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
    50  // are relative to the current working directory.
    51  type unixFilesystem struct {
    52  }
    53  
    54  func (fs unixFilesystem) Mkdir(path string, perm FileMode) error {
    55  	return handleSyscallError(syscall.Mkdir(path, uint32(perm)))
    56  }
    57  
    58  func (fs unixFilesystem) Remove(path string) error {
    59  	// System call interface forces us to know
    60  	// whether name is a file or directory.
    61  	// Try both: it is cheaper on average than
    62  	// doing a Stat plus the right one.
    63  	e := handleSyscallError(syscall.Unlink(path))
    64  	if e == nil {
    65  		return nil
    66  	}
    67  	e1 := handleSyscallError(syscall.Rmdir(path))
    68  	if e1 == nil {
    69  		return nil
    70  	}
    71  
    72  	// Both failed: figure out which error to return.
    73  	// OS X and Linux differ on whether unlink(dir)
    74  	// returns EISDIR, so can't use that. However,
    75  	// both agree that rmdir(file) returns ENOTDIR,
    76  	// so we can use that to decide which error is real.
    77  	// Rmdir might also return ENOTDIR if given a bad
    78  	// file path, like /etc/passwd/foo, but in that case,
    79  	// both errors will be ENOTDIR, so it's okay to
    80  	// use the error from unlink.
    81  	if e1 != syscall.ENOTDIR {
    82  		e = e1
    83  	}
    84  	return &PathError{Op: "remove", Path: path, Err: e}
    85  }
    86  
    87  func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (uintptr, error) {
    88  	fp, err := syscall.Open(path, flag, uint32(perm))
    89  	return uintptr(fp), handleSyscallError(err)
    90  }
    91  
    92  // unixFileHandle is a Unix file pointer with associated methods that implement
    93  // the FileHandle interface.
    94  type unixFileHandle uintptr
    95  
    96  // Read reads up to len(b) bytes from the File. It returns the number of bytes
    97  // read and any error encountered. At end of file, Read returns 0, io.EOF.
    98  func (f unixFileHandle) Read(b []byte) (n int, err error) {
    99  	n, err = syscall.Read(syscallFd(f), b)
   100  	err = handleSyscallError(err)
   101  	if n == 0 && len(b) > 0 && err == nil {
   102  		err = io.EOF
   103  	}
   104  	return
   105  }
   106  
   107  // Write writes len(b) bytes to the File. It returns the number of bytes written
   108  // and an error, if any. Write returns a non-nil error when n != len(b).
   109  func (f unixFileHandle) Write(b []byte) (n int, err error) {
   110  	n, err = syscall.Write(syscallFd(f), b)
   111  	err = handleSyscallError(err)
   112  	return
   113  }
   114  
   115  // Close closes the File, rendering it unusable for I/O.
   116  func (f unixFileHandle) Close() error {
   117  	return handleSyscallError(syscall.Close(syscallFd(f)))
   118  }
   119  
   120  func (f unixFileHandle) Fd() uintptr {
   121  	return uintptr(f)
   122  }
   123  
   124  // Chmod changes the mode of the named file to mode.
   125  // If the file is a symbolic link, it changes the mode of the link's target.
   126  // If there is an error, it will be of type *PathError.
   127  //
   128  // A different subset of the mode bits are used, depending on the
   129  // operating system.
   130  //
   131  // On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
   132  // ModeSticky are used.
   133  //
   134  // On Windows, only the 0200 bit (owner writable) of mode is used; it
   135  // controls whether the file's read-only attribute is set or cleared.
   136  // The other bits are currently unused. For compatibility with Go 1.12
   137  // and earlier, use a non-zero mode. Use mode 0400 for a read-only
   138  // file and 0600 for a readable+writable file.
   139  func Chmod(name string, mode FileMode) error {
   140  	longName := fixLongPath(name)
   141  	e := ignoringEINTR(func() error {
   142  		return syscall.Chmod(longName, syscallMode(mode))
   143  	})
   144  	if e != nil {
   145  		return &PathError{Op: "chmod", Path: name, Err: e}
   146  	}
   147  	return nil
   148  }
   149  
   150  // ignoringEINTR makes a function call and repeats it if it returns an
   151  // EINTR error. This appears to be required even though we install all
   152  // signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
   153  // Also #20400 and #36644 are issues in which a signal handler is
   154  // installed without setting SA_RESTART. None of these are the common case,
   155  // but there are enough of them that it seems that we can't avoid
   156  // an EINTR loop.
   157  func ignoringEINTR(fn func() error) error {
   158  	for {
   159  		err := fn()
   160  		if err != syscall.EINTR {
   161  			return err
   162  		}
   163  	}
   164  }
   165  
   166  // handleSyscallError converts syscall errors into regular os package errors.
   167  // The err parameter must be either nil or of type syscall.Errno.
   168  func handleSyscallError(err error) error {
   169  	if err == nil {
   170  		return nil
   171  	}
   172  	switch err.(syscall.Errno) {
   173  	case syscall.EEXIST:
   174  		return ErrExist
   175  	case syscall.ENOENT:
   176  		return ErrNotExist
   177  	default:
   178  		return err
   179  	}
   180  }
   181  
   182  // syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
   183  func syscallMode(i FileMode) (o uint32) {
   184  	o |= uint32(i.Perm())
   185  	if i&ModeSetuid != 0 {
   186  		o |= syscall.S_ISUID
   187  	}
   188  	if i&ModeSetgid != 0 {
   189  		o |= syscall.S_ISGID
   190  	}
   191  	if i&ModeSticky != 0 {
   192  		o |= syscall.S_ISVTX
   193  	}
   194  	// No mapping for Go's ModeTemporary (plan9 only).
   195  	return
   196  }