summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/sysfs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/sysfs')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go105
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_linux.go14
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_tinygo.go13
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_unsupported.go14
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go24
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go99
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_supported.go42
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_unsupported.go34
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go520
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go39
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go28
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go175
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go37
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go51
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.s8
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go49
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go18
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go42
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go22
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go15
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_tinygo.go14
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go28
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go17
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unsupported.go13
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go23
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go38
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go26
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go24
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go30
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go20
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go31
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_tinygo.go25
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go18
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go161
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go295
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go18
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go55
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s8
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go59
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go13
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go224
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go117
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go16
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go14
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_windows.go55
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go187
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_supported.go77
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go49
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unsupported.go81
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go80
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/stat.go16
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go37
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go40
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go40
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go120
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/sync.go13
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/sync_windows.go20
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/syscall6_darwin.go13
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/sysfs.go6
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go17
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go12
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go25
62 files changed, 3524 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go
new file mode 100644
index 000000000..51a9a5480
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go
@@ -0,0 +1,105 @@
+package sysfs
+
+import (
+ "fmt"
+ "io/fs"
+ "path"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+type AdaptFS struct {
+ FS fs.FS
+}
+
+// String implements fmt.Stringer
+func (a *AdaptFS) String() string {
+ return fmt.Sprintf("%v", a.FS)
+}
+
+// OpenFile implements the same method as documented on sys.FS
+func (a *AdaptFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
+ return OpenFSFile(a.FS, cleanPath(path), flag, perm)
+}
+
+// Lstat implements the same method as documented on sys.FS
+func (a *AdaptFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ // At this time, we make the assumption sys.FS instances do not support
+ // symbolic links, therefore Lstat is the same as Stat. This is obviously
+ // not true, but until FS.FS has a solid story for how to handle symlinks,
+ // we are better off not making a decision that would be difficult to
+ // revert later on.
+ //
+ // For further discussions on the topic, see:
+ // https://github.com/golang/go/issues/49580
+ return a.Stat(path)
+}
+
+// Stat implements the same method as documented on sys.FS
+func (a *AdaptFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ f, errno := a.OpenFile(path, experimentalsys.O_RDONLY, 0)
+ if errno != 0 {
+ return sys.Stat_t{}, errno
+ }
+ defer f.Close()
+ return f.Stat()
+}
+
+// Readlink implements the same method as documented on sys.FS
+func (a *AdaptFS) Readlink(string) (string, experimentalsys.Errno) {
+ return "", experimentalsys.ENOSYS
+}
+
+// Mkdir implements the same method as documented on sys.FS
+func (a *AdaptFS) Mkdir(string, fs.FileMode) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Chmod implements the same method as documented on sys.FS
+func (a *AdaptFS) Chmod(string, fs.FileMode) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Rename implements the same method as documented on sys.FS
+func (a *AdaptFS) Rename(string, string) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Rmdir implements the same method as documented on sys.FS
+func (a *AdaptFS) Rmdir(string) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Link implements the same method as documented on sys.FS
+func (a *AdaptFS) Link(string, string) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Symlink implements the same method as documented on sys.FS
+func (a *AdaptFS) Symlink(string, string) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Unlink implements the same method as documented on sys.FS
+func (a *AdaptFS) Unlink(string) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Utimens implements the same method as documented on sys.FS
+func (a *AdaptFS) Utimens(string, int64, int64) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+func cleanPath(name string) string {
+ if len(name) == 0 {
+ return name
+ }
+ // fs.ValidFile cannot be rooted (start with '/')
+ cleaned := name
+ if name[0] == '/' {
+ cleaned = name[1:]
+ }
+ cleaned = path.Clean(cleaned) // e.g. "sub/." -> "sub"
+ return cleaned
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_linux.go
new file mode 100644
index 000000000..5a8a415c5
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_linux.go
@@ -0,0 +1,14 @@
+//go:build linux && !tinygo
+
+package sysfs
+
+import (
+ "os"
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func datasync(f *os.File) sys.Errno {
+ return sys.UnwrapOSError(syscall.Fdatasync(int(f.Fd())))
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_tinygo.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_tinygo.go
new file mode 100644
index 000000000..e58fc9142
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_tinygo.go
@@ -0,0 +1,13 @@
+//go:build tinygo
+
+package sysfs
+
+import (
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func datasync(f *os.File) sys.Errno {
+ return sys.ENOSYS
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_unsupported.go
new file mode 100644
index 000000000..aa05719be
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_unsupported.go
@@ -0,0 +1,14 @@
+//go:build !linux
+
+package sysfs
+
+import (
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func datasync(f *os.File) sys.Errno {
+ // Attempt to sync everything, even if we only need to sync the data.
+ return fsync(f)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go
new file mode 100644
index 000000000..f9823287c
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go
@@ -0,0 +1,24 @@
+package sysfs
+
+import (
+ "io"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func adjustReaddirErr(f sys.File, isClosed bool, err error) sys.Errno {
+ if err == io.EOF {
+ return 0 // e.g. Readdir on darwin returns io.EOF, but linux doesn't.
+ } else if errno := sys.UnwrapOSError(err); errno != 0 {
+ errno = dirError(f, isClosed, errno)
+ // Comply with errors allowed on sys.File Readdir
+ switch errno {
+ case sys.EINVAL: // os.File Readdir can return this
+ return sys.EBADF
+ case sys.ENOTDIR: // dirError can return this
+ return sys.EBADF
+ }
+ return errno
+ }
+ return 0
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go
new file mode 100644
index 000000000..04384038f
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go
@@ -0,0 +1,99 @@
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/platform"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+func DirFS(dir string) experimentalsys.FS {
+ return &dirFS{
+ dir: dir,
+ cleanedDir: ensureTrailingPathSeparator(dir),
+ }
+}
+
+func ensureTrailingPathSeparator(dir string) string {
+ if !os.IsPathSeparator(dir[len(dir)-1]) {
+ return dir + string(os.PathSeparator)
+ }
+ return dir
+}
+
+// dirFS is not exported because the input fields must be maintained together.
+// This is likely why os.DirFS doesn't, either!
+type dirFS struct {
+ experimentalsys.UnimplementedFS
+
+ dir string
+ // cleanedDir is for easier OS-specific concatenation, as it always has
+ // a trailing path separator.
+ cleanedDir string
+}
+
+// String implements fmt.Stringer
+func (d *dirFS) String() string {
+ return d.dir
+}
+
+// OpenFile implements the same method as documented on sys.FS
+func (d *dirFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
+ return OpenOSFile(d.join(path), flag, perm)
+}
+
+// Lstat implements the same method as documented on sys.FS
+func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ return lstat(d.join(path))
+}
+
+// Stat implements the same method as documented on sys.FS
+func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ return stat(d.join(path))
+}
+
+// Mkdir implements the same method as documented on sys.FS
+func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) {
+ err := os.Mkdir(d.join(path), perm)
+ if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR {
+ errno = experimentalsys.ENOENT
+ }
+ return
+}
+
+// Readlink implements the same method as documented on sys.FS
+func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) {
+ // Note: do not use syscall.Readlink as that causes race on Windows.
+ // In any case, syscall.Readlink does almost the same logic as os.Readlink.
+ dst, err := os.Readlink(d.join(path))
+ if err != nil {
+ return "", experimentalsys.UnwrapOSError(err)
+ }
+ return platform.ToPosixPath(dst), 0
+}
+
+// Rmdir implements the same method as documented on sys.FS
+func (d *dirFS) Rmdir(path string) experimentalsys.Errno {
+ return rmdir(d.join(path))
+}
+
+// Utimens implements the same method as documented on sys.FS
+func (d *dirFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno {
+ return utimens(d.join(path), atim, mtim)
+}
+
+func (d *dirFS) join(path string) string {
+ switch path {
+ case "", ".", "/":
+ if d.cleanedDir == "/" {
+ return "/"
+ }
+ // cleanedDir includes an unnecessary delimiter for the root path.
+ return d.cleanedDir[:len(d.cleanedDir)-1]
+ }
+ // TODO: Enforce similar to safefilepath.FromFS(path), but be careful as
+ // relative path inputs are allowed. e.g. dir or path == ../
+ return d.cleanedDir + path
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_supported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_supported.go
new file mode 100644
index 000000000..ff93415b9
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_supported.go
@@ -0,0 +1,42 @@
+//go:build !tinygo
+
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+// Link implements the same method as documented on sys.FS
+func (d *dirFS) Link(oldName, newName string) experimentalsys.Errno {
+ err := os.Link(d.join(oldName), d.join(newName))
+ return experimentalsys.UnwrapOSError(err)
+}
+
+// Unlink implements the same method as documented on sys.FS
+func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) {
+ return unlink(d.join(path))
+}
+
+// Rename implements the same method as documented on sys.FS
+func (d *dirFS) Rename(from, to string) experimentalsys.Errno {
+ from, to = d.join(from), d.join(to)
+ return rename(from, to)
+}
+
+// Chmod implements the same method as documented on sys.FS
+func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
+ err := os.Chmod(d.join(path), perm)
+ return experimentalsys.UnwrapOSError(err)
+}
+
+// Symlink implements the same method as documented on sys.FS
+func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno {
+ // Note: do not resolve `oldName` relative to this dirFS. The link result is always resolved
+ // when dereference the `link` on its usage (e.g. readlink, read, etc).
+ // https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409
+ err := os.Symlink(oldName, d.join(link))
+ return experimentalsys.UnwrapOSError(err)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_unsupported.go
new file mode 100644
index 000000000..98b1a3b84
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_unsupported.go
@@ -0,0 +1,34 @@
+//go:build tinygo
+
+package sysfs
+
+import (
+ "io/fs"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+// Link implements the same method as documented on sys.FS
+func (d *dirFS) Link(oldName, newName string) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Unlink implements the same method as documented on sys.FS
+func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) {
+ return experimentalsys.ENOSYS
+}
+
+// Rename implements the same method as documented on sys.FS
+func (d *dirFS) Rename(from, to string) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Chmod implements the same method as documented on sys.FS
+func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
+
+// Symlink implements the same method as documented on sys.FS
+func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno {
+ return experimentalsys.ENOSYS
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go
new file mode 100644
index 000000000..9a77205bb
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go
@@ -0,0 +1,520 @@
+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) {
+ if flag&experimentalsys.O_DIRECTORY != 0 && flag&(experimentalsys.O_WRONLY|experimentalsys.O_RDWR) != 0 {
+ return nil, experimentalsys.EISDIR // invalid to open a directory writeable
+ }
+ 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.reopen()); 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)
+}
+
+// reopenFile allows re-opening a file for reasons such as applying flags or
+// directory iteration.
+type reopenFile func() experimentalsys.Errno
+
+// compile-time check to ensure fsFile.reopen implements reopenFile.
+var _ reopenFile = (*fsFile)(nil).reopen
+
+// reopen implements the same method as documented on reopenFile.
+func (f *fsFile) reopen() experimentalsys.Errno {
+ _ = f.close()
+ var err error
+ f.file, err = f.fs.Open(f.name)
+ return experimentalsys.UnwrapOSError(err)
+}
+
+// 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)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go
new file mode 100644
index 000000000..f201e813d
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go
@@ -0,0 +1,39 @@
+//go:build unix && !tinygo
+
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const (
+ nonBlockingFileReadSupported = true
+ nonBlockingFileWriteSupported = true
+)
+
+func rmdir(path string) sys.Errno {
+ err := syscall.Rmdir(path)
+ return sys.UnwrapOSError(err)
+}
+
+// readFd exposes syscall.Read.
+func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
+ if len(buf) == 0 {
+ return 0, 0 // Short-circuit 0-len reads.
+ }
+ n, err := syscall.Read(int(fd), buf)
+ errno := sys.UnwrapOSError(err)
+ return n, errno
+}
+
+// writeFd exposes syscall.Write.
+func writeFd(fd uintptr, buf []byte) (int, sys.Errno) {
+ if len(buf) == 0 {
+ return 0, 0 // Short-circuit 0-len writes.
+ }
+ n, err := syscall.Write(int(fd), buf)
+ errno := sys.UnwrapOSError(err)
+ return n, errno
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go
new file mode 100644
index 000000000..a028b9479
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go
@@ -0,0 +1,28 @@
+//go:build !(unix || windows) || tinygo
+
+package sysfs
+
+import (
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const (
+ nonBlockingFileReadSupported = false
+ nonBlockingFileWriteSupported = false
+)
+
+func rmdir(path string) sys.Errno {
+ return sys.UnwrapOSError(os.Remove(path))
+}
+
+// readFd returns ENOSYS on unsupported platforms.
+func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
+ return -1, sys.ENOSYS
+}
+
+// writeFd returns ENOSYS on unsupported platforms.
+func writeFd(fd uintptr, buf []byte) (int, sys.Errno) {
+ return -1, sys.ENOSYS
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go
new file mode 100644
index 000000000..37870ea36
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go
@@ -0,0 +1,175 @@
+package sysfs
+
+import (
+ "errors"
+ "syscall"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const (
+ nonBlockingFileReadSupported = true
+ nonBlockingFileWriteSupported = false
+
+ _ERROR_IO_INCOMPLETE = syscall.Errno(996)
+)
+
+var kernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+// procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
+var (
+ // procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
+ procPeekNamedPipe = kernel32.NewProc("PeekNamedPipe")
+ // procGetOverlappedResult is the syscall.LazyProc in kernel32 for GetOverlappedResult
+ procGetOverlappedResult = kernel32.NewProc("GetOverlappedResult")
+ // procCreateEventW is the syscall.LazyProc in kernel32 for CreateEventW
+ procCreateEventW = kernel32.NewProc("CreateEventW")
+)
+
+// readFd returns ENOSYS on unsupported platforms.
+//
+// PeekNamedPipe: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
+// "GetFileType can assist in determining what device type the handle refers to. A console handle presents as FILE_TYPE_CHAR."
+// https://learn.microsoft.com/en-us/windows/console/console-handles
+func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
+ handle := syscall.Handle(fd)
+ fileType, err := syscall.GetFileType(handle)
+ if err != nil {
+ return 0, sys.UnwrapOSError(err)
+ }
+ if fileType&syscall.FILE_TYPE_CHAR == 0 {
+ return -1, sys.ENOSYS
+ }
+ n, errno := peekNamedPipe(handle)
+ if errno == syscall.ERROR_BROKEN_PIPE {
+ return 0, 0
+ }
+ if n == 0 {
+ return -1, sys.EAGAIN
+ }
+ un, err := syscall.Read(handle, buf[0:n])
+ return un, sys.UnwrapOSError(err)
+}
+
+func writeFd(fd uintptr, buf []byte) (int, sys.Errno) {
+ return -1, sys.ENOSYS
+}
+
+func readSocket(h uintptr, buf []byte) (int, sys.Errno) {
+ // Poll the socket to ensure that we never perform a blocking/overlapped Read.
+ //
+ // When the socket is closed by the remote peer, wsaPoll will return n=1 and
+ // errno=0, and syscall.ReadFile will return n=0 and errno=0 -- which indicates
+ // io.EOF.
+ if n, errno := wsaPoll(
+ []pollFd{newPollFd(h, _POLLIN, 0)}, 0); !errors.Is(errno, sys.Errno(0)) {
+ return 0, sys.UnwrapOSError(errno)
+ } else if n <= 0 {
+ return 0, sys.EAGAIN
+ }
+
+ // Properly use overlapped result.
+ //
+ // If hFile was opened with FILE_FLAG_OVERLAPPED, the following conditions are in effect:
+ // - The lpOverlapped parameter must point to a valid and unique OVERLAPPED structure,
+ // otherwise the function can incorrectly report that the read operation is complete.
+ // - The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult
+ // function to get the actual number of bytes read. If the hFile parameter is associated
+ // with an I/O completion port, you can also get the number of bytes read by calling the
+ // GetQueuedCompletionStatus function.
+ //
+ // We are currently skipping checking if hFile was opened with FILE_FLAG_OVERLAPPED but using
+ // both lpOverlapped and lpNumberOfBytesRead.
+ var overlapped syscall.Overlapped
+
+ // Create an event to wait on.
+ if hEvent, err := createEventW(nil, true, false, nil); err != 0 {
+ return 0, sys.UnwrapOSError(err)
+ } else {
+ overlapped.HEvent = syscall.Handle(hEvent)
+ }
+
+ var done uint32
+ errno := syscall.ReadFile(syscall.Handle(h), buf, &done, &overlapped)
+ if errors.Is(errno, syscall.ERROR_IO_PENDING) {
+ errno = syscall.CancelIo(syscall.Handle(h))
+ if errno != nil {
+ return 0, sys.UnwrapOSError(errno) // This is a fatal error. CancelIo failed.
+ }
+
+ done, errno = getOverlappedResult(syscall.Handle(h), &overlapped, true) // wait for I/O to complete(cancel or finish). Overwrite done and errno.
+ if errors.Is(errno, syscall.ERROR_OPERATION_ABORTED) {
+ return int(done), sys.EAGAIN // This is one of the expected behavior, I/O was cancelled(completed) before finished.
+ }
+ }
+
+ return int(done), sys.UnwrapOSError(errno)
+}
+
+func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) {
+ var done uint32
+ var overlapped syscall.Overlapped
+ errno := syscall.WriteFile(syscall.Handle(fd), buf, &done, &overlapped)
+ if errors.Is(errno, syscall.ERROR_IO_PENDING) {
+ errno = syscall.EAGAIN
+ }
+ return int(done), sys.UnwrapOSError(errno)
+}
+
+// peekNamedPipe partially exposes PeekNamedPipe from the Win32 API
+// see https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
+func peekNamedPipe(handle syscall.Handle) (uint32, syscall.Errno) {
+ var totalBytesAvail uint32
+ totalBytesPtr := unsafe.Pointer(&totalBytesAvail)
+ _, _, errno := syscall.SyscallN(
+ procPeekNamedPipe.Addr(),
+ uintptr(handle), // [in] HANDLE hNamedPipe,
+ 0, // [out, optional] LPVOID lpBuffer,
+ 0, // [in] DWORD nBufferSize,
+ 0, // [out, optional] LPDWORD lpBytesRead
+ uintptr(totalBytesPtr), // [out, optional] LPDWORD lpTotalBytesAvail,
+ 0) // [out, optional] LPDWORD lpBytesLeftThisMessage
+ return totalBytesAvail, errno
+}
+
+func rmdir(path string) sys.Errno {
+ err := syscall.Rmdir(path)
+ return sys.UnwrapOSError(err)
+}
+
+func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, wait bool) (uint32, syscall.Errno) {
+ var totalBytesAvail uint32
+ var bwait uintptr
+ if wait {
+ bwait = 0xFFFFFFFF
+ }
+ totalBytesPtr := unsafe.Pointer(&totalBytesAvail)
+ _, _, errno := syscall.SyscallN(
+ procGetOverlappedResult.Addr(),
+ uintptr(handle), // [in] HANDLE hFile,
+ uintptr(unsafe.Pointer(overlapped)), // [in] LPOVERLAPPED lpOverlapped,
+ uintptr(totalBytesPtr), // [out] LPDWORD lpNumberOfBytesTransferred,
+ bwait) // [in] BOOL bWait
+ return totalBytesAvail, errno
+}
+
+func createEventW(lpEventAttributes *syscall.SecurityAttributes, bManualReset bool, bInitialState bool, lpName *uint16) (uintptr, syscall.Errno) {
+ var manualReset uintptr
+ var initialState uintptr
+ if bManualReset {
+ manualReset = 1
+ }
+ if bInitialState {
+ initialState = 1
+ }
+ handle, _, errno := syscall.SyscallN(
+ procCreateEventW.Addr(),
+ uintptr(unsafe.Pointer(lpEventAttributes)), // [in] LPSECURITY_ATTRIBUTES lpEventAttributes,
+ manualReset, // [in] BOOL bManualReset,
+ initialState, // [in] BOOL bInitialState,
+ uintptr(unsafe.Pointer(lpName)), // [in, opt]LPCWSTR lpName,
+ )
+
+ return handle, errno
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go
new file mode 100644
index 000000000..7f6b11094
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go
@@ -0,0 +1,37 @@
+//go:build (linux || darwin) && !tinygo
+
+package sysfs
+
+import (
+ "syscall"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func timesToPtr(times *[2]syscall.Timespec) unsafe.Pointer { //nolint:unused
+ if times != nil {
+ return unsafe.Pointer(&times[0])
+ }
+ return unsafe.Pointer(nil)
+}
+
+func timesToTimespecs(atim int64, mtim int64) (times *[2]syscall.Timespec) {
+ // When both inputs are omitted, there is nothing to change.
+ if atim == sys.UTIME_OMIT && mtim == sys.UTIME_OMIT {
+ return
+ }
+
+ times = &[2]syscall.Timespec{}
+ if atim == sys.UTIME_OMIT {
+ times[0] = syscall.Timespec{Nsec: _UTIME_OMIT}
+ times[1] = syscall.NsecToTimespec(mtim)
+ } else if mtim == sys.UTIME_OMIT {
+ times[0] = syscall.NsecToTimespec(atim)
+ times[1] = syscall.Timespec{Nsec: _UTIME_OMIT}
+ } else {
+ times[0] = syscall.NsecToTimespec(atim)
+ times[1] = syscall.NsecToTimespec(mtim)
+ }
+ return
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go
new file mode 100644
index 000000000..88e4008f0
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go
@@ -0,0 +1,51 @@
+package sysfs
+
+import (
+ "syscall"
+ _ "unsafe"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const (
+ _AT_FDCWD = -0x2
+ _AT_SYMLINK_NOFOLLOW = 0x0020
+ _UTIME_OMIT = -2
+)
+
+//go:noescape
+//go:linkname utimensat syscall.utimensat
+func utimensat(dirfd int, path string, times *[2]syscall.Timespec, flags int) error
+
+func utimens(path string, atim, mtim int64) experimentalsys.Errno {
+ times := timesToTimespecs(atim, mtim)
+ if times == nil {
+ return 0
+ }
+ var flags int
+ return experimentalsys.UnwrapOSError(utimensat(_AT_FDCWD, path, times, flags))
+}
+
+func futimens(fd uintptr, atim, mtim int64) experimentalsys.Errno {
+ times := timesToTimespecs(atim, mtim)
+ if times == nil {
+ return 0
+ }
+ _p0 := timesToPtr(times)
+
+ // Warning: futimens only exists since High Sierra (10.13).
+ _, _, e1 := syscall_syscall6(libc_futimens_trampoline_addr, fd, uintptr(_p0), 0, 0, 0, 0)
+ return experimentalsys.UnwrapOSError(e1)
+}
+
+// libc_futimens_trampoline_addr is the address of the
+// `libc_futimens_trampoline` symbol, defined in `futimens_darwin.s`.
+//
+// We use this to invoke the syscall through syscall_syscall6 imported below.
+var libc_futimens_trampoline_addr uintptr
+
+// Imports the futimens symbol from libc as `libc_futimens`.
+//
+// Note: CGO mechanisms are used in darwin regardless of the CGO_ENABLED value
+// or the "cgo" build flag. See /RATIONALE.md for why.
+//go:cgo_import_dynamic libc_futimens futimens "/usr/lib/libSystem.B.dylib"
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.s b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.s
new file mode 100644
index 000000000..b86aecdf0
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.s
@@ -0,0 +1,8 @@
+// lifted from golang.org/x/sys unix
+#include "textflag.h"
+
+TEXT libc_futimens_trampoline<>(SB), NOSPLIT, $0-0
+ JMP libc_futimens(SB)
+
+GLOBL ·libc_futimens_trampoline_addr(SB), RODATA, $8
+DATA ·libc_futimens_trampoline_addr(SB)/8, $libc_futimens_trampoline<>(SB)
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go
new file mode 100644
index 000000000..db3b1b8b6
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go
@@ -0,0 +1,49 @@
+//go:build !tinygo
+
+package sysfs
+
+import (
+ "syscall"
+ "unsafe"
+ _ "unsafe"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const (
+ _AT_FDCWD = -0x64
+ _UTIME_OMIT = (1 << 30) - 2
+)
+
+func utimens(path string, atim, mtim int64) experimentalsys.Errno {
+ times := timesToTimespecs(atim, mtim)
+ if times == nil {
+ return 0
+ }
+
+ var flags int
+ var _p0 *byte
+ _p0, err := syscall.BytePtrFromString(path)
+ if err == nil {
+ err = utimensat(_AT_FDCWD, uintptr(unsafe.Pointer(_p0)), times, flags)
+ }
+ return experimentalsys.UnwrapOSError(err)
+}
+
+// On linux, implement futimens via utimensat with the NUL path.
+func futimens(fd uintptr, atim, mtim int64) experimentalsys.Errno {
+ times := timesToTimespecs(atim, mtim)
+ if times == nil {
+ return 0
+ }
+ return experimentalsys.UnwrapOSError(utimensat(int(fd), 0 /* NUL */, times, 0))
+}
+
+// utimensat is like syscall.utimensat special-cased to accept a NUL string for the path value.
+func utimensat(dirfd int, strPtr uintptr, times *[2]syscall.Timespec, flags int) (err error) {
+ _, _, e1 := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(dirfd), strPtr, uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go
new file mode 100644
index 000000000..69d564942
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go
@@ -0,0 +1,18 @@
+//go:build (!windows && !linux && !darwin) || tinygo
+
+package sysfs
+
+import (
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func utimens(path string, atim, mtim int64) sys.Errno {
+ return chtimes(path, atim, mtim)
+}
+
+func futimens(fd uintptr, atim, mtim int64) error {
+ // Go exports syscall.Futimes, which is microsecond granularity, and
+ // WASI tests expect nanosecond. We don't yet have a way to invoke the
+ // futimens syscall portably.
+ return sys.ENOSYS
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go
new file mode 100644
index 000000000..e0c89f303
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go
@@ -0,0 +1,42 @@
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func utimens(path string, atim, mtim int64) sys.Errno {
+ return chtimes(path, atim, mtim)
+}
+
+func futimens(fd uintptr, atim, mtim int64) error {
+ // Per docs, zero isn't a valid timestamp as it cannot be differentiated
+ // from nil. In both cases, it is a marker like sys.UTIME_OMIT.
+ // See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfiletime
+ a, w := timespecToFiletime(atim, mtim)
+
+ if a == nil && w == nil {
+ return nil // both omitted, so nothing to change
+ }
+
+ // Attempt to get the stat by handle, which works for normal files
+ h := syscall.Handle(fd)
+
+ // Note: This returns ERROR_ACCESS_DENIED when the input is a directory.
+ return syscall.SetFileTime(h, nil, a, w)
+}
+
+func timespecToFiletime(atim, mtim int64) (a, w *syscall.Filetime) {
+ a = timespecToFileTime(atim)
+ w = timespecToFileTime(mtim)
+ return
+}
+
+func timespecToFileTime(tim int64) *syscall.Filetime {
+ if tim == sys.UTIME_OMIT {
+ return nil
+ }
+ ft := syscall.NsecToFiletime(tim)
+ return &ft
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go
new file mode 100644
index 000000000..8344cd16f
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go
@@ -0,0 +1,22 @@
+//go:build !windows && !plan9 && !tinygo
+
+package sysfs
+
+import (
+ "io/fs"
+ "syscall"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
+ switch v := info.Sys().(type) {
+ case *sys.Stat_t:
+ return v.Ino, 0
+ case *syscall.Stat_t:
+ return v.Ino, 0
+ default:
+ return 0, 0
+ }
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go
new file mode 100644
index 000000000..9c669a475
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go
@@ -0,0 +1,15 @@
+package sysfs
+
+import (
+ "io/fs"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
+ if v, ok := info.Sys().(*sys.Stat_t); ok {
+ return v.Ino, 0
+ }
+ return 0, 0
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_tinygo.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_tinygo.go
new file mode 100644
index 000000000..2099231cf
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_tinygo.go
@@ -0,0 +1,14 @@
+//go:build tinygo
+
+package sysfs
+
+import (
+ "io/fs"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
+ return 0, experimentalsys.ENOTSUP
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go
new file mode 100644
index 000000000..d163b3601
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go
@@ -0,0 +1,28 @@
+package sysfs
+
+import (
+ "io/fs"
+ "path"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// inoFromFileInfo uses stat to get the inode information of the file.
+func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno experimentalsys.Errno) {
+ if v, ok := info.Sys().(*sys.Stat_t); ok {
+ return v.Ino, 0
+ }
+ if dirPath == "" {
+ // This is a FS.File backed implementation which doesn't have access to
+ // the original file path.
+ return
+ }
+ // Ino is no not in Win32FileAttributeData
+ inoPath := path.Clean(path.Join(dirPath, info.Name()))
+ var st sys.Stat_t
+ if st, errno = lstat(inoPath); errno == 0 {
+ ino = st.Ino
+ }
+ return
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go
new file mode 100644
index 000000000..4477ee977
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go
@@ -0,0 +1,17 @@
+//go:build !windows && !plan9 && !tinygo
+
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func setNonblock(fd uintptr, enable bool) sys.Errno {
+ return sys.UnwrapOSError(syscall.SetNonblock(int(fd), enable))
+}
+
+func isNonblock(f *osFile) bool {
+ return f.flag&sys.O_NONBLOCK == sys.O_NONBLOCK
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unsupported.go
new file mode 100644
index 000000000..3e141a7b5
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unsupported.go
@@ -0,0 +1,13 @@
+//go:build plan9 || tinygo
+
+package sysfs
+
+import "github.com/tetratelabs/wazero/experimental/sys"
+
+func setNonblock(fd uintptr, enable bool) sys.Errno {
+ return sys.ENOSYS
+}
+
+func isNonblock(f *osFile) bool {
+ return false
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go
new file mode 100644
index 000000000..eb38ea5af
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go
@@ -0,0 +1,23 @@
+package sysfs
+
+import (
+ "io/fs"
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func setNonblock(fd uintptr, enable bool) sys.Errno {
+ // We invoke the syscall, but this is currently no-op.
+ return sys.UnwrapOSError(syscall.SetNonblock(syscall.Handle(fd), enable))
+}
+
+func isNonblock(f *osFile) bool {
+ // On Windows, we support non-blocking reads only on named pipes.
+ isValid := false
+ st, errno := f.Stat()
+ if errno == 0 {
+ isValid = st.Mode&fs.ModeNamedPipe != 0
+ }
+ return isValid && f.flag&sys.O_NONBLOCK == sys.O_NONBLOCK
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go
new file mode 100644
index 000000000..be6d2c35f
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go
@@ -0,0 +1,38 @@
+package sysfs
+
+import (
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+// toOsOpenFlag converts the input to the flag parameter of os.OpenFile
+func toOsOpenFlag(oflag sys.Oflag) (flag int) {
+ // First flags are exclusive
+ switch oflag & (sys.O_RDONLY | sys.O_RDWR | sys.O_WRONLY) {
+ case sys.O_RDONLY:
+ flag |= os.O_RDONLY
+ case sys.O_RDWR:
+ flag |= os.O_RDWR
+ case sys.O_WRONLY:
+ flag |= os.O_WRONLY
+ }
+
+ // Run down the flags defined in the os package
+ if oflag&sys.O_APPEND != 0 {
+ flag |= os.O_APPEND
+ }
+ if oflag&sys.O_CREAT != 0 {
+ flag |= os.O_CREATE
+ }
+ if oflag&sys.O_EXCL != 0 {
+ flag |= os.O_EXCL
+ }
+ if oflag&sys.O_SYNC != 0 {
+ flag |= os.O_SYNC
+ }
+ if oflag&sys.O_TRUNC != 0 {
+ flag |= os.O_TRUNC
+ }
+ return withSyscallOflag(oflag, flag)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go
new file mode 100644
index 000000000..a4f54ca2c
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go
@@ -0,0 +1,26 @@
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK
+
+func withSyscallOflag(oflag sys.Oflag, flag int) int {
+ if oflag&sys.O_DIRECTORY != 0 {
+ flag |= syscall.O_DIRECTORY
+ }
+ if oflag&sys.O_DSYNC != 0 {
+ flag |= syscall.O_DSYNC
+ }
+ if oflag&sys.O_NOFOLLOW != 0 {
+ flag |= syscall.O_NOFOLLOW
+ }
+ if oflag&sys.O_NONBLOCK != 0 {
+ flag |= syscall.O_NONBLOCK
+ }
+ // syscall.O_RSYNC not defined on darwin
+ return flag
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go
new file mode 100644
index 000000000..42adaa214
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go
@@ -0,0 +1,24 @@
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_NOFOLLOW | sys.O_NONBLOCK
+
+func withSyscallOflag(oflag sys.Oflag, flag int) int {
+ if oflag&sys.O_DIRECTORY != 0 {
+ flag |= syscall.O_DIRECTORY
+ }
+ // syscall.O_DSYNC not defined on darwin
+ if oflag&sys.O_NOFOLLOW != 0 {
+ flag |= syscall.O_NOFOLLOW
+ }
+ if oflag&sys.O_NONBLOCK != 0 {
+ flag |= syscall.O_NONBLOCK
+ }
+ // syscall.O_RSYNC not defined on darwin
+ return flag
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go
new file mode 100644
index 000000000..3fe2bb6e1
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go
@@ -0,0 +1,30 @@
+//go:build !tinygo
+
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK | sys.O_RSYNC
+
+func withSyscallOflag(oflag sys.Oflag, flag int) int {
+ if oflag&sys.O_DIRECTORY != 0 {
+ flag |= syscall.O_DIRECTORY
+ }
+ if oflag&sys.O_DSYNC != 0 {
+ flag |= syscall.O_DSYNC
+ }
+ if oflag&sys.O_NOFOLLOW != 0 {
+ flag |= syscall.O_NOFOLLOW
+ }
+ if oflag&sys.O_NONBLOCK != 0 {
+ flag |= syscall.O_NONBLOCK
+ }
+ if oflag&sys.O_RSYNC != 0 {
+ flag |= syscall.O_RSYNC
+ }
+ return flag
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go
new file mode 100644
index 000000000..670e35910
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go
@@ -0,0 +1,20 @@
+//go:build !windows && !tinygo
+
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+// openFile is like os.OpenFile except it accepts a sys.Oflag and returns
+// sys.Errno. A zero sys.Errno is success.
+func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
+ f, err := os.OpenFile(path, toOsOpenFlag(oflag), perm)
+ // Note: This does not return a sys.File because sys.FS that returns
+ // one may want to hide the real OS path. For example, this is needed for
+ // pre-opens.
+ return f, sys.UnwrapOSError(err)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go
new file mode 100644
index 000000000..bdf7dd84d
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go
@@ -0,0 +1,31 @@
+//go:build illumos || solaris
+
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK | sys.O_RSYNC
+
+func withSyscallOflag(oflag sys.Oflag, flag int) int {
+ if oflag&sys.O_DIRECTORY != 0 {
+ // See https://github.com/illumos/illumos-gate/blob/edd580643f2cf1434e252cd7779e83182ea84945/usr/src/uts/common/sys/fcntl.h#L90
+ flag |= 0x1000000
+ }
+ if oflag&sys.O_DSYNC != 0 {
+ flag |= syscall.O_DSYNC
+ }
+ if oflag&sys.O_NOFOLLOW != 0 {
+ flag |= syscall.O_NOFOLLOW
+ }
+ if oflag&sys.O_NONBLOCK != 0 {
+ flag |= syscall.O_NONBLOCK
+ }
+ if oflag&sys.O_RSYNC != 0 {
+ flag |= syscall.O_RSYNC
+ }
+ return flag
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_tinygo.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_tinygo.go
new file mode 100644
index 000000000..ccf6847c0
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_tinygo.go
@@ -0,0 +1,25 @@
+//go:build tinygo
+
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const supportedSyscallOflag = sys.Oflag(0)
+
+func withSyscallOflag(oflag sys.Oflag, flag int) int {
+ // O_DIRECTORY not defined
+ // O_DSYNC not defined
+ // O_NOFOLLOW not defined
+ // O_NONBLOCK not defined
+ // O_RSYNC not defined
+ return flag
+}
+
+func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
+ return nil, sys.ENOSYS
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go
new file mode 100644
index 000000000..9f7a6d088
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go
@@ -0,0 +1,18 @@
+//go:build !darwin && !linux && !windows && !illumos && !solaris && !freebsd
+
+package sysfs
+
+import (
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+const supportedSyscallOflag = sys.Oflag(0)
+
+func withSyscallOflag(oflag sys.Oflag, flag int) int {
+ // O_DIRECTORY not defined
+ // O_DSYNC not defined
+ // O_NOFOLLOW not defined
+ // O_NONBLOCK not defined
+ // O_RSYNC not defined
+ return flag
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go
new file mode 100644
index 000000000..717f8598a
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go
@@ -0,0 +1,161 @@
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
+ isDir := oflag&sys.O_DIRECTORY > 0
+ flag := toOsOpenFlag(oflag)
+
+ // TODO: document why we are opening twice
+ fd, err := open(path, flag|syscall.O_CLOEXEC, uint32(perm))
+ if err == nil {
+ return os.NewFile(uintptr(fd), path), 0
+ }
+
+ // TODO: Set FILE_SHARE_DELETE for directory as well.
+ f, err := os.OpenFile(path, flag, perm)
+ errno := sys.UnwrapOSError(err)
+ if errno == 0 {
+ return f, 0
+ }
+
+ switch errno {
+ case sys.EINVAL:
+ // WASI expects ENOTDIR for a file path with a trailing slash.
+ if strings.HasSuffix(path, "/") {
+ errno = sys.ENOTDIR
+ }
+ // To match expectations of WASI, e.g. TinyGo TestStatBadDir, return
+ // ENOENT, not ENOTDIR.
+ case sys.ENOTDIR:
+ errno = sys.ENOENT
+ case sys.ENOENT:
+ if isSymlink(path) {
+ // Either symlink or hard link not found. We change the returned
+ // errno depending on if it is symlink or not to have consistent
+ // behavior across OSes.
+ if isDir {
+ // Dangling symlink dir must raise ENOTDIR.
+ errno = sys.ENOTDIR
+ } else {
+ errno = sys.ELOOP
+ }
+ }
+ }
+ return f, errno
+}
+
+const supportedSyscallOflag = sys.O_NONBLOCK
+
+// Map to synthetic values here https://github.com/golang/go/blob/go1.20/src/syscall/types_windows.go#L34-L48
+func withSyscallOflag(oflag sys.Oflag, flag int) int {
+ // O_DIRECTORY not defined in windows
+ // O_DSYNC not defined in windows
+ // O_NOFOLLOW not defined in windows
+ if oflag&sys.O_NONBLOCK != 0 {
+ flag |= syscall.O_NONBLOCK
+ }
+ // O_RSYNC not defined in windows
+ return flag
+}
+
+func isSymlink(path string) bool {
+ if st, e := os.Lstat(path); e == nil && st.Mode()&os.ModeSymlink != 0 {
+ return true
+ }
+ return false
+}
+
+// # Differences from syscall.Open
+//
+// This code is based on syscall.Open from the below link with some differences
+// https://github.com/golang/go/blame/go1.20/src/syscall/syscall_windows.go#L308-L379
+//
+// - syscall.O_CREAT doesn't imply syscall.GENERIC_WRITE as that breaks
+// flag expectations in wasi.
+// - add support for setting FILE_SHARE_DELETE.
+func open(path string, mode int, perm uint32) (fd syscall.Handle, err error) {
+ if len(path) == 0 {
+ return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
+ }
+ pathp, err := syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return syscall.InvalidHandle, err
+ }
+ var access uint32
+ switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
+ case syscall.O_RDONLY:
+ access = syscall.GENERIC_READ
+ case syscall.O_WRONLY:
+ access = syscall.GENERIC_WRITE
+ case syscall.O_RDWR:
+ access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
+ }
+ if mode&syscall.O_APPEND != 0 {
+ access &^= syscall.GENERIC_WRITE
+ access |= syscall.FILE_APPEND_DATA
+ }
+ sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
+ var sa *syscall.SecurityAttributes
+ if mode&syscall.O_CLOEXEC == 0 {
+ var _sa syscall.SecurityAttributes
+ _sa.Length = uint32(unsafe.Sizeof(sa))
+ _sa.InheritHandle = 1
+ sa = &_sa
+ }
+ var createmode uint32
+ switch {
+ case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
+ createmode = syscall.CREATE_NEW
+ case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
+ createmode = syscall.CREATE_ALWAYS
+ case mode&syscall.O_CREAT == syscall.O_CREAT:
+ createmode = syscall.OPEN_ALWAYS
+ case mode&syscall.O_TRUNC == syscall.O_TRUNC:
+ createmode = syscall.TRUNCATE_EXISTING
+ default:
+ createmode = syscall.OPEN_EXISTING
+ }
+ var attrs uint32 = syscall.FILE_ATTRIBUTE_NORMAL
+ if perm&syscall.S_IWRITE == 0 {
+ attrs = syscall.FILE_ATTRIBUTE_READONLY
+ if createmode == syscall.CREATE_ALWAYS {
+ // We have been asked to create a read-only file.
+ // If the file already exists, the semantics of
+ // the Unix open system call is to preserve the
+ // existing permissions. If we pass CREATE_ALWAYS
+ // and FILE_ATTRIBUTE_READONLY to CreateFile,
+ // and the file already exists, CreateFile will
+ // change the file permissions.
+ // Avoid that to preserve the Unix semantics.
+ h, e := syscall.CreateFile(pathp, access, sharemode, sa, syscall.TRUNCATE_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)
+ switch e {
+ case syscall.ERROR_FILE_NOT_FOUND, syscall.ERROR_PATH_NOT_FOUND:
+ // File does not exist. These are the same
+ // errors as Errno.Is checks for ErrNotExist.
+ // Carry on to create the file.
+ default:
+ // Success or some different error.
+ return h, e
+ }
+ }
+ }
+
+ // This shouldn't be included before 1.20 to have consistent behavior.
+ // https://github.com/golang/go/commit/0f0aa5d8a6a0253627d58b3aa083b24a1091933f
+ if createmode == syscall.OPEN_EXISTING && access == syscall.GENERIC_READ {
+ // Necessary for opening directory handles.
+ attrs |= syscall.FILE_FLAG_BACKUP_SEMANTICS
+ }
+
+ h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
+ return h, e
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go
new file mode 100644
index 000000000..490f0fa68
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go
@@ -0,0 +1,295 @@
+package sysfs
+
+import (
+ "io"
+ "io/fs"
+ "os"
+ "runtime"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/fsapi"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+func newOsFile(path string, flag experimentalsys.Oflag, perm fs.FileMode, f *os.File) fsapi.File {
+ // Windows cannot read files written to a directory after it was opened.
+ // This was noticed in #1087 in zig tests. Use a flag instead of a
+ // different type.
+ reopenDir := runtime.GOOS == "windows"
+ return &osFile{path: path, flag: flag, perm: perm, reopenDir: reopenDir, file: f, fd: f.Fd()}
+}
+
+// osFile is a file opened with this package, and uses os.File or syscalls to
+// implement api.File.
+type osFile struct {
+ path string
+ flag experimentalsys.Oflag
+ perm fs.FileMode
+ file *os.File
+ fd uintptr
+
+ // 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
+}
+
+// cachedStat returns the cacheable parts of sys.Stat_t or an error if they
+// couldn't be retrieved.
+func (f *osFile) 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 *osFile) Dev() (uint64, experimentalsys.Errno) {
+ dev, _, _, errno := f.cachedStat()
+ return dev, errno
+}
+
+// Ino implements the same method as documented on sys.File
+func (f *osFile) Ino() (sys.Inode, experimentalsys.Errno) {
+ _, ino, _, errno := f.cachedStat()
+ return ino, errno
+}
+
+// IsDir implements the same method as documented on sys.File
+func (f *osFile) IsDir() (bool, experimentalsys.Errno) {
+ _, _, isDir, errno := f.cachedStat()
+ return isDir, errno
+}
+
+// IsAppend implements File.IsAppend
+func (f *osFile) IsAppend() bool {
+ return f.flag&experimentalsys.O_APPEND == experimentalsys.O_APPEND
+}
+
+// SetAppend implements the same method as documented on sys.File
+func (f *osFile) SetAppend(enable bool) (errno experimentalsys.Errno) {
+ if enable {
+ f.flag |= experimentalsys.O_APPEND
+ } else {
+ f.flag &= ^experimentalsys.O_APPEND
+ }
+
+ // Clear any create or trunc flag, as we are re-opening, not re-creating.
+ f.flag &= ^(experimentalsys.O_CREAT | experimentalsys.O_TRUNC)
+
+ // appendMode (bool) cannot be changed later, so we have to re-open the
+ // file. https://github.com/golang/go/blob/go1.20/src/os/file_unix.go#L60
+ return fileError(f, f.closed, f.reopen())
+}
+
+// compile-time check to ensure osFile.reopen implements reopenFile.
+var _ reopenFile = (*osFile)(nil).reopen
+
+func (f *osFile) reopen() (errno experimentalsys.Errno) {
+ // Clear any create flag, as we are re-opening, not re-creating.
+ f.flag &= ^experimentalsys.O_CREAT
+
+ var (
+ isDir bool
+ offset int64
+ err error
+ )
+
+ isDir, errno = f.IsDir()
+ if errno != 0 {
+ return errno
+ }
+
+ if !isDir {
+ offset, err = f.file.Seek(0, io.SeekCurrent)
+ if err != nil {
+ return experimentalsys.UnwrapOSError(err)
+ }
+ }
+
+ _ = f.close()
+ f.file, errno = OpenFile(f.path, f.flag, f.perm)
+ if errno != 0 {
+ return errno
+ }
+
+ if !isDir {
+ _, err = f.file.Seek(offset, io.SeekStart)
+ if err != nil {
+ return experimentalsys.UnwrapOSError(err)
+ }
+ }
+
+ return 0
+}
+
+// IsNonblock implements the same method as documented on fsapi.File
+func (f *osFile) IsNonblock() bool {
+ return isNonblock(f)
+}
+
+// SetNonblock implements the same method as documented on fsapi.File
+func (f *osFile) SetNonblock(enable bool) (errno experimentalsys.Errno) {
+ if enable {
+ f.flag |= experimentalsys.O_NONBLOCK
+ } else {
+ f.flag &= ^experimentalsys.O_NONBLOCK
+ }
+ if errno = setNonblock(f.fd, enable); errno != 0 {
+ return fileError(f, f.closed, errno)
+ }
+ return 0
+}
+
+// Stat implements the same method as documented on sys.File
+func (f *osFile) 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 *osFile) Read(buf []byte) (n int, errno experimentalsys.Errno) {
+ if len(buf) == 0 {
+ return 0, 0 // Short-circuit 0-len reads.
+ }
+ if nonBlockingFileReadSupported && f.IsNonblock() {
+ n, errno = readFd(f.fd, buf)
+ } else {
+ n, errno = read(f.file, buf)
+ }
+ if 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 *osFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
+ if n, errno = pread(f.file, buf, off); errno != 0 {
+ // Defer validation overhead until we've already had an error.
+ errno = fileError(f, f.closed, errno)
+ }
+ return
+}
+
+// Seek implements the same method as documented on sys.File
+func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) {
+ if newOffset, errno = seek(f.file, offset, whence); errno != 0 {
+ // Defer validation overhead until we've already had an error.
+ errno = fileError(f, f.closed, errno)
+
+ // If the error was trying to rewind a directory, re-open it. Notably,
+ // seeking to zero on a directory doesn't work on Windows with Go 1.19.
+ if errno == experimentalsys.EISDIR && offset == 0 && whence == io.SeekStart {
+ errno = 0
+ f.reopenDir = true
+ }
+ }
+ return
+}
+
+// Poll implements the same method as documented on fsapi.File
+func (f *osFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
+ return poll(f.fd, flag, timeoutMillis)
+}
+
+// Readdir implements File.Readdir. Notably, this uses "Readdir", not
+// "ReadDir", from os.File.
+func (f *osFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
+ if f.reopenDir { // re-open the directory if needed.
+ f.reopenDir = false
+ if errno = adjustReaddirErr(f, f.closed, f.reopen()); errno != 0 {
+ return
+ }
+ }
+
+ if dirents, errno = readdir(f.file, f.path, n); errno != 0 {
+ errno = adjustReaddirErr(f, f.closed, errno)
+ }
+ return
+}
+
+// Write implements the same method as documented on sys.File
+func (f *osFile) Write(buf []byte) (n int, errno experimentalsys.Errno) {
+ if len(buf) == 0 {
+ return 0, 0 // Short-circuit 0-len writes.
+ }
+ if nonBlockingFileWriteSupported && f.IsNonblock() {
+ n, errno = writeFd(f.fd, buf)
+ } else if n, errno = write(f.file, buf); errno != 0 {
+ // Defer validation overhead until we've already had an error.
+ errno = fileError(f, f.closed, errno)
+ }
+ return
+}
+
+// Pwrite implements the same method as documented on sys.File
+func (f *osFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
+ if n, errno = pwrite(f.file, buf, off); errno != 0 {
+ // Defer validation overhead until we've already had an error.
+ errno = fileError(f, f.closed, errno)
+ }
+ return
+}
+
+// Truncate implements the same method as documented on sys.File
+func (f *osFile) Truncate(size int64) (errno experimentalsys.Errno) {
+ if errno = experimentalsys.UnwrapOSError(f.file.Truncate(size)); errno != 0 {
+ // Defer validation overhead until we've already had an error.
+ errno = fileError(f, f.closed, errno)
+ }
+ return
+}
+
+// Sync implements the same method as documented on sys.File
+func (f *osFile) Sync() experimentalsys.Errno {
+ return fsync(f.file)
+}
+
+// Datasync implements the same method as documented on sys.File
+func (f *osFile) Datasync() experimentalsys.Errno {
+ return datasync(f.file)
+}
+
+// Utimens implements the same method as documented on sys.File
+func (f *osFile) Utimens(atim, mtim int64) experimentalsys.Errno {
+ if f.closed {
+ return experimentalsys.EBADF
+ }
+
+ err := futimens(f.fd, atim, mtim)
+ return experimentalsys.UnwrapOSError(err)
+}
+
+// Close implements the same method as documented on sys.File
+func (f *osFile) Close() experimentalsys.Errno {
+ if f.closed {
+ return 0
+ }
+ f.closed = true
+ return f.close()
+}
+
+func (f *osFile) close() experimentalsys.Errno {
+ return experimentalsys.UnwrapOSError(f.file.Close())
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go
new file mode 100644
index 000000000..a2e1103e0
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go
@@ -0,0 +1,18 @@
+//go:build windows || (linux && !tinygo) || darwin
+
+package sysfs
+
+import (
+ "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/fsapi"
+)
+
+// poll implements `Poll` as documented on sys.File via a file descriptor.
+func poll(fd uintptr, flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) {
+ if flag != fsapi.POLLIN {
+ return false, sys.ENOTSUP
+ }
+ fds := []pollFd{newPollFd(fd, _POLLIN, 0)}
+ count, errno := _poll(fds, timeoutMillis)
+ return count > 0, errno
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go
new file mode 100644
index 000000000..1f7f89093
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go
@@ -0,0 +1,55 @@
+package sysfs
+
+import (
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+// pollFd is the struct to query for file descriptor events using poll.
+type pollFd struct {
+ // fd is the file descriptor.
+ fd int32
+ // events is a bitmap containing the requested events.
+ events int16
+ // revents is a bitmap containing the returned events.
+ revents int16
+}
+
+// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors.
+func newPollFd(fd uintptr, events, revents int16) pollFd {
+ return pollFd{fd: int32(fd), events: events, revents: revents}
+}
+
+// _POLLIN subscribes a notification when any readable data is available.
+const _POLLIN = 0x0001
+
+// _poll implements poll on Darwin via the corresponding libc function.
+func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
+ var fdptr *pollFd
+ nfds := len(fds)
+ if nfds > 0 {
+ fdptr = &fds[0]
+ }
+ n1, _, err := syscall_syscall6(
+ libc_poll_trampoline_addr,
+ uintptr(unsafe.Pointer(fdptr)),
+ uintptr(nfds),
+ uintptr(int(timeoutMillis)),
+ uintptr(unsafe.Pointer(nil)),
+ uintptr(unsafe.Pointer(nil)),
+ uintptr(unsafe.Pointer(nil)))
+ return int(n1), sys.UnwrapOSError(err)
+}
+
+// libc_poll_trampoline_addr is the address of the
+// `libc_poll_trampoline` symbol, defined in `poll_darwin.s`.
+//
+// We use this to invoke the syscall through syscall_syscall6 imported below.
+var libc_poll_trampoline_addr uintptr
+
+// Imports the select symbol from libc as `libc_poll`.
+//
+// Note: CGO mechanisms are used in darwin regardless of the CGO_ENABLED value
+// or the "cgo" build flag. See /RATIONALE.md for why.
+//go:cgo_import_dynamic libc_poll poll "/usr/lib/libSystem.B.dylib"
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s
new file mode 100644
index 000000000..e04fca583
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s
@@ -0,0 +1,8 @@
+// lifted from golang.org/x/sys unix
+#include "textflag.h"
+
+TEXT libc_poll_trampoline<>(SB), NOSPLIT, $0-0
+ JMP libc_poll(SB)
+
+GLOBL ·libc_poll_trampoline_addr(SB), RODATA, $8
+DATA ·libc_poll_trampoline_addr(SB)/8, $libc_poll_trampoline<>(SB)
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go
new file mode 100644
index 000000000..49bf4fd06
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go
@@ -0,0 +1,59 @@
+//go:build !tinygo
+
+package sysfs
+
+import (
+ "syscall"
+ "time"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+// pollFd is the struct to query for file descriptor events using poll.
+type pollFd struct {
+ // fd is the file descriptor.
+ fd int32
+ // events is a bitmap containing the requested events.
+ events int16
+ // revents is a bitmap containing the returned events.
+ revents int16
+}
+
+// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors.
+func newPollFd(fd uintptr, events, revents int16) pollFd {
+ return pollFd{fd: int32(fd), events: events, revents: revents}
+}
+
+// _POLLIN subscribes a notification when any readable data is available.
+const _POLLIN = 0x0001
+
+// _poll implements poll on Linux via ppoll.
+func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
+ var ts syscall.Timespec
+ if timeoutMillis >= 0 {
+ ts = syscall.NsecToTimespec(int64(time.Duration(timeoutMillis) * time.Millisecond))
+ }
+ return ppoll(fds, &ts)
+}
+
+// ppoll is a poll variant that allows to subscribe to a mask of signals.
+// However, we do not need such mask, so the corresponding argument is always nil.
+func ppoll(fds []pollFd, timespec *syscall.Timespec) (n int, err sys.Errno) {
+ var fdptr *pollFd
+ nfd := len(fds)
+ if nfd != 0 {
+ fdptr = &fds[0]
+ }
+
+ n1, _, errno := syscall.Syscall6(
+ uintptr(syscall.SYS_PPOLL),
+ uintptr(unsafe.Pointer(fdptr)),
+ uintptr(nfd),
+ uintptr(unsafe.Pointer(timespec)),
+ uintptr(unsafe.Pointer(nil)), // sigmask is currently always ignored
+ uintptr(unsafe.Pointer(nil)),
+ uintptr(unsafe.Pointer(nil)))
+
+ return int(n1), sys.UnwrapOSError(errno)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go
new file mode 100644
index 000000000..2301a067e
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go
@@ -0,0 +1,13 @@
+//go:build !(linux || darwin || windows) || tinygo
+
+package sysfs
+
+import (
+ "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/fsapi"
+)
+
+// poll implements `Poll` as documented on fsapi.File via a file descriptor.
+func poll(uintptr, fsapi.Pflag, int32) (bool, sys.Errno) {
+ return false, sys.ENOSYS
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go
new file mode 100644
index 000000000..82c8b2baf
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go
@@ -0,0 +1,224 @@
+package sysfs
+
+import (
+ "syscall"
+ "time"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+var (
+ procWSAPoll = modws2_32.NewProc("WSAPoll")
+ procGetNamedPipeInfo = kernel32.NewProc("GetNamedPipeInfo")
+)
+
+const (
+ // _POLLRDNORM subscribes to normal data for read.
+ _POLLRDNORM = 0x0100
+ // _POLLRDBAND subscribes to priority band (out-of-band) data for read.
+ _POLLRDBAND = 0x0200
+ // _POLLIN subscribes a notification when any readable data is available.
+ _POLLIN = (_POLLRDNORM | _POLLRDBAND)
+)
+
+// pollFd is the struct to query for file descriptor events using poll.
+type pollFd struct {
+ // fd is the file descriptor.
+ fd uintptr
+ // events is a bitmap containing the requested events.
+ events int16
+ // revents is a bitmap containing the returned events.
+ revents int16
+}
+
+// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors.
+func newPollFd(fd uintptr, events, revents int16) pollFd {
+ return pollFd{fd: fd, events: events, revents: revents}
+}
+
+// pollInterval is the interval between each calls to peekNamedPipe in selectAllHandles
+const pollInterval = 100 * time.Millisecond
+
+// _poll implements poll on Windows, for a subset of cases.
+//
+// fds may contain any number of file handles, but regular files and pipes are only processed for _POLLIN.
+// Stdin is a pipe, thus it is checked for readiness when present. Pipes are checked using PeekNamedPipe.
+// Regular files always immediately reported as ready, regardless their actual state and timeouts.
+//
+// If n==0 it will wait for the given timeout duration, but it will return sys.ENOSYS if timeout is nil,
+// i.e. it won't block indefinitely. The given ctx is used to allow for cancellation,
+// and it is currently used only in tests.
+//
+// The implementation actually polls every 100 milliseconds (pollInterval) until it reaches the
+// given timeout (in millis).
+//
+// The duration may be negative, in which case it will wait indefinitely. The given ctx is
+// used to allow for cancellation, and it is currently used only in tests.
+func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
+ if fds == nil {
+ return -1, sys.ENOSYS
+ }
+
+ regular, pipes, sockets, errno := partionByFtype(fds)
+ nregular := len(regular)
+ if errno != 0 {
+ return -1, errno
+ }
+
+ // Ticker that emits at every pollInterval.
+ tick := time.NewTicker(pollInterval)
+ tickCh := tick.C
+ defer tick.Stop()
+
+ // Timer that expires after the given duration.
+ // Initialize afterCh as nil: the select below will wait forever.
+ var afterCh <-chan time.Time
+ if timeoutMillis >= 0 {
+ // If duration is not nil, instantiate the timer.
+ after := time.NewTimer(time.Duration(timeoutMillis) * time.Millisecond)
+ defer after.Stop()
+ afterCh = after.C
+ }
+
+ npipes, nsockets, errno := peekAll(pipes, sockets)
+ if errno != 0 {
+ return -1, errno
+ }
+ count := nregular + npipes + nsockets
+ if count > 0 {
+ return count, 0
+ }
+
+ for {
+ select {
+ case <-afterCh:
+ return 0, 0
+ case <-tickCh:
+ npipes, nsockets, errno := peekAll(pipes, sockets)
+ if errno != 0 {
+ return -1, errno
+ }
+ count = nregular + npipes + nsockets
+ if count > 0 {
+ return count, 0
+ }
+ }
+ }
+}
+
+func peekAll(pipes, sockets []pollFd) (npipes, nsockets int, errno sys.Errno) {
+ npipes, errno = peekPipes(pipes)
+ if errno != 0 {
+ return
+ }
+
+ // Invoke wsaPoll with a 0-timeout to avoid blocking.
+ // Timeouts are handled in pollWithContext instead.
+ nsockets, errno = wsaPoll(sockets, 0)
+ if errno != 0 {
+ return
+ }
+
+ count := npipes + nsockets
+ if count > 0 {
+ return
+ }
+
+ return
+}
+
+func peekPipes(fds []pollFd) (n int, errno sys.Errno) {
+ for _, fd := range fds {
+ bytes, errno := peekNamedPipe(syscall.Handle(fd.fd))
+ if errno != 0 {
+ return -1, sys.UnwrapOSError(errno)
+ }
+ if bytes > 0 {
+ n++
+ }
+ }
+ return
+}
+
+// wsaPoll is the WSAPoll function from winsock2.
+//
+// See https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll
+func wsaPoll(fds []pollFd, timeout int) (n int, errno sys.Errno) {
+ if len(fds) > 0 {
+ sockptr := &fds[0]
+ ns, _, e := syscall.SyscallN(
+ procWSAPoll.Addr(),
+ uintptr(unsafe.Pointer(sockptr)),
+ uintptr(len(fds)),
+ uintptr(timeout))
+ if e != 0 {
+ return -1, sys.UnwrapOSError(e)
+ }
+ n = int(ns)
+ }
+ return
+}
+
+// ftype is a type of file that can be handled by poll.
+type ftype uint8
+
+const (
+ ftype_regular ftype = iota
+ ftype_pipe
+ ftype_socket
+)
+
+// partionByFtype checks the type of each fd in fds and returns 3 distinct partitions
+// for regular files, named pipes and sockets.
+func partionByFtype(fds []pollFd) (regular, pipe, socket []pollFd, errno sys.Errno) {
+ for _, pfd := range fds {
+ t, errno := ftypeOf(pfd.fd)
+ if errno != 0 {
+ return nil, nil, nil, errno
+ }
+ switch t {
+ case ftype_regular:
+ regular = append(regular, pfd)
+ case ftype_pipe:
+ pipe = append(pipe, pfd)
+ case ftype_socket:
+ socket = append(socket, pfd)
+ }
+ }
+ return
+}
+
+// ftypeOf checks the type of fd and return the corresponding ftype.
+func ftypeOf(fd uintptr) (ftype, sys.Errno) {
+ h := syscall.Handle(fd)
+ t, err := syscall.GetFileType(h)
+ if err != nil {
+ return 0, sys.UnwrapOSError(err)
+ }
+ switch t {
+ case syscall.FILE_TYPE_CHAR, syscall.FILE_TYPE_DISK:
+ return ftype_regular, 0
+ case syscall.FILE_TYPE_PIPE:
+ if isSocket(h) {
+ return ftype_socket, 0
+ } else {
+ return ftype_pipe, 0
+ }
+ default:
+ return ftype_regular, 0
+ }
+}
+
+// isSocket returns true if the given file handle
+// is a pipe.
+func isSocket(fd syscall.Handle) bool {
+ r, _, errno := syscall.SyscallN(
+ procGetNamedPipeInfo.Addr(),
+ uintptr(fd),
+ uintptr(unsafe.Pointer(nil)),
+ uintptr(unsafe.Pointer(nil)),
+ uintptr(unsafe.Pointer(nil)),
+ uintptr(unsafe.Pointer(nil)))
+ return r == 0 || errno != 0
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go
new file mode 100644
index 000000000..59e331a29
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go
@@ -0,0 +1,117 @@
+package sysfs
+
+import (
+ "io/fs"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+type ReadFS struct {
+ experimentalsys.FS
+}
+
+// OpenFile implements the same method as documented on sys.FS
+func (r *ReadFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
+ // Mask the mutually exclusive bits as they determine write mode.
+ switch flag & (experimentalsys.O_RDONLY | experimentalsys.O_WRONLY | experimentalsys.O_RDWR) {
+ case experimentalsys.O_WRONLY, experimentalsys.O_RDWR:
+ // Return the correct error if a directory was opened for write.
+ if flag&experimentalsys.O_DIRECTORY != 0 {
+ return nil, experimentalsys.EISDIR
+ }
+ return nil, experimentalsys.ENOSYS
+ default: // sys.O_RDONLY (integer zero) so we are ok!
+ }
+
+ f, errno := r.FS.OpenFile(path, flag, perm)
+ if errno != 0 {
+ return nil, errno
+ }
+ return &readFile{f}, 0
+}
+
+// Mkdir implements the same method as documented on sys.FS
+func (r *ReadFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno {
+ return experimentalsys.EROFS
+}
+
+// Chmod implements the same method as documented on sys.FS
+func (r *ReadFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
+ return experimentalsys.EROFS
+}
+
+// Rename implements the same method as documented on sys.FS
+func (r *ReadFS) Rename(from, to string) experimentalsys.Errno {
+ return experimentalsys.EROFS
+}
+
+// Rmdir implements the same method as documented on sys.FS
+func (r *ReadFS) Rmdir(path string) experimentalsys.Errno {
+ return experimentalsys.EROFS
+}
+
+// Link implements the same method as documented on sys.FS
+func (r *ReadFS) Link(_, _ string) experimentalsys.Errno {
+ return experimentalsys.EROFS
+}
+
+// Symlink implements the same method as documented on sys.FS
+func (r *ReadFS) Symlink(_, _ string) experimentalsys.Errno {
+ return experimentalsys.EROFS
+}
+
+// Unlink implements the same method as documented on sys.FS
+func (r *ReadFS) Unlink(path string) experimentalsys.Errno {
+ return experimentalsys.EROFS
+}
+
+// Utimens implements the same method as documented on sys.FS
+func (r *ReadFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno {
+ return experimentalsys.EROFS
+}
+
+// compile-time check to ensure readFile implements api.File.
+var _ experimentalsys.File = (*readFile)(nil)
+
+type readFile struct {
+ experimentalsys.File
+}
+
+// Write implements the same method as documented on sys.File.
+func (r *readFile) Write([]byte) (int, experimentalsys.Errno) {
+ return 0, r.writeErr()
+}
+
+// Pwrite implements the same method as documented on sys.File.
+func (r *readFile) Pwrite([]byte, int64) (n int, errno experimentalsys.Errno) {
+ return 0, r.writeErr()
+}
+
+// Truncate implements the same method as documented on sys.File.
+func (r *readFile) Truncate(int64) experimentalsys.Errno {
+ return r.writeErr()
+}
+
+// Sync implements the same method as documented on sys.File.
+func (r *readFile) Sync() experimentalsys.Errno {
+ return experimentalsys.EBADF
+}
+
+// Datasync implements the same method as documented on sys.File.
+func (r *readFile) Datasync() experimentalsys.Errno {
+ return experimentalsys.EBADF
+}
+
+// Utimens implements the same method as documented on sys.File.
+func (r *readFile) Utimens(int64, int64) experimentalsys.Errno {
+ return experimentalsys.EBADF
+}
+
+func (r *readFile) writeErr() experimentalsys.Errno {
+ if isDir, errno := r.IsDir(); errno != 0 {
+ return errno
+ } else if isDir {
+ return experimentalsys.EISDIR
+ }
+ return experimentalsys.EBADF
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go
new file mode 100644
index 000000000..37c53571d
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go
@@ -0,0 +1,16 @@
+//go:build !windows && !plan9 && !tinygo
+
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func rename(from, to string) sys.Errno {
+ if from == to {
+ return 0
+ }
+ return sys.UnwrapOSError(syscall.Rename(from, to))
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go
new file mode 100644
index 000000000..474cc7595
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go
@@ -0,0 +1,14 @@
+package sysfs
+
+import (
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func rename(from, to string) sys.Errno {
+ if from == to {
+ return 0
+ }
+ return sys.UnwrapOSError(os.Rename(from, to))
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_windows.go
new file mode 100644
index 000000000..5e8102239
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_windows.go
@@ -0,0 +1,55 @@
+package sysfs
+
+import (
+ "os"
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func rename(from, to string) sys.Errno {
+ if from == to {
+ return 0
+ }
+
+ var fromIsDir, toIsDir bool
+ if fromStat, errno := stat(from); errno != 0 {
+ return errno // failed to stat from
+ } else {
+ fromIsDir = fromStat.Mode.IsDir()
+ }
+ if toStat, errno := stat(to); errno == sys.ENOENT {
+ return syscallRename(from, to) // file or dir to not-exist is ok
+ } else if errno != 0 {
+ return errno // failed to stat to
+ } else {
+ toIsDir = toStat.Mode.IsDir()
+ }
+
+ // Now, handle known cases
+ switch {
+ case !fromIsDir && toIsDir: // file to dir
+ return sys.EISDIR
+ case !fromIsDir && !toIsDir: // file to file
+ // Use os.Rename instead of syscall.Rename to overwrite a file.
+ // This uses MoveFileEx instead of MoveFile (used by syscall.Rename).
+ return sys.UnwrapOSError(os.Rename(from, to))
+ case fromIsDir && !toIsDir: // dir to file
+ return sys.ENOTDIR
+ default: // dir to dir
+
+ // We can't tell if a directory is empty or not, via stat information.
+ // Reading the directory is expensive, as it can buffer large amounts
+ // of data on fail. Instead, speculatively try to remove the directory.
+ // This is only one syscall and won't buffer anything.
+ if errno := rmdir(to); errno == 0 || errno == sys.ENOENT {
+ return syscallRename(from, to)
+ } else {
+ return errno
+ }
+ }
+}
+
+func syscallRename(from string, to string) sys.Errno {
+ return sys.UnwrapOSError(syscall.Rename(from, to))
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go
new file mode 100644
index 000000000..ab9bb1ffa
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go
@@ -0,0 +1,187 @@
+package sysfs
+
+import (
+ "net"
+ "os"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/fsapi"
+ socketapi "github.com/tetratelabs/wazero/internal/sock"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// NewTCPListenerFile creates a socketapi.TCPSock for a given *net.TCPListener.
+func NewTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
+ return newTCPListenerFile(tl)
+}
+
+// baseSockFile implements base behavior for all TCPSock, TCPConn files,
+// regardless the platform.
+type baseSockFile struct {
+ experimentalsys.UnimplementedFile
+}
+
+var _ experimentalsys.File = (*baseSockFile)(nil)
+
+// IsDir implements the same method as documented on File.IsDir
+func (*baseSockFile) IsDir() (bool, experimentalsys.Errno) {
+ // We need to override this method because WASI-libc prestats the FD
+ // and the default impl returns ENOSYS otherwise.
+ return false, 0
+}
+
+// Stat implements the same method as documented on File.Stat
+func (f *baseSockFile) Stat() (fs sys.Stat_t, errno experimentalsys.Errno) {
+ // The mode is not really important, but it should be neither a regular file nor a directory.
+ fs.Mode = os.ModeIrregular
+ return
+}
+
+var _ socketapi.TCPSock = (*tcpListenerFile)(nil)
+
+type tcpListenerFile struct {
+ baseSockFile
+
+ tl *net.TCPListener
+ closed bool
+ nonblock bool
+}
+
+// newTCPListenerFile is a constructor for a socketapi.TCPSock.
+//
+// The current strategy is to wrap a net.TCPListener
+// and invoking raw syscalls using syscallConnControl:
+// this internal calls RawConn.Control(func(fd)), making sure
+// that the underlying file descriptor is valid throughout
+// the duration of the syscall.
+func newDefaultTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
+ return &tcpListenerFile{tl: tl}
+}
+
+// Close implements the same method as documented on experimentalsys.File
+func (f *tcpListenerFile) Close() experimentalsys.Errno {
+ if !f.closed {
+ return experimentalsys.UnwrapOSError(f.tl.Close())
+ }
+ return 0
+}
+
+// Addr is exposed for testing.
+func (f *tcpListenerFile) Addr() *net.TCPAddr {
+ return f.tl.Addr().(*net.TCPAddr)
+}
+
+// IsNonblock implements the same method as documented on fsapi.File
+func (f *tcpListenerFile) IsNonblock() bool {
+ return f.nonblock
+}
+
+// Poll implements the same method as documented on fsapi.File
+func (f *tcpListenerFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
+ return false, experimentalsys.ENOSYS
+}
+
+var _ socketapi.TCPConn = (*tcpConnFile)(nil)
+
+type tcpConnFile struct {
+ baseSockFile
+
+ tc *net.TCPConn
+
+ // nonblock is true when the underlying connection is flagged as non-blocking.
+ // This ensures that reads and writes return experimentalsys.EAGAIN without blocking the caller.
+ nonblock bool
+ // closed is true when closed was called. This ensures proper experimentalsys.EBADF
+ closed bool
+}
+
+func newTcpConn(tc *net.TCPConn) socketapi.TCPConn {
+ return &tcpConnFile{tc: tc}
+}
+
+// Read implements the same method as documented on experimentalsys.File
+func (f *tcpConnFile) Read(buf []byte) (n int, errno experimentalsys.Errno) {
+ if len(buf) == 0 {
+ return 0, 0 // Short-circuit 0-len reads.
+ }
+ if nonBlockingFileReadSupported && f.IsNonblock() {
+ n, errno = syscallConnControl(f.tc, func(fd uintptr) (int, experimentalsys.Errno) {
+ n, err := readSocket(fd, buf)
+ errno = experimentalsys.UnwrapOSError(err)
+ errno = fileError(f, f.closed, errno)
+ return n, errno
+ })
+ } else {
+ n, errno = read(f.tc, buf)
+ }
+ if errno != 0 {
+ // Defer validation overhead until we've already had an error.
+ errno = fileError(f, f.closed, errno)
+ }
+ return
+}
+
+// Write implements the same method as documented on experimentalsys.File
+func (f *tcpConnFile) Write(buf []byte) (n int, errno experimentalsys.Errno) {
+ if nonBlockingFileWriteSupported && f.IsNonblock() {
+ return syscallConnControl(f.tc, func(fd uintptr) (int, experimentalsys.Errno) {
+ n, err := writeSocket(fd, buf)
+ errno = experimentalsys.UnwrapOSError(err)
+ errno = fileError(f, f.closed, errno)
+ return n, errno
+ })
+ } else {
+ n, errno = write(f.tc, buf)
+ }
+ if errno != 0 {
+ // Defer validation overhead until we've already had an error.
+ errno = fileError(f, f.closed, errno)
+ }
+ return
+}
+
+// Recvfrom implements the same method as documented on socketapi.TCPConn
+func (f *tcpConnFile) Recvfrom(p []byte, flags int) (n int, errno experimentalsys.Errno) {
+ if flags != MSG_PEEK {
+ errno = experimentalsys.EINVAL
+ return
+ }
+ return syscallConnControl(f.tc, func(fd uintptr) (int, experimentalsys.Errno) {
+ n, err := recvfrom(fd, p, MSG_PEEK)
+ errno = experimentalsys.UnwrapOSError(err)
+ errno = fileError(f, f.closed, errno)
+ return n, errno
+ })
+}
+
+// Close implements the same method as documented on experimentalsys.File
+func (f *tcpConnFile) Close() experimentalsys.Errno {
+ return f.close()
+}
+
+func (f *tcpConnFile) close() experimentalsys.Errno {
+ if f.closed {
+ return 0
+ }
+ f.closed = true
+ return f.Shutdown(socketapi.SHUT_RDWR)
+}
+
+// SetNonblock implements the same method as documented on fsapi.File
+func (f *tcpConnFile) SetNonblock(enabled bool) (errno experimentalsys.Errno) {
+ f.nonblock = enabled
+ _, errno = syscallConnControl(f.tc, func(fd uintptr) (int, experimentalsys.Errno) {
+ return 0, experimentalsys.UnwrapOSError(setNonblockSocket(fd, enabled))
+ })
+ return
+}
+
+// IsNonblock implements the same method as documented on fsapi.File
+func (f *tcpConnFile) IsNonblock() bool {
+ return f.nonblock
+}
+
+// Poll implements the same method as documented on fsapi.File
+func (f *tcpConnFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
+ return false, experimentalsys.ENOSYS
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_supported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_supported.go
new file mode 100644
index 000000000..6c976fb86
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_supported.go
@@ -0,0 +1,77 @@
+//go:build (linux || darwin || windows) && !tinygo
+
+package sysfs
+
+import (
+ "net"
+ "syscall"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/fsapi"
+ socketapi "github.com/tetratelabs/wazero/internal/sock"
+)
+
+// Accept implements the same method as documented on socketapi.TCPSock
+func (f *tcpListenerFile) Accept() (socketapi.TCPConn, experimentalsys.Errno) {
+ // Ensure we have an incoming connection, otherwise return immediately.
+ if f.nonblock {
+ if ready, errno := _pollSock(f.tl, fsapi.POLLIN, 0); !ready || errno != 0 {
+ return nil, experimentalsys.EAGAIN
+ }
+ }
+
+ // Accept normally blocks goroutines, but we
+ // made sure that we have an incoming connection,
+ // so we should be safe.
+ if conn, err := f.tl.Accept(); err != nil {
+ return nil, experimentalsys.UnwrapOSError(err)
+ } else {
+ return newTcpConn(conn.(*net.TCPConn)), 0
+ }
+}
+
+// SetNonblock implements the same method as documented on fsapi.File
+func (f *tcpListenerFile) SetNonblock(enabled bool) (errno experimentalsys.Errno) {
+ f.nonblock = enabled
+ _, errno = syscallConnControl(f.tl, func(fd uintptr) (int, experimentalsys.Errno) {
+ return 0, setNonblockSocket(fd, enabled)
+ })
+ return
+}
+
+// Shutdown implements the same method as documented on experimentalsys.Conn
+func (f *tcpConnFile) Shutdown(how int) experimentalsys.Errno {
+ // FIXME: can userland shutdown listeners?
+ var err error
+ switch how {
+ case socketapi.SHUT_RD:
+ err = f.tc.CloseRead()
+ case socketapi.SHUT_WR:
+ err = f.tc.CloseWrite()
+ case socketapi.SHUT_RDWR:
+ return f.close()
+ default:
+ return experimentalsys.EINVAL
+ }
+ return experimentalsys.UnwrapOSError(err)
+}
+
+// syscallConnControl extracts a syscall.RawConn from the given syscall.Conn and applies
+// the given fn to a file descriptor, returning an integer or a nonzero syscall.Errno on failure.
+//
+// syscallConnControl streamlines the pattern of extracting the syscall.Rawconn,
+// invoking its syscall.RawConn.Control method, then handling properly the errors that may occur
+// within fn or returned by syscall.RawConn.Control itself.
+func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, experimentalsys.Errno)) (n int, errno experimentalsys.Errno) {
+ syscallConn, err := conn.SyscallConn()
+ if err != nil {
+ return 0, experimentalsys.UnwrapOSError(err)
+ }
+ // Prioritize the inner errno over Control
+ if controlErr := syscallConn.Control(func(fd uintptr) {
+ n, errno = fn(fd)
+ }); errno == 0 {
+ errno = experimentalsys.UnwrapOSError(controlErr)
+ }
+ return
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go
new file mode 100644
index 000000000..99ef018a4
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go
@@ -0,0 +1,49 @@
+//go:build (linux || darwin) && !tinygo
+
+package sysfs
+
+import (
+ "net"
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/fsapi"
+ socketapi "github.com/tetratelabs/wazero/internal/sock"
+)
+
+// MSG_PEEK is the constant syscall.MSG_PEEK
+const MSG_PEEK = syscall.MSG_PEEK
+
+func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
+ return newDefaultTCPListenerFile(tl)
+}
+
+func _pollSock(conn syscall.Conn, flag fsapi.Pflag, timeoutMillis int32) (bool, sys.Errno) {
+ n, errno := syscallConnControl(conn, func(fd uintptr) (int, sys.Errno) {
+ if ready, errno := poll(fd, fsapi.POLLIN, 0); !ready || errno != 0 {
+ return -1, errno
+ } else {
+ return 0, errno
+ }
+ })
+ return n >= 0, errno
+}
+
+func setNonblockSocket(fd uintptr, enabled bool) sys.Errno {
+ return sys.UnwrapOSError(setNonblock(fd, enabled))
+}
+
+func readSocket(fd uintptr, buf []byte) (int, sys.Errno) {
+ n, err := syscall.Read(int(fd), buf)
+ return n, sys.UnwrapOSError(err)
+}
+
+func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) {
+ n, err := syscall.Write(int(fd), buf)
+ return n, sys.UnwrapOSError(err)
+}
+
+func recvfrom(fd uintptr, buf []byte, flags int32) (n int, errno sys.Errno) {
+ n, _, err := syscall.Recvfrom(int(fd), buf, int(flags))
+ return n, sys.UnwrapOSError(err)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unsupported.go
new file mode 100644
index 000000000..8c27fed7f
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unsupported.go
@@ -0,0 +1,81 @@
+//go:build (!linux && !darwin && !windows) || tinygo
+
+package sysfs
+
+import (
+ "net"
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/fsapi"
+ socketapi "github.com/tetratelabs/wazero/internal/sock"
+)
+
+// MSG_PEEK is a filler value.
+const MSG_PEEK = 0x2
+
+func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
+ return &unsupportedSockFile{}
+}
+
+type unsupportedSockFile struct {
+ baseSockFile
+}
+
+// Accept implements the same method as documented on socketapi.TCPSock
+func (f *unsupportedSockFile) Accept() (socketapi.TCPConn, sys.Errno) {
+ return nil, sys.ENOSYS
+}
+
+func _pollSock(conn syscall.Conn, flag fsapi.Pflag, timeoutMillis int32) (bool, sys.Errno) {
+ return false, sys.ENOTSUP
+}
+
+func setNonblockSocket(fd uintptr, enabled bool) sys.Errno {
+ return sys.ENOTSUP
+}
+
+func readSocket(fd uintptr, buf []byte) (int, sys.Errno) {
+ return -1, sys.ENOTSUP
+}
+
+func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) {
+ return -1, sys.ENOTSUP
+}
+
+func recvfrom(fd uintptr, buf []byte, flags int32) (n int, errno sys.Errno) {
+ return -1, sys.ENOTSUP
+}
+
+// syscallConnControl extracts a syscall.RawConn from the given syscall.Conn and applies
+// the given fn to a file descriptor, returning an integer or a nonzero syscall.Errno on failure.
+//
+// syscallConnControl streamlines the pattern of extracting the syscall.Rawconn,
+// invoking its syscall.RawConn.Control method, then handling properly the errors that may occur
+// within fn or returned by syscall.RawConn.Control itself.
+func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, experimentalsys.Errno)) (n int, errno sys.Errno) {
+ return -1, sys.ENOTSUP
+}
+
+// Accept implements the same method as documented on socketapi.TCPSock
+func (f *tcpListenerFile) Accept() (socketapi.TCPConn, experimentalsys.Errno) {
+ return nil, experimentalsys.ENOSYS
+}
+
+// Shutdown implements the same method as documented on experimentalsys.Conn
+func (f *tcpConnFile) Shutdown(how int) experimentalsys.Errno {
+ // FIXME: can userland shutdown listeners?
+ var err error
+ switch how {
+ case socketapi.SHUT_RD:
+ err = f.tc.Close()
+ case socketapi.SHUT_WR:
+ err = f.tc.Close()
+ case socketapi.SHUT_RDWR:
+ return f.close()
+ default:
+ return experimentalsys.EINVAL
+ }
+ return experimentalsys.UnwrapOSError(err)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go
new file mode 100644
index 000000000..703df42fc
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go
@@ -0,0 +1,80 @@
+//go:build windows
+
+package sysfs
+
+import (
+ "net"
+ "syscall"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/fsapi"
+ socketapi "github.com/tetratelabs/wazero/internal/sock"
+)
+
+const (
+ // MSG_PEEK is the flag PEEK for syscall.Recvfrom on Windows.
+ // This constant is not exported on this platform.
+ MSG_PEEK = 0x2
+ // _FIONBIO is the flag to set the O_NONBLOCK flag on socket handles using ioctlsocket.
+ _FIONBIO = 0x8004667e
+)
+
+var (
+ // modws2_32 is WinSock.
+ modws2_32 = syscall.NewLazyDLL("ws2_32.dll")
+ // procrecvfrom exposes recvfrom from WinSock.
+ procrecvfrom = modws2_32.NewProc("recvfrom")
+ // procioctlsocket exposes ioctlsocket from WinSock.
+ procioctlsocket = modws2_32.NewProc("ioctlsocket")
+)
+
+func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
+ return newDefaultTCPListenerFile(tl)
+}
+
+// recvfrom exposes the underlying syscall in Windows.
+//
+// Note: since we are only using this to expose MSG_PEEK,
+// we do not need really need all the parameters that are actually
+// allowed in WinSock.
+// We ignore `from *sockaddr` and `fromlen *int`.
+func recvfrom(s uintptr, buf []byte, flags int32) (n int, errno sys.Errno) {
+ var _p0 *byte
+ if len(buf) > 0 {
+ _p0 = &buf[0]
+ }
+ r0, _, e1 := syscall.SyscallN(
+ procrecvfrom.Addr(),
+ s,
+ uintptr(unsafe.Pointer(_p0)),
+ uintptr(len(buf)),
+ uintptr(flags),
+ 0, // from *sockaddr (optional)
+ 0) // fromlen *int (optional)
+ return int(r0), sys.UnwrapOSError(e1)
+}
+
+func setNonblockSocket(fd uintptr, enabled bool) sys.Errno {
+ opt := uint64(0)
+ if enabled {
+ opt = 1
+ }
+ // ioctlsocket(fd, FIONBIO, &opt)
+ _, _, errno := syscall.SyscallN(
+ procioctlsocket.Addr(),
+ uintptr(fd),
+ uintptr(_FIONBIO),
+ uintptr(unsafe.Pointer(&opt)))
+ return sys.UnwrapOSError(errno)
+}
+
+func _pollSock(conn syscall.Conn, flag fsapi.Pflag, timeoutMillis int32) (bool, sys.Errno) {
+ if flag != fsapi.POLLIN {
+ return false, sys.ENOTSUP
+ }
+ n, errno := syscallConnControl(conn, func(fd uintptr) (int, sys.Errno) {
+ return _poll([]pollFd{newPollFd(fd, _POLLIN, 0)}, timeoutMillis)
+ })
+ return n > 0, errno
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat.go
new file mode 100644
index 000000000..2d973b16c
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat.go
@@ -0,0 +1,16 @@
+package sysfs
+
+import (
+ "io/fs"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+func defaultStatFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
+ if info, err := f.Stat(); err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ } else {
+ return sys.NewStat_t(info), 0
+ }
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go
new file mode 100644
index 000000000..254e204cd
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go
@@ -0,0 +1,37 @@
+//go:build (amd64 || arm64) && (darwin || freebsd)
+
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// dirNlinkIncludesDot is true because even though os.File filters out dot
+// entries, the underlying syscall.Stat includes them.
+//
+// Note: this is only used in tests
+const dirNlinkIncludesDot = true
+
+func lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ if info, err := os.Lstat(path); err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ } else {
+ return sys.NewStat_t(info), 0
+ }
+}
+
+func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ if info, err := os.Stat(path); err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ } else {
+ return sys.NewStat_t(info), 0
+ }
+}
+
+func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
+ return defaultStatFile(f)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go
new file mode 100644
index 000000000..fd289756d
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go
@@ -0,0 +1,40 @@
+//go:build (amd64 || arm64 || riscv64) && linux
+
+// Note: This expression is not the same as compiler support, even if it looks
+// similar. Platform functions here are used in interpreter mode as well.
+
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// dirNlinkIncludesDot is true because even though os.File filters out dot
+// entries, the underlying syscall.Stat includes them.
+//
+// Note: this is only used in tests
+const dirNlinkIncludesDot = true
+
+func lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ if info, err := os.Lstat(path); err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ } else {
+ return sys.NewStat_t(info), 0
+ }
+}
+
+func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ if info, err := os.Stat(path); err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ } else {
+ return sys.NewStat_t(info), 0
+ }
+}
+
+func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
+ return defaultStatFile(f)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go
new file mode 100644
index 000000000..4b05a8977
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go
@@ -0,0 +1,40 @@
+//go:build (!((amd64 || arm64 || riscv64) && linux) && !((amd64 || arm64) && (darwin || freebsd)) && !((amd64 || arm64) && windows)) || js
+
+package sysfs
+
+import (
+ "io/fs"
+ "os"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// Note: go:build constraints must be the same as /sys.stat_unsupported.go for
+// the same reasons.
+
+// dirNlinkIncludesDot might be true for some operating systems, which can have
+// new stat_XX.go files as necessary.
+//
+// Note: this is only used in tests
+const dirNlinkIncludesDot = false
+
+func lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ if info, err := os.Lstat(path); err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ } else {
+ return sys.NewStat_t(info), 0
+ }
+}
+
+func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ if info, err := os.Stat(path); err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ } else {
+ return sys.NewStat_t(info), 0
+ }
+}
+
+func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
+ return defaultStatFile(f)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go
new file mode 100644
index 000000000..4456dd782
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go
@@ -0,0 +1,120 @@
+//go:build (amd64 || arm64) && windows
+
+package sysfs
+
+import (
+ "io/fs"
+ "syscall"
+
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// dirNlinkIncludesDot is false because Windows does not return dot entries.
+//
+// Note: this is only used in tests
+const dirNlinkIncludesDot = false
+
+func lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
+ // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
+ // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
+ attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
+ return statPath(attrs, path)
+}
+
+func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
+ attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
+ return statPath(attrs, path)
+}
+
+func statPath(createFileAttrs uint32, path string) (sys.Stat_t, experimentalsys.Errno) {
+ if len(path) == 0 {
+ return sys.Stat_t{}, experimentalsys.ENOENT
+ }
+ pathp, err := syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return sys.Stat_t{}, experimentalsys.EINVAL
+ }
+
+ // open the file handle
+ h, err := syscall.CreateFile(pathp, 0, 0, nil,
+ syscall.OPEN_EXISTING, createFileAttrs, 0)
+ if err != nil {
+ errno := experimentalsys.UnwrapOSError(err)
+ // To match expectations of WASI, e.g. TinyGo TestStatBadDir, return
+ // ENOENT, not ENOTDIR.
+ if errno == experimentalsys.ENOTDIR {
+ errno = experimentalsys.ENOENT
+ }
+ return sys.Stat_t{}, errno
+ }
+ defer syscall.CloseHandle(h)
+
+ return statHandle(h)
+}
+
+// fdFile allows masking the `Fd` function on os.File.
+type fdFile interface {
+ Fd() uintptr
+}
+
+func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
+ if osF, ok := f.(fdFile); ok {
+ // Attempt to get the stat by handle, which works for normal files
+ st, err := statHandle(syscall.Handle(osF.Fd()))
+
+ // ERROR_INVALID_HANDLE happens before Go 1.20. Don't fail as we only
+ // use that approach to fill in inode data, which is not critical.
+ //
+ // Note: statHandle uses UnwrapOSError which coerces
+ // ERROR_INVALID_HANDLE to EBADF.
+ if err != experimentalsys.EBADF {
+ return st, err
+ }
+ }
+ return defaultStatFile(f)
+}
+
+func statHandle(h syscall.Handle) (sys.Stat_t, experimentalsys.Errno) {
+ winFt, err := syscall.GetFileType(h)
+ if err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ }
+
+ var fi syscall.ByHandleFileInformation
+ if err = syscall.GetFileInformationByHandle(h, &fi); err != nil {
+ return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
+ }
+
+ var m fs.FileMode
+ if fi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
+ m |= 0o444
+ } else {
+ m |= 0o666
+ }
+
+ switch { // check whether this is a symlink first
+ case fi.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0:
+ m |= fs.ModeSymlink
+ case winFt == syscall.FILE_TYPE_PIPE:
+ m |= fs.ModeNamedPipe
+ case winFt == syscall.FILE_TYPE_CHAR:
+ m |= fs.ModeDevice | fs.ModeCharDevice
+ case fi.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0:
+ m |= fs.ModeDir | 0o111 // e.g. 0o444 -> 0o555
+ }
+
+ st := sys.Stat_t{}
+ // FileIndex{High,Low} can be combined and used as a unique identifier like inode.
+ // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information
+ st.Dev = uint64(fi.VolumeSerialNumber)
+ st.Ino = (uint64(fi.FileIndexHigh) << 32) | uint64(fi.FileIndexLow)
+ st.Mode = m
+ st.Nlink = uint64(fi.NumberOfLinks)
+ st.Size = int64(fi.FileSizeHigh)<<32 + int64(fi.FileSizeLow)
+ st.Atim = fi.LastAccessTime.Nanoseconds()
+ st.Mtim = fi.LastWriteTime.Nanoseconds()
+ st.Ctim = fi.CreationTime.Nanoseconds()
+ return st, 0
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sync.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sync.go
new file mode 100644
index 000000000..86f9a0865
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sync.go
@@ -0,0 +1,13 @@
+//go:build !windows
+
+package sysfs
+
+import (
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func fsync(f *os.File) sys.Errno {
+ return sys.UnwrapOSError(f.Sync())
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sync_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sync_windows.go
new file mode 100644
index 000000000..f288eb25b
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sync_windows.go
@@ -0,0 +1,20 @@
+package sysfs
+
+import (
+ "os"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func fsync(f *os.File) sys.Errno {
+ errno := sys.UnwrapOSError(f.Sync())
+ // Coerce error performing stat on a directory to 0, as it won't work
+ // on Windows.
+ switch errno {
+ case sys.EACCES /* Go 1.20 */, sys.EBADF /* Go 1.19 */ :
+ if st, err := f.Stat(); err == nil && st.IsDir() {
+ errno = 0
+ }
+ }
+ return errno
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/syscall6_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/syscall6_darwin.go
new file mode 100644
index 000000000..9fde5baa5
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/syscall6_darwin.go
@@ -0,0 +1,13 @@
+package sysfs
+
+import (
+ "syscall"
+ _ "unsafe"
+)
+
+// syscall_syscall6 is a private symbol that we link below. We need to use this
+// instead of syscall.Syscall6 because the public syscall.Syscall6 won't work
+// when fn is an address.
+//
+//go:linkname syscall_syscall6 syscall.syscall6
+func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sysfs.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sysfs.go
new file mode 100644
index 000000000..dd0a8882e
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sysfs.go
@@ -0,0 +1,6 @@
+// Package sysfs includes a low-level filesystem interface and utilities needed
+// for WebAssembly host functions (ABI) such as WASI.
+//
+// The name sysfs was chosen because wazero's public API has a "sys" package,
+// which was named after https://github.com/golang/sys.
+package sysfs
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go
new file mode 100644
index 000000000..e3f051008
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go
@@ -0,0 +1,17 @@
+//go:build !windows && !plan9 && !tinygo
+
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func unlink(name string) (errno sys.Errno) {
+ err := syscall.Unlink(name)
+ if errno = sys.UnwrapOSError(err); errno == sys.EPERM {
+ errno = sys.EISDIR
+ }
+ return errno
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go
new file mode 100644
index 000000000..16ed06ab2
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go
@@ -0,0 +1,12 @@
+package sysfs
+
+import (
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func unlink(name string) sys.Errno {
+ err := syscall.Remove(name)
+ return sys.UnwrapOSError(err)
+}
diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go
new file mode 100644
index 000000000..be31c7b91
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go
@@ -0,0 +1,25 @@
+package sysfs
+
+import (
+ "os"
+ "syscall"
+
+ "github.com/tetratelabs/wazero/experimental/sys"
+)
+
+func unlink(name string) sys.Errno {
+ err := syscall.Unlink(name)
+ if err == nil {
+ return 0
+ }
+ errno := sys.UnwrapOSError(err)
+ if errno == sys.EBADF {
+ lstat, errLstat := os.Lstat(name)
+ if errLstat == nil && lstat.Mode()&os.ModeSymlink != 0 {
+ errno = sys.UnwrapOSError(os.Remove(name))
+ } else {
+ errno = sys.EISDIR
+ }
+ }
+ return errno
+}