From 2f8dd23097e7c5bcaf511eb8ea84fbf2d3acd20c Mon Sep 17 00:00:00 2001 From: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com> Date: Mon, 17 Jul 2023 08:13:29 +0800 Subject: [PATCH] adds experimental sys.Errno to begin decoupling from the syscall package (#1582) Signed-off-by: Adrian Cole --- cmd/wazero/wazero_test.go | 2 +- experimental/sys/errno.go | 95 +++++ .../platform => experimental/sys}/error.go | 27 +- .../sys}/error_test.go | 46 +-- experimental/sys/syscall_errno.go | 98 ++++++ experimental/sys/syscall_errno_notwindows.go | 16 + experimental/sys/syscall_errno_windows.go | 57 +++ imports/wasi_snapshot_preview1/args.go | 14 +- imports/wasi_snapshot_preview1/clock.go | 26 +- imports/wasi_snapshot_preview1/environ.go | 14 +- imports/wasi_snapshot_preview1/fs.go | 333 +++++++++--------- imports/wasi_snapshot_preview1/fs_test.go | 3 +- imports/wasi_snapshot_preview1/poll.go | 38 +- imports/wasi_snapshot_preview1/poll_test.go | 8 +- imports/wasi_snapshot_preview1/random.go | 12 +- imports/wasi_snapshot_preview1/sched.go | 4 +- imports/wasi_snapshot_preview1/sock.go | 34 +- .../wasi_snapshot_preview1/sock_unit_test.go | 8 +- imports/wasi_snapshot_preview1/wasi.go | 10 +- .../wasi_snapshot_preview1/wasi_bench_test.go | 4 +- .../wasi_stdlib_test.go | 2 +- internal/fsapi/dir.go | 36 +- internal/fsapi/file.go | 153 ++++---- internal/fsapi/fs.go | 159 +++++---- internal/fsapi/unimplemented.go | 123 +++---- internal/gojs/errno.go | 47 ++- internal/gojs/errno_test.go | 79 +++-- internal/gojs/fs.go | 47 +-- internal/gojs/process.go | 4 +- internal/gojs/runtime.go | 6 +- internal/gojs/testdata/writefs/stat.go | 4 +- internal/platform/errno.go | 9 - internal/platform/errno_windows.go | 72 ---- internal/sock/sock.go | 10 +- internal/sys/fs.go | 52 +-- internal/sys/fs_test.go | 18 +- internal/sys/lazy.go | 47 +-- internal/sys/stdio.go | 22 +- internal/sysfs/adapter.go | 7 +- internal/sysfs/adapter_test.go | 15 +- internal/sysfs/bench_test.go | 4 +- internal/sysfs/datasync_linux.go | 6 +- internal/sysfs/datasync_unsupported.go | 5 +- internal/sysfs/dir.go | 15 +- internal/sysfs/dir_test.go | 5 +- internal/sysfs/dirfs.go | 41 +-- internal/sysfs/dirfs_test.go | 61 ++-- internal/sysfs/file.go | 122 +++---- internal/sysfs/file_test.go | 85 ++--- internal/sysfs/file_unix.go | 6 +- internal/sysfs/file_windows.go | 19 +- internal/sysfs/futimens.go | 20 +- internal/sysfs/futimens_test.go | 9 +- internal/sysfs/futimens_unsupported.go | 8 +- internal/sysfs/futimens_windows.go | 3 +- internal/sysfs/open_file.go | 11 +- internal/sysfs/open_file_js.go | 7 +- internal/sysfs/open_file_sun.go | 7 +- internal/sysfs/open_file_test.go | 19 +- internal/sysfs/open_file_windows.go | 19 +- internal/sysfs/osfile.go | 65 ++-- internal/sysfs/readfs.go | 93 ++--- internal/sysfs/readfs_test.go | 14 +- internal/sysfs/rename.go | 6 +- internal/sysfs/rename_test.go | 20 +- internal/sysfs/rename_windows.go | 18 +- internal/sysfs/select_test.go | 10 +- internal/sysfs/select_unsupported.go | 4 +- internal/sysfs/select_windows.go | 9 +- internal/sysfs/sock.go | 6 +- internal/sysfs/sock_test.go | 10 +- internal/sysfs/sock_unix.go | 46 +-- internal/sysfs/sock_unsupported.go | 6 +- internal/sysfs/sock_windows.go | 46 +-- internal/sysfs/stat.go | 7 +- internal/sysfs/stat_bsd.go | 14 +- internal/sysfs/stat_linux.go | 14 +- internal/sysfs/stat_test.go | 11 +- internal/sysfs/stat_unsupported.go | 14 +- internal/sysfs/stat_windows.go | 31 +- internal/sysfs/sync.go | 7 +- internal/sysfs/sync_windows.go | 9 +- internal/sysfs/sysfs_test.go | 18 +- internal/sysfs/unlink.go | 8 +- internal/sysfs/unlink_test.go | 6 +- internal/sysfs/unlink_windows.go | 12 +- internal/testing/require/require.go | 13 +- .../testing/require/require_errno_test.go | 21 +- internal/wasip1/errno.go | 43 +-- internal/wasip1/errno_test.go | 86 ++--- internal/wasm/module_instance_test.go | 10 +- sys/error_test.go | 17 +- sys/stat_export_test.go | 3 + sys/stat_test.go | 19 +- 94 files changed, 1583 insertions(+), 1366 deletions(-) create mode 100644 experimental/sys/errno.go rename {internal/platform => experimental/sys}/error.go (54%) rename {internal/platform => experimental/sys}/error_test.go (61%) create mode 100644 experimental/sys/syscall_errno.go create mode 100644 experimental/sys/syscall_errno_notwindows.go create mode 100644 experimental/sys/syscall_errno_windows.go delete mode 100644 internal/platform/errno.go delete mode 100644 internal/platform/errno_windows.go create mode 100644 sys/stat_export_test.go diff --git a/cmd/wazero/wazero_test.go b/cmd/wazero/wazero_test.go index f75dd50c8a..c57799dec8 100644 --- a/cmd/wazero/wazero_test.go +++ b/cmd/wazero/wazero_test.go @@ -540,7 +540,7 @@ Consider switching to GOOS=wasip1. ==> go.runtime.getRandomData(r_len=8) <== ==> go.syscall/js.valueCall(fs.open(path=/bear.txt,flags=,perm=----------)) -<== (err=function not implemented,fd=0) +<== (err=functionality not supported,fd=0) `, // Test only shows logging happens in two scopes; it is ok to fail. expectedExitCode: 1, } diff --git a/experimental/sys/errno.go b/experimental/sys/errno.go new file mode 100644 index 0000000000..a07c21c29d --- /dev/null +++ b/experimental/sys/errno.go @@ -0,0 +1,95 @@ +package sys + +import "strconv" + +// Errno is a subset of POSIX errno used by wazero interfaces. Zero is not an +// error. Other values should not be interpreted numerically, rather by constants +// prefixed with 'E'. +// +// See https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html +type Errno uint16 + +// ^-- Note: This will eventually move to the public /sys package. It is +// experimental until we audit the socket related APIs to ensure we have all +// the Errno it returns, and we export fs.FS. This is not in /internal/sys as +// that would introduce a package cycle. + +// This is a subset of errors to reduce implementation burden. `wasip` defines +// almost all POSIX error numbers, but not all are used in practice. wazero +// will add ones needed in POSIX order, as needed by functions that explicitly +// document returning them. +// +// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-errno-enumu16 +const ( + EACCES Errno = iota + 1 + EAGAIN + EBADF + EEXIST + EFAULT + EINTR + EINVAL + EIO + EISDIR + ELOOP + ENAMETOOLONG + ENOENT + ENOSYS + ENOTDIR + ENOTEMPTY + ENOTSOCK + ENOTSUP + EPERM + EROFS + + // NOTE ENOTCAPABLE is defined in wasip1, but not in POSIX. wasi-libc + // converts it to EBADF, ESPIPE or EINVAL depending on the call site. + // It isn't known if compilers who don't use ENOTCAPABLE would crash on it. +) + +// Error implements error +func (e Errno) Error() string { + switch e { + case 0: // not an error + return "success" + case EACCES: + return "permission denied" + case EAGAIN: + return "resource unavailable, try again" + case EBADF: + return "bad file descriptor" + case EEXIST: + return "file exists" + case EFAULT: + return "bad address" + case EINTR: + return "interrupted function" + case EINVAL: + return "invalid argument" + case EIO: + return "input/output error" + case EISDIR: + return "is a directory" + case ELOOP: + return "too many levels of symbolic links" + case ENAMETOOLONG: + return "filename too long" + case ENOENT: + return "no such file or directory" + case ENOSYS: + return "functionality not supported" + case ENOTDIR: + return "not a directory or a symbolic link to a directory" + case ENOTEMPTY: + return "directory not empty" + case ENOTSOCK: + return "not a socket" + case ENOTSUP: + return "not supported (may be the same value as [EOPNOTSUPP])" + case EPERM: + return "operation not permitted" + case EROFS: + return "read-only file system" + default: + return "Errno(" + strconv.Itoa(int(e)) + ")" + } +} diff --git a/internal/platform/error.go b/experimental/sys/error.go similarity index 54% rename from internal/platform/error.go rename to experimental/sys/error.go index 3219e5265e..a0c76019aa 100644 --- a/internal/platform/error.go +++ b/experimental/sys/error.go @@ -1,39 +1,32 @@ -package platform +package sys import ( "io" "io/fs" "os" - "syscall" ) -// UnwrapOSError returns a syscall.Errno or zero if the input is nil. -func UnwrapOSError(err error) syscall.Errno { +// UnwrapOSError returns an Errno or zero if the input is nil. +func UnwrapOSError(err error) Errno { if err == nil { return 0 } err = underlyingError(err) - if se, ok := err.(syscall.Errno); ok { - return adjustErrno(se) - } - // Below are all the fs.ErrXXX in fs.go. - // - // Note: Once we have our own file type, we should never see these. switch err { case nil, io.EOF: - return 0 // EOF is not a syscall.Errno + return 0 // EOF is not a Errno case fs.ErrInvalid: - return syscall.EINVAL + return EINVAL case fs.ErrPermission: - return syscall.EPERM + return EPERM case fs.ErrExist: - return syscall.EEXIST + return EEXIST case fs.ErrNotExist: - return syscall.ENOENT + return ENOENT case fs.ErrClosed: - return syscall.EBADF + return EBADF } - return syscall.EIO + return errorToErrno(err) } // underlyingError returns the underlying error if a well-known OS error type. diff --git a/internal/platform/error_test.go b/experimental/sys/error_test.go similarity index 61% rename from internal/platform/error_test.go rename to experimental/sys/error_test.go index 81a071fbf7..2007f004ec 100644 --- a/internal/platform/error_test.go +++ b/experimental/sys/error_test.go @@ -1,4 +1,4 @@ -package platform +package sys import ( "errors" @@ -6,17 +6,14 @@ import ( "io" "io/fs" "os" - "syscall" "testing" - - "github.com/tetratelabs/wazero/internal/testing/require" ) func TestUnwrapOSError(t *testing.T) { tests := []struct { name string input error - expected syscall.Errno + expected Errno }{ { name: "io.EOF is not an error", @@ -26,64 +23,69 @@ func TestUnwrapOSError(t *testing.T) { { name: "LinkError ErrInvalid", input: &os.LinkError{Err: fs.ErrInvalid}, - expected: syscall.EINVAL, + expected: EINVAL, }, { name: "PathError ErrInvalid", input: &os.PathError{Err: fs.ErrInvalid}, - expected: syscall.EINVAL, + expected: EINVAL, }, { name: "SyscallError ErrInvalid", input: &os.SyscallError{Err: fs.ErrInvalid}, - expected: syscall.EINVAL, + expected: EINVAL, }, { name: "PathError ErrPermission", input: &os.PathError{Err: os.ErrPermission}, - expected: syscall.EPERM, + expected: EPERM, }, { name: "PathError ErrExist", input: &os.PathError{Err: os.ErrExist}, - expected: syscall.EEXIST, + expected: EEXIST, }, { - name: "PathError syscall.ErrnotExist", + name: "PathError ErrnotExist", input: &os.PathError{Err: os.ErrNotExist}, - expected: syscall.ENOENT, + expected: ENOENT, }, { name: "PathError ErrClosed", input: &os.PathError{Err: os.ErrClosed}, - expected: syscall.EBADF, + expected: EBADF, }, { - name: "PathError unknown == syscall.EIO", + name: "PathError unknown == EIO", input: &os.PathError{Err: errors.New("ice cream")}, - expected: syscall.EIO, + expected: EIO, }, { - name: "unknown == syscall.EIO", + name: "unknown == EIO", input: errors.New("ice cream"), - expected: syscall.EIO, + expected: EIO, }, { - name: "very wrapped unknown == syscall.EIO", + name: "very wrapped unknown == EIO", input: fmt.Errorf("%w", fmt.Errorf("%w", fmt.Errorf("%w", errors.New("ice cream")))), - expected: syscall.EIO, + expected: EIO, }, } for _, tt := range tests { tc := tt t.Run(tc.name, func(t *testing.T) { - errno := UnwrapOSError(tc.input) - require.EqualErrno(t, tc.expected, errno) + // don't use require package as that introduces a package cycle + if want, have := tc.expected, UnwrapOSError(tc.input); have != want { + t.Fatalf("unexpected errno: %v != %v", have, want) + } }) } t.Run("nil -> zero", func(t *testing.T) { - require.Zero(t, UnwrapOSError(nil)) + // don't use require package as that introduces a package cycle + if want, have := Errno(0), UnwrapOSError(nil); have != want { + t.Fatalf("unexpected errno: %v != %v", have, want) + } }) } diff --git a/experimental/sys/syscall_errno.go b/experimental/sys/syscall_errno.go new file mode 100644 index 0000000000..aa6eb23d80 --- /dev/null +++ b/experimental/sys/syscall_errno.go @@ -0,0 +1,98 @@ +package sys + +import "syscall" + +func syscallToErrno(errno syscall.Errno) Errno { + switch errno { + case 0: + return 0 + case syscall.EACCES: + return EACCES + case syscall.EAGAIN: + return EAGAIN + case syscall.EBADF: + return EBADF + case syscall.EEXIST: + return EEXIST + case syscall.EFAULT: + return EFAULT + case syscall.EINTR: + return EINTR + case syscall.EINVAL: + return EINVAL + case syscall.EIO: + return EIO + case syscall.EISDIR: + return EISDIR + case syscall.ELOOP: + return ELOOP + case syscall.ENAMETOOLONG: + return ENAMETOOLONG + case syscall.ENOENT: + return ENOENT + case syscall.ENOSYS: + return ENOSYS + case syscall.ENOTDIR: + return ENOTDIR + case syscall.ENOTEMPTY: + return ENOTEMPTY + case syscall.ENOTSOCK: + return ENOTSOCK + case syscall.ENOTSUP: + return ENOTSUP + case syscall.EPERM: + return EPERM + case syscall.EROFS: + return EROFS + default: + return EIO + } +} + +// Unwrap is a convenience for runtime.GOOS which define syscall.Errno. +func (e Errno) Unwrap() error { + switch e { + case 0: + return nil + case EACCES: + return syscall.EACCES + case EAGAIN: + return syscall.EAGAIN + case EBADF: + return syscall.EBADF + case EEXIST: + return syscall.EEXIST + case EFAULT: + return syscall.EFAULT + case EINTR: + return syscall.EINTR + case EINVAL: + return syscall.EINVAL + case EIO: + return syscall.EIO + case EISDIR: + return syscall.EISDIR + case ELOOP: + return syscall.ELOOP + case ENAMETOOLONG: + return syscall.ENAMETOOLONG + case ENOENT: + return syscall.ENOENT + case ENOSYS: + return syscall.ENOSYS + case ENOTDIR: + return syscall.ENOTDIR + case ENOTEMPTY: + return syscall.ENOTEMPTY + case ENOTSOCK: + return syscall.ENOTSOCK + case ENOTSUP: + return syscall.ENOTSUP + case EPERM: + return syscall.EPERM + case EROFS: + return syscall.EROFS + default: + return syscall.EIO + } +} diff --git a/experimental/sys/syscall_errno_notwindows.go b/experimental/sys/syscall_errno_notwindows.go new file mode 100644 index 0000000000..56c8441d95 --- /dev/null +++ b/experimental/sys/syscall_errno_notwindows.go @@ -0,0 +1,16 @@ +//go:build !windows + +package sys + +import "syscall" + +func errorToErrno(err error) Errno { + switch err := err.(type) { + case Errno: + return err + case syscall.Errno: + return syscallToErrno(err) + default: + return EIO + } +} diff --git a/experimental/sys/syscall_errno_windows.go b/experimental/sys/syscall_errno_windows.go new file mode 100644 index 0000000000..522bd2cac3 --- /dev/null +++ b/experimental/sys/syscall_errno_windows.go @@ -0,0 +1,57 @@ +package sys + +import "syscall" + +// These are errors not defined in the syscall package. They are prefixed with +// underscore to avoid exporting them. +// +// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- +const ( + // _ERROR_INVALID_HANDLE is a Windows error returned by syscall.Write + // instead of syscall.EBADF + _ERROR_INVALID_HANDLE = syscall.Errno(6) + + // _ERROR_INVALID_NAME is a Windows error returned by open when a file + // path has a trailing slash + _ERROR_INVALID_NAME = syscall.Errno(0x7B) + + // _ERROR_NEGATIVE_SEEK is a Windows error returned by os.Truncate + // instead of syscall.EINVAL + _ERROR_NEGATIVE_SEEK = syscall.Errno(0x83) + + // _ERROR_DIRECTORY is a Windows error returned by syscall.Rmdir + // instead of syscall.ENOTDIR + _ERROR_DIRECTORY = syscall.Errno(0x10B) +) + +func errorToErrno(err error) Errno { + switch err := err.(type) { + case Errno: + return err + case syscall.Errno: + // Note: In windows, _ERROR_PATH_NOT_FOUND(0x3) maps to syscall.ENOTDIR + switch err { + case syscall.ERROR_ALREADY_EXISTS: + return EEXIST + case _ERROR_DIRECTORY: + return ENOTDIR + case syscall.ERROR_DIR_NOT_EMPTY: + return ENOTEMPTY + case syscall.ERROR_FILE_EXISTS: + return EEXIST + case _ERROR_INVALID_HANDLE: + return EBADF + case syscall.ERROR_ACCESS_DENIED: + // POSIX read and write functions expect EBADF, not EACCES when not + // open for reading or writing. + return EBADF + case syscall.ERROR_PRIVILEGE_NOT_HELD: + return EPERM + case _ERROR_NEGATIVE_SEEK, _ERROR_INVALID_NAME: + return EINVAL + } + return syscallToErrno(err) + default: + return EIO + } +} diff --git a/imports/wasi_snapshot_preview1/args.go b/imports/wasi_snapshot_preview1/args.go index 71b2c41181..4c82e95e24 100644 --- a/imports/wasi_snapshot_preview1/args.go +++ b/imports/wasi_snapshot_preview1/args.go @@ -2,9 +2,9 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -23,7 +23,7 @@ import ( // Result (Errno) // // The return value is ErrnoSuccess except the following error conditions: -// - syscall.EFAULT: there is not enough memory to write results +// - sys.EFAULT: there is not enough memory to write results // // For example, if argsSizesGet wrote argc=2 and argvLen=5 for arguments: // "a" and "bc" parameters argv=7 and argvBuf=1, this function writes the below @@ -43,7 +43,7 @@ import ( // See https://en.wikipedia.org/wiki/Null-terminated_string var argsGet = newHostFunc(wasip1.ArgsGetName, argsGetFn, []api.ValueType{i32, i32}, "argv", "argv_buf") -func argsGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func argsGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { sysCtx := mod.(*wasm.ModuleInstance).Sys argv, argvBuf := uint32(params[0]), uint32(params[1]) return writeOffsetsAndNullTerminatedValues(mod.Memory(), sysCtx.Args(), argv, argvBuf, sysCtx.ArgsSize()) @@ -61,7 +61,7 @@ func argsGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno // Result (Errno) // // The return value is ErrnoSuccess except the following error conditions: -// - syscall.EFAULT: there is not enough memory to write results +// - sys.EFAULT: there is not enough memory to write results // // For example, if args are "a", "bc" and parameters resultArgc=1 and // resultArgvLen=6, this function writes the below to api.Memory: @@ -80,7 +80,7 @@ func argsGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno // See https://en.wikipedia.org/wiki/Null-terminated_string var argsSizesGet = newHostFunc(wasip1.ArgsSizesGetName, argsSizesGetFn, []api.ValueType{i32, i32}, "result.argc", "result.argv_len") -func argsSizesGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func argsSizesGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { sysCtx := mod.(*wasm.ModuleInstance).Sys mem := mod.Memory() resultArgc, resultArgvLen := uint32(params[0]), uint32(params[1]) @@ -88,10 +88,10 @@ func argsSizesGetFn(_ context.Context, mod api.Module, params []uint64) syscall. // argc and argv_len offsets are not necessarily sequential, so we have to // write them independently. if !mem.WriteUint32Le(resultArgc, uint32(len(sysCtx.Args()))) { - return syscall.EFAULT + return sys.EFAULT } if !mem.WriteUint32Le(resultArgvLen, sysCtx.ArgsSize()) { - return syscall.EFAULT + return sys.EFAULT } return 0 } diff --git a/imports/wasi_snapshot_preview1/clock.go b/imports/wasi_snapshot_preview1/clock.go index 6e732a06c7..31af910718 100644 --- a/imports/wasi_snapshot_preview1/clock.go +++ b/imports/wasi_snapshot_preview1/clock.go @@ -2,9 +2,9 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -21,9 +21,9 @@ import ( // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.ENOTSUP: the clock ID is not supported. -// - syscall.EINVAL: the clock ID is invalid. -// - syscall.EFAULT: there is not enough memory to write results +// - sys.ENOTSUP: the clock ID is not supported. +// - sys.EINVAL: the clock ID is invalid. +// - sys.EFAULT: there is not enough memory to write results // // For example, if the resolution is 100ns, this function writes the below to // api.Memory: @@ -39,7 +39,7 @@ import ( // See https://linux.die.net/man/3/clock_getres var clockResGet = newHostFunc(wasip1.ClockResGetName, clockResGetFn, []api.ValueType{i32, i32}, "id", "result.resolution") -func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func clockResGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { sysCtx := mod.(*wasm.ModuleInstance).Sys id, resultResolution := uint32(params[0]), uint32(params[1]) @@ -50,11 +50,11 @@ func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E case wasip1.ClockIDMonotonic: resolution = uint64(sysCtx.NanotimeResolution()) default: - return syscall.EINVAL + return sys.EINVAL } if !mod.Memory().WriteUint64Le(resultResolution, resolution) { - return syscall.EFAULT + return sys.EFAULT } return 0 } @@ -73,9 +73,9 @@ func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.ENOTSUP: the clock ID is not supported. -// - syscall.EINVAL: the clock ID is invalid. -// - syscall.EFAULT: there is not enough memory to write results +// - sys.ENOTSUP: the clock ID is not supported. +// - sys.EINVAL: the clock ID is invalid. +// - sys.EFAULT: there is not enough memory to write results // // For example, if time.Now returned exactly midnight UTC 2022-01-01 // (1640995200000000000), and parameters resultTimestamp=1, this function @@ -92,7 +92,7 @@ func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E // See https://linux.die.net/man/3/clock_gettime var clockTimeGet = newHostFunc(wasip1.ClockTimeGetName, clockTimeGetFn, []api.ValueType{i32, i64, i32}, "id", "precision", "result.timestamp") -func clockTimeGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func clockTimeGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { sysCtx := mod.(*wasm.ModuleInstance).Sys id := uint32(params[0]) // TODO: precision is currently ignored. @@ -106,11 +106,11 @@ func clockTimeGetFn(_ context.Context, mod api.Module, params []uint64) syscall. case wasip1.ClockIDMonotonic: val = sysCtx.Nanotime() default: - return syscall.EINVAL + return sys.EINVAL } if !mod.Memory().WriteUint64Le(resultTimestamp, uint64(val)) { - return syscall.EFAULT + return sys.EFAULT } return 0 } diff --git a/imports/wasi_snapshot_preview1/environ.go b/imports/wasi_snapshot_preview1/environ.go index 995f82f2a8..ec8df708a8 100644 --- a/imports/wasi_snapshot_preview1/environ.go +++ b/imports/wasi_snapshot_preview1/environ.go @@ -2,9 +2,9 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -24,7 +24,7 @@ import ( // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EFAULT: there is not enough memory to write results +// - sys.EFAULT: there is not enough memory to write results // // For example, if environSizesGet wrote environc=2 and environLen=9 for // environment variables: "a=b", "b=cd" and parameters environ=11 and @@ -43,7 +43,7 @@ import ( // See https://en.wikipedia.org/wiki/Null-terminated_string var environGet = newHostFunc(wasip1.EnvironGetName, environGetFn, []api.ValueType{i32, i32}, "environ", "environ_buf") -func environGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func environGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { sysCtx := mod.(*wasm.ModuleInstance).Sys environ, environBuf := uint32(params[0]), uint32(params[1]) @@ -63,7 +63,7 @@ func environGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EFAULT: there is not enough memory to write results +// - sys.EFAULT: there is not enough memory to write results // // For example, if environ are "a=b","b=cd" and parameters resultEnvironc=1 and // resultEnvironvLen=6, this function writes the below to api.Memory: @@ -83,7 +83,7 @@ func environGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // and https://en.wikipedia.org/wiki/Null-terminated_string var environSizesGet = newHostFunc(wasip1.EnvironSizesGetName, environSizesGetFn, []api.ValueType{i32, i32}, "result.environc", "result.environv_len") -func environSizesGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func environSizesGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { sysCtx := mod.(*wasm.ModuleInstance).Sys mem := mod.Memory() resultEnvironc, resultEnvironvLen := uint32(params[0]), uint32(params[1]) @@ -91,10 +91,10 @@ func environSizesGetFn(_ context.Context, mod api.Module, params []uint64) sysca // environc and environv_len offsets are not necessarily sequential, so we // have to write them independently. if !mem.WriteUint32Le(resultEnvironc, uint32(len(sysCtx.Environ()))) { - return syscall.EFAULT + return sys.EFAULT } if !mem.WriteUint32Le(resultEnvironvLen, sysCtx.EnvironSize()) { - return syscall.EFAULT + return sys.EFAULT } return 0 } diff --git a/imports/wasi_snapshot_preview1/fs.go b/imports/wasi_snapshot_preview1/fs.go index 55a9206805..bec2f037c0 100644 --- a/imports/wasi_snapshot_preview1/fs.go +++ b/imports/wasi_snapshot_preview1/fs.go @@ -11,6 +11,7 @@ import ( "unsafe" "github.com/tetratelabs/wazero/api" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" socketapi "github.com/tetratelabs/wazero/internal/sock" "github.com/tetratelabs/wazero/internal/sys" @@ -30,7 +31,7 @@ var fdAdvise = newHostFunc( "fd", "offset", "len", "advice", ) -func fdAdviseFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdAdviseFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fd := int32(params[0]) _ = params[1] _ = params[2] @@ -39,7 +40,7 @@ func fdAdviseFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn _, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return experimentalsys.EBADF } switch advice { @@ -50,7 +51,7 @@ func fdAdviseFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn wasip1.FdAdviceDontNeed, wasip1.FdAdviceNoReuse: default: - return syscall.EINVAL + return experimentalsys.EINVAL } // FdAdvice corresponds to posix_fadvise, but it can only be supported on linux. @@ -73,7 +74,7 @@ var fdAllocate = newHostFunc( "fd", "offset", "len", ) -func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fd := int32(params[0]) offset := params[1] length := params[2] @@ -81,12 +82,12 @@ func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) syscall.Er fsc := mod.(*wasm.ModuleInstance).Sys.FS() f, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return experimentalsys.EBADF } tail := int64(offset + length) if tail < 0 { - return syscall.EINVAL + return experimentalsys.EINVAL } st, errno := f.File.Stat() @@ -111,15 +112,15 @@ func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: the fd was not open. -// - syscall.ENOTSUP: the fs was a pre-open +// - sys.EBADF: the fd was not open. +// - sys.ENOTSUP: the fs was a pre-open // // Note: This is similar to `close` in POSIX. // See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_close // and https://linux.die.net/man/3/close var fdClose = newHostFunc(wasip1.FdCloseName, fdCloseFn, []api.ValueType{i32}, "fd") -func fdCloseFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdCloseFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) @@ -132,13 +133,13 @@ func fdCloseFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_datasyncfd-fd---errno var fdDatasync = newHostFunc(wasip1.FdDatasyncName, fdDatasyncFn, []api.ValueType{i32}, "fd") -func fdDatasyncFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdDatasyncFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) // Check to see if the file descriptor is available if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else { return f.File.Datasync() } @@ -155,8 +156,8 @@ func fdDatasyncFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.EFAULT: `resultFdstat` points to an offset out of memory +// - sys.EBADF: `fd` is invalid +// - sys.EFAULT: `resultFdstat` points to an offset out of memory // // fdstat byte layout is 24-byte size, with the following fields: // - fs_filetype 1 byte: the file type @@ -185,7 +186,7 @@ var fdFdstatGet = newHostFunc(wasip1.FdFdstatGetName, fdFdstatGetFn, []api.Value // fdFdstatGetFn cannot currently use proxyResultParams because fdstat is larger // than api.ValueTypeI64 (i64 == 8 bytes, but fdstat is 24). -func fdFdstatGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdFdstatGetFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd, resultFdstat := int32(params[0]), uint32(params[1]) @@ -193,15 +194,15 @@ func fdFdstatGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E // Ensure we can write the fdstat buf, ok := mod.Memory().Read(resultFdstat, 24) if !ok { - return syscall.EFAULT + return experimentalsys.EFAULT } var fdflags uint16 var st sysapi.Stat_t - var errno syscall.Errno + var errno experimentalsys.Errno f, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return experimentalsys.EBADF } else if st, errno = f.File.Stat(); errno != 0 { return errno } else if f.File.IsAppend() { @@ -300,17 +301,17 @@ func writeFdstat(buf []byte, fileType uint8, fdflags uint16, fsRightsBase, fsRig // adjusts the flags associated with a file descriptor. var fdFdstatSetFlags = newHostFunc(wasip1.FdFdstatSetFlagsName, fdFdstatSetFlagsFn, []wasm.ValueType{i32, i32}, "fd", "flags") -func fdFdstatSetFlagsFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdFdstatSetFlagsFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fd, wasiFlag := int32(params[0]), uint16(params[1]) fsc := mod.(*wasm.ModuleInstance).Sys.FS() // Currently we only support APPEND and NONBLOCK. if wasip1.FD_DSYNC&wasiFlag != 0 || wasip1.FD_RSYNC&wasiFlag != 0 || wasip1.FD_SYNC&wasiFlag != 0 { - return syscall.EINVAL + return experimentalsys.EINVAL } if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else { nonblock := wasip1.FD_NONBLOCK&wasiFlag != 0 errno := f.File.SetNonblock(nonblock) @@ -347,9 +348,9 @@ var fdFdstatSetRights = stubFunction( // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.EIO: could not stat `fd` on filesystem -// - syscall.EFAULT: `resultFilestat` points to an offset out of memory +// - sys.EBADF: `fd` is invalid +// - sys.EIO: could not stat `fd` on filesystem +// - sys.EFAULT: `resultFilestat` points to an offset out of memory // // filestat byte layout is 64-byte size, with the following fields: // - dev 8 bytes: the device ID of device containing the file @@ -387,22 +388,22 @@ var fdFilestatGet = newHostFunc(wasip1.FdFilestatGetName, fdFilestatGetFn, []api // fdFilestatGetFn cannot currently use proxyResultParams because filestat is // larger than api.ValueTypeI64 (i64 == 8 bytes, but filestat is 64). -func fdFilestatGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdFilestatGetFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { return fdFilestatGetFunc(mod, int32(params[0]), uint32(params[1])) } -func fdFilestatGetFunc(mod api.Module, fd int32, resultBuf uint32) syscall.Errno { +func fdFilestatGetFunc(mod api.Module, fd int32, resultBuf uint32) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() // Ensure we can write the filestat buf, ok := mod.Memory().Read(resultBuf, 64) if !ok { - return syscall.EFAULT + return experimentalsys.EFAULT } f, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return experimentalsys.EBADF } st, errno := f.File.Stat() @@ -446,7 +447,7 @@ func getWasiFiletype(fm fs.FileMode) uint8 { } } -func writeFilestat(buf []byte, st *sysapi.Stat_t, ftype uint8) (errno syscall.Errno) { +func writeFilestat(buf []byte, st *sysapi.Stat_t, ftype uint8) (errno experimentalsys.Errno) { le.PutUint64(buf, st.Dev) le.PutUint64(buf[8:], st.Ino) le.PutUint64(buf[16:], uint64(ftype)) @@ -464,7 +465,7 @@ func writeFilestat(buf []byte, st *sysapi.Stat_t, ftype uint8) (errno syscall.Er // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_set_sizefd-fd-size-filesize---errno var fdFilestatSetSize = newHostFunc(wasip1.FdFilestatSetSizeName, fdFilestatSetSizeFn, []wasm.ValueType{i32, i64}, "fd", "size") -func fdFilestatSetSizeFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdFilestatSetSizeFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fd := int32(params[0]) size := uint32(params[1]) @@ -472,7 +473,7 @@ func fdFilestatSetSizeFn(_ context.Context, mod api.Module, params []uint64) sys // Check to see if the file descriptor is available if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else { return f.File.Truncate(int64(size)) } @@ -488,7 +489,7 @@ var fdFilestatSetTimes = newHostFunc( "fd", "atim", "mtim", "fst_flags", ) -func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fd := int32(params[0]) atim := int64(params[1]) mtim := int64(params[2]) @@ -499,7 +500,7 @@ func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) sy f, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return experimentalsys.EBADF } times, errno := toTimes(atim, mtim, fstFlags) @@ -512,19 +513,19 @@ func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) sy // Fall back to path based, despite it being less precise. switch errno { - case syscall.EPERM, syscall.ENOSYS: + case experimentalsys.EPERM, experimentalsys.ENOSYS: errno = f.FS.Utimens(f.Name, ×, true) } return errno } -func toTimes(atim, mtime int64, fstFlags uint16) (times [2]syscall.Timespec, errno syscall.Errno) { +func toTimes(atim, mtime int64, fstFlags uint16) (times [2]syscall.Timespec, errno experimentalsys.Errno) { // times[0] == atim, times[1] == mtim // coerce atim into a timespec if set, now := fstFlags&wasip1.FstflagsAtim != 0, fstFlags&wasip1.FstflagsAtimNow != 0; set && now { - errno = syscall.EINVAL + errno = experimentalsys.EINVAL return } else if set { times[0] = syscall.NsecToTimespec(atim) @@ -536,7 +537,7 @@ func toTimes(atim, mtime int64, fstFlags uint16) (times [2]syscall.Timespec, err // coerce mtim into a timespec if set, now := fstFlags&wasip1.FstflagsMtim != 0, fstFlags&wasip1.FstflagsMtimNow != 0; set && now { - errno = syscall.EINVAL + errno = experimentalsys.EINVAL return } else if set { times[1] = syscall.NsecToTimespec(mtime) @@ -560,7 +561,7 @@ var fdPread = newHostFunc( "fd", "iovs", "iovs_len", "offset", "result.nread", ) -func fdPreadFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdPreadFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { return fdReadOrPread(mod, params, true) } @@ -575,8 +576,8 @@ func fdPreadFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid or the `fd` is not a pre-opened directory -// - syscall.EFAULT: `resultPrestat` points to an offset out of memory +// - sys.EBADF: `fd` is invalid or the `fd` is not a pre-opened directory +// - sys.EFAULT: `resultPrestat` points to an offset out of memory // // prestat byte layout is 8 bytes, beginning with an 8-bit tag and 3 pad bytes. // The only valid tag is `prestat_dir`, which is tag zero. This simplifies the @@ -597,7 +598,7 @@ func fdPreadFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno // https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#prestat var fdPrestatGet = newHostFunc(wasip1.FdPrestatGetName, fdPrestatGetFn, []api.ValueType{i32, i32}, "fd", "result.prestat") -func fdPrestatGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdPrestatGetFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd, resultPrestat := int32(params[0]), uint32(params[1]) @@ -610,7 +611,7 @@ func fdPrestatGetFn(_ context.Context, mod api.Module, params []uint64) syscall. // * Zero-value 8-bit tag, and 3-byte zero-value padding prestat := uint64(len(name) << 32) if !mod.Memory().WriteUint64Le(resultPrestat, prestat) { - return syscall.EFAULT + return experimentalsys.EFAULT } return 0 } @@ -629,9 +630,9 @@ func fdPrestatGetFn(_ context.Context, mod api.Module, params []uint64) syscall. // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.EFAULT: `path` points to an offset out of memory -// - syscall.ENAMETOOLONG: `pathLen` is longer than the actual length of the result +// - sys.EBADF: `fd` is invalid +// - sys.EFAULT: `path` points to an offset out of memory +// - sys.ENAMETOOLONG: `pathLen` is longer than the actual length of the result // // For example, the directory name corresponding with `fd` was "/tmp" and // # Parameters path=1 pathLen=4 (correct), this function will write the below to @@ -651,7 +652,7 @@ var fdPrestatDirName = newHostFunc( "fd", "result.path", "result.path_len", ) -func fdPrestatDirNameFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdPrestatDirNameFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd, path, pathLen := int32(params[0]), uint32(params[1]), uint32(params[2]) @@ -662,11 +663,11 @@ func fdPrestatDirNameFn(_ context.Context, mod api.Module, params []uint64) sysc // Some runtimes may have another semantics. See /RATIONALE.md if uint32(len(name)) < pathLen { - return syscall.ENAMETOOLONG + return experimentalsys.ENAMETOOLONG } if !mod.Memory().Write(path, []byte(name)[:pathLen]) { - return syscall.EFAULT + return experimentalsys.EFAULT } return 0 } @@ -683,7 +684,7 @@ var fdPwrite = newHostFunc( "fd", "iovs", "iovs_len", "offset", "result.nwritten", ) -func fdPwriteFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdPwriteFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { return fdWriteOrPwrite(mod, params, true) } @@ -703,9 +704,9 @@ func fdPwriteFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.EFAULT: `iovs` or `resultNread` point to an offset out of memory -// - syscall.EIO: a file system error +// - sys.EBADF: `fd` is invalid +// - sys.EFAULT: `iovs` or `resultNread` point to an offset out of memory +// - sys.EIO: a file system error // // For example, this function needs to first read `iovs` to determine where // to write contents. If parameters iovs=1 iovsCount=2, this function reads two @@ -749,7 +750,7 @@ type preader struct { } // Read implements the same function as documented on fsapi.File. -func (w *preader) Read(buf []byte) (n int, errno syscall.Errno) { +func (w *preader) Read(buf []byte) (n int, errno experimentalsys.Errno) { if len(buf) == 0 { return 0, 0 // less overhead on zero-length reads. } @@ -759,11 +760,11 @@ func (w *preader) Read(buf []byte) (n int, errno syscall.Errno) { return n, err } -func fdReadFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdReadFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { return fdReadOrPread(mod, params, false) } -func fdReadOrPread(mod api.Module, params []uint64, isPread bool) syscall.Errno { +func fdReadOrPread(mod api.Module, params []uint64, isPread bool) experimentalsys.Errno { mem := mod.Memory() fsc := mod.(*wasm.ModuleInstance).Sys.FS() @@ -772,9 +773,9 @@ func fdReadOrPread(mod api.Module, params []uint64, isPread bool) syscall.Errno iovsCount := uint32(params[2]) var resultNread uint32 - var reader func(buf []byte) (n int, errno syscall.Errno) + var reader func(buf []byte) (n int, errno experimentalsys.Errno) if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else if isPread { offset := int64(params[3]) reader = (&preader{f: f.File, offset: offset}).Read @@ -789,18 +790,18 @@ func fdReadOrPread(mod api.Module, params []uint64, isPread bool) syscall.Errno return errno } if !mem.WriteUint32Le(resultNread, nread) { - return syscall.EFAULT + return experimentalsys.EFAULT } else { return 0 } } -func readv(mem api.Memory, iovs uint32, iovsCount uint32, reader func(buf []byte) (nread int, errno syscall.Errno)) (uint32, syscall.Errno) { +func readv(mem api.Memory, iovs uint32, iovsCount uint32, reader func(buf []byte) (nread int, errno experimentalsys.Errno)) (uint32, experimentalsys.Errno) { var nread uint32 iovsStop := iovsCount << 3 // iovsCount * 8 iovsBuf, ok := mem.Read(iovs, iovsStop) if !ok { - return 0, syscall.EFAULT + return 0, experimentalsys.EFAULT } for iovsPos := uint32(0); iovsPos < iovsStop; iovsPos += 8 { @@ -813,14 +814,14 @@ func readv(mem api.Memory, iovs uint32, iovsCount uint32, reader func(buf []byte b, ok := mem.Read(offset, l) if !ok { - return 0, syscall.EFAULT + return 0, experimentalsys.EFAULT } n, errno := reader(b) nread += uint32(n) - if errno == syscall.ENOSYS { - return 0, syscall.EBADF // e.g. unimplemented for read + if errno == experimentalsys.ENOSYS { + return 0, experimentalsys.EBADF // e.g. unimplemented for read } else if errno != 0 { return 0, errno } else if n < int(l) { @@ -839,11 +840,11 @@ func readv(mem api.Memory, iovs uint32, iovsCount uint32, reader func(buf []byte // # Result (Errno) // // The return value is 0 except the following known error conditions: -// - syscall.ENOSYS: the implementation does not support this function. -// - syscall.EBADF: the file was closed or not a directory. -// - syscall.EFAULT: `buf` or `buf_len` point to an offset out of memory. -// - syscall.ENOENT: `cookie` was invalid. -// - syscall.EINVAL: `buf_len` was not large enough to write a dirent header. +// - sys.ENOSYS: the implementation does not support this function. +// - sys.EBADF: the file was closed or not a directory. +// - sys.EFAULT: `buf` or `buf_len` point to an offset out of memory. +// - sys.ENOENT: `cookie` was invalid. +// - sys.EINVAL: `buf_len` was not large enough to write a dirent header. // // # End of Directory (EOF) // @@ -856,7 +857,7 @@ var fdReaddir = newHostFunc( "fd", "buf", "buf_len", "cookie", "result.bufused", ) -func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { mem := mod.Memory() fsc := mod.(*wasm.ModuleInstance).Sys.FS() @@ -870,7 +871,7 @@ func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Err if bufLen < wasip1.DirentSize { // This is a bug in the caller, as unless `buf_len` is large enough to // write a dirent, it can't read the `d_namlen` from it. - return syscall.EINVAL + return experimentalsys.EINVAL } // Get or open a dirent cache for this file descriptor. @@ -916,7 +917,7 @@ func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Err buf, ok := mem.Read(buf, bufToWrite) if !ok { - return syscall.EFAULT + return experimentalsys.EFAULT } writeDirents(buf, dirents, d_next, direntCount, truncatedLen) @@ -930,7 +931,7 @@ func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Err } if !mem.WriteUint32Le(resultBufused, bufused) { - return syscall.EFAULT + return experimentalsys.EFAULT } return 0 } @@ -1029,19 +1030,19 @@ func writeDirent(buf []byte, dNext uint64, ino sysapi.Inode, dNamlen uint32, dTy // direntCache lazy opens a sys.DirentCache for this directory or returns an // error. -func direntCache(fsc *sys.FSContext, fd int32) (*sys.DirentCache, syscall.Errno) { +func direntCache(fsc *sys.FSContext, fd int32) (*sys.DirentCache, experimentalsys.Errno) { if f, ok := fsc.LookupFile(fd); !ok { - return nil, syscall.EBADF + return nil, experimentalsys.EBADF } else if dir, errno := f.DirentCache(); errno == 0 { return dir, 0 - } else if errno == syscall.ENOTDIR { - // fd_readdir docs don't indicate whether to return syscall.ENOTDIR or - // syscall.EBADF. It has been noticed that rust will crash on syscall.ENOTDIR, + } else if errno == experimentalsys.ENOTDIR { + // fd_readdir docs don't indicate whether to return sys.ENOTDIR or + // sys.EBADF. It has been noticed that rust will crash on sys.ENOTDIR, // and POSIX C ref seems to not return this, so we don't either. // // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_readdir // and https://en.wikibooks.org/wiki/C_Programming/POSIX_Reference/dirent.h - return nil, syscall.EBADF + return nil, experimentalsys.EBADF } else { return nil, errno } @@ -1053,7 +1054,7 @@ func direntCache(fsc *sys.FSContext, fd int32) (*sys.DirentCache, syscall.Errno) // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_renumberfd-fd-to-fd---errno var fdRenumber = newHostFunc(wasip1.FdRenumberName, fdRenumberFn, []wasm.ValueType{i32, i32}, "fd", "to") -func fdRenumberFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdRenumberFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() from := int32(params[0]) @@ -1083,11 +1084,11 @@ func fdRenumberFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.EFAULT: `resultNewoffset` points to an offset out of memory -// - syscall.EINVAL: `whence` is an invalid value -// - syscall.EIO: a file system error -// - syscall.EISDIR: the file was a directory. +// - sys.EBADF: `fd` is invalid +// - sys.EFAULT: `resultNewoffset` points to an offset out of memory +// - sys.EINVAL: `whence` is an invalid value +// - sys.EIO: a file system error +// - sys.EISDIR: the file was a directory. // // For example, if fd 3 is a file with offset 0, and parameters fd=3, offset=4, // whence=0 (=io.SeekStart), resultNewOffset=1, this function writes the below @@ -1109,7 +1110,7 @@ var fdSeek = newHostFunc( "fd", "offset", "whence", "result.newoffset", ) -func fdSeekFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdSeekFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) offset := params[1] @@ -1117,13 +1118,13 @@ func fdSeekFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno resultNewoffset := uint32(params[3]) if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else if isDir, _ := f.File.IsDir(); isDir { - return syscall.EISDIR // POSIX doesn't forbid seeking a directory, but wasi-testsuite does. + return experimentalsys.EISDIR // POSIX doesn't forbid seeking a directory, but wasi-testsuite does. } else if newOffset, errno := f.File.Seek(int64(offset), int(whence)); errno != 0 { return errno } else if !mod.Memory().WriteUint64Le(resultNewoffset, uint64(newOffset)) { - return syscall.EFAULT + return experimentalsys.EFAULT } return 0 } @@ -1134,13 +1135,13 @@ func fdSeekFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_syncfd-fd---errno var fdSync = newHostFunc(wasip1.FdSyncName, fdSyncFn, []api.ValueType{i32}, "fd") -func fdSyncFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdSyncFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) // Check to see if the file descriptor is available if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else { return f.File.Sync() } @@ -1152,7 +1153,7 @@ func fdSyncFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_tellfd-fd---errno-filesize var fdTell = newHostFunc(wasip1.FdTellName, fdTellFn, []api.ValueType{i32, i32}, "fd", "result.offset") -func fdTellFn(ctx context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdTellFn(ctx context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fd := params[0] offset := uint64(0) whence := uint64(io.SeekCurrent) @@ -1179,9 +1180,9 @@ func fdTellFn(ctx context.Context, mod api.Module, params []uint64) syscall.Errn // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.EFAULT: `iovs` or `resultNwritten` point to an offset out of memory -// - syscall.EIO: a file system error +// - sys.EBADF: `fd` is invalid +// - sys.EFAULT: `iovs` or `resultNwritten` point to an offset out of memory +// - sys.EIO: a file system error // // For example, this function needs to first read `iovs` to determine what to // write to `fd`. If parameters iovs=1 iovsCount=2, this function reads two @@ -1226,7 +1227,7 @@ var fdWrite = newHostFunc( "fd", "iovs", "iovs_len", "result.nwritten", ) -func fdWriteFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdWriteFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { return fdWriteOrPwrite(mod, params, false) } @@ -1237,7 +1238,7 @@ type pwriter struct { } // Write implements the same function as documented on fsapi.File. -func (w *pwriter) Write(buf []byte) (n int, errno syscall.Errno) { +func (w *pwriter) Write(buf []byte) (n int, errno experimentalsys.Errno) { if len(buf) == 0 { return 0, 0 // less overhead on zero-length writes. } @@ -1247,7 +1248,7 @@ func (w *pwriter) Write(buf []byte) (n int, errno syscall.Errno) { return n, err } -func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) syscall.Errno { +func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) experimentalsys.Errno { mem := mod.Memory() fsc := mod.(*wasm.ModuleInstance).Sys.FS() @@ -1256,9 +1257,9 @@ func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) syscall.Err iovsCount := uint32(params[2]) var resultNwritten uint32 - var writer func(buf []byte) (n int, errno syscall.Errno) + var writer func(buf []byte) (n int, errno experimentalsys.Errno) if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else if isPwrite { offset := int64(params[3]) writer = (&pwriter{f: f.File, offset: offset}).Write @@ -1274,17 +1275,17 @@ func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) syscall.Err } if !mod.Memory().WriteUint32Le(resultNwritten, nwritten) { - return syscall.EFAULT + return experimentalsys.EFAULT } return 0 } -func writev(mem api.Memory, iovs uint32, iovsCount uint32, writer func(buf []byte) (n int, errno syscall.Errno)) (uint32, syscall.Errno) { +func writev(mem api.Memory, iovs uint32, iovsCount uint32, writer func(buf []byte) (n int, errno experimentalsys.Errno)) (uint32, experimentalsys.Errno) { var nwritten uint32 iovsStop := iovsCount << 3 // iovsCount * 8 iovsBuf, ok := mem.Read(iovs, iovsStop) if !ok { - return 0, syscall.EFAULT + return 0, experimentalsys.EFAULT } for iovsPos := uint32(0); iovsPos < iovsStop; iovsPos += 8 { @@ -1293,12 +1294,12 @@ func writev(mem api.Memory, iovs uint32, iovsCount uint32, writer func(buf []byt b, ok := mem.Read(offset, l) if !ok { - return 0, syscall.EFAULT + return 0, experimentalsys.EFAULT } n, errno := writer(b) nwritten += uint32(n) - if errno == syscall.ENOSYS { - return 0, syscall.EBADF // e.g. unimplemented for write + if errno == experimentalsys.ENOSYS { + return 0, experimentalsys.EBADF // e.g. unimplemented for write } else if errno != 0 { return 0, errno } @@ -1318,9 +1319,9 @@ func writev(mem api.Memory, iovs uint32, iovsCount uint32, writer func(buf []byt // # Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.ENOENT: `path` does not exist. -// - syscall.ENOTDIR: `path` is a file +// - sys.EBADF: `fd` is invalid +// - sys.ENOENT: `path` does not exist. +// - sys.ENOTDIR: `path` is a file // // # Notes // - This is similar to mkdirat in POSIX. @@ -1333,7 +1334,7 @@ var pathCreateDirectory = newHostFunc( "fd", "path", "path_len", ) -func pathCreateDirectoryFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathCreateDirectoryFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) @@ -1366,13 +1367,13 @@ func pathCreateDirectoryFn(_ context.Context, mod api.Module, params []uint64) s // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.ENOTDIR: `fd` points to a file not a directory -// - syscall.EIO: could not stat `fd` on filesystem -// - syscall.EINVAL: the path contained "../" -// - syscall.ENAMETOOLONG: `path` + `path_len` is out of memory -// - syscall.EFAULT: `resultFilestat` points to an offset out of memory -// - syscall.ENOENT: could not find the path +// - sys.EBADF: `fd` is invalid +// - sys.ENOTDIR: `fd` points to a file not a directory +// - sys.EIO: could not stat `fd` on filesystem +// - sys.EINVAL: the path contained "../" +// - sys.ENAMETOOLONG: `path` + `path_len` is out of memory +// - sys.EFAULT: `resultFilestat` points to an offset out of memory +// - sys.ENOENT: could not find the path // // The rest of this implementation matches that of fdFilestatGet, so is not // repeated here. @@ -1386,7 +1387,7 @@ var pathFilestatGet = newHostFunc( "fd", "flags", "path", "path_len", "result.filestat", ) -func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) @@ -1415,7 +1416,7 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) sysca resultBuf := uint32(params[4]) buf, ok := mod.Memory().Read(resultBuf, 64) if !ok { - return syscall.EFAULT + return experimentalsys.EFAULT } filetype := getWasiFiletype(st.Mode) @@ -1432,7 +1433,7 @@ var pathFilestatSetTimes = newHostFunc( "fd", "flags", "path", "path_len", "atim", "mtim", "fst_flags", ) -func pathFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fd := int32(params[0]) flags := uint16(params[1]) path := uint32(params[2]) @@ -1468,7 +1469,7 @@ var pathLink = newHostFunc( "old_fd", "old_flags", "old_path", "old_path_len", "new_fd", "new_path", "new_path_len", ) -func pathLinkFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathLinkFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { mem := mod.Memory() fsc := mod.(*wasm.ModuleInstance).Sys.FS() @@ -1493,14 +1494,14 @@ func pathLinkFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn } if oldFS != newFS { // TODO: handle link across filesystems - return syscall.ENOSYS + return experimentalsys.ENOSYS } return oldFS.Link(oldName, newName) } // pathOpen is the WASI function named PathOpenName which opens a file or -// directory. This returns syscall.EBADF if the fd is invalid. +// directory. This returns sys.EBADF if the fd is invalid. // // # Parameters // @@ -1520,12 +1521,12 @@ func pathLinkFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.EFAULT: `resultOpenedFD` points to an offset out of memory -// - syscall.ENOENT: `path` does not exist. -// - syscall.EEXIST: `path` exists, while `oFlags` requires that it must not. -// - syscall.ENOTDIR: `path` is not a directory, while `oFlags` requires it. -// - syscall.EIO: a file system error +// - sys.EBADF: `fd` is invalid +// - sys.EFAULT: `resultOpenedFD` points to an offset out of memory +// - sys.ENOENT: `path` does not exist. +// - sys.EEXIST: `path` exists, while `oFlags` requires that it must not. +// - sys.ENOTDIR: `path` is not a directory, while `oFlags` requires it. +// - sys.EIO: a file system error // // For example, this function needs to first read `path` to determine the file // to open. If parameters `path` = 1, `pathLen` = 6, and the path is "wazero", @@ -1558,7 +1559,7 @@ var pathOpen = newHostFunc( "fd", "dirflags", "path", "path_len", "oflags", "fs_rights_base", "fs_rights_inheriting", "fdflags", "result.opened_fd", ) -func pathOpenFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathOpenFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() preopenFD := int32(params[0]) @@ -1588,7 +1589,7 @@ func pathOpenFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn isDir := fileOpenFlags&fsapi.O_DIRECTORY != 0 if isDir && oflags&wasip1.O_CREAT != 0 { - return syscall.EINVAL // use pathCreateDirectory! + return experimentalsys.EINVAL // use pathCreateDirectory! } newFD, errno := fsc.OpenFile(preopen, pathName, fileOpenFlags, 0o600) @@ -1599,19 +1600,19 @@ func pathOpenFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn // Check any flags that require the file to evaluate. if isDir { if f, ok := fsc.LookupFile(newFD); !ok { - return syscall.EBADF // unexpected + return experimentalsys.EBADF // unexpected } else if isDir, errno := f.File.IsDir(); errno != 0 { _ = fsc.CloseFile(newFD) return errno } else if !isDir { _ = fsc.CloseFile(newFD) - return syscall.ENOTDIR + return experimentalsys.ENOTDIR } } if !mod.Memory().WriteUint32Le(resultOpenedFD, uint32(newFD)) { _ = fsc.CloseFile(newFD) - return syscall.EFAULT + return experimentalsys.EFAULT } return 0 } @@ -1632,10 +1633,10 @@ func pathOpenFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn // // See https://github.com/WebAssembly/wasi-libc/blob/659ff414560721b1660a19685110e484a081c3d4/libc-bottom-half/sources/at_fdcwd.c // See https://linux.die.net/man/2/openat -func atPath(fsc *sys.FSContext, mem api.Memory, fd int32, p, pathLen uint32) (fsapi.FS, string, syscall.Errno) { +func atPath(fsc *sys.FSContext, mem api.Memory, fd int32, p, pathLen uint32) (fsapi.FS, string, experimentalsys.Errno) { b, ok := mem.Read(p, pathLen) if !ok { - return nil, "", syscall.EFAULT + return nil, "", experimentalsys.EFAULT } pathName := string(b) @@ -1650,7 +1651,7 @@ func atPath(fsc *sys.FSContext, mem api.Memory, fd int32, p, pathLen uint32) (fs // interesting_paths wants to break on root paths or anything that escapes. // This part is the same as fs.FS.Open() if !fs.ValidPath(pathName) { - return nil, "", syscall.EPERM + return nil, "", experimentalsys.EPERM } // add the trailing slash back @@ -1659,11 +1660,11 @@ func atPath(fsc *sys.FSContext, mem api.Memory, fd int32, p, pathLen uint32) (fs } if f, ok := fsc.LookupFile(fd); !ok { - return nil, "", syscall.EBADF // closed or invalid + return nil, "", experimentalsys.EBADF // closed or invalid } else if isDir, errno := f.File.IsDir(); errno != 0 { return nil, "", errno } else if !isDir { - return nil, "", syscall.ENOTDIR + return nil, "", experimentalsys.ENOTDIR } else if f.IsPreopen { // don't append the pre-open name return f.FS, pathName, 0 } else { @@ -1672,11 +1673,11 @@ func atPath(fsc *sys.FSContext, mem api.Memory, fd int32, p, pathLen uint32) (fs } } -func preopenPath(fsc *sys.FSContext, fd int32) (string, syscall.Errno) { +func preopenPath(fsc *sys.FSContext, fd int32) (string, experimentalsys.Errno) { if f, ok := fsc.LookupFile(fd); !ok { - return "", syscall.EBADF // closed + return "", experimentalsys.EBADF // closed } else if !f.IsPreopen { - return "", syscall.EBADF + return "", experimentalsys.EBADF } else if isDir, errno := f.File.IsDir(); errno != 0 || !isDir { // In wasip1, only directories can be returned by fd_prestat_get as // there are no prestat types defined for files or sockets. @@ -1713,7 +1714,7 @@ func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags int) defaultMode = syscall.O_RDWR } if fdflags&wasip1.FD_NONBLOCK != 0 { - openFlags |= syscall.O_NONBLOCK + openFlags |= fsapi.O_NONBLOCK } if fdflags&wasip1.FD_APPEND != 0 { openFlags |= syscall.O_APPEND @@ -1749,7 +1750,7 @@ var pathReadlink = newHostFunc( "fd", "path", "path_len", "buf", "buf_len", "result.bufused", ) -func pathReadlinkFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathReadlinkFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) @@ -1760,7 +1761,7 @@ func pathReadlinkFn(_ context.Context, mod api.Module, params []uint64) syscall. resultBufused := uint32(params[5]) if pathLen == 0 || bufLen == 0 { - return syscall.EINVAL + return experimentalsys.EINVAL } mem := mod.Memory() @@ -1775,11 +1776,11 @@ func pathReadlinkFn(_ context.Context, mod api.Module, params []uint64) syscall. } if ok := mem.WriteString(buf, dst); !ok { - return syscall.EFAULT + return experimentalsys.EFAULT } if !mem.WriteUint32Le(resultBufused, uint32(len(dst))) { - return syscall.EFAULT + return experimentalsys.EFAULT } return 0 } @@ -1796,10 +1797,10 @@ func pathReadlinkFn(_ context.Context, mod api.Module, params []uint64) syscall. // # Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.ENOENT: `path` does not exist. -// - syscall.ENOTEMPTY: `path` is not empty -// - syscall.ENOTDIR: `path` is a file +// - sys.EBADF: `fd` is invalid +// - sys.ENOENT: `path` does not exist. +// - sys.ENOTEMPTY: `path` is not empty +// - sys.ENOTDIR: `path` is a file // // # Notes // - This is similar to unlinkat with AT_REMOVEDIR in POSIX. @@ -1812,7 +1813,7 @@ var pathRemoveDirectory = newHostFunc( "fd", "path", "path_len", ) -func pathRemoveDirectoryFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathRemoveDirectoryFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) @@ -1842,10 +1843,10 @@ func pathRemoveDirectoryFn(_ context.Context, mod api.Module, params []uint64) s // # Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` or `new_fd` are invalid -// - syscall.ENOENT: `old_path` does not exist. -// - syscall.ENOTDIR: `old` is a directory and `new` exists, but is a file. -// - syscall.EISDIR: `old` is a file and `new` exists, but is a directory. +// - sys.EBADF: `fd` or `new_fd` are invalid +// - sys.ENOENT: `old_path` does not exist. +// - sys.ENOTDIR: `old` is a directory and `new` exists, but is a file. +// - sys.EISDIR: `old` is a file and `new` exists, but is a directory. // // # Notes // - This is similar to unlinkat in POSIX. @@ -1858,7 +1859,7 @@ var pathRename = newHostFunc( "fd", "old_path", "old_path_len", "new_fd", "new_path", "new_path_len", ) -func pathRenameFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathRenameFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) @@ -1880,7 +1881,7 @@ func pathRenameFn(_ context.Context, mod api.Module, params []uint64) syscall.Er } if oldFS != newFS { // TODO: handle renames across filesystems - return syscall.ENOSYS + return experimentalsys.ENOSYS } return oldFS.Rename(oldPathName, newPathName) @@ -1896,7 +1897,7 @@ var pathSymlink = newHostFunc( "old_path", "old_path_len", "fd", "new_path", "new_path_len", ) -func pathSymlinkFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathSymlinkFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() oldPath := uint32(params[0]) @@ -1909,25 +1910,25 @@ func pathSymlinkFn(_ context.Context, mod api.Module, params []uint64) syscall.E dir, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF // closed + return experimentalsys.EBADF // closed } else if isDir, errno := dir.File.IsDir(); errno != 0 { return errno } else if !isDir { - return syscall.ENOTDIR + return experimentalsys.ENOTDIR } if oldPathLen == 0 || newPathLen == 0 { - return syscall.EINVAL + return experimentalsys.EINVAL } oldPathBuf, ok := mem.Read(oldPath, oldPathLen) if !ok { - return syscall.EFAULT + return experimentalsys.EFAULT } newPathBuf, ok := mem.Read(newPath, newPathLen) if !ok { - return syscall.EFAULT + return experimentalsys.EFAULT } return dir.FS.Symlink( @@ -1956,9 +1957,9 @@ func bufToStr(buf []byte) string { // # Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EBADF: `fd` is invalid -// - syscall.ENOENT: `path` does not exist. -// - syscall.EISDIR: `path` is a directory +// - sys.EBADF: `fd` is invalid +// - sys.ENOENT: `path` does not exist. +// - sys.EISDIR: `path` is a directory // // # Notes // - This is similar to unlinkat without AT_REMOVEDIR in POSIX. @@ -1971,7 +1972,7 @@ var pathUnlinkFile = newHostFunc( "fd", "path", "path_len", ) -func pathUnlinkFileFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pathUnlinkFileFn(_ context.Context, mod api.Module, params []uint64) experimentalsys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) diff --git a/imports/wasi_snapshot_preview1/fs_test.go b/imports/wasi_snapshot_preview1/fs_test.go index 6eebc717dd..a86e563979 100644 --- a/imports/wasi_snapshot_preview1/fs_test.go +++ b/imports/wasi_snapshot_preview1/fs_test.go @@ -18,6 +18,7 @@ import ( "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/fstest" "github.com/tetratelabs/wazero/internal/platform" @@ -3520,7 +3521,7 @@ func Test_pathFilestatSetTimes(t *testing.T) { fsc := sys.FS() var oldSt sysapi.Stat_t - var errno syscall.Errno + var errno experimentalsys.Errno if tc.expectedErrno == wasip1.ErrnoSuccess { oldSt, errno = fsc.RootFS().Stat(pathName) require.EqualErrno(t, 0, errno) diff --git a/imports/wasi_snapshot_preview1/poll.go b/imports/wasi_snapshot_preview1/poll.go index ac281433cf..aed08b7bea 100644 --- a/imports/wasi_snapshot_preview1/poll.go +++ b/imports/wasi_snapshot_preview1/poll.go @@ -2,10 +2,10 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "time" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" internalsys "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" @@ -18,15 +18,15 @@ import ( // // - in: pointer to the subscriptions (48 bytes each) // - out: pointer to the resulting events (32 bytes each) -// - nsubscriptions: count of subscriptions, zero returns syscall.EINVAL. +// - nsubscriptions: count of subscriptions, zero returns sys.EINVAL. // - resultNevents: count of events. // // Result (Errno) // // The return value is 0 except the following error conditions: -// - syscall.EINVAL: the parameters are invalid -// - syscall.ENOTSUP: a parameters is valid, but not yet supported. -// - syscall.EFAULT: there is not enough memory to read the subscriptions or +// - sys.EINVAL: the parameters are invalid +// - sys.ENOTSUP: a parameters is valid, but not yet supported. +// - sys.EFAULT: there is not enough memory to read the subscriptions or // write results. // // # Notes @@ -49,14 +49,14 @@ type event struct { outOffset uint32 } -func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { in := uint32(params[0]) out := uint32(params[1]) nsubscriptions := uint32(params[2]) resultNevents := uint32(params[3]) if nsubscriptions == 0 { - return syscall.EINVAL + return sys.EINVAL } mem := mod.Memory() @@ -64,7 +64,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // Ensure capacity prior to the read loop to reduce error handling. inBuf, ok := mem.Read(in, nsubscriptions*48) if !ok { - return syscall.EFAULT + return sys.EFAULT } outBuf, ok := mem.Read(out, nsubscriptions*32) // zero-out all buffer before writing @@ -73,13 +73,13 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er } if !ok { - return syscall.EFAULT + return sys.EFAULT } // Eagerly write the number of events which will equal subscriptions unless // there's a fault in parsing (not processing). if !mod.Memory().WriteUint32Le(resultNevents, nsubscriptions) { - return syscall.EFAULT + return sys.EFAULT } // Loop through all subscriptions and write their output. @@ -129,7 +129,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er case wasip1.EventTypeFdRead: fd := int32(le.Uint32(argBuf)) if fd < 0 { - return syscall.EBADF + return sys.EBADF } if file, ok := fsc.LookupFile(fd); !ok { evt.errno = wasip1.ErrnoBadf @@ -147,7 +147,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er case wasip1.EventTypeFdWrite: fd := int32(le.Uint32(argBuf)) if fd < 0 { - return syscall.EBADF + return sys.EBADF } if _, ok := fsc.LookupFile(fd); ok { evt.errno = wasip1.ErrnoNotsup @@ -157,7 +157,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er readySubs++ writeEvent(outBuf, evt) default: - return syscall.EINVAL + return sys.EINVAL } } @@ -171,7 +171,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er if len(blockingStdinSubs) > 0 { stdin, ok := fsc.LookupFile(internalsys.FdStdin) if !ok { - return syscall.EBADF + return sys.EBADF } // Wait for the timeout to expire, or for some data to become available on Stdin. stdinReady, errno := stdin.File.PollRead(&timeout) @@ -195,7 +195,7 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er if readySubs != nsubscriptions { if !mod.Memory().WriteUint32Le(resultNevents, readySubs+clockEvents) { - return syscall.EFAULT + return sys.EFAULT } } @@ -204,20 +204,20 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // processClockEvent supports only relative name events, as that's what's used // to implement sleep in various compilers including Rust, Zig and TinyGo. -func processClockEvent(inBuf []byte) (time.Duration, syscall.Errno) { +func processClockEvent(inBuf []byte) (time.Duration, sys.Errno) { _ /* ID */ = le.Uint32(inBuf[0:8]) // See below timeout := le.Uint64(inBuf[8:16]) // nanos if relative _ /* precision */ = le.Uint64(inBuf[16:24]) // Unused flags := le.Uint16(inBuf[24:32]) - var err syscall.Errno + var err sys.Errno // subclockflags has only one flag defined: subscription_clock_abstime switch flags { case 0: // relative time case 1: // subscription_clock_abstime - err = syscall.ENOTSUP + err = sys.ENOTSUP default: // subclockflags has only one flag defined. - err = syscall.EINVAL + err = sys.EINVAL } if err != 0 { diff --git a/imports/wasi_snapshot_preview1/poll_test.go b/imports/wasi_snapshot_preview1/poll_test.go index 764387d0a9..ff88e358a9 100644 --- a/imports/wasi_snapshot_preview1/poll_test.go +++ b/imports/wasi_snapshot_preview1/poll_test.go @@ -3,12 +3,12 @@ package wasi_snapshot_preview1_test import ( "io/fs" "strings" - "syscall" "testing" "time" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/testing/require" @@ -537,7 +537,7 @@ var fdReadSub = fdReadSubFd(byte(sys.FdStdin)) type ttyStat struct{} // Stat implements the same method as documented on fsapi.File -func (ttyStat) Stat() (sysapi.Stat_t, syscall.Errno) { +func (ttyStat) Stat() (sysapi.Stat_t, experimentalsys.Errno) { return sysapi.Stat_t{ Mode: fs.ModeDevice | fs.ModeCharDevice, Nlink: 1, @@ -555,7 +555,7 @@ type neverReadyTtyStdinFile struct { } // PollRead implements the same method as documented on fsapi.File -func (neverReadyTtyStdinFile) PollRead(timeout *time.Duration) (ready bool, errno syscall.Errno) { +func (neverReadyTtyStdinFile) PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) { time.Sleep(*timeout) return false, 0 } @@ -567,6 +567,6 @@ type pollStdinFile struct { } // PollRead implements the same method as documented on fsapi.File -func (p *pollStdinFile) PollRead(*time.Duration) (ready bool, errno syscall.Errno) { +func (p *pollStdinFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) { return p.ready, 0 } diff --git a/imports/wasi_snapshot_preview1/random.go b/imports/wasi_snapshot_preview1/random.go index dc94bf7da1..e4d7ccee15 100644 --- a/imports/wasi_snapshot_preview1/random.go +++ b/imports/wasi_snapshot_preview1/random.go @@ -3,9 +3,9 @@ package wasi_snapshot_preview1 import ( "context" "io" - "syscall" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -21,8 +21,8 @@ import ( // Result (Errno) // // The return value is ErrnoSuccess except the following error conditions: -// - syscall.EFAULT: `buf` or `bufLen` point to an offset out of memory -// - syscall.EIO: a file system error +// - sys.EFAULT: `buf` or `bufLen` point to an offset out of memory +// - sys.EIO: a file system error // // For example, if underlying random source was seeded like // `rand.NewSource(42)`, we expect api.Memory to contain: @@ -36,19 +36,19 @@ import ( // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-bufLen-size---errno var randomGet = newHostFunc(wasip1.RandomGetName, randomGetFn, []api.ValueType{i32, i32}, "buf", "buf_len") -func randomGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func randomGetFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { sysCtx := mod.(*wasm.ModuleInstance).Sys randSource := sysCtx.RandSource() buf, bufLen := uint32(params[0]), uint32(params[1]) randomBytes, ok := mod.Memory().Read(buf, bufLen) if !ok { // out-of-range - return syscall.EFAULT + return sys.EFAULT } // We can ignore the returned n as it only != byteCount on error if _, err := io.ReadAtLeast(randSource, randomBytes, int(bufLen)); err != nil { - return syscall.EIO + return sys.EIO } return 0 diff --git a/imports/wasi_snapshot_preview1/sched.go b/imports/wasi_snapshot_preview1/sched.go index b6dbf190d5..86748e6d6f 100644 --- a/imports/wasi_snapshot_preview1/sched.go +++ b/imports/wasi_snapshot_preview1/sched.go @@ -2,9 +2,9 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -15,7 +15,7 @@ import ( // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sched_yield---errno var schedYield = newHostFunc(wasip1.SchedYieldName, schedYieldFn, nil) -func schedYieldFn(_ context.Context, mod api.Module, _ []uint64) syscall.Errno { +func schedYieldFn(_ context.Context, mod api.Module, _ []uint64) sys.Errno { sysCtx := mod.(*wasm.ModuleInstance).Sys sysCtx.Osyield() return 0 diff --git a/imports/wasi_snapshot_preview1/sock.go b/imports/wasi_snapshot_preview1/sock.go index 3dacf3df15..a3c20ad586 100644 --- a/imports/wasi_snapshot_preview1/sock.go +++ b/imports/wasi_snapshot_preview1/sock.go @@ -5,6 +5,7 @@ import ( "syscall" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" socketapi "github.com/tetratelabs/wazero/internal/sock" "github.com/tetratelabs/wazero/internal/sysfs" "github.com/tetratelabs/wazero/internal/wasip1" @@ -23,7 +24,7 @@ var sockAccept = newHostFunc( "fd", "flags", "result.fd", ) -func sockAcceptFn(_ context.Context, mod api.Module, params []uint64) (errno syscall.Errno) { +func sockAcceptFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) { mem := mod.Memory() fsc := mod.(*wasm.ModuleInstance).Sys.FS() @@ -50,7 +51,7 @@ var sockRecv = newHostFunc( "fd", "ri_data", "ri_data_len", "ri_flags", "result.ro_datalen", "result.ro_flags", ) -func sockRecvFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func sockRecvFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { mem := mod.Memory() fsc := mod.(*wasm.ModuleInstance).Sys.FS() @@ -63,13 +64,13 @@ func sockRecvFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn var conn socketapi.TCPConn if e, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF // Not open + return sys.EBADF // Not open } else if conn, ok = e.File.(socketapi.TCPConn); !ok { - return syscall.EBADF // Not a conn + return sys.EBADF // Not a conn } if riFlags & ^(wasip1.RI_RECV_PEEK|wasip1.RI_RECV_WAITALL) != 0 { - return syscall.ENOTSUP + return sys.ENOTSUP } if riFlags&wasip1.RI_RECV_PEEK != 0 { @@ -78,16 +79,16 @@ func sockRecvFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn // This means that the first `uint32` is a `buf *uint8`. firstIovecBufAddr, ok := mem.ReadUint32Le(riData) if !ok { - return syscall.EINVAL + return sys.EINVAL } // Read bufLen firstIovecBufLen, ok := mem.ReadUint32Le(riData + 4) if !ok { - return syscall.EINVAL + return sys.EINVAL } firstIovecBuf, ok := mem.Read(firstIovecBufAddr, firstIovecBufLen) if !ok { - return syscall.EINVAL + return sys.EINVAL } n, err := conn.Recvfrom(firstIovecBuf, sysfs.MSG_PEEK) if err != 0 { @@ -122,7 +123,7 @@ var sockSend = newHostFunc( "fd", "si_data", "si_data_len", "si_flags", "result.so_datalen", ) -func sockSendFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func sockSendFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { mem := mod.Memory() fsc := mod.(*wasm.ModuleInstance).Sys.FS() @@ -133,14 +134,14 @@ func sockSendFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn resultSoDatalen := uint32(params[4]) if siFlags != 0 { - return syscall.ENOTSUP + return sys.ENOTSUP } var conn socketapi.TCPConn if e, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF // Not open + return sys.EBADF // Not open } else if conn, ok = e.File.(socketapi.TCPConn); !ok { - return syscall.EBADF // Not a conn + return sys.EBADF // Not a conn } bufSize, errno := writev(mem, siData, siDataCount, conn.Write) @@ -157,7 +158,7 @@ func sockSendFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn // See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_shutdownfd-fd-how-sdflags---errno var sockShutdown = newHostFunc(wasip1.SockShutdownName, sockShutdownFn, []wasm.ValueType{i32, i32}, "fd", "how") -func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { fsc := mod.(*wasm.ModuleInstance).Sys.FS() fd := int32(params[0]) @@ -165,9 +166,9 @@ func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) syscall. var conn socketapi.TCPConn if e, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF // Not open + return sys.EBADF // Not open } else if conn, ok = e.File.(socketapi.TCPConn); !ok { - return syscall.EBADF // Not a conn + return sys.EBADF // Not a conn } sysHow := 0 @@ -180,8 +181,9 @@ func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) syscall. case wasip1.SD_WR: sysHow = syscall.SHUT_WR default: - return syscall.EINVAL + return sys.EINVAL } + // TODO: Map this instead of relying on syscall symbols. return conn.Shutdown(sysHow) } diff --git a/imports/wasi_snapshot_preview1/sock_unit_test.go b/imports/wasi_snapshot_preview1/sock_unit_test.go index 762d4d9d24..5f12c45a52 100644 --- a/imports/wasi_snapshot_preview1/sock_unit_test.go +++ b/imports/wasi_snapshot_preview1/sock_unit_test.go @@ -2,9 +2,9 @@ package wasi_snapshot_preview1 import ( "os" - "syscall" "testing" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/sock" "github.com/tetratelabs/wazero/internal/testing/require" @@ -25,7 +25,7 @@ type testSock struct { fsapi.UnimplementedFile } -func (t testSock) Accept() (sock.TCPConn, syscall.Errno) { +func (t testSock) Accept() (sock.TCPConn, sys.Errno) { panic("no-op") } @@ -33,10 +33,10 @@ type testConn struct { fsapi.UnimplementedFile } -func (t testConn) Recvfrom([]byte, int) (n int, errno syscall.Errno) { +func (t testConn) Recvfrom([]byte, int) (n int, errno sys.Errno) { panic("no-op") } -func (t testConn) Shutdown(int) syscall.Errno { +func (t testConn) Shutdown(int) sys.Errno { panic("no-op") } diff --git a/imports/wasi_snapshot_preview1/wasi.go b/imports/wasi_snapshot_preview1/wasi.go index c6190316df..4ef41d501c 100644 --- a/imports/wasi_snapshot_preview1/wasi.go +++ b/imports/wasi_snapshot_preview1/wasi.go @@ -19,10 +19,10 @@ package wasi_snapshot_preview1 import ( "context" "encoding/binary" - "syscall" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -229,18 +229,18 @@ func exportFunctions(builder wazero.HostModuleBuilder) { // writeOffsetsAndNullTerminatedValues is used to write NUL-terminated values // for args or environ, given a pre-defined bytesLen (which includes NUL // terminators). -func writeOffsetsAndNullTerminatedValues(mem api.Memory, values [][]byte, offsets, bytes, bytesLen uint32) syscall.Errno { +func writeOffsetsAndNullTerminatedValues(mem api.Memory, values [][]byte, offsets, bytes, bytesLen uint32) sys.Errno { // The caller may not place bytes directly after offsets, so we have to // read them independently. valuesLen := len(values) offsetsLen := uint32(valuesLen * 4) // uint32Le offsetsBuf, ok := mem.Read(offsets, offsetsLen) if !ok { - return syscall.EFAULT + return sys.EFAULT } bytesBuf, ok := mem.Read(bytes, bytesLen) if !ok { - return syscall.EFAULT + return sys.EFAULT } // Loop through the values, first writing the location of its data to @@ -285,7 +285,7 @@ func newHostFunc( // wasiFunc special cases that all WASI functions return a single Errno // result. The returned value will be written back to the stack at index zero. -type wasiFunc func(ctx context.Context, mod api.Module, params []uint64) syscall.Errno +type wasiFunc func(ctx context.Context, mod api.Module, params []uint64) sys.Errno // Call implements the same method as documented on api.GoModuleFunction. func (f wasiFunc) Call(ctx context.Context, mod api.Module, stack []uint64) { diff --git a/imports/wasi_snapshot_preview1/wasi_bench_test.go b/imports/wasi_snapshot_preview1/wasi_bench_test.go index 4fe14c6a3f..b03f99445b 100644 --- a/imports/wasi_snapshot_preview1/wasi_bench_test.go +++ b/imports/wasi_snapshot_preview1/wasi_bench_test.go @@ -131,7 +131,7 @@ func Benchmark_fdReaddir(b *testing.B) { benches := []struct { name string fs fs.FS - // dirMount ensures direct use of syscall.FS + // dirMount ensures direct use of fsapi.FS dirMount string // twoCalls tests performance of reading a directory in two calls. twoCalls bool @@ -243,7 +243,7 @@ func Benchmark_pathFilestat(b *testing.B) { benches := []struct { name string fs fs.FS - // dirMount ensures direct use of syscall.FS + // dirMount ensures direct use of fsapi.FS dirMount string path string fd int32 diff --git a/imports/wasi_snapshot_preview1/wasi_stdlib_test.go b/imports/wasi_snapshot_preview1/wasi_stdlib_test.go index 2fe9babe25..5537fb092f 100644 --- a/imports/wasi_snapshot_preview1/wasi_stdlib_test.go +++ b/imports/wasi_snapshot_preview1/wasi_stdlib_test.go @@ -429,7 +429,7 @@ func testSock(t *testing.T, bin []byte) { func Test_HTTP(t *testing.T) { if runtime.GOOS == "windows" { - t.Skip("syscall.Nonblocking() is not supported on wasip1+windows.") + t.Skip("fsapi.Nonblocking() is not supported on wasip1+windows.") } toolchains := map[string][]byte{} if wasmGotip != nil { diff --git a/internal/fsapi/dir.go b/internal/fsapi/dir.go index 6929e8c1b2..6e99b1b5d0 100644 --- a/internal/fsapi/dir.go +++ b/internal/fsapi/dir.go @@ -3,9 +3,9 @@ package fsapi import ( "fmt" "io/fs" - "syscall" "time" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -59,8 +59,8 @@ func (DirFile) IsAppend() bool { } // SetAppend implements File.SetAppend -func (DirFile) SetAppend(bool) syscall.Errno { - return syscall.EISDIR +func (DirFile) SetAppend(bool) experimentalsys.Errno { + return experimentalsys.EISDIR } // IsNonblock implements File.IsNonblock @@ -69,41 +69,41 @@ func (DirFile) IsNonblock() bool { } // SetNonblock implements File.SetNonblock -func (DirFile) SetNonblock(bool) syscall.Errno { - return syscall.EISDIR +func (DirFile) SetNonblock(bool) experimentalsys.Errno { + return experimentalsys.EISDIR } // IsDir implements File.IsDir -func (DirFile) IsDir() (bool, syscall.Errno) { +func (DirFile) IsDir() (bool, experimentalsys.Errno) { return true, 0 } // Read implements File.Read -func (DirFile) Read([]byte) (int, syscall.Errno) { - return 0, syscall.EISDIR +func (DirFile) Read([]byte) (int, experimentalsys.Errno) { + return 0, experimentalsys.EISDIR } // Pread implements File.Pread -func (DirFile) Pread([]byte, int64) (int, syscall.Errno) { - return 0, syscall.EISDIR +func (DirFile) Pread([]byte, int64) (int, experimentalsys.Errno) { + return 0, experimentalsys.EISDIR } // PollRead implements File.PollRead -func (DirFile) PollRead(*time.Duration) (ready bool, errno syscall.Errno) { - return false, syscall.ENOSYS +func (DirFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) { + return false, experimentalsys.ENOSYS } // Write implements File.Write -func (DirFile) Write([]byte) (int, syscall.Errno) { - return 0, syscall.EISDIR +func (DirFile) Write([]byte) (int, experimentalsys.Errno) { + return 0, experimentalsys.EISDIR } // Pwrite implements File.Pwrite -func (DirFile) Pwrite([]byte, int64) (int, syscall.Errno) { - return 0, syscall.EISDIR +func (DirFile) Pwrite([]byte, int64) (int, experimentalsys.Errno) { + return 0, experimentalsys.EISDIR } // Truncate implements File.Truncate -func (DirFile) Truncate(int64) syscall.Errno { - return syscall.EISDIR +func (DirFile) Truncate(int64) experimentalsys.Errno { + return experimentalsys.EISDIR } diff --git a/internal/fsapi/file.go b/internal/fsapi/file.go index a8531f737e..b4ee85b83e 100644 --- a/internal/fsapi/file.go +++ b/internal/fsapi/file.go @@ -4,6 +4,7 @@ import ( "syscall" "time" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -11,14 +12,14 @@ import ( // including WASI and runtime.GOOS=js. // // Implementations should embed UnimplementedFile for forward compatability. Any -// unsupported method or parameter should return syscall.ENOSYS. +// unsupported method or parameter should return ENOSYS. // // # Errors // -// All methods that can return an error return a syscall.Errno, which is zero +// All methods that can return an error return a Errno, which is zero // on success. // -// Restricting to syscall.Errno matches current WebAssembly host functions, +// Restricting to Errno matches current WebAssembly host functions, // which are constrained to well-known error codes. For example, `GOOS=js` maps // hard coded values and panics otherwise. More commonly, WASI maps syscall // errors to u32 numeric values. @@ -36,41 +37,41 @@ type File interface { // // # Errors // - // Possible errors are those from Stat, except syscall.ENOSYS should not + // Possible errors are those from Stat, except ENOSYS should not // be returned. Zero should be returned if there is no implementation. // // # Notes // // - Implementations should cache this result. // - This combined with Ino can implement os.SameFile. - Dev() (uint64, syscall.Errno) + Dev() (uint64, experimentalsys.Errno) // Ino returns the serial number (Stat_t.Ino) of this file, zero if unknown // or an error retrieving it. // // # Errors // - // Possible errors are those from Stat, except syscall.ENOSYS should not + // Possible errors are those from Stat, except ENOSYS should not // be returned. Zero should be returned if there is no implementation. // // # Notes // // - Implementations should cache this result. // - This combined with Dev can implement os.SameFile. - Ino() (sys.Inode, syscall.Errno) + Ino() (sys.Inode, experimentalsys.Errno) // IsDir returns true if this file is a directory or an error there was an // error retrieving this information. // // # Errors // - // Possible errors are those from Stat, except syscall.ENOSYS should not + // Possible errors are those from Stat, except ENOSYS should not // be returned. false should be returned if there is no implementation. // // # Notes // // - Implementations should cache this result. - IsDir() (bool, syscall.Errno) + IsDir() (bool, experimentalsys.Errno) // IsNonblock returns true if the file was opened with O_NONBLOCK, or // SetNonblock was successfully enabled on this file. @@ -85,15 +86,15 @@ type File interface { // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. // // # Notes // // - This is like syscall.SetNonblock and `fcntl` with O_NONBLOCK in // POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html - SetNonblock(enable bool) syscall.Errno + SetNonblock(enable bool) experimentalsys.Errno // IsAppend returns true if the file was opened with syscall.O_APPEND, or // SetAppend was successfully enabled on this file. @@ -108,24 +109,24 @@ type File interface { // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. // // # Notes // // - There is no `O_APPEND` for `fcntl` in POSIX, so implementations may // have to re-open the underlying file to apply this. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html - SetAppend(enable bool) syscall.Errno + SetAppend(enable bool) experimentalsys.Errno // Stat is similar to syscall.Fstat. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. // // # Notes // @@ -134,17 +135,17 @@ type File interface { // - A fs.FileInfo backed implementation sets atim, mtim and ctim to the // same value. // - Windows allows you to stat a closed directory. - Stat() (sys.Stat_t, syscall.Errno) + Stat() (sys.Stat_t, experimentalsys.Errno) // Read attempts to read all bytes in the file into `buf`, and returns the // count read even on error. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed or not readable. - // - syscall.EISDIR: the file was a directory. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed or not readable. + // - EISDIR: the file was a directory. // // # Notes // @@ -152,18 +153,18 @@ type File interface { // io.Reader. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html // - Unlike io.Reader, there is no io.EOF returned on end-of-file. To // read the file completely, the caller must repeat until `n` is zero. - Read(buf []byte) (n int, errno syscall.Errno) + Read(buf []byte) (n int, errno experimentalsys.Errno) // Pread attempts to read all bytes in the file into `p`, starting at the // offset `off`, and returns the count read even on error. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed or not readable. - // - syscall.EINVAL: the offset was negative. - // - syscall.EISDIR: the file was a directory. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed or not readable. + // - EINVAL: the offset was negative. + // - EISDIR: the file was a directory. // // # Notes // @@ -171,7 +172,7 @@ type File interface { // of io.ReaderAt. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html // - Unlike io.ReaderAt, there is no io.EOF returned on end-of-file. To // read the file completely, the caller must repeat until `n` is zero. - Pread(buf []byte, off int64) (n int, errno syscall.Errno) + Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) // Seek attempts to set the next offset for Read or Write and returns the // resulting absolute offset or an error. @@ -194,16 +195,16 @@ type File interface { // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed or not readable. - // - syscall.EINVAL: the offset was negative. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed or not readable. + // - EINVAL: the offset was negative. // // # Notes // // - This is like io.Seeker and `fseek` in POSIX, preferring semantics // of io.Seeker. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseek.html - Seek(offset int64, whence int) (newOffset int64, errno syscall.Errno) + Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) // PollRead returns if the file has data ready to be read or an error. // @@ -213,8 +214,8 @@ type File interface { // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. // // # Notes // @@ -223,7 +224,7 @@ type File interface { // - No-op files, such as those which read from /dev/null, should return // immediately true to avoid hangs (because data will never become // available). - PollRead(timeout *time.Duration) (ready bool, errno syscall.Errno) + PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) // Readdir reads the contents of the directory associated with file and // returns a slice of up to n Dirent values in an arbitrary order. This is @@ -234,10 +235,10 @@ type File interface { // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file was closed or not a directory. - // - syscall.ENOENT: the directory could not be read (e.g. deleted). + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file was closed or not a directory. + // - ENOENT: the directory could not be read (e.g. deleted). // // # Notes // @@ -247,88 +248,88 @@ type File interface { // read the directory completely, the caller must repeat until the // count read (`len(dirents)`) is less than `n`. // - See /RATIONALE.md for design notes. - Readdir(n int) (dirents []Dirent, errno syscall.Errno) + Readdir(n int) (dirents []Dirent, errno experimentalsys.Errno) // Write attempts to write all bytes in `p` to the file, and returns the // count written even on error. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file was closed, not writeable, or a directory. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file was closed, not writeable, or a directory. // // # Notes // // - This is like io.Writer and `write` in POSIX, preferring semantics of // io.Writer. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - Write(buf []byte) (n int, errno syscall.Errno) + Write(buf []byte) (n int, errno experimentalsys.Errno) // Pwrite attempts to write all bytes in `p` to the file at the given // offset `off`, and returns the count written even on error. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed or not writeable. - // - syscall.EINVAL: the offset was negative. - // - syscall.EISDIR: the file was a directory. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed or not writeable. + // - EINVAL: the offset was negative. + // - EISDIR: the file was a directory. // // # Notes // // - This is like io.WriterAt and `pwrite` in POSIX, preferring semantics // of io.WriterAt. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html - Pwrite(buf []byte, off int64) (n int, errno syscall.Errno) + Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) // Truncate truncates a file to a specified length. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed. - // - syscall.EINVAL: the `size` is negative. - // - syscall.EISDIR: the file was a directory. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. + // - EINVAL: the `size` is negative. + // - EISDIR: the file was a directory. // // # Notes // // - This is like syscall.Ftruncate and `ftruncate` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html // - Windows does not error when calling Truncate on a closed file. - Truncate(size int64) syscall.Errno + Truncate(size int64) experimentalsys.Errno // Sync synchronizes changes to the file. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.EBADF: the file or directory was closed. + // A zero Errno is success. The below are expected otherwise: + // - EBADF: the file or directory was closed. // // # Notes // // - This is like syscall.Fsync and `fsync` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html - // - This returns with no error instead of syscall.ENOSYS when + // - This returns with no error instead of ENOSYS when // unimplemented. This prevents fake filesystems from erring. // - Windows does not error when calling Sync on a closed file. - Sync() syscall.Errno + Sync() experimentalsys.Errno // Datasync synchronizes the data of a file. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.EBADF: the file or directory was closed. + // A zero Errno is success. The below are expected otherwise: + // - EBADF: the file or directory was closed. // // # Notes // // - This is like syscall.Fdatasync and `fdatasync` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html - // - This returns with no error instead of syscall.ENOSYS when + // - This returns with no error instead of ENOSYS when // unimplemented. This prevents fake filesystems from erring. // - As this is commonly missing, some implementations dispatch to Sync. - Datasync() syscall.Errno + Datasync() experimentalsys.Errno // Utimens set file access and modification times of this file, at // nanosecond precision. @@ -342,25 +343,25 @@ type File interface { // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EBADF: the file or directory was closed. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. // // # Notes // // - This is like syscall.UtimesNano and `futimens` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html // - Windows requires files to be open with syscall.O_RDWR, which means you - // cannot use this to update timestamps on a directory (syscall.EPERM). - Utimens(times *[2]syscall.Timespec) syscall.Errno + // cannot use this to update timestamps on a directory (EPERM). + Utimens(times *[2]syscall.Timespec) experimentalsys.Errno // Close closes the underlying file. // - // A zero syscall.Errno is returned if unimplemented or success. + // A zero Errno is returned if unimplemented or success. // // # Notes // // - This is like syscall.Close and `close` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html - Close() syscall.Errno + Close() experimentalsys.Errno } diff --git a/internal/fsapi/fs.go b/internal/fsapi/fs.go index 391b23f97d..50dfd1e6ea 100644 --- a/internal/fsapi/fs.go +++ b/internal/fsapi/fs.go @@ -4,6 +4,7 @@ import ( "io/fs" "syscall" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -11,14 +12,14 @@ import ( // including WASI and runtime.GOOS=js. // // Implementations should embed UnimplementedFS for forward compatability. Any -// unsupported method or parameter should return syscall.ENO +// unsupported method or parameter should return ENO // // # Errors // -// All methods that can return an error return a syscall.Errno, which is zero +// All methods that can return an error return a Errno, which is zero // on success. // -// Restricting to syscall.Errno matches current WebAssembly host functions, +// Restricting to Errno matches current WebAssembly host functions, // which are constrained to well-known error codes. For example, `GOOS=js` maps // hard coded values and panics otherwise. More commonly, WASI maps syscall // errors to u32 numeric values. @@ -32,12 +33,12 @@ type FS interface { // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EINVAL: `path` or `flag` is invalid. - // - syscall.EISDIR: the path was a directory, but flag included + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` or `flag` is invalid. + // - EISDIR: the path was a directory, but flag included // syscall.O_RDWR or syscall.O_WRONLY - // - syscall.ENOENT: `path` doesn't exist and `flag` doesn't contain + // - ENOENT: `path` doesn't exist and `flag` doesn't contain // os.O_CREATE. // // # Constraints on the returned file @@ -45,7 +46,7 @@ type FS interface { // Implementations that can read flags should enforce them regardless of // the type returned. For example, while os.File implements io.Writer, // attempts to write to a directory or a file opened with os.O_RDONLY fail - // with a syscall.EBADF. + // with a EBADF. // // Some implementations choose whether to enforce read-only opens, namely // fs.FS. While fs.FS is supported (Adapt), wazero cannot runtime enforce @@ -55,21 +56,22 @@ type FS interface { // # Notes // // - This is like os.OpenFile, except the path is relative to this file - // system, and syscall.Errno is returned instead of os.PathError. + // system, and Errno is returned instead of os.PathError. // - flag are the same as os.OpenFile, for example, os.O_CREATE. // - Implications of permissions when os.O_CREATE are described in Chmod // notes. // - This is like `open` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html - OpenFile(path string, flag int, perm fs.FileMode) (File, syscall.Errno) + OpenFile(path string, flag int, perm fs.FileMode) (File, experimentalsys.Errno) + // TODO: make sure all flags are not in the syscall package // Lstat gets file status without following symbolic links. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.ENOENT: `path` doesn't exist. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - ENOENT: `path` doesn't exist. // // # Notes // @@ -81,15 +83,15 @@ type FS interface { // same value. // - When the path is a symbolic link, the stat returned is for the link, // not the file it refers to. - Lstat(path string) (sys.Stat_t, syscall.Errno) + Lstat(path string) (sys.Stat_t, experimentalsys.Errno) // Stat gets file status. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.ENOENT: `path` doesn't exist. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - ENOENT: `path` doesn't exist. // // # Notes // @@ -101,17 +103,17 @@ type FS interface { // same value. // - When the path is a symbolic link, the stat returned is for the file // it refers to. - Stat(path string) (sys.Stat_t, syscall.Errno) + Stat(path string) (sys.Stat_t, experimentalsys.Errno) // Mkdir makes a directory. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EINVAL: `path` is invalid. - // - syscall.EEXIST: `path` exists and is a directory. - // - syscall.ENOTDIR: `path` exists and is a file. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - EEXIST: `path` exists and is a directory. + // - ENOTDIR: `path` exists and is a file. // // # Notes // @@ -120,16 +122,16 @@ type FS interface { // - This is like `mkdir` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html // - Implications of permissions are described in Chmod notes. - Mkdir(path string, perm fs.FileMode) syscall.Errno + Mkdir(path string, perm fs.FileMode) experimentalsys.Errno // Chmod changes the mode of the file. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EINVAL: `path` is invalid. - // - syscall.ENOENT: `path` does not exist. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - ENOENT: `path` does not exist. // // # Notes // @@ -140,19 +142,19 @@ type FS interface { // - Windows ignores the execute bit, and any permissions come back as // group and world. For example, chmod of 0400 reads back as 0444, and // 0700 0666. Also, permissions on directories aren't supported at all. - Chmod(path string, perm fs.FileMode) syscall.Errno + Chmod(path string, perm fs.FileMode) experimentalsys.Errno // Rename renames file or directory. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EINVAL: `from` or `to` is invalid. - // - syscall.ENOENT: `from` or `to` don't exist. - // - syscall.ENOTDIR: `from` is a directory and `to` exists as a file. - // - syscall.EISDIR: `from` is a file and `to` exists as a directory. - // - syscall.ENOTEMPTY: `both from` and `to` are existing directory, but + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `from` or `to` is invalid. + // - ENOENT: `from` or `to` don't exist. + // - ENOTDIR: `from` is a directory and `to` exists as a file. + // - EISDIR: `from` is a file and `to` exists as a directory. + // - ENOTEMPTY: `both from` and `to` are existing directory, but // `to` is not empty. // // # Notes @@ -162,18 +164,18 @@ type FS interface { // - This is like `rename` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html // - Windows doesn't let you overwrite an existing directory. - Rename(from, to string) syscall.Errno + Rename(from, to string) experimentalsys.Errno // Rmdir removes a directory. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EINVAL: `path` is invalid. - // - syscall.ENOENT: `path` doesn't exist. - // - syscall.ENOTDIR: `path` exists, but isn't a directory. - // - syscall.ENOTEMPTY: `path` exists, but isn't empty. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - ENOENT: `path` doesn't exist. + // - ENOTDIR: `path` exists, but isn't a directory. + // - ENOTEMPTY: `path` exists, but isn't empty. // // # Notes // @@ -181,18 +183,18 @@ type FS interface { // file system. // - This is like `rmdir` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html - // - As of Go 1.19, Windows maps syscall.ENOTDIR to syscall.ENOENT. - Rmdir(path string) syscall.Errno + // - As of Go 1.19, Windows maps ENOTDIR to ENOENT. + Rmdir(path string) experimentalsys.Errno // Unlink removes a directory entry. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EINVAL: `path` is invalid. - // - syscall.ENOENT: `path` doesn't exist. - // - syscall.EISDIR: `path` exists, but is a directory. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - ENOENT: `path` doesn't exist. + // - EISDIR: `path` exists, but is a directory. // // # Notes // @@ -203,18 +205,18 @@ type FS interface { // - On Windows, syscall.Unlink doesn't delete symlink to directory unlike other platforms. Implementations might // want to combine syscall.RemoveDirectory with syscall.Unlink in order to delete such links on Windows. // See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya - Unlink(path string) syscall.Errno + Unlink(path string) experimentalsys.Errno // Link creates a "hard" link from oldPath to newPath, in contrast to a // soft link (via Symlink). // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EPERM: `oldPath` is invalid. - // - syscall.ENOENT: `oldPath` doesn't exist. - // - syscall.EISDIR: `newPath` exists, but is a directory. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EPERM: `oldPath` is invalid. + // - ENOENT: `oldPath` doesn't exist. + // - EISDIR: `newPath` exists, but is a directory. // // # Notes // @@ -222,17 +224,17 @@ type FS interface { // file system. // - This is like `link` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html - Link(oldPath, newPath string) syscall.Errno + Link(oldPath, newPath string) experimentalsys.Errno // Symlink creates a "soft" link from oldPath to newPath, in contrast to a // hard link (via Link). // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EPERM: `oldPath` or `newPath` is invalid. - // - syscall.EEXIST: `newPath` exists. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EPERM: `oldPath` or `newPath` is invalid. + // - EEXIST: `newPath` exists. // // # Notes // @@ -246,17 +248,17 @@ type FS interface { // See https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409 // for how others implement this. // - Symlinks in Windows requires `SeCreateSymbolicLinkPrivilege`. - // Otherwise, syscall.EPERM results. + // Otherwise, EPERM results. // See https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links - Symlink(oldPath, linkName string) syscall.Errno + Symlink(oldPath, linkName string) experimentalsys.Errno // Readlink reads the contents of a symbolic link. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EINVAL: `path` is invalid. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. // // # Notes // @@ -267,7 +269,7 @@ type FS interface { // - On Windows, the path separator is different from other platforms, // but to provide consistent results to Wasm, this normalizes to a "/" // separator. - Readlink(path string) (string, syscall.Errno) + Readlink(path string) (string, experimentalsys.Errno) // Utimens set file access and modification times on a path relative to // this file system, at nanosecond precision. @@ -275,25 +277,26 @@ type FS interface { // # Parameters // // The `times` parameter includes the access and modification timestamps to - // assign. Special syscall.Timespec NSec values platform.UTIME_NOW and - // platform.UTIME_OMIT may be specified instead of real timestamps. A nil - // `times` parameter behaves the same as if both were set to - // platform.UTIME_NOW. + // assign. Special syscall.Timespec NSec values UTIME_NOW and UTIME_OMIT + // may be specified instead of real timestamps. A nil `times` parameter + // behaves the same as if both were set to UTIME_NOW. // // When the `symlinkFollow` parameter is true and the path is a symbolic link, // the target of expanding that link is updated. // // # Errors // - // A zero syscall.Errno is success. The below are expected otherwise: - // - syscall.ENOSYS: the implementation does not support this function. - // - syscall.EINVAL: `path` is invalid. - // - syscall.EEXIST: `path` exists and is a directory. - // - syscall.ENOTDIR: `path` exists and is a file. + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - EEXIST: `path` exists and is a directory. + // - ENOTDIR: `path` exists and is a file. // // # Notes // // - This is like syscall.UtimesNano and `utimensat` with `AT_FDCWD` in // POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html - Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno + Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) experimentalsys.Errno + // TODO: change impl to not use syscall package, + // possibly by being just a pair of int64s.. } diff --git a/internal/fsapi/unimplemented.go b/internal/fsapi/unimplemented.go index 9d0b8b4766..81a636f34c 100644 --- a/internal/fsapi/unimplemented.go +++ b/internal/fsapi/unimplemented.go @@ -5,10 +5,11 @@ import ( "syscall" "time" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) -// UnimplementedFS is an FS that returns syscall.ENOSYS for all functions, +// UnimplementedFS is an FS that returns ENOSYS for all functions, // This should be embedded to have forward compatible implementations. type UnimplementedFS struct{} @@ -19,92 +20,92 @@ func (UnimplementedFS) String() string { // Open implements the same method as documented on fs.FS func (UnimplementedFS) Open(name string) (fs.File, error) { - return nil, &fs.PathError{Op: "open", Path: name, Err: syscall.ENOSYS} + return nil, &fs.PathError{Op: "open", Path: name, Err: experimentalsys.ENOSYS} } // OpenFile implements FS.OpenFile -func (UnimplementedFS) OpenFile(path string, flag int, perm fs.FileMode) (File, syscall.Errno) { - return nil, syscall.ENOSYS +func (UnimplementedFS) OpenFile(path string, flag int, perm fs.FileMode) (File, experimentalsys.Errno) { + return nil, experimentalsys.ENOSYS } // Lstat implements FS.Lstat -func (UnimplementedFS) Lstat(path string) (sys.Stat_t, syscall.Errno) { - return sys.Stat_t{}, syscall.ENOSYS +func (UnimplementedFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { + return sys.Stat_t{}, experimentalsys.ENOSYS } // Stat implements FS.Stat -func (UnimplementedFS) Stat(path string) (sys.Stat_t, syscall.Errno) { - return sys.Stat_t{}, syscall.ENOSYS +func (UnimplementedFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { + return sys.Stat_t{}, experimentalsys.ENOSYS } // Readlink implements FS.Readlink -func (UnimplementedFS) Readlink(path string) (string, syscall.Errno) { - return "", syscall.ENOSYS +func (UnimplementedFS) Readlink(path string) (string, experimentalsys.Errno) { + return "", experimentalsys.ENOSYS } // Mkdir implements FS.Mkdir -func (UnimplementedFS) Mkdir(path string, perm fs.FileMode) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Chmod implements FS.Chmod -func (UnimplementedFS) Chmod(path string, perm fs.FileMode) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Rename implements FS.Rename -func (UnimplementedFS) Rename(from, to string) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Rename(from, to string) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Rmdir implements FS.Rmdir -func (UnimplementedFS) Rmdir(path string) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Rmdir(path string) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Link implements FS.Link -func (UnimplementedFS) Link(_, _ string) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Link(_, _ string) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Symlink implements FS.Symlink -func (UnimplementedFS) Symlink(_, _ string) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Symlink(_, _ string) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Unlink implements FS.Unlink -func (UnimplementedFS) Unlink(path string) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Unlink(path string) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Utimens implements FS.Utimens -func (UnimplementedFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Truncate implements FS.Truncate -func (UnimplementedFS) Truncate(string, int64) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFS) Truncate(string, int64) experimentalsys.Errno { + return experimentalsys.ENOSYS } -// UnimplementedFile is a File that returns syscall.ENOSYS for all functions, +// UnimplementedFile is a File that returns ENOSYS for all functions, // except where no-op are otherwise documented. // // This should be embedded to have forward compatible implementations. type UnimplementedFile struct{} // Dev implements File.Dev -func (UnimplementedFile) Dev() (uint64, syscall.Errno) { +func (UnimplementedFile) Dev() (uint64, experimentalsys.Errno) { return 0, 0 } // Ino implements File.Ino -func (UnimplementedFile) Ino() (sys.Inode, syscall.Errno) { +func (UnimplementedFile) Ino() (sys.Inode, experimentalsys.Errno) { return 0, 0 } // IsDir implements File.IsDir -func (UnimplementedFile) IsDir() (bool, syscall.Errno) { +func (UnimplementedFile) IsDir() (bool, experimentalsys.Errno) { return false, 0 } @@ -114,8 +115,8 @@ func (UnimplementedFile) IsAppend() bool { } // SetAppend implements File.SetAppend -func (UnimplementedFile) SetAppend(bool) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFile) SetAppend(bool) experimentalsys.Errno { + return experimentalsys.ENOSYS } // IsNonblock implements File.IsNonblock @@ -124,69 +125,69 @@ func (UnimplementedFile) IsNonblock() bool { } // SetNonblock implements File.SetNonblock -func (UnimplementedFile) SetNonblock(bool) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFile) SetNonblock(bool) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Stat implements File.Stat -func (UnimplementedFile) Stat() (sys.Stat_t, syscall.Errno) { - return sys.Stat_t{}, syscall.ENOSYS +func (UnimplementedFile) Stat() (sys.Stat_t, experimentalsys.Errno) { + return sys.Stat_t{}, experimentalsys.ENOSYS } // Read implements File.Read -func (UnimplementedFile) Read([]byte) (int, syscall.Errno) { - return 0, syscall.ENOSYS +func (UnimplementedFile) Read([]byte) (int, experimentalsys.Errno) { + return 0, experimentalsys.ENOSYS } // Pread implements File.Pread -func (UnimplementedFile) Pread([]byte, int64) (int, syscall.Errno) { - return 0, syscall.ENOSYS +func (UnimplementedFile) Pread([]byte, int64) (int, experimentalsys.Errno) { + return 0, experimentalsys.ENOSYS } // Seek implements File.Seek -func (UnimplementedFile) Seek(int64, int) (int64, syscall.Errno) { - return 0, syscall.ENOSYS +func (UnimplementedFile) Seek(int64, int) (int64, experimentalsys.Errno) { + return 0, experimentalsys.ENOSYS } // Readdir implements File.Readdir -func (UnimplementedFile) Readdir(int) (dirents []Dirent, errno syscall.Errno) { - return nil, syscall.ENOSYS +func (UnimplementedFile) Readdir(int) (dirents []Dirent, errno experimentalsys.Errno) { + return nil, experimentalsys.ENOSYS } // PollRead implements File.PollRead -func (UnimplementedFile) PollRead(*time.Duration) (ready bool, errno syscall.Errno) { - return false, syscall.ENOSYS +func (UnimplementedFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) { + return false, experimentalsys.ENOSYS } // Write implements File.Write -func (UnimplementedFile) Write([]byte) (int, syscall.Errno) { - return 0, syscall.ENOSYS +func (UnimplementedFile) Write([]byte) (int, experimentalsys.Errno) { + return 0, experimentalsys.ENOSYS } // Pwrite implements File.Pwrite -func (UnimplementedFile) Pwrite([]byte, int64) (int, syscall.Errno) { - return 0, syscall.ENOSYS +func (UnimplementedFile) Pwrite([]byte, int64) (int, experimentalsys.Errno) { + return 0, experimentalsys.ENOSYS } // Truncate implements File.Truncate -func (UnimplementedFile) Truncate(int64) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFile) Truncate(int64) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Sync implements File.Sync -func (UnimplementedFile) Sync() syscall.Errno { - return 0 // not syscall.ENOSYS +func (UnimplementedFile) Sync() experimentalsys.Errno { + return 0 // not ENOSYS } // Datasync implements File.Datasync -func (UnimplementedFile) Datasync() syscall.Errno { - return 0 // not syscall.ENOSYS +func (UnimplementedFile) Datasync() experimentalsys.Errno { + return 0 // not ENOSYS } // Utimens implements File.Utimens -func (UnimplementedFile) Utimens(*[2]syscall.Timespec) syscall.Errno { - return syscall.ENOSYS +func (UnimplementedFile) Utimens(*[2]syscall.Timespec) experimentalsys.Errno { + return experimentalsys.ENOSYS } // Close implements File.Close -func (UnimplementedFile) Close() (errno syscall.Errno) { return } +func (UnimplementedFile) Close() (errno experimentalsys.Errno) { return } diff --git a/internal/gojs/errno.go b/internal/gojs/errno.go index afc6ee07f5..48284da278 100644 --- a/internal/gojs/errno.go +++ b/internal/gojs/errno.go @@ -2,9 +2,8 @@ package gojs import ( "io" - "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) // Errno is a (GOARCH=wasm) error, which must match a key in mapJSError. @@ -62,50 +61,50 @@ var ( // ToErrno maps I/O errors as the message must be the code, ex. "EINVAL", not // the message, e.g. "invalid argument". -// -// This should match wasi_snapshot_preview1.ToErrno for maintenance ease. func ToErrno(err error) *Errno { if err == nil || err == io.EOF { return nil // io.EOF has no value in GOOS=js, and isn't an error. } - errno := platform.UnwrapOSError(err) - + errno, ok := err.(sys.Errno) + if !ok { + return ErrnoIo + } switch errno { - case syscall.EACCES: + case sys.EACCES: return ErrnoAcces - case syscall.EAGAIN: + case sys.EAGAIN: return ErrnoAgain - case syscall.EBADF: + case sys.EBADF: return ErrnoBadf - case syscall.EEXIST: + case sys.EEXIST: return ErrnoExist - case syscall.EFAULT: + case sys.EFAULT: return ErrnoFault - case syscall.EINTR: + case sys.EINTR: return ErrnoIntr - case syscall.EINVAL: + case sys.EINVAL: return ErrnoInval - case syscall.EIO: + case sys.EIO: return ErrnoIo - case syscall.EISDIR: + case sys.EISDIR: return ErrnoIsdir - case syscall.ELOOP: + case sys.ELOOP: return ErrnoLoop - case syscall.ENAMETOOLONG: + case sys.ENAMETOOLONG: return ErrnoNametoolong - case syscall.ENOENT: + case sys.ENOENT: return ErrnoNoent - case syscall.ENOSYS: + case sys.ENOSYS: return ErrnoNosys - case syscall.ENOTDIR: + case sys.ENOTDIR: return ErrnoNotdir - case syscall.ENOTEMPTY: + case sys.ENOTEMPTY: return ErrnoNotempty - case syscall.ENOTSUP: + case sys.ENOTSUP: return ErrnoNotsup - case syscall.EPERM: + case sys.EPERM: return ErrnoPerm - case syscall.EROFS: + case sys.EROFS: return ErrnoRofs default: return ErrnoIo diff --git a/internal/gojs/errno_test.go b/internal/gojs/errno_test.go index 40a380d956..d8b9f3992a 100644 --- a/internal/gojs/errno_test.go +++ b/internal/gojs/errno_test.go @@ -2,8 +2,9 @@ package gojs import ( "io" - "syscall" "testing" + + "github.com/tetratelabs/wazero/experimental/sys" ) func TestToErrno(t *testing.T) { @@ -20,98 +21,98 @@ func TestToErrno(t *testing.T) { input: io.EOF, }, { - name: "syscall.EACCES", - input: syscall.EACCES, + name: "sys.EACCES", + input: sys.EACCES, expected: ErrnoAcces, }, { - name: "syscall.EAGAIN", - input: syscall.EAGAIN, + name: "sys.EAGAIN", + input: sys.EAGAIN, expected: ErrnoAgain, }, { - name: "syscall.EBADF", - input: syscall.EBADF, + name: "sys.EBADF", + input: sys.EBADF, expected: ErrnoBadf, }, { - name: "syscall.EEXIST", - input: syscall.EEXIST, + name: "sys.EEXIST", + input: sys.EEXIST, expected: ErrnoExist, }, { - name: "syscall.EFAULT", - input: syscall.EFAULT, + name: "sys.EFAULT", + input: sys.EFAULT, expected: ErrnoFault, }, { - name: "syscall.EINTR", - input: syscall.EINTR, + name: "sys.EINTR", + input: sys.EINTR, expected: ErrnoIntr, }, { - name: "syscall.EINVAL", - input: syscall.EINVAL, + name: "sys.EINVAL", + input: sys.EINVAL, expected: ErrnoInval, }, { - name: "syscall.EIO", - input: syscall.EIO, + name: "sys.EIO", + input: sys.EIO, expected: ErrnoIo, }, { - name: "syscall.EISDIR", - input: syscall.EISDIR, + name: "sys.EISDIR", + input: sys.EISDIR, expected: ErrnoIsdir, }, { - name: "syscall.ELOOP", - input: syscall.ELOOP, + name: "sys.ELOOP", + input: sys.ELOOP, expected: ErrnoLoop, }, { - name: "syscall.ENAMETOOLONG", - input: syscall.ENAMETOOLONG, + name: "sys.ENAMETOOLONG", + input: sys.ENAMETOOLONG, expected: ErrnoNametoolong, }, { - name: "syscall.ENOENT", - input: syscall.ENOENT, + name: "sys.ENOENT", + input: sys.ENOENT, expected: ErrnoNoent, }, { - name: "syscall.ENOSYS", - input: syscall.ENOSYS, + name: "sys.ENOSYS", + input: sys.ENOSYS, expected: ErrnoNosys, }, { - name: "syscall.ENOTDIR", - input: syscall.ENOTDIR, + name: "sys.ENOTDIR", + input: sys.ENOTDIR, expected: ErrnoNotdir, }, { - name: "syscall.ENOTEMPTY", - input: syscall.ENOTEMPTY, + name: "sys.ENOTEMPTY", + input: sys.ENOTEMPTY, expected: ErrnoNotempty, }, { - name: "syscall.ENOTSUP", - input: syscall.ENOTSUP, + name: "sys.ENOTSUP", + input: sys.ENOTSUP, expected: ErrnoNotsup, }, { - name: "syscall.EPERM", - input: syscall.EPERM, + name: "sys.EPERM", + input: sys.EPERM, expected: ErrnoPerm, }, { - name: "syscall.EROFS", - input: syscall.EROFS, + name: "sys.EROFS", + input: sys.EROFS, expected: ErrnoRofs, }, { - name: "syscall.Errno unexpected == ErrnoIo", - input: syscall.Errno(0xfe), + name: "sys.Errno unexpected == ErrnoIo", + input: sys.Errno(0xfe), expected: ErrnoIo, }, } diff --git a/internal/gojs/fs.go b/internal/gojs/fs.go index c7b25dfb00..eeabecfdc9 100644 --- a/internal/gojs/fs.go +++ b/internal/gojs/fs.go @@ -7,6 +7,7 @@ import ( "syscall" "github.com/tetratelabs/wazero/api" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/gojs/custom" "github.com/tetratelabs/wazero/internal/gojs/goos" "github.com/tetratelabs/wazero/internal/gojs/util" @@ -174,7 +175,7 @@ func (jsfsFstat) invoke(ctx context.Context, mod api.Module, args ...interface{} func syscallFstat(fsc *internalsys.FSContext, fd int32) (*jsSt, error) { f, ok := fsc.LookupFile(fd) if !ok { - return nil, syscall.EBADF + return nil, experimentalsys.EBADF } if st, errno := f.File.Stat(); errno != 0 { @@ -239,11 +240,11 @@ func (jsfsRead) invoke(ctx context.Context, mod api.Module, args ...interface{}) } // syscallRead is like syscall.Read -func syscallRead(mod api.Module, fd int32, offset interface{}, buf []byte) (n int, errno syscall.Errno) { +func syscallRead(mod api.Module, fd int32, offset interface{}, buf []byte) (n int, errno experimentalsys.Errno) { fsc := mod.(*wasm.ModuleInstance).Sys.FS() if f, ok := fsc.LookupFile(fd); !ok { - return 0, syscall.EBADF + return 0, experimentalsys.EBADF } else if offset != nil { return f.File.Pread(buf, toInt64(offset)) } else { @@ -282,17 +283,17 @@ func (jsfsWrite) invoke(ctx context.Context, mod api.Module, args ...interface{} } // syscallWrite is like syscall.Write -func syscallWrite(mod api.Module, fd int32, offset interface{}, buf []byte) (n int, errno syscall.Errno) { +func syscallWrite(mod api.Module, fd int32, offset interface{}, buf []byte) (n int, errno experimentalsys.Errno) { fsc := mod.(*wasm.ModuleInstance).Sys.FS() if f, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + errno = experimentalsys.EBADF } else if offset != nil { n, errno = f.File.Pwrite(buf, toInt64(offset)) } else { n, errno = f.File.Write(buf) } - if errno == syscall.ENOSYS { - errno = syscall.EBADF // e.g. unimplemented for write + if errno == experimentalsys.ENOSYS { + errno = experimentalsys.EBADF // e.g. unimplemented for write } return } @@ -350,7 +351,7 @@ func (m *jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interfac root := fsc.RootFS() var fd int32 - var errno syscall.Errno + var errno experimentalsys.Errno // We need at least read access to open the file descriptor if perm == 0 { perm = 0o0500 @@ -467,11 +468,11 @@ func (jsfsFchmod) invoke(ctx context.Context, mod api.Module, args ...interface{ // Check to see if the file descriptor is available fsc := mod.(*wasm.ModuleInstance).Sys.FS() - var errno syscall.Errno + var errno experimentalsys.Errno if _, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + errno = experimentalsys.EBADF } else { - errno = syscall.ENOSYS // We only support functions used in wasip1 + errno = experimentalsys.ENOSYS // We only support functions used in wasip1 } return jsfsInvoke(ctx, mod, callback, errno) @@ -490,7 +491,7 @@ func (c *jsfsChown) invoke(ctx context.Context, mod api.Module, args ...interfac _ = args[2] // gid callback := args[3].(funcWrapper) - errno := syscall.ENOSYS // We only support functions used in wasip1 + errno := experimentalsys.ENOSYS // We only support functions used in wasip1 return jsfsInvoke(ctx, mod, callback, errno) } @@ -508,11 +509,11 @@ func (jsfsFchown) invoke(ctx context.Context, mod api.Module, args ...interface{ // Check to see if the file descriptor is available fsc := mod.(*wasm.ModuleInstance).Sys.FS() - var errno syscall.Errno + var errno experimentalsys.Errno if _, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + errno = experimentalsys.EBADF } else { - errno = syscall.ENOSYS // We only support functions used in wasip1 + errno = experimentalsys.ENOSYS // We only support functions used in wasip1 } return jsfsInvoke(ctx, mod, callback, errno) @@ -531,7 +532,7 @@ func (l *jsfsLchown) invoke(ctx context.Context, mod api.Module, args ...interfa _ = args[2] // gid callback := args[3].(funcWrapper) - errno := syscall.ENOSYS // We only support functions used in wasip1 + errno := experimentalsys.ENOSYS // We only support functions used in wasip1 return jsfsInvoke(ctx, mod, callback, errno) } @@ -548,7 +549,7 @@ func (t *jsfsTruncate) invoke(ctx context.Context, mod api.Module, args ...inter _ = args[1] // length callback := args[2].(funcWrapper) - errno := syscall.ENOSYS // We only support functions used in wasip1 + errno := experimentalsys.ENOSYS // We only support functions used in wasip1 return jsfsInvoke(ctx, mod, callback, errno) } @@ -565,9 +566,9 @@ func (jsfsFtruncate) invoke(ctx context.Context, mod api.Module, args ...interfa // Check to see if the file descriptor is available fsc := mod.(*wasm.ModuleInstance).Sys.FS() - var errno syscall.Errno + var errno experimentalsys.Errno if f, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + errno = experimentalsys.EBADF } else { errno = f.File.Truncate(length) } @@ -640,9 +641,9 @@ func (jsfsFsync) invoke(ctx context.Context, mod api.Module, args ...interface{} // Check to see if the file descriptor is available fsc := mod.(*wasm.ModuleInstance).Sys.FS() - var errno syscall.Errno + var errno experimentalsys.Errno if f, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + errno = experimentalsys.EBADF } else { errno = f.File.Sync() } @@ -714,11 +715,11 @@ func (s *jsSt) call(_ context.Context, _ api.Module, _ goos.Ref, method string, panic(fmt.Sprintf("TODO: stat.%s", method)) } -func jsfsInvoke(ctx context.Context, mod api.Module, callback funcWrapper, err syscall.Errno) (interface{}, error) { +func jsfsInvoke(ctx context.Context, mod api.Module, callback funcWrapper, err experimentalsys.Errno) (interface{}, error) { return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(err), err == 0) // note: error first } -func maybeError(errno syscall.Errno) error { +func maybeError(errno experimentalsys.Errno) error { if errno != 0 { return errno } diff --git a/internal/gojs/process.go b/internal/gojs/process.go index 667acbb00f..64af320464 100644 --- a/internal/gojs/process.go +++ b/internal/gojs/process.go @@ -3,9 +3,9 @@ package gojs import ( "context" "path" - "syscall" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/gojs/custom" "github.com/tetratelabs/wazero/internal/gojs/goos" "github.com/tetratelabs/wazero/internal/gojs/util" @@ -65,7 +65,7 @@ func (p *processChdir) invoke(_ context.Context, mod api.Module, args ...interfa if s, err := syscallStat(mod, newWd); err != nil { return nil, err } else if !s.isDir { - return nil, syscall.ENOTDIR + return nil, sys.ENOTDIR } else { p.proc.cwd = newWd return nil, nil diff --git a/internal/gojs/runtime.go b/internal/gojs/runtime.go index dc6c3a0b66..eec1c86be8 100644 --- a/internal/gojs/runtime.go +++ b/internal/gojs/runtime.go @@ -3,10 +3,10 @@ package gojs import ( "context" "fmt" - "syscall" "time" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/gojs/custom" "github.com/tetratelabs/wazero/internal/gojs/goarch" "github.com/tetratelabs/wazero/internal/wasm" @@ -47,9 +47,9 @@ func wasmWrite(_ context.Context, mod api.Module, stack goarch.Stack) { switch errno { case 0: return // success - case syscall.ENOSYS: + case sys.ENOSYS: return // e.g. unimplemented for write - case syscall.EBADF: + case sys.EBADF: return // e.g. not opened for write default: panic(fmt.Errorf("error writing p: %w", errno)) diff --git a/internal/gojs/testdata/writefs/stat.go b/internal/gojs/testdata/writefs/stat.go index eb58ceb3bd..8091840bae 100644 --- a/internal/gojs/testdata/writefs/stat.go +++ b/internal/gojs/testdata/writefs/stat.go @@ -3,10 +3,10 @@ package writefs import ( - "syscall" + "github.com/tetratelabs/wazero/experimental/sys" ) // statFields isn't used outside JS, it is only for compilation func statFields(string) (atimeNsec, mtimeNsec int64, dev, inode uint64) { - panic(syscall.ENOSYS) + panic(sys.ENOSYS) } diff --git a/internal/platform/errno.go b/internal/platform/errno.go deleted file mode 100644 index 43e0342690..0000000000 --- a/internal/platform/errno.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !windows - -package platform - -import "syscall" - -func adjustErrno(err syscall.Errno) syscall.Errno { - return err -} diff --git a/internal/platform/errno_windows.go b/internal/platform/errno_windows.go deleted file mode 100644 index d07e10cb3c..0000000000 --- a/internal/platform/errno_windows.go +++ /dev/null @@ -1,72 +0,0 @@ -package platform - -import "syscall" - -// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- -const ( - // ERROR_ACCESS_DENIED is a Windows error returned by syscall.Unlink - // instead of syscall.EACCES - ERROR_ACCESS_DENIED = syscall.Errno(5) - - // ERROR_INVALID_HANDLE is a Windows error returned by syscall.Write - // instead of syscall.EBADF - ERROR_INVALID_HANDLE = syscall.Errno(6) - - // ERROR_FILE_EXISTS is a Windows error returned by os.OpenFile - // instead of syscall.EEXIST - ERROR_FILE_EXISTS = syscall.Errno(0x50) - - // ERROR_INVALID_NAME is a Windows error returned by open when a file - // path has a trailing slash - ERROR_INVALID_NAME = syscall.Errno(0x7B) - - // ERROR_NEGATIVE_SEEK is a Windows error returned by os.Truncate - // instead of syscall.EINVAL - ERROR_NEGATIVE_SEEK = syscall.Errno(0x83) - - // ERROR_DIR_NOT_EMPTY is a Windows error returned by syscall.Rmdir - // instead of syscall.ENOTEMPTY - ERROR_DIR_NOT_EMPTY = syscall.Errno(0x91) - - // ERROR_ALREADY_EXISTS is a Windows error returned by os.Mkdir - // instead of syscall.EEXIST - ERROR_ALREADY_EXISTS = syscall.Errno(0xB7) - - // ERROR_DIRECTORY is a Windows error returned by syscall.Rmdir - // instead of syscall.ENOTDIR - ERROR_DIRECTORY = syscall.Errno(0x10B) -) - -// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--1300-1699- -const ( - // ERROR_PRIVILEGE_NOT_HELD is a Windows error returned by os.Symlink - // instead of syscall.EPERM. - // - // Note: This can happen when trying to create symlinks w/o admin perms. - ERROR_PRIVILEGE_NOT_HELD = syscall.Errno(0x522) -) - -func adjustErrno(err syscall.Errno) syscall.Errno { - // Note: In windows, ERROR_PATH_NOT_FOUND(0x3) maps to syscall.ENOTDIR - switch err { - case ERROR_ALREADY_EXISTS: - return syscall.EEXIST - case ERROR_DIRECTORY: - return syscall.ENOTDIR - case ERROR_DIR_NOT_EMPTY: - return syscall.ENOTEMPTY - case ERROR_FILE_EXISTS: - return syscall.EEXIST - case ERROR_INVALID_HANDLE: - return syscall.EBADF - case ERROR_ACCESS_DENIED: - // POSIX read and write functions expect EBADF, not EACCES when not - // open for reading or writing. - return syscall.EBADF - case ERROR_PRIVILEGE_NOT_HELD: - return syscall.EPERM - case ERROR_NEGATIVE_SEEK, ERROR_INVALID_NAME: - return syscall.EINVAL - } - return err -} diff --git a/internal/sock/sock.go b/internal/sock/sock.go index 6c0be1516b..c49d1fbb22 100644 --- a/internal/sock/sock.go +++ b/internal/sock/sock.go @@ -3,8 +3,8 @@ package sock import ( "fmt" "net" - "syscall" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" ) @@ -12,7 +12,7 @@ import ( type TCPSock interface { fsapi.File - Accept() (TCPConn, syscall.Errno) + Accept() (TCPConn, sys.Errno) } // TCPConn is a pseudo-file representing a TCP connection. @@ -20,9 +20,11 @@ type TCPConn interface { fsapi.File // Recvfrom only supports the flag sysfs.MSG_PEEK - Recvfrom(p []byte, flags int) (n int, errno syscall.Errno) + // TODO: document this like fsapi.File with known sys.Errno + Recvfrom(p []byte, flags int) (n int, errno sys.Errno) - Shutdown(how int) syscall.Errno + // TODO: document this like fsapi.File with known sys.Errno + Shutdown(how int) sys.Errno } // ConfigKey is a context.Context Value key. Its associated value should be a Config. diff --git a/internal/sys/fs.go b/internal/sys/fs.go index 2e0c04d336..32e2e7b4e9 100644 --- a/internal/sys/fs.go +++ b/internal/sys/fs.go @@ -4,8 +4,8 @@ import ( "io" "io/fs" "net" - "syscall" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/descriptor" "github.com/tetratelabs/wazero/internal/fsapi" socketapi "github.com/tetratelabs/wazero/internal/sock" @@ -61,15 +61,15 @@ type FileEntry struct { // // # Errors // -// A zero syscall.Errno is success. The below are expected otherwise: -// - syscall.ENOSYS: the implementation does not support this function. -// - syscall.EBADF: the dir was closed or not readable. -// - syscall.ENOTDIR: the file was not a directory. +// A zero sys.Errno is success. The below are expected otherwise: +// - sys.ENOSYS: the implementation does not support this function. +// - sys.EBADF: the dir was closed or not readable. +// - sys.ENOTDIR: the file was not a directory. // // # Notes // // - See /RATIONALE.md for design notes. -func (f *FileEntry) DirentCache() (*DirentCache, syscall.Errno) { +func (f *FileEntry) DirentCache() (*DirentCache, sys.Errno) { if dir := f.direntCache; dir != nil { return dir, 0 } @@ -78,7 +78,7 @@ func (f *FileEntry) DirentCache() (*DirentCache, syscall.Errno) { if isDir, errno := f.File.IsDir(); errno != 0 { return nil, errno } else if !isDir { - return nil, syscall.ENOTDIR + return nil, sys.ENOTDIR } // Generate the dotEntries only once. @@ -132,7 +132,7 @@ type DirentCache struct { } // synthesizeDotEntries generates a slice of the two elements "." and "..". -func synthesizeDotEntries(f *FileEntry) ([]fsapi.Dirent, syscall.Errno) { +func synthesizeDotEntries(f *FileEntry) ([]fsapi.Dirent, sys.Errno) { dotIno, errno := f.File.Ino() if errno != 0 { return nil, errno @@ -158,16 +158,16 @@ var exhaustedDirents = [0]fsapi.Dirent{} // // When non-zero, `pos` is the zero based index of all dirents returned since // last rewind. Only entries beginning at `pos` are cached for subsequent -// calls. A non-zero `pos` before the cache returns syscall.ENOENT for reasons +// calls. A non-zero `pos` before the cache returns sys.ENOENT for reasons // described on DirentCache documentation. // // Up to `n` entries are cached and returned. When `n` exceeds the cache, the // difference are read from the underlying fsapi.File via `Readdir`. EOF is // when `len(dirents)` returned are less than `n`. -func (d *DirentCache) Read(pos uint64, n uint32) (dirents []fsapi.Dirent, errno syscall.Errno) { +func (d *DirentCache) Read(pos uint64, n uint32) (dirents []fsapi.Dirent, errno sys.Errno) { switch { case pos > d.countRead: // farther than read or negative coerced to uint64. - return nil, syscall.ENOENT + return nil, sys.ENOENT case pos == 0 && d.dirents != nil: // Rewind if we have already read entries. This allows us to see new // entries added after the directory was opened. @@ -209,7 +209,7 @@ func (d *DirentCache) Read(pos uint64, n uint32) (dirents []fsapi.Dirent, errno // won't do unless wasi-testsuite starts requiring it. Implementing // this would allow re-reading a large directory, so care would be // needed to not buffer the entire directory in memory while skipping. - errno = syscall.ENOENT + errno = sys.ENOENT return } else if posInCache := pos - cacheStart; posInCache != 0 { if uint64(len(d.dirents)) == posInCache { @@ -284,7 +284,7 @@ func (c *FSContext) LookupFile(fd int32) (*FileEntry, bool) { // OpenFile opens the file into the table and returns its file descriptor. // The result must be closed by CloseFile or Close. -func (c *FSContext) OpenFile(fs fsapi.FS, path string, flag int, perm fs.FileMode) (int32, syscall.Errno) { +func (c *FSContext) OpenFile(fs fsapi.FS, path string, flag int, perm fs.FileMode) (int32, sys.Errno) { if f, errno := fs.OpenFile(path, flag, perm); errno != 0 { return 0, errno } else { @@ -295,7 +295,7 @@ func (c *FSContext) OpenFile(fs fsapi.FS, path string, flag int, perm fs.FileMod fe.Name = path } if newFD, ok := c.openedFiles.Insert(fe); !ok { - return 0, syscall.EBADF + return 0, sys.EBADF } else { return newFD, 0 } @@ -303,12 +303,12 @@ func (c *FSContext) OpenFile(fs fsapi.FS, path string, flag int, perm fs.FileMod } // Renumber assigns the file pointed by the descriptor `from` to `to`. -func (c *FSContext) Renumber(from, to int32) syscall.Errno { +func (c *FSContext) Renumber(from, to int32) sys.Errno { fromFile, ok := c.openedFiles.Lookup(from) if !ok || to < 0 { - return syscall.EBADF + return sys.EBADF } else if fromFile.IsPreopen { - return syscall.ENOTSUP + return sys.ENOTSUP } // If toFile is already open, we close it to prevent windows lock issues. @@ -318,30 +318,30 @@ func (c *FSContext) Renumber(from, to int32) syscall.Errno { // https://github.com/bytecodealliance/wasmtime/blob/main/crates/wasi-common/src/snapshots/preview_1.rs#L531-L546 if toFile, ok := c.openedFiles.Lookup(to); ok { if toFile.IsPreopen { - return syscall.ENOTSUP + return sys.ENOTSUP } _ = toFile.File.Close() } c.openedFiles.Delete(from) if !c.openedFiles.InsertAt(fromFile, to) { - return syscall.EBADF + return sys.EBADF } return 0 } // SockAccept accepts a socketapi.TCPConn into the file table and returns // its file descriptor. -func (c *FSContext) SockAccept(sockFD int32, nonblock bool) (int32, syscall.Errno) { +func (c *FSContext) SockAccept(sockFD int32, nonblock bool) (int32, sys.Errno) { var sock socketapi.TCPSock if e, ok := c.LookupFile(sockFD); !ok || !e.IsPreopen { - return 0, syscall.EBADF // Not a preopen + return 0, sys.EBADF // Not a preopen } else if sock, ok = e.File.(socketapi.TCPSock); !ok { - return 0, syscall.EBADF // Not a sock + return 0, sys.EBADF // Not a sock } var conn socketapi.TCPConn - var errno syscall.Errno + var errno sys.Errno if conn, errno = sock.Accept(); errno != 0 { return 0, errno } else if nonblock { @@ -353,17 +353,17 @@ func (c *FSContext) SockAccept(sockFD int32, nonblock bool) (int32, syscall.Errn fe := &FileEntry{File: conn} if newFD, ok := c.openedFiles.Insert(fe); !ok { - return 0, syscall.EBADF + return 0, sys.EBADF } else { return newFD, 0 } } // CloseFile returns any error closing the existing file. -func (c *FSContext) CloseFile(fd int32) (errno syscall.Errno) { +func (c *FSContext) CloseFile(fd int32) (errno sys.Errno) { f, ok := c.openedFiles.Lookup(fd) if !ok { - return syscall.EBADF + return sys.EBADF } if errno = f.File.Close(); errno != 0 { return errno diff --git a/internal/sys/fs_test.go b/internal/sys/fs_test.go index 0a199963c3..b8b7a1b749 100644 --- a/internal/sys/fs_test.go +++ b/internal/sys/fs_test.go @@ -7,10 +7,10 @@ import ( "io/fs" "os" "path" - "syscall" "testing" gofstest "testing/fstest" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/fstest" "github.com/tetratelabs/wazero/internal/sysfs" @@ -123,7 +123,7 @@ func TestFSContext_CloseFile(t *testing.T) { require.True(t, ok) t.Run("EBADF for an invalid FD", func(t *testing.T) { - require.EqualErrno(t, syscall.EBADF, fsc.CloseFile(42)) // 42 is an arbitrary invalid FD + require.EqualErrno(t, sys.EBADF, fsc.CloseFile(42)) // 42 is an arbitrary invalid FD }) t.Run("Can close a pre-open", func(t *testing.T) { require.EqualErrno(t, 0, fsc.CloseFile(FdPreopen)) @@ -194,7 +194,7 @@ func TestContext_Close_Error(t *testing.T) { require.EqualErrno(t, 0, errno) // arbitrary errors coerce to EIO - require.EqualErrno(t, syscall.EIO, fsc.Close()) + require.EqualErrno(t, sys.EIO, fsc.Close()) // Paths should clear even under error require.Zero(t, fsc.openedFiles.Len(), "expected no opened files") @@ -241,13 +241,13 @@ func TestFSContext_Renumber(t *testing.T) { require.True(t, preopen.IsPreopen) // From is preopen. - require.Equal(t, syscall.ENOTSUP, fsc.Renumber(3, 100)) + require.Equal(t, sys.ENOTSUP, fsc.Renumber(3, 100)) // From does not exist. - require.Equal(t, syscall.EBADF, fsc.Renumber(12345, 3)) + require.Equal(t, sys.EBADF, fsc.Renumber(12345, 3)) // Both are preopen. - require.Equal(t, syscall.ENOTSUP, fsc.Renumber(3, 3)) + require.Equal(t, sys.ENOTSUP, fsc.Renumber(3, 3)) }) } @@ -279,7 +279,7 @@ func TestDirentCache_Read(t *testing.T) { pos uint64 n uint32 expectedDirents []fsapi.Dirent - expectedErrno syscall.Errno + expectedErrno sys.Errno }{ { name: "empty dir has dot entries", @@ -391,14 +391,14 @@ func TestDirentCache_Read(t *testing.T) { initialDir: "dir/-", pos: 0, n: 1, - expectedErrno: syscall.ENOTDIR, + expectedErrno: sys.ENOTDIR, }, { name: "pos invalid when no prior state", initialDir: "dir", pos: 1, n: 1, - expectedErrno: syscall.ENOENT, + expectedErrno: sys.ENOENT, }, } diff --git a/internal/sys/lazy.go b/internal/sys/lazy.go index acda223696..718398b920 100644 --- a/internal/sys/lazy.go +++ b/internal/sys/lazy.go @@ -4,6 +4,7 @@ import ( "os" "syscall" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/sys" ) @@ -19,29 +20,29 @@ type lazyDir struct { } // Dev implements the same method as documented on fsapi.File -func (r *lazyDir) Dev() (uint64, syscall.Errno) { +func (r *lazyDir) Dev() (uint64, experimentalsys.Errno) { if f, ok := r.file(); !ok { - return 0, syscall.EBADF + return 0, experimentalsys.EBADF } else { return f.Dev() } } // Ino implements the same method as documented on fsapi.File -func (r *lazyDir) Ino() (sys.Inode, syscall.Errno) { +func (r *lazyDir) Ino() (sys.Inode, experimentalsys.Errno) { if f, ok := r.file(); !ok { - return 0, syscall.EBADF + return 0, experimentalsys.EBADF } else { return f.Ino() } } // IsDir implements the same method as documented on fsapi.File -func (r *lazyDir) IsDir() (bool, syscall.Errno) { +func (r *lazyDir) IsDir() (bool, experimentalsys.Errno) { // Note: we don't return a constant because we don't know if this is really // backed by a dir, until the first call. if f, ok := r.file(); !ok { - return false, syscall.EBADF + return false, experimentalsys.EBADF } else { return f.IsDir() } @@ -53,59 +54,59 @@ func (r *lazyDir) IsAppend() bool { } // SetAppend implements the same method as documented on fsapi.File -func (r *lazyDir) SetAppend(bool) syscall.Errno { - return syscall.EISDIR +func (r *lazyDir) SetAppend(bool) experimentalsys.Errno { + return experimentalsys.EISDIR } // Seek implements the same method as documented on fsapi.File -func (r *lazyDir) Seek(offset int64, whence int) (newOffset int64, errno syscall.Errno) { +func (r *lazyDir) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) { if f, ok := r.file(); !ok { - return 0, syscall.EBADF + return 0, experimentalsys.EBADF } else { return f.Seek(offset, whence) } } // Stat implements the same method as documented on fsapi.File -func (r *lazyDir) Stat() (sys.Stat_t, syscall.Errno) { +func (r *lazyDir) Stat() (sys.Stat_t, experimentalsys.Errno) { if f, ok := r.file(); !ok { - return sys.Stat_t{}, syscall.EBADF + return sys.Stat_t{}, experimentalsys.EBADF } else { return f.Stat() } } // Readdir implements the same method as documented on fsapi.File -func (r *lazyDir) Readdir(n int) (dirents []fsapi.Dirent, errno syscall.Errno) { +func (r *lazyDir) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.Errno) { if f, ok := r.file(); !ok { - return nil, syscall.EBADF + return nil, experimentalsys.EBADF } else { return f.Readdir(n) } } // Sync implements the same method as documented on fsapi.File -func (r *lazyDir) Sync() syscall.Errno { +func (r *lazyDir) Sync() experimentalsys.Errno { if f, ok := r.file(); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else { return f.Sync() } } // Datasync implements the same method as documented on fsapi.File -func (r *lazyDir) Datasync() syscall.Errno { +func (r *lazyDir) Datasync() experimentalsys.Errno { if f, ok := r.file(); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else { return f.Datasync() } } // Utimens implements the same method as documented on fsapi.File -func (r *lazyDir) Utimens(times *[2]syscall.Timespec) syscall.Errno { +func (r *lazyDir) Utimens(times *[2]syscall.Timespec) experimentalsys.Errno { if f, ok := r.file(); !ok { - return syscall.EBADF + return experimentalsys.EBADF } else { return f.Utimens(times) } @@ -116,12 +117,12 @@ func (r *lazyDir) file() (fsapi.File, bool) { if f := r.f; r.f != nil { return f, true } - var errno syscall.Errno + var errno experimentalsys.Errno r.f, errno = r.fs.OpenFile(".", os.O_RDONLY, 0) switch errno { case 0: return r.f, true - case syscall.ENOENT: + case experimentalsys.ENOENT: return nil, false default: panic(errno) // unexpected @@ -129,7 +130,7 @@ func (r *lazyDir) file() (fsapi.File, bool) { } // Close implements fs.File -func (r *lazyDir) Close() syscall.Errno { +func (r *lazyDir) Close() experimentalsys.Errno { f := r.f if f == nil { return 0 // never opened diff --git a/internal/sys/stdio.go b/internal/sys/stdio.go index 84ed6d901c..59ffd084a7 100644 --- a/internal/sys/stdio.go +++ b/internal/sys/stdio.go @@ -6,8 +6,8 @@ import ( "syscall" "time" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" - "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/sysfs" "github.com/tetratelabs/wazero/sys" ) @@ -21,9 +21,9 @@ type StdinFile struct { } // Read implements the same method as documented on fsapi.File -func (f *StdinFile) Read(buf []byte) (int, syscall.Errno) { +func (f *StdinFile) Read(buf []byte) (int, experimentalsys.Errno) { n, err := f.Reader.Read(buf) - return n, platform.UnwrapOSError(err) + return n, experimentalsys.UnwrapOSError(err) } type writerFile struct { @@ -33,9 +33,9 @@ type writerFile struct { } // Write implements the same method as documented on fsapi.File -func (f *writerFile) Write(buf []byte) (int, syscall.Errno) { +func (f *writerFile) Write(buf []byte) (int, experimentalsys.Errno) { n, err := f.w.Write(buf) - return n, platform.UnwrapOSError(err) + return n, experimentalsys.UnwrapOSError(err) } // noopStdinFile is a fs.ModeDevice file for use implementing FdStdin. This is @@ -51,12 +51,12 @@ func (noopStdinFile) AccessMode() int { } // Read implements the same method as documented on fsapi.File -func (noopStdinFile) Read([]byte) (int, syscall.Errno) { +func (noopStdinFile) Read([]byte) (int, experimentalsys.Errno) { return 0, 0 // Always EOF } // PollRead implements the same method as documented on fsapi.File -func (noopStdinFile) PollRead(*time.Duration) (ready bool, errno syscall.Errno) { +func (noopStdinFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) { return true, 0 // always ready to read nothing } @@ -72,7 +72,7 @@ func (noopStdoutFile) AccessMode() int { } // Write implements the same method as documented on fsapi.File -func (noopStdoutFile) Write(buf []byte) (int, syscall.Errno) { +func (noopStdoutFile) Write(buf []byte) (int, experimentalsys.Errno) { return len(buf), 0 // same as io.Discard } @@ -81,17 +81,17 @@ type noopStdioFile struct { } // Stat implements the same method as documented on fsapi.File -func (noopStdioFile) Stat() (sys.Stat_t, syscall.Errno) { +func (noopStdioFile) Stat() (sys.Stat_t, experimentalsys.Errno) { return sys.Stat_t{Mode: modeDevice, Nlink: 1}, 0 } // IsDir implements the same method as documented on fsapi.File -func (noopStdioFile) IsDir() (bool, syscall.Errno) { +func (noopStdioFile) IsDir() (bool, experimentalsys.Errno) { return false, 0 } // Close implements the same method as documented on fsapi.File -func (noopStdioFile) Close() (errno syscall.Errno) { return } +func (noopStdioFile) Close() (errno experimentalsys.Errno) { return } func stdinFileEntry(r io.Reader) (*FileEntry, error) { if r == nil { diff --git a/internal/sysfs/adapter.go b/internal/sysfs/adapter.go index a052d08056..06b5369d81 100644 --- a/internal/sysfs/adapter.go +++ b/internal/sysfs/adapter.go @@ -6,6 +6,7 @@ import ( "path" "syscall" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/sys" ) @@ -38,12 +39,12 @@ func (a *adapter) String() string { } // OpenFile implements the same method as documented on fsapi.FS -func (a *adapter) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, syscall.Errno) { +func (a *adapter) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { return OpenFSFile(a.fs, cleanPath(path), flag, perm) } // Stat implements the same method as documented on fsapi.FS -func (a *adapter) Stat(path string) (sys.Stat_t, syscall.Errno) { +func (a *adapter) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { f, errno := a.OpenFile(path, syscall.O_RDONLY, 0) if errno != 0 { return sys.Stat_t{}, errno @@ -53,7 +54,7 @@ func (a *adapter) Stat(path string) (sys.Stat_t, syscall.Errno) { } // Lstat implements the same method as documented on fsapi.FS -func (a *adapter) Lstat(path string) (sys.Stat_t, syscall.Errno) { +func (a *adapter) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { // At this time, we make the assumption that fsapi.FS instances do not support // symbolic links, therefore Lstat is the same as Stat. This is obviously // not true but until fsapi.FS has a solid story for how to handle symlinks we diff --git a/internal/sysfs/adapter_test.go b/internal/sysfs/adapter_test.go index 23adf0a6b7..6c6ce917fa 100644 --- a/internal/sysfs/adapter_test.go +++ b/internal/sysfs/adapter_test.go @@ -11,6 +11,7 @@ import ( "syscall" "testing" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/fstest" "github.com/tetratelabs/wazero/internal/testing/require" @@ -27,14 +28,14 @@ func TestAdapt_MkDir(t *testing.T) { testFS := Adapt(os.DirFS(t.TempDir())) err := testFS.Mkdir("mkdir", fs.ModeDir) - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, experimentalsys.ENOSYS, err) } func TestAdapt_Chmod(t *testing.T) { testFS := Adapt(os.DirFS(t.TempDir())) err := testFS.Chmod("chmod", fs.ModeDir) - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, experimentalsys.ENOSYS, err) } func TestAdapt_Rename(t *testing.T) { @@ -54,7 +55,7 @@ func TestAdapt_Rename(t *testing.T) { require.NoError(t, err) err = testFS.Rename(file1, file2) - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, experimentalsys.ENOSYS, err) } func TestAdapt_Rmdir(t *testing.T) { @@ -66,7 +67,7 @@ func TestAdapt_Rmdir(t *testing.T) { require.NoError(t, os.Mkdir(realPath, 0o700)) err := testFS.Rmdir(path) - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, experimentalsys.ENOSYS, err) } func TestAdapt_Unlink(t *testing.T) { @@ -78,7 +79,7 @@ func TestAdapt_Unlink(t *testing.T) { require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600)) err := testFS.Unlink(path) - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, experimentalsys.ENOSYS, err) } func TestAdapt_UtimesNano(t *testing.T) { @@ -90,7 +91,7 @@ func TestAdapt_UtimesNano(t *testing.T) { require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600)) err := testFS.Utimens(path, nil, true) - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, experimentalsys.ENOSYS, err) } func TestAdapt_Open_Read(t *testing.T) { @@ -109,7 +110,7 @@ func TestAdapt_Open_Read(t *testing.T) { _, err := testFS.OpenFile("../foo", os.O_RDONLY, 0) // fsapi.FS doesn't allow relative path lookups - require.EqualErrno(t, syscall.EINVAL, err) + require.EqualErrno(t, experimentalsys.EINVAL, err) }) } diff --git a/internal/sysfs/bench_test.go b/internal/sysfs/bench_test.go index 23b20fe5c4..fd7e1a288e 100644 --- a/internal/sysfs/bench_test.go +++ b/internal/sysfs/bench_test.go @@ -7,6 +7,8 @@ import ( "path" "syscall" "testing" + + "github.com/tetratelabs/wazero/experimental/sys" ) func BenchmarkFsFileUtimesNs(b *testing.B) { @@ -65,7 +67,7 @@ func BenchmarkFsFileRead(b *testing.B) { b.StopTimer() var n int - var errno syscall.Errno + var errno sys.Errno // Reset the read position back to the beginning of the file. if _, errno = f.Seek(0, io.SeekStart); errno != 0 { diff --git a/internal/sysfs/datasync_linux.go b/internal/sysfs/datasync_linux.go index 715a952dfa..c37e698d2f 100644 --- a/internal/sysfs/datasync_linux.go +++ b/internal/sysfs/datasync_linux.go @@ -6,9 +6,9 @@ import ( "os" "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func datasync(f *os.File) syscall.Errno { - return platform.UnwrapOSError(syscall.Fdatasync(int(f.Fd()))) +func datasync(f *os.File) sys.Errno { + return sys.UnwrapOSError(syscall.Fdatasync(int(f.Fd()))) } diff --git a/internal/sysfs/datasync_unsupported.go b/internal/sysfs/datasync_unsupported.go index 4261f56575..aa05719be6 100644 --- a/internal/sysfs/datasync_unsupported.go +++ b/internal/sysfs/datasync_unsupported.go @@ -4,10 +4,11 @@ package sysfs import ( "os" - "syscall" + + "github.com/tetratelabs/wazero/experimental/sys" ) -func datasync(f *os.File) syscall.Errno { +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/internal/sysfs/dir.go b/internal/sysfs/dir.go index c3256f9c1e..5a217394b3 100644 --- a/internal/sysfs/dir.go +++ b/internal/sysfs/dir.go @@ -2,23 +2,22 @@ package sysfs import ( "io" - "syscall" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" - "github.com/tetratelabs/wazero/internal/platform" ) -func adjustReaddirErr(f fsapi.File, isClosed bool, err error) syscall.Errno { +func adjustReaddirErr(f fsapi.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 := platform.UnwrapOSError(err); errno != 0 { + } else if errno := sys.UnwrapOSError(err); errno != 0 { errno = dirError(f, isClosed, errno) // Comply with errors allowed on fsapi.File Readdir switch errno { - case syscall.EINVAL: // os.File Readdir can return this - return syscall.EBADF - case syscall.ENOTDIR: // dirError can return this - return syscall.EBADF + case sys.EINVAL: // os.File Readdir can return this + return sys.EBADF + case sys.ENOTDIR: // dirError can return this + return sys.EBADF } return errno } diff --git a/internal/sysfs/dir_test.go b/internal/sysfs/dir_test.go index 42886f178a..5aafb89ddb 100644 --- a/internal/sysfs/dir_test.go +++ b/internal/sysfs/dir_test.go @@ -9,6 +9,7 @@ import ( "syscall" "testing" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/fstest" "github.com/tetratelabs/wazero/internal/sysfs" @@ -72,7 +73,7 @@ func TestFSFileReaddir(t *testing.T) { t.Run("closed dir", func(t *testing.T) { require.EqualErrno(t, 0, dotF.Close()) _, errno := dotF.Readdir(-1) - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, sys.EBADF, errno) }) fileF, errno := sysfs.OpenFSFile(tc.fs, "empty.txt", syscall.O_RDONLY, 0) @@ -81,7 +82,7 @@ func TestFSFileReaddir(t *testing.T) { t.Run("file", func(t *testing.T) { _, errno := fileF.Readdir(-1) - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, sys.EBADF, errno) }) dirF, errno := sysfs.OpenFSFile(tc.fs, "dir", syscall.O_RDONLY, 0) diff --git a/internal/sysfs/dirfs.go b/internal/sysfs/dirfs.go index acf1c3b87b..281381271a 100644 --- a/internal/sysfs/dirfs.go +++ b/internal/sysfs/dirfs.go @@ -5,6 +5,7 @@ import ( "os" "syscall" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/sys" @@ -38,84 +39,84 @@ func (d *dirFS) String() string { } // OpenFile implements the same method as documented on fsapi.FS -func (d *dirFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, syscall.Errno) { +func (d *dirFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { return OpenOSFile(d.join(path), flag, perm) } // Lstat implements the same method as documented on fsapi.FS -func (d *dirFS) Lstat(path string) (sys.Stat_t, syscall.Errno) { +func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { return lstat(d.join(path)) } // Stat implements the same method as documented on fsapi.FS -func (d *dirFS) Stat(path string) (sys.Stat_t, syscall.Errno) { +func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { return stat(d.join(path)) } // Mkdir implements the same method as documented on fsapi.FS -func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno syscall.Errno) { +func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) { err := os.Mkdir(d.join(path), perm) - if errno = platform.UnwrapOSError(err); errno == syscall.ENOTDIR { - errno = syscall.ENOENT + if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR { + errno = experimentalsys.ENOENT } return } // Chmod implements the same method as documented on fsapi.FS -func (d *dirFS) Chmod(path string, perm fs.FileMode) syscall.Errno { +func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { err := os.Chmod(d.join(path), perm) - return platform.UnwrapOSError(err) + return experimentalsys.UnwrapOSError(err) } // Rename implements the same method as documented on fsapi.FS -func (d *dirFS) Rename(from, to string) syscall.Errno { +func (d *dirFS) Rename(from, to string) experimentalsys.Errno { from, to = d.join(from), d.join(to) return rename(from, to) } // Readlink implements the same method as documented on fsapi.FS -func (d *dirFS) Readlink(path string) (string, syscall.Errno) { +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 "", platform.UnwrapOSError(err) + return "", experimentalsys.UnwrapOSError(err) } return platform.ToPosixPath(dst), 0 } // Link implements the same method as documented on fsapi.FS -func (d *dirFS) Link(oldName, newName string) syscall.Errno { +func (d *dirFS) Link(oldName, newName string) experimentalsys.Errno { err := os.Link(d.join(oldName), d.join(newName)) - return platform.UnwrapOSError(err) + return experimentalsys.UnwrapOSError(err) } // Rmdir implements the same method as documented on fsapi.FS -func (d *dirFS) Rmdir(path string) syscall.Errno { +func (d *dirFS) Rmdir(path string) experimentalsys.Errno { return rmdir(d.join(path)) } -func rmdir(path string) syscall.Errno { +func rmdir(path string) experimentalsys.Errno { err := syscall.Rmdir(path) - return platform.UnwrapOSError(err) + return experimentalsys.UnwrapOSError(err) } // Unlink implements the same method as documented on fsapi.FS -func (d *dirFS) Unlink(path string) (err syscall.Errno) { +func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) { return unlink(d.join(path)) } // Symlink implements the same method as documented on fsapi.FS -func (d *dirFS) Symlink(oldName, link string) syscall.Errno { +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 platform.UnwrapOSError(err) + return experimentalsys.UnwrapOSError(err) } // Utimens implements the same method as documented on fsapi.FS -func (d *dirFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { +func (d *dirFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) experimentalsys.Errno { return Utimens(d.join(path), times, symlinkFollow) } diff --git a/internal/sysfs/dirfs_test.go b/internal/sysfs/dirfs_test.go index 2e032bf026..632efcd609 100644 --- a/internal/sysfs/dirfs_test.go +++ b/internal/sysfs/dirfs_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/fstest" "github.com/tetratelabs/wazero/internal/platform" @@ -28,7 +29,7 @@ func TestNewDirFS(t *testing.T) { t.Run("host path not found", func(t *testing.T) { testFS := NewDirFS("a") _, errno = testFS.OpenFile(".", os.O_RDONLY, 0) - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, sys.ENOENT, errno) }) t.Run("host path not a directory", func(t *testing.T) { arg0 := os.Args[0] // should be safe in scratch tests which don't have the source mounted. @@ -37,7 +38,7 @@ func TestNewDirFS(t *testing.T) { d, errno := testFS.OpenFile(".", os.O_RDONLY, 0) require.EqualErrno(t, 0, errno) _, errno = d.Readdir(-1) - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, sys.EBADF, errno) }) } @@ -93,7 +94,7 @@ func TestDirFS_MkDir(t *testing.T) { t.Run("dir exists", func(t *testing.T) { err := testFS.Mkdir(name, fs.ModeDir) - require.EqualErrno(t, syscall.EEXIST, err) + require.EqualErrno(t, sys.EEXIST, err) }) t.Run("file exists", func(t *testing.T) { @@ -101,12 +102,12 @@ func TestDirFS_MkDir(t *testing.T) { require.NoError(t, os.Mkdir(realPath, 0o700)) err := testFS.Mkdir(name, fs.ModeDir) - require.EqualErrno(t, syscall.EEXIST, err) + require.EqualErrno(t, sys.EEXIST, err) }) t.Run("try creating on file", func(t *testing.T) { filePath := path.Join("non-existing-dir", "foo.txt") err := testFS.Mkdir(filePath, fs.ModeDir) - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) }) // Remove the path so that we can test creating it with perms. @@ -161,7 +162,7 @@ func TestDirFS_Rename(t *testing.T) { require.NoError(t, err) err = testFS.Rename("file2", file1) - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) }) t.Run("file to non-exist", func(t *testing.T) { tmpDir := t.TempDir() @@ -180,7 +181,7 @@ func TestDirFS_Rename(t *testing.T) { // Show the prior path no longer exists _, errno = os.Stat(file1Path) - require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(errno)) + require.EqualErrno(t, sys.ENOENT, sys.UnwrapOSError(errno)) s, err := os.Stat(file2Path) require.NoError(t, err) @@ -201,7 +202,7 @@ func TestDirFS_Rename(t *testing.T) { // Show the prior path no longer exists _, err := os.Stat(dir1Path) - require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(err)) + require.EqualErrno(t, sys.ENOENT, sys.UnwrapOSError(err)) s, err := os.Stat(dir2Path) require.NoError(t, err) @@ -224,7 +225,7 @@ func TestDirFS_Rename(t *testing.T) { require.NoError(t, f.Close()) errno := testFS.Rename(dir1, dir2) - require.EqualErrno(t, syscall.ENOTDIR, errno) + require.EqualErrno(t, sys.ENOTDIR, errno) }) t.Run("file to dir", func(t *testing.T) { tmpDir := t.TempDir() @@ -241,7 +242,7 @@ func TestDirFS_Rename(t *testing.T) { require.NoError(t, os.Mkdir(dir1Path, 0o700)) errno := testFS.Rename(file1, dir1) - require.EqualErrno(t, syscall.EISDIR, errno) + require.EqualErrno(t, sys.EISDIR, errno) }) // Similar to https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs/test.zig#L567-L582 @@ -269,7 +270,7 @@ func TestDirFS_Rename(t *testing.T) { // Show the prior path no longer exists _, err = os.Stat(dir1Path) - require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(err)) + require.EqualErrno(t, sys.ENOENT, sys.UnwrapOSError(err)) // Show the file inside that directory moved s, err := os.Stat(path.Join(dir2Path, file1)) @@ -302,7 +303,7 @@ func TestDirFS_Rename(t *testing.T) { require.NoError(t, err) errno := testFS.Rename(dir1, dir2) - require.EqualErrno(t, syscall.ENOTEMPTY, errno) + require.EqualErrno(t, sys.ENOTEMPTY, errno) }) t.Run("file to file", func(t *testing.T) { @@ -326,7 +327,7 @@ func TestDirFS_Rename(t *testing.T) { // Show the prior path no longer exists _, err = os.Stat(file1Path) - require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(err)) + require.EqualErrno(t, sys.ENOENT, sys.UnwrapOSError(err)) // Show the file1 overwrote file2 b, err := os.ReadFile(file2Path) @@ -375,7 +376,7 @@ func TestDirFS_Rmdir(t *testing.T) { name := "rmdir" err := testFS.Rmdir(name) - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) }) t.Run("dir not empty", func(t *testing.T) { @@ -390,7 +391,7 @@ func TestDirFS_Rmdir(t *testing.T) { require.NoError(t, os.WriteFile(fileInDir, []byte{}, 0o600)) err := testFS.Rmdir(name) - require.EqualErrno(t, syscall.ENOTEMPTY, err) + require.EqualErrno(t, sys.ENOTEMPTY, err) require.NoError(t, os.Remove(fileInDir)) }) @@ -452,7 +453,7 @@ func TestDirFS_Rmdir(t *testing.T) { require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600)) err := testFS.Rmdir(name) - require.EqualErrno(t, syscall.ENOTDIR, err) + require.EqualErrno(t, sys.ENOTDIR, err) require.NoError(t, os.Remove(realPath)) }) @@ -465,7 +466,7 @@ func TestDirFS_Unlink(t *testing.T) { name := "unlink" err := testFS.Unlink(name) - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) }) t.Run("target: dir", func(t *testing.T) { @@ -478,7 +479,7 @@ func TestDirFS_Unlink(t *testing.T) { require.NoError(t, os.Mkdir(realPath, 0o700)) err := testFS.Unlink(dir) - require.EqualErrno(t, syscall.EISDIR, err) + require.EqualErrno(t, sys.EISDIR, err) require.NoError(t, os.Remove(realPath)) }) @@ -531,12 +532,12 @@ func TestDirFS_Utimesns(t *testing.T) { t.Run("doesn't exist", func(t *testing.T) { err := testFS.Utimens("nope", nil, true) - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) err = testFS.Utimens("nope", nil, false) if SupportsSymlinkNoFollow { - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) } else { - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, sys.ENOSYS, err) } }) @@ -647,7 +648,7 @@ func TestDirFS_Utimesns(t *testing.T) { errno = testFS.Utimens(path, tc.times, !symlinkNoFollow) if symlinkNoFollow && !SupportsSymlinkNoFollow { - require.EqualErrno(t, syscall.ENOSYS, errno) + require.EqualErrno(t, sys.ENOSYS, errno) return } require.EqualErrno(t, 0, errno) @@ -759,12 +760,12 @@ func TestDirFS_Readdir(t *testing.T) { // tests enforce this. This test is Windows sensitive as well. // // https://github.com/ziglang/zig/blob/e3736baddb8ecff90f0594be9f604c7484ce9aa2/lib/std/fs/test.zig#L311C1-L311C1 - t.Run("syscall.ENOENT or no error, deleted while reading", func(t *testing.T) { + t.Run("sys.ENOENT or no error, deleted while reading", func(t *testing.T) { require.NoError(t, os.RemoveAll(path.Join(root, readDirTarget))) dirents, errno := dirFile.Readdir(-1) if errno != 0 { - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, sys.ENOENT, errno) } require.Equal(t, 0, len(dirents)) @@ -780,11 +781,11 @@ func TestDirFS_Link(t *testing.T) { testFS := NewDirFS(tmpDir) - require.EqualErrno(t, testFS.Link("cat", ""), syscall.ENOENT) - require.EqualErrno(t, testFS.Link("sub/test.txt", "sub/test.txt"), syscall.EEXIST) - require.EqualErrno(t, testFS.Link("sub/test.txt", "."), syscall.EEXIST) - require.EqualErrno(t, testFS.Link("sub/test.txt", ""), syscall.EEXIST) - require.EqualErrno(t, testFS.Link("sub/test.txt", "/"), syscall.EEXIST) + require.EqualErrno(t, testFS.Link("cat", ""), sys.ENOENT) + require.EqualErrno(t, testFS.Link("sub/test.txt", "sub/test.txt"), sys.EEXIST) + require.EqualErrno(t, testFS.Link("sub/test.txt", "."), sys.EEXIST) + require.EqualErrno(t, testFS.Link("sub/test.txt", ""), sys.EEXIST) + require.EqualErrno(t, testFS.Link("sub/test.txt", "/"), sys.EEXIST) require.EqualErrno(t, 0, testFS.Link("sub/test.txt", "foo")) } @@ -797,7 +798,7 @@ func TestDirFS_Symlink(t *testing.T) { testFS := NewDirFS(tmpDir) - require.EqualErrno(t, syscall.EEXIST, testFS.Symlink("sub/test.txt", "sub/test.txt")) + require.EqualErrno(t, sys.EEXIST, testFS.Symlink("sub/test.txt", "sub/test.txt")) // Non-existing old name is allowed. require.EqualErrno(t, 0, testFS.Symlink("non-existing", "aa")) require.EqualErrno(t, 0, testFS.Symlink("sub/", "symlinked-subdir")) diff --git a/internal/sysfs/file.go b/internal/sysfs/file.go index a1fdc3ffe1..72d0886827 100644 --- a/internal/sysfs/file.go +++ b/internal/sysfs/file.go @@ -6,8 +6,8 @@ import ( "os" "syscall" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" - "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/sys" ) @@ -37,14 +37,14 @@ func NewStdioFile(stdin bool, f fs.File) (fsapi.File, error) { return &stdioFile{File: file, st: sys.Stat_t{Mode: mode, Nlink: 1}}, nil } -func OpenFile(path string, flag int, perm fs.FileMode) (*os.File, syscall.Errno) { +func OpenFile(path string, flag int, perm fs.FileMode) (*os.File, experimentalsys.Errno) { if flag&fsapi.O_DIRECTORY != 0 && flag&(syscall.O_WRONLY|syscall.O_RDWR) != 0 { - return nil, syscall.EISDIR // invalid to open a directory writeable + return nil, experimentalsys.EISDIR // invalid to open a directory writeable } return openFile(path, flag, perm) } -func OpenOSFile(path string, flag int, perm fs.FileMode) (fsapi.File, syscall.Errno) { +func OpenOSFile(path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { f, errno := OpenFile(path, flag, perm) if errno != 0 { return nil, errno @@ -52,12 +52,12 @@ func OpenOSFile(path string, flag int, perm fs.FileMode) (fsapi.File, syscall.Er return newOsFile(path, flag, perm, f), 0 } -func OpenFSFile(fs fs.FS, path string, flag int, perm fs.FileMode) (fsapi.File, syscall.Errno) { +func OpenFSFile(fs fs.FS, path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { if flag&fsapi.O_DIRECTORY != 0 && flag&(syscall.O_WRONLY|syscall.O_RDWR) != 0 { - return nil, syscall.EISDIR // invalid to open a directory writeable + return nil, experimentalsys.EISDIR // invalid to open a directory writeable } f, err := fs.Open(path) - if errno := platform.UnwrapOSError(err); errno != 0 { + if errno := experimentalsys.UnwrapOSError(err); errno != 0 { return nil, errno } // Don't return an os.File because the path is not absolute. osFile needs @@ -71,7 +71,7 @@ type stdioFile struct { } // SetAppend implements File.SetAppend -func (f *stdioFile) SetAppend(bool) syscall.Errno { +func (f *stdioFile) SetAppend(bool) experimentalsys.Errno { // Ignore for stdio. return 0 } @@ -82,12 +82,12 @@ func (f *stdioFile) IsAppend() bool { } // Stat implements File.Stat -func (f *stdioFile) Stat() (sys.Stat_t, syscall.Errno) { +func (f *stdioFile) Stat() (sys.Stat_t, experimentalsys.Errno) { return f.st, 0 } // Close implements File.Close -func (f *stdioFile) Close() syscall.Errno { +func (f *stdioFile) Close() experimentalsys.Errno { return 0 } @@ -113,7 +113,7 @@ type fsFile struct { // before Readdir. reopenDir bool - // closed is true when closed was called. This ensures proper syscall.EBADF + // 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. @@ -133,7 +133,7 @@ type cachedStat struct { // cachedStat returns the cacheable parts of fsapi.Stat_t or an error if they // couldn't be retrieved. -func (f *fsFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno syscall.Errno) { +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 @@ -143,19 +143,19 @@ func (f *fsFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno sysc } // Dev implements the same method as documented on fsapi.File -func (f *fsFile) Dev() (uint64, syscall.Errno) { +func (f *fsFile) Dev() (uint64, experimentalsys.Errno) { dev, _, _, errno := f.cachedStat() return dev, errno } // Ino implements the same method as documented on fsapi.File -func (f *fsFile) Ino() (sys.Inode, syscall.Errno) { +func (f *fsFile) Ino() (sys.Inode, experimentalsys.Errno) { _, ino, _, errno := f.cachedStat() return ino, errno } // IsDir implements the same method as documented on fsapi.File -func (f *fsFile) IsDir() (bool, syscall.Errno) { +func (f *fsFile) IsDir() (bool, experimentalsys.Errno) { _, _, isDir, errno := f.cachedStat() return isDir, errno } @@ -166,28 +166,28 @@ func (f *fsFile) IsAppend() bool { } // SetAppend implements the same method as documented on fsapi.File -func (f *fsFile) SetAppend(bool) (errno syscall.Errno) { - return fileError(f, f.closed, syscall.ENOSYS) +func (f *fsFile) SetAppend(bool) (errno experimentalsys.Errno) { + return fileError(f, f.closed, experimentalsys.ENOSYS) } // Stat implements the same method as documented on fsapi.File -func (f *fsFile) Stat() (sys.Stat_t, syscall.Errno) { +func (f *fsFile) Stat() (sys.Stat_t, experimentalsys.Errno) { if f.closed { - return sys.Stat_t{}, syscall.EBADF + 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 syscall.EIO: - errno = syscall.EBADF + case experimentalsys.EIO: + errno = experimentalsys.EBADF } return st, errno } // Read implements the same method as documented on fsapi.File -func (f *fsFile) Read(buf []byte) (n int, errno syscall.Errno) { +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) @@ -196,7 +196,7 @@ func (f *fsFile) Read(buf []byte) (n int, errno syscall.Errno) { } // Pread implements the same method as documented on fsapi.File -func (f *fsFile) Pread(buf []byte, off int64) (n int, errno syscall.Errno) { +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. @@ -210,7 +210,7 @@ func (f *fsFile) Pread(buf []byte, off int64) (n int, errno syscall.Errno) { // 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, platform.UnwrapOSError(err)) + return 0, fileError(f, f.closed, experimentalsys.UnwrapOSError(err)) } // Put the read position back when complete. @@ -219,23 +219,23 @@ func (f *fsFile) Pread(buf []byte, off int64) (n int, errno syscall.Errno) { // 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, platform.UnwrapOSError(err)) + return 0, fileError(f, f.closed, experimentalsys.UnwrapOSError(err)) } } n, err = rs.Read(buf) - if errno = platform.UnwrapOSError(err); errno != 0 { + 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 = syscall.ENOSYS // unsupported + errno = experimentalsys.ENOSYS // unsupported } return } // Seek implements the same method as documented on fsapi.File -func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno syscall.Errno) { +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 @@ -252,7 +252,7 @@ func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno syscall. errno = fileError(f, f.closed, errno) } } else { - errno = syscall.ENOSYS // unsupported + errno = experimentalsys.ENOSYS // unsupported } return } @@ -261,12 +261,12 @@ func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno syscall. // // Notably, this uses readdirFile or fs.ReadDirFile if available. This does not // return inodes on windows. -func (f *fsFile) Readdir(n int) (dirents []fsapi.Dirent, errno syscall.Errno) { +func (f *fsFile) Readdir(n int) (dirents []fsapi.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 = syscall.EBADF + errno = experimentalsys.EBADF return } @@ -300,39 +300,39 @@ func (f *fsFile) Readdir(n int) (dirents []fsapi.Dirent, errno syscall.Errno) { dirents = append(dirents, fsapi.Dirent{Name: e.Name(), Type: e.Type()}) } } else { - errno = syscall.EBADF // not a directory + errno = experimentalsys.EBADF // not a directory } return } // Write implements the same method as documented on fsapi.File. -func (f *fsFile) Write(buf []byte) (n int, errno syscall.Errno) { +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 = syscall.ENOSYS // unsupported + errno = experimentalsys.ENOSYS // unsupported } return } // Pwrite implements the same method as documented on fsapi.File. -func (f *fsFile) Pwrite(buf []byte, off int64) (n int, errno syscall.Errno) { +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 = syscall.ENOSYS // unsupported + errno = experimentalsys.ENOSYS // unsupported } return } // Close implements the same method as documented on fsapi.File. -func (f *fsFile) Close() syscall.Errno { +func (f *fsFile) Close() experimentalsys.Errno { if f.closed { return 0 } @@ -340,12 +340,12 @@ func (f *fsFile) Close() syscall.Errno { return f.close() } -func (f *fsFile) close() syscall.Errno { - return platform.UnwrapOSError(f.file.Close()) +func (f *fsFile) close() experimentalsys.Errno { + return experimentalsys.UnwrapOSError(f.file.Close()) } // dirError is used for commands that work against a directory, but not a file. -func dirError(f fsapi.File, isClosed bool, errno syscall.Errno) syscall.Errno { +func dirError(f fsapi.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno { if vErrno := validate(f, isClosed, false, true); vErrno != 0 { return vErrno } @@ -353,7 +353,7 @@ func dirError(f fsapi.File, isClosed bool, errno syscall.Errno) syscall.Errno { } // fileError is used for commands that work against a file, but not a directory. -func fileError(f fsapi.File, isClosed bool, errno syscall.Errno) syscall.Errno { +func fileError(f fsapi.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno { if vErrno := validate(f, isClosed, true, false); vErrno != 0 { return vErrno } @@ -361,9 +361,9 @@ func fileError(f fsapi.File, isClosed bool, errno syscall.Errno) syscall.Errno { } // validate is used to making syscalls which will fail. -func validate(f fsapi.File, isClosed, wantFile, wantDir bool) syscall.Errno { +func validate(f fsapi.File, isClosed, wantFile, wantDir bool) experimentalsys.Errno { if isClosed { - return syscall.EBADF + return experimentalsys.EBADF } isDir, errno := f.IsDir() @@ -372,53 +372,53 @@ func validate(f fsapi.File, isClosed, wantFile, wantDir bool) syscall.Errno { } if wantFile && isDir { - return syscall.EISDIR + return experimentalsys.EISDIR } else if wantDir && !isDir { - return syscall.ENOTDIR + return experimentalsys.ENOTDIR } return 0 } -func read(r io.Reader, buf []byte) (n int, errno syscall.Errno) { +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, platform.UnwrapOSError(err) + return n, experimentalsys.UnwrapOSError(err) } -func pread(ra io.ReaderAt, buf []byte, off int64) (n int, errno syscall.Errno) { +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, platform.UnwrapOSError(err) + return n, experimentalsys.UnwrapOSError(err) } -func seek(s io.Seeker, offset int64, whence int) (int64, syscall.Errno) { +func seek(s io.Seeker, offset int64, whence int) (int64, experimentalsys.Errno) { if uint(whence) > io.SeekEnd { - return 0, syscall.EINVAL // negative or exceeds the largest valid whence + return 0, experimentalsys.EINVAL // negative or exceeds the largest valid whence } newOffset, err := s.Seek(offset, whence) - return newOffset, platform.UnwrapOSError(err) + return newOffset, experimentalsys.UnwrapOSError(err) } // reopenFile allows re-opening a file for reasons such as applying flags or // directory iteration. -type reopenFile func() syscall.Errno +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() syscall.Errno { +func (f *fsFile) reopen() experimentalsys.Errno { _ = f.close() var err error f.file, err = f.fs.Open(f.name) - return platform.UnwrapOSError(err) + return experimentalsys.UnwrapOSError(err) } // readdirFile allows masking the `Readdir` function on os.File. @@ -427,9 +427,9 @@ type readdirFile interface { } // readdir uses readdirFile.Readdir, special casing windows when path !="". -func readdir(f readdirFile, path string, n int) (dirents []fsapi.Dirent, errno syscall.Errno) { +func readdir(f readdirFile, path string, n int) (dirents []fsapi.Dirent, errno experimentalsys.Errno) { fis, e := f.Readdir(n) - if errno = platform.UnwrapOSError(e); errno != 0 { + if errno = experimentalsys.UnwrapOSError(e); errno != 0 { return } @@ -449,20 +449,20 @@ func readdir(f readdirFile, path string, n int) (dirents []fsapi.Dirent, errno s return } -func write(w io.Writer, buf []byte) (n int, errno syscall.Errno) { +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, platform.UnwrapOSError(err) + return n, experimentalsys.UnwrapOSError(err) } -func pwrite(w io.WriterAt, buf []byte, off int64) (n int, errno syscall.Errno) { +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, platform.UnwrapOSError(err) + return n, experimentalsys.UnwrapOSError(err) } diff --git a/internal/sysfs/file_test.go b/internal/sysfs/file_test.go index 9bdfcf58d2..9d84cf1f0b 100644 --- a/internal/sysfs/file_test.go +++ b/internal/sysfs/file_test.go @@ -12,6 +12,7 @@ import ( gofstest "testing/fstest" "time" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/testing/require" @@ -67,7 +68,7 @@ func TestRegularFileSetNonblock(t *testing.T) { // Read from the file without ever writing to it should not block. buf := make([]byte, 8) _, e := rF.Read(buf) - require.EqualErrno(t, syscall.EAGAIN, e) + require.EqualErrno(t, experimentalsys.EAGAIN, e) errno = rF.SetNonblock(false) require.EqualErrno(t, 0, errno) @@ -88,7 +89,7 @@ func TestReadFdNonblock(t *testing.T) { // Read from the file without ever writing to it should not block. buf := make([]byte, 8) _, e := readFd(fd, buf) - require.EqualErrno(t, syscall.EAGAIN, e) + require.EqualErrno(t, experimentalsys.EAGAIN, e) } func TestFileSetAppend(t *testing.T) { @@ -308,7 +309,7 @@ func TestFilePollRead(t *testing.T) { // When there's nothing in the pipe, it isn't ready. ready, errno := rF.PollRead(&timeout) if runtime.GOOS == "windows" { - require.EqualErrno(t, syscall.ENOSYS, errno) + require.EqualErrno(t, experimentalsys.ENOSYS, errno) t.Skip("TODO: windows File.PollRead") } require.EqualErrno(t, 0, errno) @@ -400,7 +401,7 @@ func TestFilePread_Unsupported(t *testing.T) { buf := make([]byte, 3) _, errno = f.Pread(buf, 0) - require.EqualErrno(t, syscall.ENOSYS, errno) + require.EqualErrno(t, experimentalsys.ENOSYS, errno) } func TestFileRead_Errors(t *testing.T) { @@ -415,13 +416,13 @@ func TestFileRead_Errors(t *testing.T) { tests := []struct { name string - fn func(fsapi.File) syscall.Errno + fn func(fsapi.File) experimentalsys.Errno }{ - {name: "Read", fn: func(f fsapi.File) syscall.Errno { + {name: "Read", fn: func(f fsapi.File) experimentalsys.Errno { _, errno := f.Read(buf) return errno }}, - {name: "Pread", fn: func(f fsapi.File) syscall.Errno { + {name: "Pread", fn: func(f fsapi.File) experimentalsys.Errno { _, errno := f.Pread(buf, 0) return errno }}, @@ -434,7 +435,7 @@ func TestFileRead_Errors(t *testing.T) { t.Run("EBADF when not open for reading", func(t *testing.T) { // The descriptor exists, but not open for reading errno := tc.fn(f) - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, experimentalsys.EBADF, errno) }) testEISDIR(t, tc.fn) }) @@ -447,18 +448,18 @@ func TestFileSeek(t *testing.T) { tests := []struct { name string - openFile func(string) (fsapi.File, syscall.Errno) + openFile func(string) (fsapi.File, experimentalsys.Errno) }{ - {name: "fsFile os.DirFS", openFile: func(name string) (fsapi.File, syscall.Errno) { + {name: "fsFile os.DirFS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) { return OpenFSFile(dirFS, name, syscall.O_RDONLY, 0) }}, - {name: "fsFile embed.api.FS", openFile: func(name string) (fsapi.File, syscall.Errno) { + {name: "fsFile embed.api.FS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) { return OpenFSFile(embedFS, name, syscall.O_RDONLY, 0) }}, - {name: "fsFile fstest.MapFS", openFile: func(name string) (fsapi.File, syscall.Errno) { + {name: "fsFile fstest.MapFS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) { return OpenFSFile(mapFS, name, syscall.O_RDONLY, 0) }}, - {name: "osFile", openFile: func(name string) (fsapi.File, syscall.Errno) { + {name: "osFile", openFile: func(name string) (fsapi.File, experimentalsys.Errno) { return OpenOSFile(path.Join(tmpDir, name), syscall.O_RDONLY, 0o666) }}, } @@ -475,13 +476,13 @@ func TestFileSeek(t *testing.T) { // Shouldn't be able to use an invalid whence _, errno = f.Seek(0, io.SeekEnd+1) - require.EqualErrno(t, syscall.EINVAL, errno) + require.EqualErrno(t, experimentalsys.EINVAL, errno) _, errno = f.Seek(0, -1) - require.EqualErrno(t, syscall.EINVAL, errno) + require.EqualErrno(t, experimentalsys.EINVAL, errno) // Shouldn't be able to seek before the file starts. _, errno = f.Seek(-1, io.SeekStart) - require.EqualErrno(t, syscall.EINVAL, errno) + require.EqualErrno(t, experimentalsys.EINVAL, errno) requireRead(t, f, buf) // read 3 bytes @@ -537,7 +538,7 @@ func TestFileSeek(t *testing.T) { require.Equal(t, direntCount, len(dirents)) }) - seekToZero := func(f fsapi.File) syscall.Errno { + seekToZero := func(f fsapi.File) experimentalsys.Errno { _, errno := f.Seek(0, io.SeekStart) return errno } @@ -596,7 +597,7 @@ func TestFileSeek_Unsupported(t *testing.T) { defer f.Close() _, errno = f.Seek(0, io.SeekCurrent) - require.EqualErrno(t, syscall.ENOSYS, errno) + require.EqualErrno(t, experimentalsys.ENOSYS, errno) } func TestFileWriteAndPwrite(t *testing.T) { @@ -661,15 +662,15 @@ func TestFileWrite_empty(t *testing.T) { tests := []struct { name string - fn func(fsapi.File, []byte) (int, syscall.Errno) + fn func(fsapi.File, []byte) (int, experimentalsys.Errno) }{ - {name: "Write", fn: func(f fsapi.File, buf []byte) (int, syscall.Errno) { + {name: "Write", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { return f.Write(buf) }}, - {name: "Pwrite from zero", fn: func(f fsapi.File, buf []byte) (int, syscall.Errno) { + {name: "Pwrite from zero", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { return f.Pwrite(buf, 0) }}, - {name: "Pwrite from 3", fn: func(f fsapi.File, buf []byte) (int, syscall.Errno) { + {name: "Pwrite from 3", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { return f.Pwrite(buf, 3) }}, } @@ -703,12 +704,12 @@ func TestFileWrite_Unsupported(t *testing.T) { tests := []struct { name string - fn func(fsapi.File, []byte) (int, syscall.Errno) + fn func(fsapi.File, []byte) (int, experimentalsys.Errno) }{ - {name: "Write", fn: func(f fsapi.File, buf []byte) (int, syscall.Errno) { + {name: "Write", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { return f.Write(buf) }}, - {name: "Pwrite", fn: func(f fsapi.File, buf []byte) (int, syscall.Errno) { + {name: "Pwrite", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { return f.Pwrite(buf, 0) }}, } @@ -720,7 +721,7 @@ func TestFileWrite_Unsupported(t *testing.T) { t.Run(tc.name, func(t *testing.T) { _, errno := tc.fn(f, buf) - require.EqualErrno(t, syscall.ENOSYS, errno) + require.EqualErrno(t, experimentalsys.ENOSYS, errno) }) } } @@ -740,13 +741,13 @@ func TestFileWrite_Errors(t *testing.T) { tests := []struct { name string - fn func(fsapi.File) syscall.Errno + fn func(fsapi.File) experimentalsys.Errno }{ - {name: "Write", fn: func(f fsapi.File) syscall.Errno { + {name: "Write", fn: func(f fsapi.File) experimentalsys.Errno { _, errno := f.Write(buf) return errno }}, - {name: "Pwrite", fn: func(f fsapi.File) syscall.Errno { + {name: "Pwrite", fn: func(f fsapi.File) experimentalsys.Errno { _, errno := f.Pwrite(buf, 0) return errno }}, @@ -759,7 +760,7 @@ func TestFileWrite_Errors(t *testing.T) { t.Run("EBADF when not open for writing", func(t *testing.T) { // The descriptor exists, but not open for writing errno := tc.fn(f) - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, experimentalsys.EBADF, errno) }) testEISDIR(t, tc.fn) }) @@ -774,7 +775,7 @@ func TestFileDatasync_NoError(t *testing.T) { testSync_NoError(t, fsapi.File.Datasync) } -func testSync_NoError(t *testing.T, sync func(fsapi.File) syscall.Errno) { +func testSync_NoError(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) { roPath := "file_test.go" ro, errno := OpenFSFile(embedFS, roPath, syscall.O_RDONLY, 0) require.EqualErrno(t, 0, errno) @@ -814,7 +815,7 @@ func TestFileDatasync(t *testing.T) { // testSync doesn't guarantee sync works because the operating system may // sync anyway. There is no test in Go for syscall.Fdatasync, but closest is // similar to below. Effectively, this only tests that things don't error. -func testSync(t *testing.T, sync func(fsapi.File) syscall.Errno) { +func testSync(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) { // Even though it is invalid, try to sync a directory dPath := t.TempDir() d := requireOpenFile(t, dPath, syscall.O_RDONLY, 0) @@ -906,7 +907,7 @@ func TestFileTruncate(t *testing.T) { }) } - truncateToZero := func(f fsapi.File) syscall.Errno { + truncateToZero := func(f fsapi.File) experimentalsys.Errno { return f.Truncate(0) } @@ -924,7 +925,7 @@ func TestFileTruncate(t *testing.T) { defer f.Close() errno := f.Truncate(-1) - require.EqualErrno(t, syscall.EINVAL, errno) + require.EqualErrno(t, experimentalsys.EINVAL, errno) }) } @@ -942,10 +943,10 @@ func TestFileUtimens(t *testing.T) { testUtimens(t, true) - testEBADFIfFileClosed(t, func(f fsapi.File) syscall.Errno { + testEBADFIfFileClosed(t, func(f fsapi.File) experimentalsys.Errno { return f.Utimens(nil) }) - testEBADFIfDirClosed(t, func(d fsapi.File) syscall.Errno { + testEBADFIfDirClosed(t, func(d fsapi.File) experimentalsys.Errno { return d.Utimens(nil) }) } @@ -1019,18 +1020,18 @@ func TestNewStdioFile(t *testing.T) { } } -func testEBADFIfDirClosed(t *testing.T, fn func(fsapi.File) syscall.Errno) bool { +func testEBADFIfDirClosed(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool { return t.Run("EBADF if dir closed", func(t *testing.T) { d := requireOpenFile(t, t.TempDir(), syscall.O_RDONLY, 0o755) // close the directory underneath require.EqualErrno(t, 0, d.Close()) - require.EqualErrno(t, syscall.EBADF, fn(d)) + require.EqualErrno(t, experimentalsys.EBADF, fn(d)) }) } -func testEBADFIfFileClosed(t *testing.T, fn func(fsapi.File) syscall.Errno) bool { +func testEBADFIfFileClosed(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool { return t.Run("EBADF if file closed", func(t *testing.T) { tmpDir := t.TempDir() @@ -1039,16 +1040,16 @@ func testEBADFIfFileClosed(t *testing.T, fn func(fsapi.File) syscall.Errno) bool // close the file underneath require.EqualErrno(t, 0, f.Close()) - require.EqualErrno(t, syscall.EBADF, fn(f)) + require.EqualErrno(t, experimentalsys.EBADF, fn(f)) }) } -func testEISDIR(t *testing.T, fn func(fsapi.File) syscall.Errno) bool { +func testEISDIR(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool { return t.Run("EISDIR if directory", func(t *testing.T) { f := requireOpenFile(t, os.TempDir(), syscall.O_RDONLY|fsapi.O_DIRECTORY, 0o666) defer f.Close() - require.EqualErrno(t, syscall.EISDIR, fn(f)) + require.EqualErrno(t, experimentalsys.EISDIR, fn(f)) }) } diff --git a/internal/sysfs/file_unix.go b/internal/sysfs/file_unix.go index d549a0e1c5..4516cc5f47 100644 --- a/internal/sysfs/file_unix.go +++ b/internal/sysfs/file_unix.go @@ -5,17 +5,17 @@ package sysfs import ( "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) const nonBlockingFileIoSupported = true // readFd exposes syscall.Read. -func readFd(fd uintptr, buf []byte) (int, syscall.Errno) { +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 := platform.UnwrapOSError(err) + errno := sys.UnwrapOSError(err) return n, errno } diff --git a/internal/sysfs/file_windows.go b/internal/sysfs/file_windows.go index b72fe364eb..29b8060bd8 100644 --- a/internal/sysfs/file_windows.go +++ b/internal/sysfs/file_windows.go @@ -4,7 +4,7 @@ import ( "syscall" "unsafe" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) const nonBlockingFileIoSupported = true @@ -19,27 +19,24 @@ var procPeekNamedPipe = kernel32.NewProc("PeekNamedPipe") // 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, syscall.Errno) { +func readFd(fd uintptr, buf []byte) (int, sys.Errno) { handle := syscall.Handle(fd) fileType, err := syscall.GetFileType(syscall.Stdin) if err != nil { - return 0, platform.UnwrapOSError(err) + return 0, sys.UnwrapOSError(err) } if fileType&syscall.FILE_TYPE_CHAR == 0 { - return -1, syscall.ENOSYS + return -1, sys.ENOSYS } n, err := peekNamedPipe(handle) - if err != nil { - errno := platform.UnwrapOSError(err) - if errno == syscall.ERROR_BROKEN_PIPE { - return 0, 0 - } + if err == syscall.ERROR_BROKEN_PIPE { + return 0, 0 } if n == 0 { - return -1, syscall.EAGAIN + return -1, sys.EAGAIN } un, err := syscall.Read(handle, buf[0:n]) - return un, platform.UnwrapOSError(err) + return un, sys.UnwrapOSError(err) } // peekNamedPipe partially exposes PeekNamedPipe from the Win32 API diff --git a/internal/sysfs/futimens.go b/internal/sysfs/futimens.go index 36736c41d3..e9a445d9a9 100644 --- a/internal/sysfs/futimens.go +++ b/internal/sysfs/futimens.go @@ -5,7 +5,7 @@ import ( "time" "unsafe" - "github.com/tetratelabs/wazero/internal/platform" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -35,19 +35,19 @@ const ( // // # Errors // -// A zero syscall.Errno is success. The below are expected otherwise: -// - syscall.ENOSYS: the implementation does not support this function. -// - syscall.EINVAL: `path` is invalid. -// - syscall.EEXIST: `path` exists and is a directory. -// - syscall.ENOTDIR: `path` exists and is a file. +// A zero sys.Errno is success. The below are expected otherwise: +// - sys.ENOSYS: the implementation does not support this function. +// - sys.EINVAL: `path` is invalid. +// - sys.EEXIST: `path` exists and is a directory. +// - sys.ENOTDIR: `path` exists and is a file. // // # Notes // // - This is like syscall.UtimesNano and `utimensat` with `AT_FDCWD` in // POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html -func Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { +func Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) experimentalsys.Errno { err := utimens(path, times, symlinkFollow) - return platform.UnwrapOSError(err) + return experimentalsys.UnwrapOSError(err) } func timesToPtr(times *[2]syscall.Timespec) unsafe.Pointer { //nolint:unused @@ -59,7 +59,7 @@ func timesToPtr(times *[2]syscall.Timespec) unsafe.Pointer { //nolint:unused func utimensPortable(path string, times *[2]syscall.Timespec, symlinkFollow bool) error { //nolint:unused if !symlinkFollow { - return syscall.ENOSYS + return experimentalsys.ENOSYS } // Handle when both inputs are current system time. @@ -92,7 +92,7 @@ func utimensPortable(path string, times *[2]syscall.Timespec, symlinkFollow bool } } -func normalizeTimespec(path string, times *[2]syscall.Timespec, i int) (ts syscall.Timespec, err syscall.Errno) { //nolint:unused +func normalizeTimespec(path string, times *[2]syscall.Timespec, i int) (ts syscall.Timespec, err experimentalsys.Errno) { //nolint:unused switch times[i].Nsec { case UTIME_NOW: // declined in Go per golang/go#31880. ts = nowTimespec() diff --git a/internal/sysfs/futimens_test.go b/internal/sysfs/futimens_test.go index eb8318dccd..8292147e86 100644 --- a/internal/sysfs/futimens_test.go +++ b/internal/sysfs/futimens_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -15,13 +16,13 @@ import ( func TestUtimens(t *testing.T) { t.Run("doesn't exist", func(t *testing.T) { err := Utimens("nope", nil, true) - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) err = Utimens("nope", nil, false) if SupportsSymlinkNoFollow { - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) } else { - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, sys.ENOSYS, err) } }) testUtimens(t, false) @@ -154,7 +155,7 @@ func testUtimens(t *testing.T, futimes bool) { if !futimes { err = Utimens(path, tc.times, !symlinkNoFollow) if symlinkNoFollow && !SupportsSymlinkNoFollow { - require.EqualErrno(t, syscall.ENOSYS, err) + require.EqualErrno(t, sys.ENOSYS, err) return } require.EqualErrno(t, 0, errno) diff --git a/internal/sysfs/futimens_unsupported.go b/internal/sysfs/futimens_unsupported.go index 60860e6c48..f1602bdb01 100644 --- a/internal/sysfs/futimens_unsupported.go +++ b/internal/sysfs/futimens_unsupported.go @@ -2,7 +2,11 @@ package sysfs -import "syscall" +import ( + "syscall" + + "github.com/tetratelabs/wazero/experimental/sys" +) // Define values even if not used except as sentinels. const ( @@ -19,5 +23,5 @@ func futimens(fd uintptr, times *[2]syscall.Timespec) 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 syscall.ENOSYS + return sys.ENOSYS } diff --git a/internal/sysfs/futimens_windows.go b/internal/sysfs/futimens_windows.go index 75f7190217..4d252d3cd5 100644 --- a/internal/sysfs/futimens_windows.go +++ b/internal/sysfs/futimens_windows.go @@ -4,6 +4,7 @@ import ( "syscall" "time" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/platform" ) @@ -22,7 +23,7 @@ func futimens(fd uintptr, times *[2]syscall.Timespec) error { // Before Go 1.20, ERROR_INVALID_HANDLE was returned for too many reasons. // Kick out so that callers can use path-based operations instead. if !platform.IsAtLeastGo120 { - return syscall.ENOSYS + return sys.ENOSYS } // Per docs, zero isn't a valid timestamp as it cannot be differentiated diff --git a/internal/sysfs/open_file.go b/internal/sysfs/open_file.go index ebe9e3d5f9..61ced344ea 100644 --- a/internal/sysfs/open_file.go +++ b/internal/sysfs/open_file.go @@ -5,17 +5,16 @@ package sysfs import ( "io/fs" "os" - "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -// OpenFile is like os.OpenFile except it returns syscall.Errno. A zero -// syscall.Errno is success. -func openFile(path string, flag int, perm fs.FileMode) (*os.File, syscall.Errno) { +// OpenFile is like os.OpenFile except it returns sys.Errno. A zero +// sys.Errno is success. +func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) { f, err := os.OpenFile(path, flag, perm) // Note: This does not return a fsapi.File because fsapi.FS that returns // one may want to hide the real OS path. For example, this is needed for // pre-opens. - return f, platform.UnwrapOSError(err) + return f, sys.UnwrapOSError(err) } diff --git a/internal/sysfs/open_file_js.go b/internal/sysfs/open_file_js.go index d84e2a3770..613b06f87c 100644 --- a/internal/sysfs/open_file_js.go +++ b/internal/sysfs/open_file_js.go @@ -3,13 +3,12 @@ package sysfs import ( "io/fs" "os" - "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func openFile(path string, flag int, perm fs.FileMode) (*os.File, syscall.Errno) { +func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) { flag &= ^(O_DIRECTORY | O_NOFOLLOW) // erase placeholders f, err := os.OpenFile(path, flag, perm) - return f, platform.UnwrapOSError(err) + return f, sys.UnwrapOSError(err) } diff --git a/internal/sysfs/open_file_sun.go b/internal/sysfs/open_file_sun.go index 576c8085aa..a75ab3937c 100644 --- a/internal/sysfs/open_file_sun.go +++ b/internal/sysfs/open_file_sun.go @@ -5,12 +5,11 @@ package sysfs import ( "io/fs" "os" - "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func openFile(path string, flag int, perm fs.FileMode) (*os.File, syscall.Errno) { +func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) { f, err := os.OpenFile(path, flag, perm) - return f, platform.UnwrapOSError(err) + return f, sys.UnwrapOSError(err) } diff --git a/internal/sysfs/open_file_test.go b/internal/sysfs/open_file_test.go index 30e98db3cd..ab1d3a661d 100644 --- a/internal/sysfs/open_file_test.go +++ b/internal/sysfs/open_file_test.go @@ -7,6 +7,7 @@ import ( "syscall" "testing" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -47,12 +48,12 @@ func TestOpenFile_Errors(t *testing.T) { require.NoError(t, err) _, errno := OpenFile(nestedFile+"/", os.O_RDONLY, 0) - require.EqualErrno(t, syscall.ENOTDIR, errno) + require.EqualErrno(t, sys.ENOTDIR, errno) }) t.Run("not found must be ENOENT", func(t *testing.T) { _, errno := OpenFile(path.Join(tmpDir, "not-really-exist.txt"), os.O_RDONLY, 0o600) - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, sys.ENOENT, errno) }) // This is the same as https://github.com/ziglang/zig/blob/d24ebf1d12cf66665b52136a2807f97ff021d78d/lib/std/os/test.zig#L105-L112 @@ -63,7 +64,7 @@ func TestOpenFile_Errors(t *testing.T) { defer f.Close() _, err := OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666) - require.EqualErrno(t, syscall.EEXIST, err) + require.EqualErrno(t, sys.EEXIST, err) }) t.Run("writing to a read-only file is EBADF", func(t *testing.T) { @@ -74,7 +75,7 @@ func TestOpenFile_Errors(t *testing.T) { defer f.Close() _, errno := f.Write([]byte{1, 2, 3, 4}) - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, sys.EBADF, errno) }) t.Run("writing to a directory is EISDIR", func(t *testing.T) { @@ -85,7 +86,7 @@ func TestOpenFile_Errors(t *testing.T) { defer f.Close() _, errno := f.Write([]byte{1, 2, 3, 4}) - require.EqualErrno(t, syscall.EISDIR, errno) + require.EqualErrno(t, sys.EISDIR, errno) }) // This is similar to https://github.com/WebAssembly/wasi-testsuite/blob/dc7f8d27be1030cd4788ebdf07d9b57e5d23441e/tests/rust/src/bin/dangling_symlink.rs @@ -97,17 +98,17 @@ func TestOpenFile_Errors(t *testing.T) { require.NoError(t, err) _, errno := OpenFile(symlink, fsapi.O_DIRECTORY|fsapi.O_NOFOLLOW, 0o0666) - require.EqualErrno(t, syscall.ENOTDIR, errno) + require.EqualErrno(t, sys.ENOTDIR, errno) _, errno = OpenFile(symlink, fsapi.O_NOFOLLOW, 0o0666) - require.EqualErrno(t, syscall.ELOOP, errno) + require.EqualErrno(t, sys.ELOOP, errno) }) t.Run("opening a directory writeable is EISDIR", func(t *testing.T) { _, errno := OpenFile(tmpDir, fsapi.O_DIRECTORY|syscall.O_WRONLY, 0o0666) - require.EqualErrno(t, syscall.EISDIR, errno) + require.EqualErrno(t, sys.EISDIR, errno) _, errno = OpenFile(tmpDir, fsapi.O_DIRECTORY|syscall.O_WRONLY, 0o0666) - require.EqualErrno(t, syscall.EISDIR, errno) + require.EqualErrno(t, sys.EISDIR, errno) }) } diff --git a/internal/sysfs/open_file_windows.go b/internal/sysfs/open_file_windows.go index 14984d1fe7..9190fefadd 100644 --- a/internal/sysfs/open_file_windows.go +++ b/internal/sysfs/open_file_windows.go @@ -7,11 +7,12 @@ import ( "syscall" "unsafe" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/platform" ) -func openFile(path string, flag int, perm fs.FileMode) (*os.File, syscall.Errno) { +func openFile(path string, flag int, perm fs.FileMode) (*os.File, sys.Errno) { isDir := flag&fsapi.O_DIRECTORY > 0 flag &= ^(fsapi.O_DIRECTORY | fsapi.O_NOFOLLOW) // erase placeholders @@ -23,31 +24,31 @@ func openFile(path string, flag int, perm fs.FileMode) (*os.File, syscall.Errno) // TODO: Set FILE_SHARE_DELETE for directory as well. f, err := os.OpenFile(path, flag, perm) - errno := platform.UnwrapOSError(err) + errno := sys.UnwrapOSError(err) if errno == 0 { return f, 0 } switch errno { - case syscall.EINVAL: + case sys.EINVAL: // WASI expects ENOTDIR for a file path with a trailing slash. if strings.HasSuffix(path, "/") { - errno = syscall.ENOTDIR + errno = sys.ENOTDIR } // To match expectations of WASI, e.g. TinyGo TestStatBadDir, return // ENOENT, not ENOTDIR. - case syscall.ENOTDIR: - errno = syscall.ENOENT - case syscall.ENOENT: + 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 = syscall.ENOTDIR + errno = sys.ENOTDIR } else { - errno = syscall.ELOOP + errno = sys.ELOOP } } } diff --git a/internal/sysfs/osfile.go b/internal/sysfs/osfile.go index a306edff3d..de167de035 100644 --- a/internal/sysfs/osfile.go +++ b/internal/sysfs/osfile.go @@ -8,6 +8,7 @@ import ( "syscall" "time" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/sys" @@ -36,7 +37,7 @@ type osFile struct { // before Readdir. reopenDir bool - // closed is true when closed was called. This ensures proper syscall.EBADF + // 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. @@ -45,7 +46,7 @@ type osFile struct { // cachedStat returns the cacheable parts of fsapi.Stat_t or an error if they // couldn't be retrieved. -func (f *osFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno syscall.Errno) { +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 @@ -55,19 +56,19 @@ func (f *osFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno sysc } // Dev implements the same method as documented on fsapi.File -func (f *osFile) Dev() (uint64, syscall.Errno) { +func (f *osFile) Dev() (uint64, experimentalsys.Errno) { dev, _, _, errno := f.cachedStat() return dev, errno } // Ino implements the same method as documented on fsapi.File -func (f *osFile) Ino() (sys.Inode, syscall.Errno) { +func (f *osFile) Ino() (sys.Inode, experimentalsys.Errno) { _, ino, _, errno := f.cachedStat() return ino, errno } // IsDir implements the same method as documented on fsapi.File -func (f *osFile) IsDir() (bool, syscall.Errno) { +func (f *osFile) IsDir() (bool, experimentalsys.Errno) { _, _, isDir, errno := f.cachedStat() return isDir, errno } @@ -78,7 +79,7 @@ func (f *osFile) IsAppend() bool { } // SetAppend implements the same method as documented on fsapi.File -func (f *osFile) SetAppend(enable bool) (errno syscall.Errno) { +func (f *osFile) SetAppend(enable bool) (errno experimentalsys.Errno) { if enable { f.flag |= syscall.O_APPEND } else { @@ -96,7 +97,7 @@ func (f *osFile) SetAppend(enable bool) (errno syscall.Errno) { // compile-time check to ensure osFile.reopen implements reopenFile. var _ reopenFile = (*fsFile)(nil).reopen -func (f *osFile) reopen() (errno syscall.Errno) { +func (f *osFile) reopen() (errno experimentalsys.Errno) { // Clear any create flag, as we are re-opening, not re-creating. f.flag &= ^syscall.O_CREAT @@ -111,36 +112,36 @@ func (f *osFile) IsNonblock() bool { } // SetNonblock implements the same method as documented on fsapi.File -func (f *osFile) SetNonblock(enable bool) (errno syscall.Errno) { +func (f *osFile) SetNonblock(enable bool) (errno experimentalsys.Errno) { if enable { f.flag |= fsapi.O_NONBLOCK } else { f.flag &= ^fsapi.O_NONBLOCK } if err := setNonblock(f.fd, enable); err != nil { - return fileError(f, f.closed, platform.UnwrapOSError(err)) + return fileError(f, f.closed, experimentalsys.UnwrapOSError(err)) } return 0 } // Stat implements the same method as documented on fsapi.File -func (f *osFile) Stat() (sys.Stat_t, syscall.Errno) { +func (f *osFile) Stat() (sys.Stat_t, experimentalsys.Errno) { if f.closed { - return sys.Stat_t{}, syscall.EBADF + 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 syscall.EIO: - errno = syscall.EBADF + case experimentalsys.EIO: + errno = experimentalsys.EBADF } return st, errno } // Read implements the same method as documented on fsapi.File -func (f *osFile) Read(buf []byte) (n int, errno syscall.Errno) { +func (f *osFile) Read(buf []byte) (n int, errno experimentalsys.Errno) { if len(buf) == 0 { return 0, 0 // Short-circuit 0-len reads. } @@ -157,7 +158,7 @@ func (f *osFile) Read(buf []byte) (n int, errno syscall.Errno) { } // Pread implements the same method as documented on fsapi.File -func (f *osFile) Pread(buf []byte, off int64) (n int, errno syscall.Errno) { +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) @@ -166,14 +167,14 @@ func (f *osFile) Pread(buf []byte, off int64) (n int, errno syscall.Errno) { } // Seek implements the same method as documented on fsapi.File -func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno syscall.Errno) { +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.18. - if errno == syscall.EISDIR && offset == 0 && whence == io.SeekStart { + if errno == experimentalsys.EISDIR && offset == 0 && whence == io.SeekStart { errno = 0 f.reopenDir = true } @@ -182,13 +183,13 @@ func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno syscall. } // PollRead implements the same method as documented on fsapi.File -func (f *osFile) PollRead(timeout *time.Duration) (ready bool, errno syscall.Errno) { +func (f *osFile) PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) { fdSet := platform.FdSet{} fd := int(f.fd) fdSet.Set(fd) nfds := fd + 1 // See https://man7.org/linux/man-pages/man2/select.2.html#:~:text=condition%20has%20occurred.-,nfds,-This%20argument%20should count, err := _select(nfds, &fdSet, nil, nil, timeout) - if errno = platform.UnwrapOSError(err); errno != 0 { + if errno = experimentalsys.UnwrapOSError(err); errno != 0 { // Defer validation overhead until we've already had an error. errno = fileError(f, f.closed, errno) } @@ -197,7 +198,7 @@ func (f *osFile) PollRead(timeout *time.Duration) (ready bool, errno syscall.Err // Readdir implements File.Readdir. Notably, this uses "Readdir", not // "ReadDir", from os.File. -func (f *osFile) Readdir(n int) (dirents []fsapi.Dirent, errno syscall.Errno) { +func (f *osFile) Readdir(n int) (dirents []fsapi.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 { @@ -212,7 +213,7 @@ func (f *osFile) Readdir(n int) (dirents []fsapi.Dirent, errno syscall.Errno) { } // Write implements the same method as documented on fsapi.File -func (f *osFile) Write(buf []byte) (n int, errno syscall.Errno) { +func (f *osFile) Write(buf []byte) (n int, errno experimentalsys.Errno) { 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) @@ -221,7 +222,7 @@ func (f *osFile) Write(buf []byte) (n int, errno syscall.Errno) { } // Pwrite implements the same method as documented on fsapi.File -func (f *osFile) Pwrite(buf []byte, off int64) (n int, errno syscall.Errno) { +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) @@ -230,8 +231,8 @@ func (f *osFile) Pwrite(buf []byte, off int64) (n int, errno syscall.Errno) { } // Truncate implements the same method as documented on fsapi.File -func (f *osFile) Truncate(size int64) (errno syscall.Errno) { - if errno = platform.UnwrapOSError(f.file.Truncate(size)); errno != 0 { +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) } @@ -239,27 +240,27 @@ func (f *osFile) Truncate(size int64) (errno syscall.Errno) { } // Sync implements the same method as documented on fsapi.File -func (f *osFile) Sync() syscall.Errno { +func (f *osFile) Sync() experimentalsys.Errno { return fsync(f.file) } // Datasync implements the same method as documented on fsapi.File -func (f *osFile) Datasync() syscall.Errno { +func (f *osFile) Datasync() experimentalsys.Errno { return datasync(f.file) } // Utimens implements the same method as documented on fsapi.File -func (f *osFile) Utimens(times *[2]syscall.Timespec) syscall.Errno { +func (f *osFile) Utimens(times *[2]syscall.Timespec) experimentalsys.Errno { if f.closed { - return syscall.EBADF + return experimentalsys.EBADF } err := futimens(f.fd, times) - return platform.UnwrapOSError(err) + return experimentalsys.UnwrapOSError(err) } // Close implements the same method as documented on fsapi.File -func (f *osFile) Close() syscall.Errno { +func (f *osFile) Close() experimentalsys.Errno { if f.closed { return 0 } @@ -267,6 +268,6 @@ func (f *osFile) Close() syscall.Errno { return f.close() } -func (f *osFile) close() syscall.Errno { - return platform.UnwrapOSError(f.file.Close()) +func (f *osFile) close() experimentalsys.Errno { + return experimentalsys.UnwrapOSError(f.file.Close()) } diff --git a/internal/sysfs/readfs.go b/internal/sysfs/readfs.go index 802d272906..6150a0968b 100644 --- a/internal/sysfs/readfs.go +++ b/internal/sysfs/readfs.go @@ -6,6 +6,7 @@ import ( "syscall" "time" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/sys" ) @@ -28,7 +29,7 @@ type readFS struct { } // OpenFile implements the same method as documented on fsapi.FS -func (r *readFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, syscall.Errno) { +func (r *readFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { // TODO: Once the real implementation is complete, move the below to // /RATIONALE.md. Doing this while the type is unstable creates // documentation drift as we expect a lot of reshaping meanwhile. @@ -51,9 +52,9 @@ func (r *readFS) OpenFile(path string, flag int, perm fs.FileMode) (fsapi.File, switch flag & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) { case os.O_WRONLY, os.O_RDWR: if flag&fsapi.O_DIRECTORY != 0 { - return nil, syscall.EISDIR + return nil, experimentalsys.EISDIR } - return nil, syscall.ENOSYS + return nil, experimentalsys.ENOSYS default: // os.O_RDONLY (or no flag) so we are ok! } @@ -72,17 +73,17 @@ type readFile struct { } // Dev implements the same method as documented on fsapi.File. -func (r *readFile) Dev() (uint64, syscall.Errno) { +func (r *readFile) Dev() (uint64, experimentalsys.Errno) { return r.f.Dev() } // Ino implements the same method as documented on fsapi.File. -func (r *readFile) Ino() (sys.Inode, syscall.Errno) { +func (r *readFile) Ino() (sys.Inode, experimentalsys.Errno) { return r.f.Ino() } // IsDir implements the same method as documented on fsapi.File. -func (r *readFile) IsDir() (bool, syscall.Errno) { +func (r *readFile) IsDir() (bool, experimentalsys.Errno) { return r.f.IsDir() } @@ -92,7 +93,7 @@ func (r *readFile) IsNonblock() bool { } // SetNonblock implements the same method as documented on fsapi.File. -func (r *readFile) SetNonblock(enabled bool) syscall.Errno { +func (r *readFile) SetNonblock(enabled bool) experimentalsys.Errno { return r.f.SetNonblock(enabled) } @@ -102,135 +103,135 @@ func (r *readFile) IsAppend() bool { } // SetAppend implements the same method as documented on fsapi.File. -func (r *readFile) SetAppend(enabled bool) syscall.Errno { +func (r *readFile) SetAppend(enabled bool) experimentalsys.Errno { return r.f.SetAppend(enabled) } // Stat implements the same method as documented on fsapi.File. -func (r *readFile) Stat() (sys.Stat_t, syscall.Errno) { +func (r *readFile) Stat() (sys.Stat_t, experimentalsys.Errno) { return r.f.Stat() } // Read implements the same method as documented on fsapi.File. -func (r *readFile) Read(buf []byte) (int, syscall.Errno) { +func (r *readFile) Read(buf []byte) (int, experimentalsys.Errno) { return r.f.Read(buf) } // Pread implements the same method as documented on fsapi.File. -func (r *readFile) Pread(buf []byte, offset int64) (int, syscall.Errno) { +func (r *readFile) Pread(buf []byte, offset int64) (int, experimentalsys.Errno) { return r.f.Pread(buf, offset) } // Seek implements the same method as documented on fsapi.File. -func (r *readFile) Seek(offset int64, whence int) (int64, syscall.Errno) { +func (r *readFile) Seek(offset int64, whence int) (int64, experimentalsys.Errno) { return r.f.Seek(offset, whence) } // Readdir implements the same method as documented on fsapi.File. -func (r *readFile) Readdir(n int) (dirents []fsapi.Dirent, errno syscall.Errno) { +func (r *readFile) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.Errno) { return r.f.Readdir(n) } // Write implements the same method as documented on fsapi.File. -func (r *readFile) Write([]byte) (int, syscall.Errno) { +func (r *readFile) Write([]byte) (int, experimentalsys.Errno) { return 0, r.writeErr() } // Pwrite implements the same method as documented on fsapi.File. -func (r *readFile) Pwrite([]byte, int64) (n int, errno syscall.Errno) { +func (r *readFile) Pwrite([]byte, int64) (n int, errno experimentalsys.Errno) { return 0, r.writeErr() } // Truncate implements the same method as documented on fsapi.File. -func (r *readFile) Truncate(int64) syscall.Errno { +func (r *readFile) Truncate(int64) experimentalsys.Errno { return r.writeErr() } // Sync implements the same method as documented on fsapi.File. -func (r *readFile) Sync() syscall.Errno { - return syscall.EBADF +func (r *readFile) Sync() experimentalsys.Errno { + return experimentalsys.EBADF } // Datasync implements the same method as documented on fsapi.File. -func (r *readFile) Datasync() syscall.Errno { - return syscall.EBADF +func (r *readFile) Datasync() experimentalsys.Errno { + return experimentalsys.EBADF } // Utimens implements the same method as documented on fsapi.File. -func (r *readFile) Utimens(*[2]syscall.Timespec) syscall.Errno { - return syscall.EBADF +func (r *readFile) Utimens(*[2]syscall.Timespec) experimentalsys.Errno { + return experimentalsys.EBADF } -func (r *readFile) writeErr() syscall.Errno { +func (r *readFile) writeErr() experimentalsys.Errno { if isDir, errno := r.IsDir(); errno != 0 { return errno } else if isDir { - return syscall.EISDIR + return experimentalsys.EISDIR } - return syscall.EBADF + return experimentalsys.EBADF } // Close implements the same method as documented on fsapi.File. -func (r *readFile) Close() syscall.Errno { +func (r *readFile) Close() experimentalsys.Errno { return r.f.Close() } // PollRead implements File.PollRead -func (r *readFile) PollRead(timeout *time.Duration) (ready bool, errno syscall.Errno) { +func (r *readFile) PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) { return r.f.PollRead(timeout) } // Lstat implements the same method as documented on fsapi.FS -func (r *readFS) Lstat(path string) (sys.Stat_t, syscall.Errno) { +func (r *readFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { return r.fs.Lstat(path) } // Stat implements the same method as documented on fsapi.FS -func (r *readFS) Stat(path string) (sys.Stat_t, syscall.Errno) { +func (r *readFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { return r.fs.Stat(path) } // Readlink implements the same method as documented on fsapi.FS -func (r *readFS) Readlink(path string) (dst string, err syscall.Errno) { +func (r *readFS) Readlink(path string) (dst string, err experimentalsys.Errno) { return r.fs.Readlink(path) } // Mkdir implements the same method as documented on fsapi.FS -func (r *readFS) Mkdir(path string, perm fs.FileMode) syscall.Errno { - return syscall.EROFS +func (r *readFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno { + return experimentalsys.EROFS } // Chmod implements the same method as documented on fsapi.FS -func (r *readFS) Chmod(path string, perm fs.FileMode) syscall.Errno { - return syscall.EROFS +func (r *readFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { + return experimentalsys.EROFS } // Rename implements the same method as documented on fsapi.FS -func (r *readFS) Rename(from, to string) syscall.Errno { - return syscall.EROFS +func (r *readFS) Rename(from, to string) experimentalsys.Errno { + return experimentalsys.EROFS } // Rmdir implements the same method as documented on fsapi.FS -func (r *readFS) Rmdir(path string) syscall.Errno { - return syscall.EROFS +func (r *readFS) Rmdir(path string) experimentalsys.Errno { + return experimentalsys.EROFS } // Link implements the same method as documented on fsapi.FS -func (r *readFS) Link(_, _ string) syscall.Errno { - return syscall.EROFS +func (r *readFS) Link(_, _ string) experimentalsys.Errno { + return experimentalsys.EROFS } // Symlink implements the same method as documented on fsapi.FS -func (r *readFS) Symlink(_, _ string) syscall.Errno { - return syscall.EROFS +func (r *readFS) Symlink(_, _ string) experimentalsys.Errno { + return experimentalsys.EROFS } // Unlink implements the same method as documented on fsapi.FS -func (r *readFS) Unlink(path string) syscall.Errno { - return syscall.EROFS +func (r *readFS) Unlink(path string) experimentalsys.Errno { + return experimentalsys.EROFS } // Utimens implements the same method as documented on fsapi.FS -func (r *readFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { - return syscall.EROFS +func (r *readFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) experimentalsys.Errno { + return experimentalsys.EROFS } diff --git a/internal/sysfs/readfs_test.go b/internal/sysfs/readfs_test.go index d7da21fc13..dd6d6092d1 100644 --- a/internal/sysfs/readfs_test.go +++ b/internal/sysfs/readfs_test.go @@ -4,9 +4,9 @@ import ( "io/fs" "os" "runtime" - "syscall" "testing" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/fstest" "github.com/tetratelabs/wazero/internal/testing/require" @@ -47,7 +47,7 @@ func TestReadFS_MkDir(t *testing.T) { testFS := NewReadFS(writeable) err := testFS.Mkdir("mkdir", fs.ModeDir) - require.EqualErrno(t, syscall.EROFS, err) + require.EqualErrno(t, sys.EROFS, err) } func TestReadFS_Chmod(t *testing.T) { @@ -55,7 +55,7 @@ func TestReadFS_Chmod(t *testing.T) { testFS := NewReadFS(writeable) err := testFS.Chmod("chmod", fs.ModeDir) - require.EqualErrno(t, syscall.EROFS, err) + require.EqualErrno(t, sys.EROFS, err) } func TestReadFS_Rename(t *testing.T) { @@ -76,7 +76,7 @@ func TestReadFS_Rename(t *testing.T) { require.NoError(t, err) err = testFS.Rename(file1, file2) - require.EqualErrno(t, syscall.EROFS, err) + require.EqualErrno(t, sys.EROFS, err) } func TestReadFS_Rmdir(t *testing.T) { @@ -89,7 +89,7 @@ func TestReadFS_Rmdir(t *testing.T) { require.NoError(t, os.Mkdir(realPath, 0o700)) err := testFS.Rmdir(path) - require.EqualErrno(t, syscall.EROFS, err) + require.EqualErrno(t, sys.EROFS, err) } func TestReadFS_Unlink(t *testing.T) { @@ -102,7 +102,7 @@ func TestReadFS_Unlink(t *testing.T) { require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600)) err := testFS.Unlink(path) - require.EqualErrno(t, syscall.EROFS, err) + require.EqualErrno(t, sys.EROFS, err) } func TestReadFS_UtimesNano(t *testing.T) { @@ -115,7 +115,7 @@ func TestReadFS_UtimesNano(t *testing.T) { require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600)) err := testFS.Utimens(path, nil, true) - require.EqualErrno(t, syscall.EROFS, err) + require.EqualErrno(t, sys.EROFS, err) } func TestReadFS_Open_Read(t *testing.T) { diff --git a/internal/sysfs/rename.go b/internal/sysfs/rename.go index d558a7a503..5ec22539e4 100644 --- a/internal/sysfs/rename.go +++ b/internal/sysfs/rename.go @@ -5,12 +5,12 @@ package sysfs import ( "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func rename(from, to string) syscall.Errno { +func rename(from, to string) sys.Errno { if from == to { return 0 } - return platform.UnwrapOSError(syscall.Rename(from, to)) + return sys.UnwrapOSError(syscall.Rename(from, to)) } diff --git a/internal/sysfs/rename_test.go b/internal/sysfs/rename_test.go index 89ed94b944..cc057e2f3a 100644 --- a/internal/sysfs/rename_test.go +++ b/internal/sysfs/rename_test.go @@ -1,13 +1,11 @@ package sysfs import ( - "errors" "os" "path" - "syscall" "testing" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -20,7 +18,7 @@ func TestRename(t *testing.T) { require.NoError(t, err) err = rename(path.Join(tmpDir, "non-exist"), file1Path) - require.EqualErrno(t, syscall.ENOENT, err) + require.EqualErrno(t, sys.ENOENT, err) }) t.Run("file to non-exist", func(t *testing.T) { tmpDir := t.TempDir() @@ -36,7 +34,7 @@ func TestRename(t *testing.T) { // Show the prior path no longer exists _, err = os.Stat(file1Path) - require.EqualErrno(t, syscall.ENOENT, errors.Unwrap(err)) + require.EqualErrno(t, sys.ENOENT, sys.UnwrapOSError(err)) s, err := os.Stat(file2Path) require.NoError(t, err) @@ -54,7 +52,7 @@ func TestRename(t *testing.T) { // Show the prior path no longer exists _, err := os.Stat(dir1Path) - require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(err)) + require.EqualErrno(t, sys.ENOENT, sys.UnwrapOSError(err)) s, err := os.Stat(dir2Path) require.NoError(t, err) @@ -73,7 +71,7 @@ func TestRename(t *testing.T) { require.NoError(t, err) err = rename(dir1Path, dir2Path) - require.EqualErrno(t, syscall.ENOTDIR, err) + require.EqualErrno(t, sys.ENOTDIR, err) }) t.Run("file to dir", func(t *testing.T) { tmpDir := t.TempDir() @@ -87,7 +85,7 @@ func TestRename(t *testing.T) { require.NoError(t, os.Mkdir(dir1Path, 0o700)) err = rename(file1Path, dir1Path) - require.EqualErrno(t, syscall.EISDIR, err) + require.EqualErrno(t, sys.EISDIR, err) }) // Similar to https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs/test.zig#L567-L582 @@ -113,7 +111,7 @@ func TestRename(t *testing.T) { // Show the prior path no longer exists _, err = os.Stat(dir1Path) - require.EqualErrno(t, syscall.ENOENT, errors.Unwrap(err)) + require.EqualErrno(t, sys.ENOENT, sys.UnwrapOSError(err)) // Show the file inside that directory moved s, err := os.Stat(path.Join(dir2Path, file1)) @@ -143,7 +141,7 @@ func TestRename(t *testing.T) { require.NoError(t, err) err = rename(dir1Path, dir2Path) - require.EqualErrno(t, syscall.ENOTEMPTY, err) + require.EqualErrno(t, sys.ENOTEMPTY, err) }) t.Run("file to file", func(t *testing.T) { @@ -164,7 +162,7 @@ func TestRename(t *testing.T) { // Show the prior path no longer exists _, err = os.Stat(file1Path) - require.EqualErrno(t, syscall.ENOENT, errors.Unwrap(err)) + require.EqualErrno(t, sys.ENOENT, sys.UnwrapOSError(err)) // Show the file1 overwrote file2 b, err := os.ReadFile(file2Path) diff --git a/internal/sysfs/rename_windows.go b/internal/sysfs/rename_windows.go index 1d38faadc1..5e81022391 100644 --- a/internal/sysfs/rename_windows.go +++ b/internal/sysfs/rename_windows.go @@ -4,10 +4,10 @@ import ( "os" "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func rename(from, to string) syscall.Errno { +func rename(from, to string) sys.Errno { if from == to { return 0 } @@ -18,7 +18,7 @@ func rename(from, to string) syscall.Errno { } else { fromIsDir = fromStat.Mode.IsDir() } - if toStat, errno := stat(to); errno == syscall.ENOENT { + 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 @@ -29,20 +29,20 @@ func rename(from, to string) syscall.Errno { // Now, handle known cases switch { case !fromIsDir && toIsDir: // file to dir - return syscall.EISDIR + 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 platform.UnwrapOSError(os.Rename(from, to)) + return sys.UnwrapOSError(os.Rename(from, to)) case fromIsDir && !toIsDir: // dir to file - return syscall.ENOTDIR + 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 == syscall.ENOENT { + if errno := rmdir(to); errno == 0 || errno == sys.ENOENT { return syscallRename(from, to) } else { return errno @@ -50,6 +50,6 @@ func rename(from, to string) syscall.Errno { } } -func syscallRename(from string, to string) syscall.Errno { - return platform.UnwrapOSError(syscall.Rename(from, to)) +func syscallRename(from string, to string) sys.Errno { + return sys.UnwrapOSError(syscall.Rename(from, to)) } diff --git a/internal/sysfs/select_test.go b/internal/sysfs/select_test.go index 50d2e69062..8cee83e4e4 100644 --- a/internal/sysfs/select_test.go +++ b/internal/sysfs/select_test.go @@ -3,10 +3,10 @@ package sysfs import ( "os" "runtime" - "syscall" "testing" "time" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -16,7 +16,7 @@ func TestSelect(t *testing.T) { for { dur := time.Duration(0) n, err := _select(0, nil, nil, nil, &dur) - if err == syscall.EINTR { + if err == sys.EINTR { t.Logf("Select interrupted") continue } @@ -36,7 +36,7 @@ func TestSelect(t *testing.T) { start := time.Now() n, err := _select(0, nil, nil, nil, &dur) took = time.Since(start) - if err == syscall.EINTR { + if err == sys.EINTR { t.Logf("Select interrupted after %v", took) continue } @@ -77,11 +77,11 @@ func TestSelect(t *testing.T) { n, err := _select(fd+1, rFdSet, nil, nil, nil) if runtime.GOOS == "windows" { // Not implemented for fds != wasiFdStdin - require.ErrorIs(t, err, syscall.ENOSYS) + require.ErrorIs(t, err, sys.ENOSYS) require.Equal(t, -1, n) break } - if err == syscall.EINTR { + if err == sys.EINTR { t.Log("Select interrupted") continue } diff --git a/internal/sysfs/select_unsupported.go b/internal/sysfs/select_unsupported.go index 5244374b32..400df900e6 100644 --- a/internal/sysfs/select_unsupported.go +++ b/internal/sysfs/select_unsupported.go @@ -3,12 +3,12 @@ package sysfs import ( - "syscall" "time" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/platform" ) func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) { - return -1, syscall.ENOSYS + return -1, sys.ENOSYS } diff --git a/internal/sysfs/select_windows.go b/internal/sysfs/select_windows.go index 387b0e75fe..0791bfd25d 100644 --- a/internal/sysfs/select_windows.go +++ b/internal/sysfs/select_windows.go @@ -5,6 +5,7 @@ import ( "syscall" "time" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/platform" ) @@ -15,12 +16,12 @@ const wasiFdStdin = 0 // pollInterval is the interval between each calls to peekNamedPipe in pollNamedPipe const pollInterval = 100 * time.Millisecond -// syscall_select emulates the select syscall on Windows for two, well-known cases, returns syscall.ENOSYS for all others. +// syscall_select emulates the select syscall on Windows for two, well-known cases, returns sys.ENOSYS for all others. // If r contains fd 0, and it is a regular file, then it immediately returns 1 (data ready on stdin) // and r will have the fd 0 bit set. // If r contains fd 0, and it is a FILE_TYPE_CHAR, then it invokes PeekNamedPipe to check the buffer for input; // if there is data ready, then it returns 1 and r will have fd 0 bit set. -// If n==0 it will wait for the given timeout duration, but it will return syscall.ENOSYS if timeout is nil, +// 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. // // Note: idea taken from https://stackoverflow.com/questions/6839508/test-if-stdin-has-input-for-c-windows-and-or-linux @@ -31,7 +32,7 @@ func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int if n == 0 { // Don't block indefinitely. if timeout == nil { - return -1, syscall.ENOSYS + return -1, sys.ENOSYS } time.Sleep(*timeout) return 0, nil @@ -55,7 +56,7 @@ func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int r.Set(wasiFdStdin) return 1, nil } - return -1, syscall.ENOSYS + return -1, sys.ENOSYS } // pollNamedPipe polls the given named pipe handle for the given duration. diff --git a/internal/sysfs/sock.go b/internal/sysfs/sock.go index 6f5a63b071..ea59409943 100644 --- a/internal/sysfs/sock.go +++ b/internal/sysfs/sock.go @@ -3,8 +3,8 @@ package sysfs import ( "net" "os" - "syscall" + 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" @@ -24,14 +24,14 @@ type baseSockFile struct { var _ fsapi.File = (*baseSockFile)(nil) // IsDir implements the same method as documented on File.IsDir -func (*baseSockFile) IsDir() (bool, syscall.Errno) { +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 syscall.Errno) { +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 diff --git a/internal/sysfs/sock_test.go b/internal/sysfs/sock_test.go index 133dcd37b8..e250592583 100644 --- a/internal/sysfs/sock_test.go +++ b/internal/sysfs/sock_test.go @@ -2,10 +2,10 @@ package sysfs import ( "net" - "syscall" "testing" "time" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -21,12 +21,12 @@ func TestTcpConnFile_Write(t *testing.T) { defer tcp.Close() //nolint file := newTcpConn(tcp) - errno := syscall.Errno(0) + errno := sys.Errno(0) // Ensure we don't interrupt until we get a non-zero errno, // and we retry on EAGAIN (i.e. when nonblocking is true). for { _, errno = file.Write([]byte("wazero")) - if errno != syscall.EAGAIN { + if errno != sys.EAGAIN { break } time.Sleep(100 * time.Millisecond) @@ -68,13 +68,13 @@ func TestTcpConnFile_Read(t *testing.T) { bytes := make([]byte, 4) require.NoError(t, err) - errno := syscall.Errno(0) + errno := sys.Errno(0) file := newTcpConn(conn.(*net.TCPConn)) // Ensure we don't interrupt until we get a non-zero errno, // and we retry on EAGAIN (i.e. when nonblocking is true). for { _, errno = file.Read(bytes) - if errno != syscall.EAGAIN { + if errno != sys.EAGAIN { break } time.Sleep(100 * time.Millisecond) diff --git a/internal/sysfs/sock_unix.go b/internal/sysfs/sock_unix.go index aa3d3bb59d..2509a17303 100644 --- a/internal/sysfs/sock_unix.go +++ b/internal/sysfs/sock_unix.go @@ -6,7 +6,7 @@ import ( "net" "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" socketapi "github.com/tetratelabs/wazero/internal/sock" ) @@ -46,9 +46,9 @@ type tcpListenerFile struct { } // Accept implements the same method as documented on socketapi.TCPSock -func (f *tcpListenerFile) Accept() (socketapi.TCPConn, syscall.Errno) { +func (f *tcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) { nfd, _, err := syscall.Accept(int(f.fd)) - errno := platform.UnwrapOSError(err) + errno := sys.UnwrapOSError(err) if errno != 0 { return nil, errno } @@ -56,13 +56,13 @@ func (f *tcpListenerFile) Accept() (socketapi.TCPConn, syscall.Errno) { } // SetNonblock implements the same method as documented on fsapi.File -func (f *tcpListenerFile) SetNonblock(enabled bool) syscall.Errno { - return platform.UnwrapOSError(setNonblock(f.fd, enabled)) +func (f *tcpListenerFile) SetNonblock(enabled bool) sys.Errno { + return sys.UnwrapOSError(setNonblock(f.fd, enabled)) } // Close implements the same method as documented on fsapi.File -func (f *tcpListenerFile) Close() syscall.Errno { - return platform.UnwrapOSError(syscall.Close(int(f.fd))) +func (f *tcpListenerFile) Close() sys.Errno { + return sys.UnwrapOSError(syscall.Close(int(f.fd))) } // Addr is exposed for testing. @@ -77,7 +77,7 @@ type tcpConnFile struct { fd uintptr - // closed is true when closed was called. This ensures proper syscall.EBADF + // closed is true when closed was called. This ensures proper sys.EBADF closed bool } @@ -90,45 +90,45 @@ func newTcpConn(tc *net.TCPConn) socketapi.TCPConn { } // SetNonblock implements the same method as documented on fsapi.File -func (f *tcpConnFile) SetNonblock(enabled bool) (errno syscall.Errno) { - return platform.UnwrapOSError(setNonblock(f.fd, enabled)) +func (f *tcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) { + return sys.UnwrapOSError(setNonblock(f.fd, enabled)) } // Read implements the same method as documented on fsapi.File -func (f *tcpConnFile) Read(buf []byte) (n int, errno syscall.Errno) { +func (f *tcpConnFile) Read(buf []byte) (n int, errno sys.Errno) { n, err := syscall.Read(int(f.fd), buf) if err != nil { // Defer validation overhead until we've already had an error. - errno = platform.UnwrapOSError(err) + errno = sys.UnwrapOSError(err) errno = fileError(f, f.closed, errno) } return n, errno } // Write implements the same method as documented on fsapi.File -func (f *tcpConnFile) Write(buf []byte) (n int, errno syscall.Errno) { +func (f *tcpConnFile) Write(buf []byte) (n int, errno sys.Errno) { n, err := syscall.Write(int(f.fd), buf) if err != nil { // Defer validation overhead until we've already had an error. - errno = platform.UnwrapOSError(err) + errno = sys.UnwrapOSError(err) errno = fileError(f, f.closed, errno) } return n, errno } // Recvfrom implements the same method as documented on socketapi.TCPConn -func (f *tcpConnFile) Recvfrom(p []byte, flags int) (n int, errno syscall.Errno) { +func (f *tcpConnFile) Recvfrom(p []byte, flags int) (n int, errno sys.Errno) { if flags != MSG_PEEK { - errno = syscall.EINVAL + errno = sys.EINVAL return } n, _, recvfromErr := syscall.Recvfrom(int(f.fd), p, MSG_PEEK) - errno = platform.UnwrapOSError(recvfromErr) + errno = sys.UnwrapOSError(recvfromErr) return n, errno } // Shutdown implements the same method as documented on fsapi.Conn -func (f *tcpConnFile) Shutdown(how int) syscall.Errno { +func (f *tcpConnFile) Shutdown(how int) sys.Errno { var err error switch how { case syscall.SHUT_RD, syscall.SHUT_WR: @@ -136,20 +136,20 @@ func (f *tcpConnFile) Shutdown(how int) syscall.Errno { case syscall.SHUT_RDWR: return f.close() default: - return syscall.EINVAL + return sys.EINVAL } - return platform.UnwrapOSError(err) + return sys.UnwrapOSError(err) } // Close implements the same method as documented on fsapi.File -func (f *tcpConnFile) Close() syscall.Errno { +func (f *tcpConnFile) Close() sys.Errno { return f.close() } -func (f *tcpConnFile) close() syscall.Errno { +func (f *tcpConnFile) close() sys.Errno { if f.closed { return 0 } f.closed = true - return platform.UnwrapOSError(syscall.Shutdown(int(f.fd), syscall.SHUT_RDWR)) + return sys.UnwrapOSError(syscall.Shutdown(int(f.fd), syscall.SHUT_RDWR)) } diff --git a/internal/sysfs/sock_unsupported.go b/internal/sysfs/sock_unsupported.go index 57e8eb10a2..76a10a8b61 100644 --- a/internal/sysfs/sock_unsupported.go +++ b/internal/sysfs/sock_unsupported.go @@ -4,8 +4,8 @@ package sysfs import ( "net" - "syscall" + "github.com/tetratelabs/wazero/experimental/sys" socketapi "github.com/tetratelabs/wazero/internal/sock" ) @@ -21,6 +21,6 @@ type unsupportedSockFile struct { } // Accept implements the same method as documented on socketapi.TCPSock -func (f *unsupportedSockFile) Accept() (socketapi.TCPConn, syscall.Errno) { - return nil, syscall.ENOSYS +func (f *unsupportedSockFile) Accept() (socketapi.TCPConn, sys.Errno) { + return nil, sys.ENOSYS } diff --git a/internal/sysfs/sock_windows.go b/internal/sysfs/sock_windows.go index 9f3b46913a..a150a7403c 100644 --- a/internal/sysfs/sock_windows.go +++ b/internal/sysfs/sock_windows.go @@ -7,7 +7,7 @@ import ( "syscall" "unsafe" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" socketapi "github.com/tetratelabs/wazero/internal/sock" ) @@ -65,22 +65,22 @@ type winTcpListenerFile struct { } // Accept implements the same method as documented on socketapi.TCPSock -func (f *winTcpListenerFile) Accept() (socketapi.TCPConn, syscall.Errno) { +func (f *winTcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) { conn, err := f.tl.Accept() if err != nil { - return nil, platform.UnwrapOSError(err) + return nil, sys.UnwrapOSError(err) } return &winTcpConnFile{tc: conn.(*net.TCPConn)}, 0 } // SetNonblock implements the same method as documented on fsapi.File -func (f *winTcpListenerFile) SetNonblock(enabled bool) syscall.Errno { +func (f *winTcpListenerFile) SetNonblock(enabled bool) sys.Errno { return 0 // setNonblock() is a no-op on Windows } // Close implements the same method as documented on fsapi.File -func (f *winTcpListenerFile) Close() syscall.Errno { - return platform.UnwrapOSError(f.tl.Close()) +func (f *winTcpListenerFile) Close() sys.Errno { + return sys.UnwrapOSError(f.tl.Close()) } // Addr is exposed for testing. @@ -95,7 +95,7 @@ type winTcpConnFile struct { tc *net.TCPConn - // closed is true when closed was called. This ensures proper syscall.EBADF + // closed is true when closed was called. This ensures proper sys.EBADF closed bool } @@ -104,23 +104,23 @@ func newTcpConn(tc *net.TCPConn) socketapi.TCPConn { } // SetNonblock implements the same method as documented on fsapi.File -func (f *winTcpConnFile) SetNonblock(enabled bool) (errno syscall.Errno) { +func (f *winTcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) { syscallConn, err := f.tc.SyscallConn() if err != nil { - return platform.UnwrapOSError(err) + return sys.UnwrapOSError(err) } // Prioritize the error from setNonblock over Control if controlErr := syscallConn.Control(func(fd uintptr) { - errno = platform.UnwrapOSError(setNonblock(fd, enabled)) + errno = sys.UnwrapOSError(setNonblock(fd, enabled)) }); errno == 0 { - errno = platform.UnwrapOSError(controlErr) + errno = sys.UnwrapOSError(controlErr) } return } // Read implements the same method as documented on fsapi.File -func (f *winTcpConnFile) Read(buf []byte) (n int, errno syscall.Errno) { +func (f *winTcpConnFile) Read(buf []byte) (n int, errno sys.Errno) { if n, errno = read(f.tc, buf); errno != 0 { // Defer validation overhead until we've already had an error. errno = fileError(f, f.closed, errno) @@ -129,7 +129,7 @@ func (f *winTcpConnFile) Read(buf []byte) (n int, errno syscall.Errno) { } // Write implements the same method as documented on fsapi.File -func (f *winTcpConnFile) Write(buf []byte) (n int, errno syscall.Errno) { +func (f *winTcpConnFile) Write(buf []byte) (n int, errno sys.Errno) { if n, errno = write(f.tc, buf); errno != 0 { // Defer validation overhead until we've already had an error. errno = fileError(f, f.closed, errno) @@ -138,15 +138,15 @@ func (f *winTcpConnFile) Write(buf []byte) (n int, errno syscall.Errno) { } // Recvfrom implements the same method as documented on socketapi.TCPConn -func (f *winTcpConnFile) Recvfrom(p []byte, flags int) (n int, errno syscall.Errno) { +func (f *winTcpConnFile) Recvfrom(p []byte, flags int) (n int, errno sys.Errno) { if flags != MSG_PEEK { - errno = syscall.EINVAL + errno = sys.EINVAL return } conn := f.tc syscallConn, err := conn.SyscallConn() if err != nil { - errno = platform.UnwrapOSError(err) + errno = sys.UnwrapOSError(err) return } @@ -154,15 +154,15 @@ func (f *winTcpConnFile) Recvfrom(p []byte, flags int) (n int, errno syscall.Err if controlErr := syscallConn.Control(func(fd uintptr) { var recvfromErr error n, recvfromErr = recvfrom(syscall.Handle(fd), p, MSG_PEEK) - errno = platform.UnwrapOSError(recvfromErr) + errno = sys.UnwrapOSError(recvfromErr) }); errno == 0 { - errno = platform.UnwrapOSError(controlErr) + errno = sys.UnwrapOSError(controlErr) } return } // Shutdown implements the same method as documented on fsapi.Conn -func (f *winTcpConnFile) Shutdown(how int) syscall.Errno { +func (f *winTcpConnFile) Shutdown(how int) sys.Errno { // FIXME: can userland shutdown listeners? var err error switch how { @@ -173,17 +173,17 @@ func (f *winTcpConnFile) Shutdown(how int) syscall.Errno { case syscall.SHUT_RDWR: return f.close() default: - return syscall.EINVAL + return sys.EINVAL } - return platform.UnwrapOSError(err) + return sys.UnwrapOSError(err) } // Close implements the same method as documented on fsapi.File -func (f *winTcpConnFile) Close() syscall.Errno { +func (f *winTcpConnFile) Close() sys.Errno { return f.close() } -func (f *winTcpConnFile) close() syscall.Errno { +func (f *winTcpConnFile) close() sys.Errno { if f.closed { return 0 } diff --git a/internal/sysfs/stat.go b/internal/sysfs/stat.go index 1e05fdc86b..2d973b16cb 100644 --- a/internal/sysfs/stat.go +++ b/internal/sysfs/stat.go @@ -2,15 +2,14 @@ package sysfs import ( "io/fs" - "syscall" - "github.com/tetratelabs/wazero/internal/platform" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) -func defaultStatFile(f fs.File) (sys.Stat_t, syscall.Errno) { +func defaultStatFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) { if info, err := f.Stat(); err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } else { return sys.NewStat_t(info), 0 } diff --git a/internal/sysfs/stat_bsd.go b/internal/sysfs/stat_bsd.go index ed21f87cba..aeb7419716 100644 --- a/internal/sysfs/stat_bsd.go +++ b/internal/sysfs/stat_bsd.go @@ -7,7 +7,7 @@ import ( "os" "syscall" - "github.com/tetratelabs/wazero/internal/platform" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -17,27 +17,27 @@ import ( // Note: this is only used in tests const dirNlinkIncludesDot = true -func lstat(path string) (sys.Stat_t, syscall.Errno) { +func lstat(path string) (sys.Stat_t, experimentalsys.Errno) { if info, err := os.Lstat(path); err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } else { return sys.NewStat_t(info), 0 } } -func stat(path string) (sys.Stat_t, syscall.Errno) { +func stat(path string) (sys.Stat_t, experimentalsys.Errno) { if info, err := os.Stat(path); err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } else { return sys.NewStat_t(info), 0 } } -func statFile(f fs.File) (sys.Stat_t, syscall.Errno) { +func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) { return defaultStatFile(f) } -func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, syscall.Errno) { +func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) { switch v := info.Sys().(type) { case *sys.Stat_t: return v.Ino, 0 diff --git a/internal/sysfs/stat_linux.go b/internal/sysfs/stat_linux.go index c48e5f7418..426b2850a4 100644 --- a/internal/sysfs/stat_linux.go +++ b/internal/sysfs/stat_linux.go @@ -10,7 +10,7 @@ import ( "os" "syscall" - "github.com/tetratelabs/wazero/internal/platform" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -20,27 +20,27 @@ import ( // Note: this is only used in tests const dirNlinkIncludesDot = true -func lstat(path string) (sys.Stat_t, syscall.Errno) { +func lstat(path string) (sys.Stat_t, experimentalsys.Errno) { if info, err := os.Lstat(path); err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } else { return sys.NewStat_t(info), 0 } } -func stat(path string) (sys.Stat_t, syscall.Errno) { +func stat(path string) (sys.Stat_t, experimentalsys.Errno) { if info, err := os.Stat(path); err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } else { return sys.NewStat_t(info), 0 } } -func statFile(f fs.File) (sys.Stat_t, syscall.Errno) { +func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) { return defaultStatFile(f) } -func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, syscall.Errno) { +func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) { switch v := info.Sys().(type) { case *sys.Stat_t: return v.Ino, 0 diff --git a/internal/sysfs/stat_test.go b/internal/sysfs/stat_test.go index 2f714a2f0a..36d42564b1 100644 --- a/internal/sysfs/stat_test.go +++ b/internal/sysfs/stat_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/sys" @@ -17,9 +18,9 @@ func TestStat(t *testing.T) { tmpDir := t.TempDir() _, errno := stat(path.Join(tmpDir, "cat")) - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, experimentalsys.ENOENT, errno) _, errno = stat(path.Join(tmpDir, "sub/cat")) - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, experimentalsys.ENOENT, errno) var st sys.Stat_t @@ -120,7 +121,7 @@ func TestStatFile(t *testing.T) { t.Run("closed dir", func(t *testing.T) { require.EqualErrno(t, 0, tmpDirF.Close()) _, errno := tmpDirF.Stat() - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, experimentalsys.EBADF, errno) }) } @@ -140,7 +141,7 @@ func TestStatFile(t *testing.T) { t.Run("closed fsFile", func(t *testing.T) { require.EqualErrno(t, 0, fileF.Close()) _, errno := fileF.Stat() - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, experimentalsys.EBADF, errno) }) subdir := path.Join(tmpDir, "sub") @@ -159,7 +160,7 @@ func TestStatFile(t *testing.T) { t.Run("closed subdir", func(t *testing.T) { require.EqualErrno(t, 0, subdirF.Close()) _, errno := subdirF.Stat() - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, experimentalsys.EBADF, errno) }) } } diff --git a/internal/sysfs/stat_unsupported.go b/internal/sysfs/stat_unsupported.go index 12330070cf..b220041582 100644 --- a/internal/sysfs/stat_unsupported.go +++ b/internal/sysfs/stat_unsupported.go @@ -7,7 +7,7 @@ import ( "os" "syscall" - "github.com/tetratelabs/wazero/internal/platform" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -20,27 +20,27 @@ import ( // Note: this is only used in tests const dirNlinkIncludesDot = false -func lstat(path string) (sys.Stat_t, syscall.Errno) { +func lstat(path string) (sys.Stat_t, experimentalsys.Errno) { if info, err := os.Lstat(path); err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } else { return sys.NewStat_t(info), 0 } } -func stat(path string) (sys.Stat_t, syscall.Errno) { +func stat(path string) (sys.Stat_t, experimentalsys.Errno) { if info, err := os.Stat(path); err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } else { return sys.NewStat_t(info), 0 } } -func statFile(f fs.File) (sys.Stat_t, syscall.Errno) { +func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) { return defaultStatFile(f) } -func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, syscall.Errno) { +func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) { if st, ok := info.Sys().(*syscall.Stat_t); ok { return st.Ino, 0 } diff --git a/internal/sysfs/stat_windows.go b/internal/sysfs/stat_windows.go index 973b574ead..db28300bf4 100644 --- a/internal/sysfs/stat_windows.go +++ b/internal/sysfs/stat_windows.go @@ -7,7 +7,7 @@ import ( "path" "syscall" - "github.com/tetratelabs/wazero/internal/platform" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -16,7 +16,7 @@ import ( // Note: this is only used in tests const dirNlinkIncludesDot = false -func lstat(path string) (sys.Stat_t, syscall.Errno) { +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 @@ -24,30 +24,31 @@ func lstat(path string) (sys.Stat_t, syscall.Errno) { return statPath(attrs, path) } -func stat(path string) (sys.Stat_t, syscall.Errno) { +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, syscall.Errno) { +func statPath(createFileAttrs uint32, path string) (sys.Stat_t, experimentalsys.Errno) { if len(path) == 0 { - return sys.Stat_t{}, syscall.ENOENT + return sys.Stat_t{}, experimentalsys.ENOENT } pathp, err := syscall.UTF16PtrFromString(path) if err != nil { - return sys.Stat_t{}, syscall.EINVAL + 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 err == syscall.ENOTDIR { - err = syscall.ENOENT + if errno == experimentalsys.ENOTDIR { + errno = experimentalsys.ENOENT } - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, errno } defer syscall.CloseHandle(h) @@ -59,7 +60,7 @@ type fdFile interface { Fd() uintptr } -func statFile(f fs.File) (sys.Stat_t, syscall.Errno) { +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())) @@ -69,7 +70,7 @@ func statFile(f fs.File) (sys.Stat_t, syscall.Errno) { // // Note: statHandle uses UnwrapOSError which coerces // ERROR_INVALID_HANDLE to EBADF. - if err != syscall.EBADF { + if err != experimentalsys.EBADF { return st, err } } @@ -77,7 +78,7 @@ func statFile(f fs.File) (sys.Stat_t, syscall.Errno) { } // inoFromFileInfo uses stat to get the inode information of the file. -func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno syscall.Errno) { +func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno experimentalsys.Errno) { if dirPath == "" { // This is a fs.File backed implementation which doesn't have access to // the original file path. @@ -92,15 +93,15 @@ func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno sys return } -func statHandle(h syscall.Handle) (sys.Stat_t, syscall.Errno) { +func statHandle(h syscall.Handle) (sys.Stat_t, experimentalsys.Errno) { winFt, err := syscall.GetFileType(h) if err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } var fi syscall.ByHandleFileInformation if err = syscall.GetFileInformationByHandle(h, &fi); err != nil { - return sys.Stat_t{}, platform.UnwrapOSError(err) + return sys.Stat_t{}, experimentalsys.UnwrapOSError(err) } var m fs.FileMode diff --git a/internal/sysfs/sync.go b/internal/sysfs/sync.go index ba336c8d5d..86f9a0865d 100644 --- a/internal/sysfs/sync.go +++ b/internal/sysfs/sync.go @@ -4,11 +4,10 @@ package sysfs import ( "os" - "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func fsync(f *os.File) syscall.Errno { - return platform.UnwrapOSError(f.Sync()) +func fsync(f *os.File) sys.Errno { + return sys.UnwrapOSError(f.Sync()) } diff --git a/internal/sysfs/sync_windows.go b/internal/sysfs/sync_windows.go index e83dc3044a..0f05ba7764 100644 --- a/internal/sysfs/sync_windows.go +++ b/internal/sysfs/sync_windows.go @@ -2,17 +2,16 @@ package sysfs import ( "os" - "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func fsync(f *os.File) syscall.Errno { - errno := platform.UnwrapOSError(f.Sync()) +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 syscall.EACCES /* Go 1.20 */, syscall.EBADF /* Go 1.18 */ : + case sys.EACCES /* Go 1.20 */, sys.EBADF /* Go 1.18 */ : if st, err := f.Stat(); err == nil && st.IsDir() { errno = 0 } diff --git a/internal/sysfs/sysfs_test.go b/internal/sysfs/sysfs_test.go index 795ed2ff78..667a12de21 100644 --- a/internal/sysfs/sysfs_test.go +++ b/internal/sysfs/sysfs_test.go @@ -8,9 +8,9 @@ import ( "path" "runtime" "sort" - "syscall" "testing" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/testing/require" @@ -49,7 +49,7 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS fsapi.FS) { if runtime.GOOS != "windows" { // If the read-only flag was honored, we should not be able to write! _, err = f.Write(fileContents) - require.EqualErrno(t, syscall.EBADF, platform.UnwrapOSError(err)) + require.EqualErrno(t, experimentalsys.EBADF, experimentalsys.UnwrapOSError(err)) } // Verify stat on the file @@ -94,7 +94,7 @@ func testOpen_Read(t *testing.T, testFS fsapi.FS, requireFileIno, expectDirIno b _, errno := testFS.OpenFile("nope", os.O_RDONLY, 0) // We currently follow os.Open not syscall.Open, so the error is wrapped. - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, experimentalsys.ENOENT, errno) }) t.Run("readdir . opens root", func(t *testing.T) { @@ -228,20 +228,20 @@ human defer f.Close() _, errno = f.Write([]byte{1, 2, 3, 4}) - require.EqualErrno(t, syscall.EBADF, errno) + require.EqualErrno(t, experimentalsys.EBADF, errno) }) t.Run("opening a directory with O_RDWR is EISDIR", func(t *testing.T) { _, errno := testFS.OpenFile("sub", fsapi.O_DIRECTORY|os.O_RDWR, 0) - require.EqualErrno(t, syscall.EISDIR, errno) + require.EqualErrno(t, experimentalsys.EISDIR, errno) }) } func testLstat(t *testing.T, testFS fsapi.FS) { _, errno := testFS.Lstat("cat") - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, experimentalsys.ENOENT, errno) _, errno = testFS.Lstat("sub/cat") - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, experimentalsys.ENOENT, errno) var st sys.Stat_t @@ -308,9 +308,9 @@ func requireLinkStat(t *testing.T, testFS fsapi.FS, path string, stat sys.Stat_t func testStat(t *testing.T, testFS fsapi.FS) { _, errno := testFS.Stat("cat") - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, experimentalsys.ENOENT, errno) _, errno = testFS.Stat("sub/cat") - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, experimentalsys.ENOENT, errno) st, errno := testFS.Stat("sub/test.txt") require.EqualErrno(t, 0, errno) diff --git a/internal/sysfs/unlink.go b/internal/sysfs/unlink.go index 37b4751729..4f20b00ea2 100644 --- a/internal/sysfs/unlink.go +++ b/internal/sysfs/unlink.go @@ -5,13 +5,13 @@ package sysfs import ( "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func unlink(name string) (errno syscall.Errno) { +func unlink(name string) (errno sys.Errno) { err := syscall.Unlink(name) - if errno = platform.UnwrapOSError(err); errno == syscall.EPERM { - errno = syscall.EISDIR + if errno = sys.UnwrapOSError(err); errno == sys.EPERM { + errno = sys.EISDIR } return errno } diff --git a/internal/sysfs/unlink_test.go b/internal/sysfs/unlink_test.go index b7f837f64c..0ee3043014 100644 --- a/internal/sysfs/unlink_test.go +++ b/internal/sysfs/unlink_test.go @@ -3,9 +3,9 @@ package sysfs import ( "os" "path" - "syscall" "testing" + "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -13,7 +13,7 @@ func TestUnlink(t *testing.T) { t.Run("doesn't exist", func(t *testing.T) { name := "non-existent" errno := unlink(name) - require.EqualErrno(t, syscall.ENOENT, errno) + require.EqualErrno(t, sys.ENOENT, errno) }) t.Run("target: dir", func(t *testing.T) { @@ -23,7 +23,7 @@ func TestUnlink(t *testing.T) { require.NoError(t, os.Mkdir(dir, 0o700)) errno := unlink(dir) - require.EqualErrno(t, syscall.EISDIR, errno) + require.EqualErrno(t, sys.EISDIR, errno) require.NoError(t, os.Remove(dir)) }) diff --git a/internal/sysfs/unlink_windows.go b/internal/sysfs/unlink_windows.go index ca7cdeb3d3..e247809eec 100644 --- a/internal/sysfs/unlink_windows.go +++ b/internal/sysfs/unlink_windows.go @@ -6,21 +6,21 @@ import ( "os" "syscall" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/experimental/sys" ) -func unlink(name string) syscall.Errno { +func unlink(name string) sys.Errno { err := syscall.Unlink(name) if err == nil { return 0 } - errno := platform.UnwrapOSError(err) - if errno == syscall.EBADF { + errno := sys.UnwrapOSError(err) + if errno == sys.EBADF { lstat, errLstat := os.Lstat(name) if errLstat == nil && lstat.Mode()&os.ModeSymlink != 0 { - errno = platform.UnwrapOSError(os.Remove(name)) + errno = sys.UnwrapOSError(os.Remove(name)) } else { - errno = syscall.EISDIR + errno = sys.EISDIR } } return errno diff --git a/internal/testing/require/require.go b/internal/testing/require/require.go index 909ac7049c..eabfbe7d6a 100644 --- a/internal/testing/require/require.go +++ b/internal/testing/require/require.go @@ -13,9 +13,10 @@ import ( "reflect" "runtime" "strings" - "syscall" "unicode" "unicode/utf8" + + "github.com/tetratelabs/wazero/experimental/sys" ) // TestingT is an interface wrapper of functions used in TestingT @@ -131,14 +132,14 @@ func Error(t TestingT, err error, formatWithArgs ...interface{}) { } } -// EqualErrno should be used for functions that return syscall.Errno or nil. -func EqualErrno(t TestingT, expected syscall.Errno, err error, formatWithArgs ...interface{}) { +// EqualErrno should be used for functions that return sys.Errno or nil. +func EqualErrno(t TestingT, expected sys.Errno, err error, formatWithArgs ...interface{}) { if err == nil { - fail(t, "expected a syscall.Errno, but was nil", "", formatWithArgs...) + fail(t, "expected a sys.Errno, but was nil", "", formatWithArgs...) return } - if se, ok := err.(syscall.Errno); !ok { - fail(t, fmt.Sprintf("expected %v to be a syscall.Errno", err), "", formatWithArgs...) + if se, ok := err.(sys.Errno); !ok { + fail(t, fmt.Sprintf("expected %v to be a sys.Errno", err), "", formatWithArgs...) } else if se != expected { fail(t, fmt.Sprintf("expected Errno %#[1]v(%[1]s), but was %#[2]v(%[2]s)", expected, err), "", formatWithArgs...) } diff --git a/internal/testing/require/require_errno_test.go b/internal/testing/require/require_errno_test.go index c70da689d7..9dbbe0c2b6 100644 --- a/internal/testing/require/require_errno_test.go +++ b/internal/testing/require/require_errno_test.go @@ -3,8 +3,9 @@ package require import ( "io" "runtime" - "syscall" "testing" + + "github.com/tetratelabs/wazero/experimental/sys" ) func TestEqualErrno(t *testing.T) { @@ -22,36 +23,36 @@ func TestEqualErrno(t *testing.T) { { name: "EqualErrno passes on equal", require: func(t TestingT) { - EqualErrno(t, syscall.ENOENT, syscall.ENOENT) + EqualErrno(t, sys.ENOENT, sys.ENOENT) }, }, { name: "EqualErrno fails on nil", require: func(t TestingT) { - EqualErrno(t, syscall.ENOENT, nil) + EqualErrno(t, sys.ENOENT, nil) }, - expectedLog: "expected a syscall.Errno, but was nil", + expectedLog: "expected a sys.Errno, but was nil", }, { name: "EqualErrno fails on not Errno", require: func(t TestingT) { - EqualErrno(t, syscall.ENOENT, io.EOF) + EqualErrno(t, sys.ENOENT, io.EOF) }, - expectedLog: `expected EOF to be a syscall.Errno`, + expectedLog: `expected EOF to be a sys.Errno`, }, { name: "EqualErrno fails on not equal", require: func(t TestingT) { - EqualErrno(t, syscall.ENOENT, syscall.EIO) + EqualErrno(t, sys.ENOENT, sys.EIO) }, - expectedLog: `expected Errno 0x2(no such file or directory), but was 0x5(input/output error)`, + expectedLog: `expected Errno 0xc(no such file or directory), but was 0x8(input/output error)`, }, { name: "EqualErrno fails on not equal with format", require: func(t TestingT) { - EqualErrno(t, syscall.ENOENT, syscall.EIO, "pay me %d", 5) + EqualErrno(t, sys.ENOENT, sys.EIO, "pay me %d", 5) }, - expectedLog: `expected Errno 0x2(no such file or directory), but was 0x5(input/output error): pay me 5`, + expectedLog: `expected Errno 0xc(no such file or directory), but was 0x8(input/output error): pay me 5`, }, } diff --git a/internal/wasip1/errno.go b/internal/wasip1/errno.go index b4ec34eb76..b4af415083 100644 --- a/internal/wasip1/errno.go +++ b/internal/wasip1/errno.go @@ -2,7 +2,8 @@ package wasip1 import ( "fmt" - "syscall" + + "github.com/tetratelabs/wazero/experimental/sys" ) // Errno is neither uint16 nor an alias for parity with wasm.ValueType. @@ -263,47 +264,47 @@ var errnoToString = [...]string{ // Note: Coercion isn't centralized in sys.FSContext because ABI use different // error codes. For example, wasi-filesystem and GOOS=js don't map to these // Errno. -func ToErrno(errno syscall.Errno) Errno { +func ToErrno(errno sys.Errno) Errno { switch errno { case 0: return ErrnoSuccess - case syscall.EACCES: + case sys.EACCES: return ErrnoAcces - case syscall.EAGAIN: + case sys.EAGAIN: return ErrnoAgain - case syscall.EBADF: + case sys.EBADF: return ErrnoBadf - case syscall.EEXIST: + case sys.EEXIST: return ErrnoExist - case syscall.EFAULT: + case sys.EFAULT: return ErrnoFault - case syscall.EINTR: + case sys.EINTR: return ErrnoIntr - case syscall.EINVAL: + case sys.EINVAL: return ErrnoInval - case syscall.EIO: + case sys.EIO: return ErrnoIo - case syscall.EISDIR: + case sys.EISDIR: return ErrnoIsdir - case syscall.ELOOP: + case sys.ELOOP: return ErrnoLoop - case syscall.ENAMETOOLONG: + case sys.ENAMETOOLONG: return ErrnoNametoolong - case syscall.ENOENT: + case sys.ENOENT: return ErrnoNoent - case syscall.ENOSYS: + case sys.ENOSYS: return ErrnoNosys - case syscall.ENOTDIR: + case sys.ENOTDIR: return ErrnoNotdir - case syscall.ENOTEMPTY: + case sys.ENOTEMPTY: return ErrnoNotempty - case syscall.ENOTSOCK: + case sys.ENOTSOCK: return ErrnoNotsock - case syscall.ENOTSUP: + case sys.ENOTSUP: return ErrnoNotsup - case syscall.EPERM: + case sys.EPERM: return ErrnoPerm - case syscall.EROFS: + case sys.EROFS: return ErrnoRofs default: return ErrnoIo diff --git a/internal/wasip1/errno_test.go b/internal/wasip1/errno_test.go index 24ec547071..961e9fe5f8 100644 --- a/internal/wasip1/errno_test.go +++ b/internal/wasip1/errno_test.go @@ -1,14 +1,15 @@ package wasip1 import ( - "syscall" "testing" + + "github.com/tetratelabs/wazero/experimental/sys" ) func TestToErrno(t *testing.T) { tests := []struct { name string - input syscall.Errno + input sys.Errno expected Errno }{ { @@ -16,98 +17,103 @@ func TestToErrno(t *testing.T) { expected: ErrnoSuccess, }, { - name: "syscall.EACCES", - input: syscall.EACCES, + name: "sys.EACCES", + input: sys.EACCES, expected: ErrnoAcces, }, { - name: "syscall.EAGAIN", - input: syscall.EAGAIN, + name: "sys.EAGAIN", + input: sys.EAGAIN, expected: ErrnoAgain, }, { - name: "syscall.EBADF", - input: syscall.EBADF, + name: "sys.EBADF", + input: sys.EBADF, expected: ErrnoBadf, }, { - name: "syscall.EEXIST", - input: syscall.EEXIST, + name: "sys.EEXIST", + input: sys.EEXIST, expected: ErrnoExist, }, { - name: "syscall.EFAULT", - input: syscall.EFAULT, + name: "sys.EFAULT", + input: sys.EFAULT, expected: ErrnoFault, }, { - name: "syscall.EINTR", - input: syscall.EINTR, + name: "sys.EINTR", + input: sys.EINTR, expected: ErrnoIntr, }, { - name: "syscall.EINVAL", - input: syscall.EINVAL, + name: "sys.EINVAL", + input: sys.EINVAL, expected: ErrnoInval, }, { - name: "syscall.EIO", - input: syscall.EIO, + name: "sys.EIO", + input: sys.EIO, expected: ErrnoIo, }, { - name: "syscall.EISDIR", - input: syscall.EISDIR, + name: "sys.EISDIR", + input: sys.EISDIR, expected: ErrnoIsdir, }, { - name: "syscall.ELOOP", - input: syscall.ELOOP, + name: "sys.ELOOP", + input: sys.ELOOP, expected: ErrnoLoop, }, { - name: "syscall.ENAMETOOLONG", - input: syscall.ENAMETOOLONG, + name: "sys.ENAMETOOLONG", + input: sys.ENAMETOOLONG, expected: ErrnoNametoolong, }, { - name: "syscall.ENOENT", - input: syscall.ENOENT, + name: "sys.ENOENT", + input: sys.ENOENT, expected: ErrnoNoent, }, { - name: "syscall.ENOSYS", - input: syscall.ENOSYS, + name: "sys.ENOSYS", + input: sys.ENOSYS, expected: ErrnoNosys, }, { - name: "syscall.ENOTDIR", - input: syscall.ENOTDIR, + name: "sys.ENOTDIR", + input: sys.ENOTDIR, expected: ErrnoNotdir, }, { - name: "syscall.ENOTEMPTY", - input: syscall.ENOTEMPTY, + name: "sys.ENOTEMPTY", + input: sys.ENOTEMPTY, expected: ErrnoNotempty, }, { - name: "syscall.ENOTSUP", - input: syscall.ENOTSUP, + name: "sys.ENOTSOCK", + input: sys.ENOTSOCK, + expected: ErrnoNotsock, + }, + { + name: "sys.ENOTSUP", + input: sys.ENOTSUP, expected: ErrnoNotsup, }, { - name: "syscall.EPERM", - input: syscall.EPERM, + name: "sys.EPERM", + input: sys.EPERM, expected: ErrnoPerm, }, { - name: "syscall.EROFS", - input: syscall.EROFS, + name: "sys.EROFS", + input: sys.EROFS, expected: ErrnoRofs, }, { - name: "syscall.EqualErrno unexpected == ErrnoIo", - input: syscall.Errno(0xfe), + name: "sys.EqualErrno unexpected == ErrnoIo", + input: sys.Errno(0xfe), expected: ErrnoIo, }, } diff --git a/internal/wasm/module_instance_test.go b/internal/wasm/module_instance_test.go index cc1f2c91d3..761bdf42c7 100644 --- a/internal/wasm/module_instance_test.go +++ b/internal/wasm/module_instance_test.go @@ -7,10 +7,10 @@ import ( "os" "sync" "sync/atomic" - "syscall" "testing" "time" + "github.com/tetratelabs/wazero/experimental/sys" internalsys "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/sysfs" testfs "github.com/tetratelabs/wazero/internal/testing/fs" @@ -158,8 +158,8 @@ func TestModuleInstance_Close(t *testing.T) { m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil) require.NoError(t, err) - // In fsapi.FS, non syscall errors map to syscall.EIO. - require.EqualErrno(t, syscall.EIO, m.Close(testCtx)) + // In fsapi.FS, non syscall errors map to sys.EIO. + require.EqualErrno(t, sys.EIO, m.Close(testCtx)) // Verify our intended side-effect _, ok := fsCtx.LookupFile(3) @@ -257,8 +257,8 @@ func TestModuleInstance_CallDynamic(t *testing.T) { m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil) require.NoError(t, err) - // In fsapi.FS, non syscall errors map to syscall.EIO. - require.EqualErrno(t, syscall.EIO, m.Close(testCtx)) + // In fsapi.FS, non syscall errors map to sys.EIO. + require.EqualErrno(t, sys.EIO, m.Close(testCtx)) // Verify our intended side-effect _, ok := fsCtx.LookupFile(3) diff --git a/sys/error_test.go b/sys/error_test.go index bbf607555c..473e476847 100644 --- a/sys/error_test.go +++ b/sys/error_test.go @@ -1,4 +1,4 @@ -package sys +package sys_test import ( "context" @@ -6,6 +6,7 @@ import ( "testing" "github.com/tetratelabs/wazero/internal/testing/require" + "github.com/tetratelabs/wazero/sys" ) type notExitError struct { @@ -17,7 +18,7 @@ func (e *notExitError) Error() string { } func TestIs(t *testing.T) { - err := NewExitError(2) + err := sys.NewExitError(2) tests := []struct { name string target error @@ -30,7 +31,7 @@ func TestIs(t *testing.T) { }, { name: "different exit code", - target: NewExitError(1), + target: sys.NewExitError(1), matches: false, }, { @@ -53,19 +54,19 @@ func TestIs(t *testing.T) { func TestExitError_Error(t *testing.T) { t.Run("timeout", func(t *testing.T) { - err := NewExitError(ExitCodeDeadlineExceeded) - require.Equal(t, ExitCodeDeadlineExceeded, err.ExitCode()) + err := sys.NewExitError(sys.ExitCodeDeadlineExceeded) + require.Equal(t, sys.ExitCodeDeadlineExceeded, err.ExitCode()) require.EqualError(t, err, "module closed with context deadline exceeded") require.ErrorIs(t, err, context.DeadlineExceeded, "exit code context deadline exceeded should work") }) t.Run("cancel", func(t *testing.T) { - err := NewExitError(ExitCodeContextCanceled) - require.Equal(t, ExitCodeContextCanceled, err.ExitCode()) + err := sys.NewExitError(sys.ExitCodeContextCanceled) + require.Equal(t, sys.ExitCodeContextCanceled, err.ExitCode()) require.EqualError(t, err, "module closed with context canceled") require.ErrorIs(t, err, context.Canceled, "exit code context canceled should work") }) t.Run("normal", func(t *testing.T) { - err := NewExitError(123) + err := sys.NewExitError(123) require.Equal(t, uint32(123), err.ExitCode()) require.EqualError(t, err, "module closed with exit_code(123)") }) diff --git a/sys/stat_export_test.go b/sys/stat_export_test.go new file mode 100644 index 0000000000..b7f737d3e5 --- /dev/null +++ b/sys/stat_export_test.go @@ -0,0 +1,3 @@ +package sys + +const SysParseable = sysParseable diff --git a/sys/stat_test.go b/sys/stat_test.go index 2c5840daef..32d0ffeac7 100644 --- a/sys/stat_test.go +++ b/sys/stat_test.go @@ -1,4 +1,4 @@ -package sys +package sys_test import ( "io/fs" @@ -9,6 +9,7 @@ import ( "testing/fstest" "github.com/tetratelabs/wazero/internal/testing/require" + "github.com/tetratelabs/wazero/sys" ) func Test_NewStat_t(t *testing.T) { @@ -30,7 +31,7 @@ func Test_NewStat_t(t *testing.T) { osSymlinkInfo, err := os.Lstat(link) require.NoError(t, err) - osFileSt := NewStat_t(osFileInfo) + osFileSt := sys.NewStat_t(osFileInfo) testFS := fstest.MapFS{ "dir": { Mode: osDirInfo.Mode(), @@ -115,7 +116,7 @@ func Test_NewStat_t(t *testing.T) { for _, tt := range tests { tc := tt t.Run(tc.name, func(t *testing.T) { - st := NewStat_t(tc.info) + st := sys.NewStat_t(tc.info) if tc.expectDevIno && runtime.GOOS != "windows" { require.NotEqual(t, uint64(0), st.Dev) require.NotEqual(t, uint64(0), st.Ino) @@ -127,7 +128,7 @@ func Test_NewStat_t(t *testing.T) { // link mode may differ on windows, so mask require.Equal(t, tc.expectedMode, st.Mode&tc.expectedMode) - if sysParseable && runtime.GOOS != "windows" { + if sys.SysParseable && runtime.GOOS != "windows" { switch st.Nlink { case 2, 4: // dirents may include dot entries. require.Equal(t, fs.ModeDir, st.Mode.Type()) @@ -140,15 +141,15 @@ func Test_NewStat_t(t *testing.T) { require.Equal(t, tc.expectedSize, st.Size) - if tc.expectAtimCtime && sysParseable { + if tc.expectAtimCtime && sys.SysParseable { // We don't validate times strictly because it is os-dependent // what updates times. There are edge cases for symlinks, too. - require.NotEqual(t, EpochNanos(0), st.Ctim) - require.NotEqual(t, EpochNanos(0), st.Mtim) - require.NotEqual(t, EpochNanos(0), st.Mtim) + require.NotEqual(t, sys.EpochNanos(0), st.Ctim) + require.NotEqual(t, sys.EpochNanos(0), st.Mtim) + require.NotEqual(t, sys.EpochNanos(0), st.Mtim) } else { // mtim is used for atim and ctime require.Equal(t, st.Mtim, st.Ctim) - require.NotEqual(t, EpochNanos(0), st.Mtim) + require.NotEqual(t, sys.EpochNanos(0), st.Mtim) require.Equal(t, st.Mtim, st.Atim) } })