file_unix.mx raw

   1  //go:build darwin || (linux && !baremetal && !wasm_unknown && !nintendoswitch) || wasip1 || wasip2
   2  
   3  // target wasi sets GOOS=linux and thus the +linux build tag,
   4  // even though it doesn't show up in "moxie 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  // Truncate changes the size of the named file.
  59  // If the file is a symbolic link, it changes the size of the link's target.
  60  // If there is an error, it will be of type *PathError.
  61  func Truncate(name string, size int64) error {
  62  	e := ignoringEINTR(func() error {
  63  		return syscall.Truncate(name, size)
  64  	})
  65  	if e != nil {
  66  		return &PathError{Op: "truncate", Path: name, Err: e}
  67  	}
  68  	return nil
  69  }
  70  
  71  func Pipe() (r *File, w *File, err error) {
  72  	var p [2]int
  73  	err = handleSyscallError(pipe(p[:]))
  74  	if err != nil {
  75  		return
  76  	}
  77  	r = NewFile(uintptr(p[0]), "|0")
  78  	w = NewFile(uintptr(p[1]), "|1")
  79  	return
  80  }
  81  
  82  func tempDir() string {
  83  	dir := Getenv("TMPDIR")
  84  	if dir == "" {
  85  		dir = "/tmp"
  86  	}
  87  	return dir
  88  }
  89  
  90  // Link creates newname as a hard link to the oldname file.
  91  // If there is an error, it will be of type *LinkError.
  92  func Link(oldname, newname string) error {
  93  	e := ignoringEINTR(func() error {
  94  		return syscall.Link(oldname, newname)
  95  	})
  96  
  97  	if e != nil {
  98  		return &LinkError{"link", oldname, newname, e}
  99  	}
 100  	return nil
 101  }
 102  
 103  // Symlink creates newname as a symbolic link to oldname.
 104  // On Windows, a symlink to a non-existent oldname creates a file symlink;
 105  // if oldname is later created as a directory the symlink will not work.
 106  // If there is an error, it will be of type *LinkError.
 107  func Symlink(oldname, newname string) error {
 108  	e := ignoringEINTR(func() error {
 109  		return syscall.Symlink(oldname, newname)
 110  	})
 111  	if e != nil {
 112  		return &LinkError{"symlink", oldname, newname, e}
 113  	}
 114  	return nil
 115  }
 116  
 117  // Readlink returns the destination of the named symbolic link.
 118  // If there is an error, it will be of type *PathError.
 119  func Readlink(name string) (string, error) {
 120  	for len := 128; ; len *= 2 {
 121  		b := make([]byte, len)
 122  		var (
 123  			n int
 124  			e error
 125  		)
 126  		for {
 127  			n, e = fixCount(syscall.Readlink(name, b))
 128  			if e != syscall.EINTR {
 129  				break
 130  			}
 131  		}
 132  		if e != nil {
 133  			return "", &PathError{Op: "readlink", Path: name, Err: e}
 134  		}
 135  		if n < len {
 136  			return string(b[0:n]), nil
 137  		}
 138  	}
 139  }
 140  
 141  // Truncate changes the size of the file.
 142  // It does not change the I/O offset.
 143  // If there is an error, it will be of type *PathError.
 144  // Alternatively just use 'raw' syscall by file name
 145  func (f *File) Truncate(size int64) (err error) {
 146  	if f.handle == nil {
 147  		return ErrClosed
 148  	}
 149  
 150  	return Truncate(f.name, size)
 151  }
 152  
 153  func (f *File) chmod(mode FileMode) error {
 154  	if f.handle == nil {
 155  		return ErrClosed
 156  	}
 157  
 158  	longName := fixLongPath(f.name)
 159  	e := ignoringEINTR(func() error {
 160  		return syscall.Chmod(longName, syscallMode(mode))
 161  	})
 162  	if e != nil {
 163  		return &PathError{Op: "chmod", Path: f.name, Err: e}
 164  	}
 165  	return nil
 166  }
 167  
 168  func (f *File) chdir() error {
 169  	if f.handle == nil {
 170  		return ErrClosed
 171  	}
 172  
 173  	// TODO: use syscall.Fchdir instead
 174  	longName := fixLongPath(f.name)
 175  	e := ignoringEINTR(func() error {
 176  		return syscall.Chdir(longName)
 177  	})
 178  	if e != nil {
 179  		return &PathError{Op: "chdir", Path: f.name, Err: e}
 180  	}
 181  	return nil
 182  }
 183  
 184  // ReadAt reads up to len(b) bytes from the File starting at the given absolute offset.
 185  // It returns the number of bytes read and any error encountered, possibly io.EOF.
 186  // At end of file, Pread returns 0, io.EOF.
 187  // TODO: move to file_anyos once ReadAt is implemented for windows
 188  func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
 189  	n, err = syscall.Pread(syscallFd(f), b, offset)
 190  	err = handleSyscallError(err)
 191  	if n == 0 && len(b) > 0 && err == nil {
 192  		err = io.EOF
 193  	}
 194  	return
 195  }
 196  
 197  // WriteAt writes len(b) bytes to the File starting at byte offset off.
 198  // It returns the number of bytes written and an error, if any.
 199  // WriteAt returns a non-nil error when n != len(b).
 200  //
 201  // If file was opened with the O_APPEND flag, WriteAt returns an error.
 202  //
 203  // TODO: move to file_anyos once WriteAt is implemented for windows.
 204  func (f unixFileHandle) WriteAt(b []byte, offset int64) (int, error) {
 205  	n, err := syscall.Pwrite(syscallFd(f), b, offset)
 206  	return n, handleSyscallError(err)
 207  }
 208  
 209  // Seek wraps syscall.Seek.
 210  func (f unixFileHandle) Seek(offset int64, whence int) (int64, error) {
 211  	newoffset, err := syscall.Seek(syscallFd(f), offset, whence)
 212  	return newoffset, handleSyscallError(err)
 213  }
 214  
 215  func (f unixFileHandle) Sync() error {
 216  	err := syscall.Fsync(syscallFd(f))
 217  	return handleSyscallError(err)
 218  }
 219  
 220  type unixDirent struct {
 221  	parent string
 222  	name   string
 223  	typ    FileMode
 224  	info   FileInfo
 225  }
 226  
 227  func (d *unixDirent) Name() string   { return d.name }
 228  func (d *unixDirent) IsDir() bool    { return d.typ.IsDir() }
 229  func (d *unixDirent) Type() FileMode { return d.typ }
 230  
 231  func (d *unixDirent) Info() (FileInfo, error) {
 232  	if d.info != nil {
 233  		return d.info, nil
 234  	}
 235  	return lstat(d.parent + "/" + d.name)
 236  }
 237  
 238  func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) {
 239  	ude := &unixDirent{
 240  		parent: parent,
 241  		name:   name,
 242  		typ:    typ,
 243  	}
 244  	if typ != ^FileMode(0) && !testingForceReadDirLstat {
 245  		return ude, nil
 246  	}
 247  
 248  	info, err := lstat(parent + "/" + name)
 249  	if err != nil {
 250  		return nil, err
 251  	}
 252  
 253  	ude.typ = info.Mode().Type()
 254  	ude.info = info
 255  	return ude, nil
 256  }
 257  
 258  // Since internal/poll is not available, we need to stub this out.
 259  // Big go requires the option to add the fd to the polling system.
 260  //
 261  //go:linkname net_newUnixFile net.newUnixFile
 262  func net_newUnixFile(fd int, name string) *File {
 263  	if fd < 0 {
 264  		panic("invalid FD")
 265  	}
 266  
 267  	// see src/os/file_unix.go:162 newFile for the original implementation.
 268  	// return newFile(fd, name, kindSock, true)
 269  	return NewFile(uintptr(fd), name)
 270  }
 271