summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go523
1 files changed, 0 insertions, 523 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go
deleted file mode 100644
index 1b6d5f3de..000000000
--- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go
+++ /dev/null
@@ -1,523 +0,0 @@
-package sysfs
-
-import (
- "io"
- "io/fs"
- "os"
- "time"
-
- experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
- "github.com/tetratelabs/wazero/internal/fsapi"
- "github.com/tetratelabs/wazero/sys"
-)
-
-func NewStdioFile(stdin bool, f fs.File) (fsapi.File, error) {
- // Return constant stat, which has fake times, but keep the underlying
- // file mode. Fake times are needed to pass wasi-testsuite.
- // https://github.com/WebAssembly/wasi-testsuite/blob/af57727/tests/rust/src/bin/fd_filestat_get.rs#L1-L19
- var mode fs.FileMode
- if st, err := f.Stat(); err != nil {
- return nil, err
- } else {
- mode = st.Mode()
- }
- var flag experimentalsys.Oflag
- if stdin {
- flag = experimentalsys.O_RDONLY
- } else {
- flag = experimentalsys.O_WRONLY
- }
- var file fsapi.File
- if of, ok := f.(*os.File); ok {
- // This is ok because functions that need path aren't used by stdioFile
- file = newOsFile("", flag, 0, of)
- } else {
- file = &fsFile{file: f}
- }
- return &stdioFile{File: file, st: sys.Stat_t{Mode: mode, Nlink: 1}}, nil
-}
-
-func OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (*os.File, experimentalsys.Errno) {
- return openFile(path, flag, perm)
-}
-
-func OpenOSFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
- f, errno := OpenFile(path, flag, perm)
- if errno != 0 {
- return nil, errno
- }
- return newOsFile(path, flag, perm, f), 0
-}
-
-func OpenFSFile(fs fs.FS, path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
- if flag&experimentalsys.O_DIRECTORY != 0 && flag&(experimentalsys.O_WRONLY|experimentalsys.O_RDWR) != 0 {
- return nil, experimentalsys.EISDIR // invalid to open a directory writeable
- }
- f, err := fs.Open(path)
- if errno := experimentalsys.UnwrapOSError(err); errno != 0 {
- return nil, errno
- }
- // Don't return an os.File because the path is not absolute. osFile needs
- // the path to be real and certain FS.File impls are subrooted.
- return &fsFile{fs: fs, name: path, file: f}, 0
-}
-
-type stdioFile struct {
- fsapi.File
- st sys.Stat_t
-}
-
-// SetAppend implements File.SetAppend
-func (f *stdioFile) SetAppend(bool) experimentalsys.Errno {
- // Ignore for stdio.
- return 0
-}
-
-// IsAppend implements File.SetAppend
-func (f *stdioFile) IsAppend() bool {
- return true
-}
-
-// Stat implements File.Stat
-func (f *stdioFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
- return f.st, 0
-}
-
-// Close implements File.Close
-func (f *stdioFile) Close() experimentalsys.Errno {
- return 0
-}
-
-// fsFile is used for wrapped fs.File, like os.Stdin or any fs.File
-// implementation. Notably, this does not have access to the full file path.
-// so certain operations can't be supported, such as inode lookups on Windows.
-type fsFile struct {
- experimentalsys.UnimplementedFile
-
- // fs is the file-system that opened the file, or nil when wrapped for
- // pre-opens like stdio.
- fs fs.FS
-
- // name is what was used in fs for Open, so it may not be the actual path.
- name string
-
- // file is always set, possibly an os.File like os.Stdin.
- file fs.File
-
- // reopenDir is true if reopen should be called before Readdir. This flag
- // is deferred until Readdir to prevent redundant rewinds. This could
- // happen if Seek(0) was called twice, or if in Windows, Seek(0) was called
- // before Readdir.
- reopenDir bool
-
- // closed is true when closed was called. This ensures proper sys.EBADF
- closed bool
-
- // cachedStat includes fields that won't change while a file is open.
- cachedSt *cachedStat
-}
-
-type cachedStat struct {
- // dev is the same as sys.Stat_t Dev.
- dev uint64
-
- // dev is the same as sys.Stat_t Ino.
- ino sys.Inode
-
- // isDir is sys.Stat_t Mode masked with fs.ModeDir
- isDir bool
-}
-
-// cachedStat returns the cacheable parts of sys.Stat_t or an error if they
-// couldn't be retrieved.
-func (f *fsFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno experimentalsys.Errno) {
- if f.cachedSt == nil {
- if _, errno = f.Stat(); errno != 0 {
- return
- }
- }
- return f.cachedSt.dev, f.cachedSt.ino, f.cachedSt.isDir, 0
-}
-
-// Dev implements the same method as documented on sys.File
-func (f *fsFile) Dev() (uint64, experimentalsys.Errno) {
- dev, _, _, errno := f.cachedStat()
- return dev, errno
-}
-
-// Ino implements the same method as documented on sys.File
-func (f *fsFile) Ino() (sys.Inode, experimentalsys.Errno) {
- _, ino, _, errno := f.cachedStat()
- return ino, errno
-}
-
-// IsDir implements the same method as documented on sys.File
-func (f *fsFile) IsDir() (bool, experimentalsys.Errno) {
- _, _, isDir, errno := f.cachedStat()
- return isDir, errno
-}
-
-// IsAppend implements the same method as documented on sys.File
-func (f *fsFile) IsAppend() bool {
- return false
-}
-
-// SetAppend implements the same method as documented on sys.File
-func (f *fsFile) SetAppend(bool) (errno experimentalsys.Errno) {
- return fileError(f, f.closed, experimentalsys.ENOSYS)
-}
-
-// Stat implements the same method as documented on sys.File
-func (f *fsFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
- if f.closed {
- return sys.Stat_t{}, experimentalsys.EBADF
- }
-
- st, errno := statFile(f.file)
- switch errno {
- case 0:
- f.cachedSt = &cachedStat{dev: st.Dev, ino: st.Ino, isDir: st.Mode&fs.ModeDir == fs.ModeDir}
- case experimentalsys.EIO:
- errno = experimentalsys.EBADF
- }
- return st, errno
-}
-
-// Read implements the same method as documented on sys.File
-func (f *fsFile) Read(buf []byte) (n int, errno experimentalsys.Errno) {
- if n, errno = read(f.file, buf); errno != 0 {
- // Defer validation overhead until we've already had an error.
- errno = fileError(f, f.closed, errno)
- }
- return
-}
-
-// Pread implements the same method as documented on sys.File
-func (f *fsFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
- if ra, ok := f.file.(io.ReaderAt); ok {
- if n, errno = pread(ra, buf, off); errno != 0 {
- // Defer validation overhead until we've already had an error.
- errno = fileError(f, f.closed, errno)
- }
- return
- }
-
- // See /RATIONALE.md "fd_pread: io.Seeker fallback when io.ReaderAt is not supported"
- if rs, ok := f.file.(io.ReadSeeker); ok {
- // Determine the current position in the file, as we need to revert it.
- currentOffset, err := rs.Seek(0, io.SeekCurrent)
- if err != nil {
- return 0, fileError(f, f.closed, experimentalsys.UnwrapOSError(err))
- }
-
- // Put the read position back when complete.
- defer func() { _, _ = rs.Seek(currentOffset, io.SeekStart) }()
-
- // If the current offset isn't in sync with this reader, move it.
- if off != currentOffset {
- if _, err = rs.Seek(off, io.SeekStart); err != nil {
- return 0, fileError(f, f.closed, experimentalsys.UnwrapOSError(err))
- }
- }
-
- n, err = rs.Read(buf)
- if errno = experimentalsys.UnwrapOSError(err); errno != 0 {
- // Defer validation overhead until we've already had an error.
- errno = fileError(f, f.closed, errno)
- }
- } else {
- errno = experimentalsys.ENOSYS // unsupported
- }
- return
-}
-
-// Seek implements the same method as documented on sys.File
-func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) {
- // If this is a directory, and we're attempting to seek to position zero,
- // we have to re-open the file to ensure the directory state is reset.
- var isDir bool
- if offset == 0 && whence == io.SeekStart {
- if isDir, errno = f.IsDir(); errno == 0 && isDir {
- f.reopenDir = true
- return
- }
- }
-
- if s, ok := f.file.(io.Seeker); ok {
- if newOffset, errno = seek(s, offset, whence); errno != 0 {
- // Defer validation overhead until we've already had an error.
- errno = fileError(f, f.closed, errno)
- }
- } else {
- errno = experimentalsys.ENOSYS // unsupported
- }
- return
-}
-
-// Readdir implements the same method as documented on sys.File
-//
-// Notably, this uses readdirFile or fs.ReadDirFile if available. This does not
-// return inodes on windows.
-func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
- // Windows lets you Readdir after close, FS.File also may not implement
- // close in a meaningful way. read our closed field to return consistent
- // results.
- if f.closed {
- errno = experimentalsys.EBADF
- return
- }
-
- if f.reopenDir { // re-open the directory if needed.
- f.reopenDir = false
- if errno = adjustReaddirErr(f, f.closed, f.rewindDir()); errno != 0 {
- return
- }
- }
-
- if of, ok := f.file.(readdirFile); ok {
- // We can't use f.name here because it is the path up to the sys.FS,
- // not necessarily the real path. For this reason, Windows may not be
- // able to populate inodes. However, Darwin and Linux will.
- if dirents, errno = readdir(of, "", n); errno != 0 {
- errno = adjustReaddirErr(f, f.closed, errno)
- }
- return
- }
-
- // Try with FS.ReadDirFile which is available on api.FS implementations
- // like embed:FS.
- if rdf, ok := f.file.(fs.ReadDirFile); ok {
- entries, e := rdf.ReadDir(n)
- if errno = adjustReaddirErr(f, f.closed, e); errno != 0 {
- return
- }
- dirents = make([]experimentalsys.Dirent, 0, len(entries))
- for _, e := range entries {
- // By default, we don't attempt to read inode data
- dirents = append(dirents, experimentalsys.Dirent{Name: e.Name(), Type: e.Type()})
- }
- } else {
- errno = experimentalsys.EBADF // not a directory
- }
- return
-}
-
-// Write implements the same method as documented on sys.File.
-func (f *fsFile) Write(buf []byte) (n int, errno experimentalsys.Errno) {
- if w, ok := f.file.(io.Writer); ok {
- if n, errno = write(w, buf); errno != 0 {
- // Defer validation overhead until we've already had an error.
- errno = fileError(f, f.closed, errno)
- }
- } else {
- errno = experimentalsys.ENOSYS // unsupported
- }
- return
-}
-
-// Pwrite implements the same method as documented on sys.File.
-func (f *fsFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
- if wa, ok := f.file.(io.WriterAt); ok {
- if n, errno = pwrite(wa, buf, off); errno != 0 {
- // Defer validation overhead until we've already had an error.
- errno = fileError(f, f.closed, errno)
- }
- } else {
- errno = experimentalsys.ENOSYS // unsupported
- }
- return
-}
-
-// Close implements the same method as documented on sys.File.
-func (f *fsFile) Close() experimentalsys.Errno {
- if f.closed {
- return 0
- }
- f.closed = true
- return f.close()
-}
-
-func (f *fsFile) close() experimentalsys.Errno {
- return experimentalsys.UnwrapOSError(f.file.Close())
-}
-
-// IsNonblock implements the same method as documented on fsapi.File
-func (f *fsFile) IsNonblock() bool {
- return false
-}
-
-// SetNonblock implements the same method as documented on fsapi.File
-func (f *fsFile) SetNonblock(bool) experimentalsys.Errno {
- return experimentalsys.ENOSYS
-}
-
-// Poll implements the same method as documented on fsapi.File
-func (f *fsFile) Poll(fsapi.Pflag, int32) (ready bool, errno experimentalsys.Errno) {
- return false, experimentalsys.ENOSYS
-}
-
-// dirError is used for commands that work against a directory, but not a file.
-func dirError(f experimentalsys.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno {
- if vErrno := validate(f, isClosed, false, true); vErrno != 0 {
- return vErrno
- }
- return errno
-}
-
-// fileError is used for commands that work against a file, but not a directory.
-func fileError(f experimentalsys.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno {
- if vErrno := validate(f, isClosed, true, false); vErrno != 0 {
- return vErrno
- }
- return errno
-}
-
-// validate is used to making syscalls which will fail.
-func validate(f experimentalsys.File, isClosed, wantFile, wantDir bool) experimentalsys.Errno {
- if isClosed {
- return experimentalsys.EBADF
- }
-
- isDir, errno := f.IsDir()
- if errno != 0 {
- return errno
- }
-
- if wantFile && isDir {
- return experimentalsys.EISDIR
- } else if wantDir && !isDir {
- return experimentalsys.ENOTDIR
- }
- return 0
-}
-
-func read(r io.Reader, buf []byte) (n int, errno experimentalsys.Errno) {
- if len(buf) == 0 {
- return 0, 0 // less overhead on zero-length reads.
- }
-
- n, err := r.Read(buf)
- return n, experimentalsys.UnwrapOSError(err)
-}
-
-func pread(ra io.ReaderAt, buf []byte, off int64) (n int, errno experimentalsys.Errno) {
- if len(buf) == 0 {
- return 0, 0 // less overhead on zero-length reads.
- }
-
- n, err := ra.ReadAt(buf, off)
- return n, experimentalsys.UnwrapOSError(err)
-}
-
-func seek(s io.Seeker, offset int64, whence int) (int64, experimentalsys.Errno) {
- if uint(whence) > io.SeekEnd {
- return 0, experimentalsys.EINVAL // negative or exceeds the largest valid whence
- }
-
- newOffset, err := s.Seek(offset, whence)
- return newOffset, experimentalsys.UnwrapOSError(err)
-}
-
-func (f *fsFile) rewindDir() experimentalsys.Errno {
- // Reopen the directory to rewind it.
- file, err := f.fs.Open(f.name)
- if err != nil {
- return experimentalsys.UnwrapOSError(err)
- }
- fi, err := file.Stat()
- if err != nil {
- return experimentalsys.UnwrapOSError(err)
- }
- // Can't check if it's still the same file,
- // but is it still a directory, at least?
- if !fi.IsDir() {
- return experimentalsys.ENOTDIR
- }
- // Only update f on success.
- _ = f.file.Close()
- f.file = file
- return 0
-}
-
-// readdirFile allows masking the `Readdir` function on os.File.
-type readdirFile interface {
- Readdir(n int) ([]fs.FileInfo, error)
-}
-
-// readdir uses readdirFile.Readdir, special casing windows when path !="".
-func readdir(f readdirFile, path string, n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
- fis, e := f.Readdir(n)
- if errno = experimentalsys.UnwrapOSError(e); errno != 0 {
- return
- }
-
- dirents = make([]experimentalsys.Dirent, 0, len(fis))
-
- // linux/darwin won't have to fan out to lstat, but windows will.
- var ino sys.Inode
- for fi := range fis {
- t := fis[fi]
- // inoFromFileInfo is more efficient than sys.NewStat_t, as it gets the
- // inode without allocating an instance and filling other fields.
- if ino, errno = inoFromFileInfo(path, t); errno != 0 {
- return
- }
- dirents = append(dirents, experimentalsys.Dirent{Name: t.Name(), Ino: ino, Type: t.Mode().Type()})
- }
- return
-}
-
-func write(w io.Writer, buf []byte) (n int, errno experimentalsys.Errno) {
- if len(buf) == 0 {
- return 0, 0 // less overhead on zero-length writes.
- }
-
- n, err := w.Write(buf)
- return n, experimentalsys.UnwrapOSError(err)
-}
-
-func pwrite(w io.WriterAt, buf []byte, off int64) (n int, errno experimentalsys.Errno) {
- if len(buf) == 0 {
- return 0, 0 // less overhead on zero-length writes.
- }
-
- n, err := w.WriteAt(buf, off)
- return n, experimentalsys.UnwrapOSError(err)
-}
-
-func chtimes(path string, atim, mtim int64) (errno experimentalsys.Errno) { //nolint:unused
- // When both inputs are omitted, there is nothing to change.
- if atim == experimentalsys.UTIME_OMIT && mtim == experimentalsys.UTIME_OMIT {
- return
- }
-
- // UTIME_OMIT is expensive until progress is made in Go, as it requires a
- // stat to read-back the value to re-apply.
- // - https://github.com/golang/go/issues/32558.
- // - https://go-review.googlesource.com/c/go/+/219638 (unmerged)
- var st sys.Stat_t
- if atim == experimentalsys.UTIME_OMIT || mtim == experimentalsys.UTIME_OMIT {
- if st, errno = stat(path); errno != 0 {
- return
- }
- }
-
- var atime, mtime time.Time
- if atim == experimentalsys.UTIME_OMIT {
- atime = epochNanosToTime(st.Atim)
- mtime = epochNanosToTime(mtim)
- } else if mtim == experimentalsys.UTIME_OMIT {
- atime = epochNanosToTime(atim)
- mtime = epochNanosToTime(st.Mtim)
- } else {
- atime = epochNanosToTime(atim)
- mtime = epochNanosToTime(mtim)
- }
- return experimentalsys.UnwrapOSError(os.Chtimes(path, atime, mtime))
-}
-
-func epochNanosToTime(epochNanos int64) time.Time { //nolint:unused
- seconds := epochNanos / 1e9
- nanos := epochNanos % 1e9
- return time.Unix(seconds, nanos)
-}