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

     1  //go:build darwin || (linux && !baremetal) || wasip1
     2  
     3  // target wasi sets GOOS=linux and thus the +linux build tag,
     4  // even though it doesn't show up in "tinygo info target -wasi"
     5  
     6  // Portions copyright 2009 The Go Authors. All rights reserved.
     7  // Use of this source code is governed by a BSD-style
     8  // license that can be found in the LICENSE file.
     9  
    10  package os
    11  
    12  import (
    13  	"io"
    14  	"syscall"
    15  )
    16  
    17  const DevNull = "/dev/null"
    18  
    19  type syscallFd = int
    20  
    21  // fixLongPath is a noop on non-Windows platforms.
    22  func fixLongPath(path string) string {
    23  	return path
    24  }
    25  
    26  func rename(oldname, newname string) error {
    27  	// TODO: import rest of upstream tests, handle fancy cases
    28  	err := syscall.Rename(oldname, newname)
    29  	if err != nil {
    30  		return &LinkError{"rename", oldname, newname, err}
    31  	}
    32  	return nil
    33  }
    34  
    35  // file is the real representation of *File.
    36  // The extra level of indirection ensures that no clients of os
    37  // can overwrite this data, which could cause the finalizer
    38  // to close the wrong file descriptor.
    39  type file struct {
    40  	handle     FileHandle
    41  	name       string
    42  	dirinfo    *dirInfo // nil unless directory being read
    43  	appendMode bool
    44  }
    45  
    46  func (f *file) close() (err error) {
    47  	if f.dirinfo != nil {
    48  		f.dirinfo.close()
    49  		f.dirinfo = nil
    50  	}
    51  	return f.handle.Close()
    52  }
    53  
    54  func NewFile(fd uintptr, name string) *File {
    55  	return &File{&file{handle: unixFileHandle(fd), name: name}}
    56  }
    57  
    58  func Pipe() (r *File, w *File, err error) {
    59  	var p [2]int
    60  	err = handleSyscallError(syscall.Pipe2(p[:], syscall.O_CLOEXEC))
    61  	if err != nil {
    62  		return
    63  	}
    64  	r = NewFile(uintptr(p[0]), "|0")
    65  	w = NewFile(uintptr(p[1]), "|1")
    66  	return
    67  }
    68  
    69  func tempDir() string {
    70  	dir := Getenv("TMPDIR")
    71  	if dir == "" {
    72  		dir = "/tmp"
    73  	}
    74  	return dir
    75  }
    76  
    77  // Symlink creates newname as a symbolic link to oldname.
    78  // On Windows, a symlink to a non-existent oldname creates a file symlink;
    79  // if oldname is later created as a directory the symlink will not work.
    80  // If there is an error, it will be of type *LinkError.
    81  func Symlink(oldname, newname string) error {
    82  	e := ignoringEINTR(func() error {
    83  		return syscall.Symlink(oldname, newname)
    84  	})
    85  	if e != nil {
    86  		return &LinkError{"symlink", oldname, newname, e}
    87  	}
    88  	return nil
    89  }
    90  
    91  // Readlink returns the destination of the named symbolic link.
    92  // If there is an error, it will be of type *PathError.
    93  func Readlink(name string) (string, error) {
    94  	for len := 128; ; len *= 2 {
    95  		b := make([]byte, len)
    96  		var (
    97  			n int
    98  			e error
    99  		)
   100  		for {
   101  			n, e = fixCount(syscall.Readlink(name, b))
   102  			if e != syscall.EINTR {
   103  				break
   104  			}
   105  		}
   106  		if e != nil {
   107  			return "", &PathError{Op: "readlink", Path: name, Err: e}
   108  		}
   109  		if n < len {
   110  			return string(b[0:n]), nil
   111  		}
   112  	}
   113  }
   114  
   115  // ReadAt reads up to len(b) bytes from the File starting at the given absolute offset.
   116  // It returns the number of bytes read and any error encountered, possibly io.EOF.
   117  // At end of file, Pread returns 0, io.EOF.
   118  // TODO: move to file_anyos once ReadAt is implemented for windows
   119  func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
   120  	n, err = syscall.Pread(syscallFd(f), b, offset)
   121  	err = handleSyscallError(err)
   122  	if n == 0 && len(b) > 0 && err == nil {
   123  		err = io.EOF
   124  	}
   125  	return
   126  }
   127  
   128  // WriteAt writes len(b) bytes to the File starting at byte offset off.
   129  // It returns the number of bytes written and an error, if any.
   130  // WriteAt returns a non-nil error when n != len(b).
   131  //
   132  // If file was opened with the O_APPEND flag, WriteAt returns an error.
   133  //
   134  // TODO: move to file_anyos once WriteAt is implemented for windows.
   135  func (f unixFileHandle) WriteAt(b []byte, offset int64) (int, error) {
   136  	n, err := syscall.Pwrite(syscallFd(f), b, offset)
   137  	return n, handleSyscallError(err)
   138  }
   139  
   140  // Seek wraps syscall.Seek.
   141  func (f unixFileHandle) Seek(offset int64, whence int) (int64, error) {
   142  	newoffset, err := syscall.Seek(syscallFd(f), offset, whence)
   143  	return newoffset, handleSyscallError(err)
   144  }
   145  
   146  func (f unixFileHandle) Sync() error {
   147  	err := syscall.Fsync(syscallFd(f))
   148  	return handleSyscallError(err)
   149  }
   150  
   151  type unixDirent struct {
   152  	parent string
   153  	name   string
   154  	typ    FileMode
   155  	info   FileInfo
   156  }
   157  
   158  func (d *unixDirent) Name() string   { return d.name }
   159  func (d *unixDirent) IsDir() bool    { return d.typ.IsDir() }
   160  func (d *unixDirent) Type() FileMode { return d.typ }
   161  
   162  func (d *unixDirent) Info() (FileInfo, error) {
   163  	if d.info != nil {
   164  		return d.info, nil
   165  	}
   166  	return lstat(d.parent + "/" + d.name)
   167  }
   168  
   169  func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) {
   170  	ude := &unixDirent{
   171  		parent: parent,
   172  		name:   name,
   173  		typ:    typ,
   174  	}
   175  	if typ != ^FileMode(0) && !testingForceReadDirLstat {
   176  		return ude, nil
   177  	}
   178  
   179  	info, err := lstat(parent + "/" + name)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	ude.typ = info.Mode().Type()
   185  	ude.info = info
   186  	return ude, nil
   187  }