diff --git a/imports/wasi_snapshot_preview1/args.go b/imports/wasi_snapshot_preview1/args.go index 8e61c472..67869025 100644 --- a/imports/wasi_snapshot_preview1/args.go +++ b/imports/wasi_snapshot_preview1/args.go @@ -2,7 +2,6 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" . "github.com/tetratelabs/wazero/internal/wasi_snapshot_preview1" @@ -23,7 +22,7 @@ import ( // Result (Errno) // // The return value is ErrnoSuccess except the following error conditions: -// - syscall.EFAULT: there is not enough memory to write results +// - ErrnoFault: 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 +42,7 @@ import ( // See https://en.wikipedia.org/wiki/Null-terminated_string var argsGet = newHostFunc(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) Errno { sysCtx := mod.(*wasm.CallContext).Sys argv, argvBuf := uint32(params[0]), uint32(params[1]) return writeOffsetsAndNullTerminatedValues(mod.Memory(), sysCtx.Args(), argv, argvBuf, sysCtx.ArgsSize()) @@ -61,7 +60,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 +// - ErrnoFault: 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 +79,7 @@ func argsGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno // See https://en.wikipedia.org/wiki/Null-terminated_string var argsSizesGet = newHostFunc(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) Errno { sysCtx := mod.(*wasm.CallContext).Sys mem := mod.Memory() resultArgc, resultArgvLen := uint32(params[0]), uint32(params[1]) @@ -88,10 +87,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 ErrnoFault } if !mem.WriteUint32Le(resultArgvLen, sysCtx.ArgsSize()) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } diff --git a/imports/wasi_snapshot_preview1/clock.go b/imports/wasi_snapshot_preview1/clock.go index be25f5c3..99fa924f 100644 --- a/imports/wasi_snapshot_preview1/clock.go +++ b/imports/wasi_snapshot_preview1/clock.go @@ -2,7 +2,6 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" . "github.com/tetratelabs/wazero/internal/wasi_snapshot_preview1" @@ -20,10 +19,10 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoNotsup: the clock ID is not supported. +// - ErrnoInval: the clock ID is invalid. +// - ErrnoFault: 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 +38,7 @@ import ( // See https://linux.die.net/man/3/clock_getres var clockResGet = newHostFunc(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) Errno { sysCtx := mod.(*wasm.CallContext).Sys id, resultResolution := uint32(params[0]), uint32(params[1]) @@ -50,13 +49,13 @@ func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E case ClockIDMonotonic: resolution = uint64(sysCtx.NanotimeResolution()) default: - return syscall.EINVAL + return ErrnoInval } if !mod.Memory().WriteUint64Le(resultResolution, resolution) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } // clockTimeGet is the WASI function named ClockTimeGetName that returns @@ -72,10 +71,10 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoNotsup: the clock ID is not supported. +// - ErrnoInval: the clock ID is invalid. +// - ErrnoFault: 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 +91,7 @@ func clockResGetFn(_ context.Context, mod api.Module, params []uint64) syscall.E // See https://linux.die.net/man/3/clock_gettime var clockTimeGet = newHostFunc(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) Errno { sysCtx := mod.(*wasm.CallContext).Sys id := uint32(params[0]) // TODO: precision is currently ignored. @@ -106,11 +105,11 @@ func clockTimeGetFn(_ context.Context, mod api.Module, params []uint64) syscall. case ClockIDMonotonic: val = sysCtx.Nanotime() default: - return syscall.EINVAL + return ErrnoInval } if !mod.Memory().WriteUint64Le(resultTimestamp, uint64(val)) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } diff --git a/imports/wasi_snapshot_preview1/environ.go b/imports/wasi_snapshot_preview1/environ.go index daaa46b4..fabf4992 100644 --- a/imports/wasi_snapshot_preview1/environ.go +++ b/imports/wasi_snapshot_preview1/environ.go @@ -2,7 +2,6 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" . "github.com/tetratelabs/wazero/internal/wasi_snapshot_preview1" @@ -23,8 +22,8 @@ import ( // // Result (Errno) // -// The return value is 0 except the following error conditions: -// - syscall.EFAULT: there is not enough memory to write results +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoFault: 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 +42,7 @@ import ( // See https://en.wikipedia.org/wiki/Null-terminated_string var environGet = newHostFunc(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) Errno { sysCtx := mod.(*wasm.CallContext).Sys environ, environBuf := uint32(params[0]), uint32(params[1]) @@ -62,8 +61,8 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoFault: 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 +82,7 @@ func environGetFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // and https://en.wikipedia.org/wiki/Null-terminated_string var environSizesGet = newHostFunc(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) Errno { sysCtx := mod.(*wasm.CallContext).Sys mem := mod.Memory() resultEnvironc, resultEnvironvLen := uint32(params[0]), uint32(params[1]) @@ -91,10 +90,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 ErrnoFault } if !mem.WriteUint32Le(resultEnvironvLen, sysCtx.EnvironSize()) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } diff --git a/imports/wasi_snapshot_preview1/fs.go b/imports/wasi_snapshot_preview1/fs.go index 15b14ee8..7b187709 100644 --- a/imports/wasi_snapshot_preview1/fs.go +++ b/imports/wasi_snapshot_preview1/fs.go @@ -37,7 +37,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) Errno { fd := uint32(params[0]) _ = params[1] _ = params[2] @@ -46,7 +46,7 @@ func fdAdviseFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn _, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return ErrnoBadf } switch advice { @@ -57,7 +57,7 @@ func fdAdviseFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn FdAdviceDontNeed, FdAdviceNoReuse: default: - return syscall.EINVAL + return ErrnoInval } // FdAdvice corresponds to posix_fadvise, but it can only be supported on linux. @@ -67,7 +67,7 @@ func fdAdviseFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn // TODO: invoke posix_fadvise on linux, and partially on darwin. // - https://gitlab.com/cznic/fileutil/-/blob/v1.1.2/fileutil_linux.go#L87-95 // - https://github.com/bytecodealliance/system-interface/blob/62b97f9776b86235f318c3a6e308395a1187439b/src/fs/file_io_ext.rs#L430-L442 - return 0 + return ErrnoSuccess } // fdAllocate is the WASI function named FdAllocateName which forces the @@ -80,7 +80,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) Errno { fd := uint32(params[0]) offset := params[1] length := params[2] @@ -88,30 +88,33 @@ func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) syscall.Er fsc := mod.(*wasm.CallContext).Sys.FS() f, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return ErrnoBadf } tail := int64(offset + length) if tail < 0 { - return syscall.EINVAL + return ErrnoInval } st, err := f.Stat() if err != nil { - return platform.UnwrapOSError(err) + return ToErrno(err) } if st.Size >= tail { // We already have enough space. - return 0 + return ErrnoSuccess } osf, ok := f.File.(truncateFile) if !ok { - return syscall.EBADF + return ErrnoBadf } - return platform.UnwrapOSError(osf.Truncate(tail)) + if err := osf.Truncate(tail); err != nil { + return ToErrno(err) + } + return ErrnoSuccess } // fdClose is the WASI function named FdCloseName which closes a file @@ -123,8 +126,8 @@ 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. +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: the fd was not open. // - ErrnoNotsup: the fs was a pre-open // // Note: This is similar to `close` in POSIX. @@ -132,11 +135,14 @@ func fdAllocateFn(_ context.Context, mod api.Module, params []uint64) syscall.Er // and https://linux.die.net/man/3/close var fdClose = newHostFunc(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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) - return fsc.CloseFile(fd) + if err := fsc.CloseFile(fd); err != nil { + return ToErrno(err) + } + return ErrnoSuccess } // fdDatasync is the WASI function named FdDatasyncName which synchronizes @@ -145,16 +151,17 @@ 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(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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) // Check to see if the file descriptor is available if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF - } else { - return sysfs.FileDatasync(f.File) + return ErrnoBadf + } else if err := sysfs.FileDatasync(f.File); err != nil { + return ToErrno(err) } + return ErrnoSuccess } // fdFdstatGet is the WASI function named FdFdstatGetName which returns the @@ -167,9 +174,9 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoFault: `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 @@ -198,7 +205,7 @@ var fdFdstatGet = newHostFunc(FdFdstatGetName, fdFdstatGetFn, []api.ValueType{i3 // 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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd, resultFdstat := uint32(params[0]), uint32(params[1]) @@ -206,25 +213,25 @@ 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 ErrnoFault } var fdflags uint16 - var st fs.FileInfo + var stat fs.FileInfo var err error if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF - } else if st, err = f.File.Stat(); err != nil { - return platform.UnwrapOSError(err) + return ErrnoBadf + } else if stat, err = f.File.Stat(); err != nil { + return ToErrno(err) } else if _, ok := f.File.(io.Writer); ok { // TODO: maybe cache flags to open instead fdflags = FD_APPEND } - filetype := getWasiFiletype(st.Mode()) + filetype := getWasiFiletype(stat.Mode()) writeFdstat(buf, filetype, fdflags) - return 0 + return ErrnoSuccess } var blockFdstat = []byte{ @@ -245,13 +252,13 @@ func writeFdstat(buf []byte, filetype uint8, fdflags uint16) { // adjusts the flags associated with a file descriptor. var fdFdstatSetFlags = newHostFunc(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) Errno { fd, wasiFlag := uint32(params[0]), uint16(params[1]) fsc := mod.(*wasm.CallContext).Sys.FS() // We can only support APPEND flag. if FD_DSYNC&wasiFlag != 0 || FD_NONBLOCK&wasiFlag != 0 || FD_RSYNC&wasiFlag != 0 || FD_SYNC&wasiFlag != 0 { - return syscall.EINVAL + return ErrnoInval } var flag int @@ -259,7 +266,10 @@ func fdFdstatSetFlagsFn(_ context.Context, mod api.Module, params []uint64) sysc flag = syscall.O_APPEND } - return fsc.ChangeOpenFlag(fd, flag) + if err := fsc.ChangeOpenFlag(fd, flag); err != nil { + return ToErrno(err) + } + return ErrnoSuccess } // fdFdstatSetRights will not be implemented as rights were removed from WASI. @@ -281,10 +291,10 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoIo: could not stat `fd` on filesystem +// - ErrnoFault: `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 @@ -322,30 +332,34 @@ var fdFilestatGet = newHostFunc(FdFilestatGetName, fdFilestatGetFn, []api.ValueT // 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) Errno { return fdFilestatGetFunc(mod, uint32(params[0]), uint32(params[1])) } -func fdFilestatGetFunc(mod api.Module, fd, resultBuf uint32) syscall.Errno { +func fdFilestatGetFunc(mod api.Module, fd, resultBuf uint32) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() // Ensure we can write the filestat buf, ok := mod.Memory().Read(resultBuf, 64) if !ok { - return syscall.EFAULT + return ErrnoFault } f, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return ErrnoBadf } st, err := f.Stat() if err != nil { - return platform.UnwrapOSError(err) + return ToErrno(err) } - return writeFilestat(buf, &st) + if err = writeFilestat(buf, &st); err != nil { + return ToErrno(err) + } + + return ErrnoSuccess } func getWasiFiletype(fm fs.FileMode) uint8 { @@ -368,7 +382,7 @@ func getWasiFiletype(fm fs.FileMode) uint8 { } } -func writeFilestat(buf []byte, st *platform.Stat_t) (errno syscall.Errno) { +func writeFilestat(buf []byte, st *platform.Stat_t) (err error) { le.PutUint64(buf, st.Dev) le.PutUint64(buf[8:], st.Ino) le.PutUint64(buf[16:], uint64(getWasiFiletype(st.Mode))) @@ -386,7 +400,7 @@ func writeFilestat(buf []byte, st *platform.Stat_t) (errno syscall.Errno) { // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_set_sizefd-fd-size-filesize---errno var fdFilestatSetSize = newHostFunc(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) Errno { fd := uint32(params[0]) size := uint32(params[1]) @@ -394,13 +408,13 @@ 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 ErrnoBadf } else if truncateFile, ok := f.File.(truncateFile); !ok { - return syscall.EBADF // possibly a fake file + return ErrnoBadf // possibly a fake file } else if err := truncateFile.Truncate(int64(size)); err != nil { - return platform.UnwrapOSError(err) + return ToErrno(err) } - return 0 + return ErrnoSuccess } // fdFilestatSetTimes is the WASI function named functionFdFilestatSetTimes @@ -413,7 +427,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) Errno { fd := uint32(params[0]) atim := int64(params[1]) mtim := int64(params[2]) @@ -424,32 +438,32 @@ func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) sy f, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return ErrnoBadf } times, errno := toTimes(atim, mtim, fstFlags) - if errno != 0 { + if errno != ErrnoSuccess { return errno } // Try to update the file timestamps by file-descriptor. - errno = platform.UtimensFile(f.File, ×) + err := platform.UtimensFile(f.File, ×) // Fall back to path based, despite it being less precise. - switch errno { + switch err { case syscall.EPERM, syscall.ENOSYS: - errno = f.FS.Utimens(f.Name, ×, true) + err = f.FS.Utimens(f.Name, ×, true) } - return errno + return ToErrno(err) } -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 Errno) { // times[0] == atim, times[1] == mtim // coerce atim into a timespec if set, now := fstFlags&FstflagsAtim != 0, fstFlags&FstflagsAtimNow != 0; set && now { - errno = syscall.EINVAL + errno = ErrnoInval return } else if set { times[0] = syscall.NsecToTimespec(atim) @@ -461,7 +475,7 @@ func toTimes(atim, mtime int64, fstFlags uint16) (times [2]syscall.Timespec, err // coerce mtim into a timespec if set, now := fstFlags&FstflagsMtim != 0, fstFlags&FstflagsMtimNow != 0; set && now { - errno = syscall.EINVAL + errno = ErrnoInval return } else if set { times[1] = syscall.NsecToTimespec(mtime) @@ -485,7 +499,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) Errno { return fdReadOrPread(mod, params, true) } @@ -499,9 +513,9 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid or the `fd` is not a pre-opened directory +// - ErrnoFault: `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 @@ -522,12 +536,12 @@ 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(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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd, resultPrestat := uint32(params[0]), uint32(params[1]) name, errno := preopenPath(fsc, fd) - if errno != 0 { + if errno != ErrnoSuccess { return errno } @@ -535,9 +549,9 @@ 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 ErrnoFault } - return 0 + return ErrnoSuccess } // fdPrestatDirName is the WASI function named FdPrestatDirNameName which @@ -553,10 +567,10 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoFault: `path` points to an offset out of memory +// - ErrnoNametoolong: `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 @@ -576,24 +590,24 @@ 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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd, path, pathLen := uint32(params[0]), uint32(params[1]), uint32(params[2]) name, errno := preopenPath(fsc, fd) - if errno != 0 { + if errno != ErrnoSuccess { return errno } // Some runtimes may have another semantics. See /RATIONALE.md if uint32(len(name)) < pathLen { - return syscall.ENAMETOOLONG + return ErrnoNametoolong } if !mod.Memory().Write(path, []byte(name)[:pathLen]) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } // fdPwrite is the WASI function named FdPwriteName which writes to a file @@ -608,7 +622,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) Errno { return fdWriteOrPwrite(mod, params, true) } @@ -627,10 +641,10 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoFault: `iovs` or `resultNread` point to an offset out of memory +// - ErrnoIo: 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 @@ -667,11 +681,11 @@ var fdRead = newHostFunc( "fd", "iovs", "iovs_len", "result.nread", ) -func fdReadFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno { +func fdReadFn(_ context.Context, mod api.Module, params []uint64) 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) Errno { mem := mod.Memory() fsc := mod.(*wasm.CallContext).Sys.FS() @@ -679,7 +693,7 @@ func fdReadOrPread(mod api.Module, params []uint64, isPread bool) syscall.Errno r, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF + return ErrnoBadf } var reader io.Reader = r.File @@ -700,7 +714,7 @@ func fdReadOrPread(mod api.Module, params []uint64, isPread bool) syscall.Errno iovsStop := iovsCount << 3 // iovsCount * 8 iovsBuf, ok := mem.Read(iovs, iovsStop) if !ok { - return syscall.EFAULT + return ErrnoFault } for iovsPos := uint32(0); iovsPos < iovsStop; iovsPos += 8 { @@ -709,23 +723,23 @@ func fdReadOrPread(mod api.Module, params []uint64, isPread bool) syscall.Errno b, ok := mem.Read(offset, l) if !ok { - return syscall.EFAULT + return ErrnoFault } n, err := reader.Read(b) nread += uint32(n) shouldContinue, errno := fdRead_shouldContinueRead(uint32(n), l, err) - if errno != 0 { + if errno != ErrnoSuccess { return errno } else if !shouldContinue { break } } if !mem.WriteUint32Le(resultNread, nread) { - return syscall.EFAULT + return ErrnoFault } else { - return 0 + return ErrnoSuccess } } @@ -734,16 +748,16 @@ func fdReadOrPread(mod api.Module, params []uint64, isPread bool) syscall.Errno // // Note: When there are both bytes read (n) and an error, this continues. // See /RATIONALE.md "Why ignore the error returned by io.Reader when n > 1?" -func fdRead_shouldContinueRead(n, l uint32, err error) (bool, syscall.Errno) { +func fdRead_shouldContinueRead(n, l uint32, err error) (bool, Errno) { if errors.Is(err, io.EOF) { - return false, 0 // EOF isn't an error, and we shouldn't continue. + return false, ErrnoSuccess // EOF isn't an error, and we shouldn't continue. } else if err != nil && n == 0 { - return false, syscall.EIO + return false, ErrnoIo } else if err != nil { - return false, 0 // Allow the caller to process n bytes. + return false, ErrnoSuccess // Allow the caller to process n bytes. } // Continue reading, unless there's a partial read or nothing to read. - return n == l && n != 0, 0 + return n == l && n != 0, ErrnoSuccess } // fdReaddir is the WASI function named FdReaddirName which reads directory @@ -756,7 +770,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) Errno { mem := mod.Memory() fsc := mod.(*wasm.CallContext).Sys.FS() @@ -772,12 +786,12 @@ func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Err // The bufLen must be enough to write a dirent. Otherwise, the caller can't // read what the next cookie is. if bufLen < DirentSize { - return syscall.EINVAL + return ErrnoInval } // Validate the FD is a directory rd, dir, errno := openedDir(fsc, fd) - if errno != 0 { + if errno != ErrnoSuccess { return errno } @@ -787,9 +801,9 @@ func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Err // https://github.com/WebAssembly/wasi-libc/blob/659ff414560721b1660a19685110e484a081c3d4/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c#L10-L12 // // Since we cannot unwind fs.ReadDirFile results, we re-open while keeping the same file descriptor. - f, errno := fsc.ReOpenDir(fd) - if errno != 0 { - return errno + f, err := fsc.ReOpenDir(fd) + if err != nil { + return ToErrno(err) } rd, dir = f.File, f.ReadDir } @@ -810,16 +824,17 @@ func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Err // The host keeps state for any unread entries from the prior call because // we cannot seek to a previous directory position. Collect these entries. dirents, errno := lastDirents(dir, cookie) - if errno != 0 { + if errno != ErrnoSuccess { return errno } // Add entries for dot and dot-dot as wasi-testsuite requires them. if cookie == 0 && dirents == nil { + var err error if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF - } else if dirents, errno = dotDirents(f); errno != 0 { - return errno + return ErrnoBadf + } else if dirents, err = dotDirents(f); err != nil { + return ToErrno(err) } dir.Dirents = dirents dir.CountRead = 2 // . and .. @@ -829,8 +844,8 @@ func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Err if entryCount := len(dirents); entryCount < maxDirEntries { // Note: platform.Readdir does not return io.EOF as it is // inconsistently returned (e.g. darwin does, but linux doesn't). - l, errno := platform.Readdir(rd, maxDirEntries-entryCount) - if errno != 0 { + l, err := platform.Readdir(rd, maxDirEntries-entryCount) + if errno = ToErrno(err); errno != ErrnoSuccess { return errno } @@ -858,31 +873,31 @@ func fdReaddirFn(_ context.Context, mod api.Module, params []uint64) syscall.Err buf, ok := mem.Read(buf, bufused) if !ok { - return syscall.EFAULT + return ErrnoFault } writeDirents(dirents, direntCount, writeTruncatedEntry, buf, d_next) } if !mem.WriteUint32Le(resultBufused, bufused) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } // dotDirents returns "." and "..", where "." because wasi-testsuite does inode // validation. -func dotDirents(f *sys.FileEntry) ([]*platform.Dirent, syscall.Errno) { +func dotDirents(f *sys.FileEntry) ([]*platform.Dirent, error) { dotIno, ft, err := f.CachedStat() if err != nil { - return nil, platform.UnwrapOSError(err) + return nil, err } else if ft.Type() != fs.ModeDir { return nil, syscall.ENOTDIR } dotDotIno := uint64(0) if !f.IsPreopen && f.Name != "." { - if st, errno := f.FS.Stat(path.Dir(f.Name)); errno != 0 { - return nil, errno + if st, err := f.FS.Stat(path.Dir(f.Name)); err != nil { + return nil, err } else { dotDotIno = st.Ino } @@ -890,22 +905,22 @@ func dotDirents(f *sys.FileEntry) ([]*platform.Dirent, syscall.Errno) { return []*platform.Dirent{ {Name: ".", Ino: dotIno, Type: fs.ModeDir}, {Name: "..", Ino: dotDotIno, Type: fs.ModeDir}, - }, 0 + }, nil } const largestDirent = int64(math.MaxUint32 - DirentSize) // lastDirents is broken out from fdReaddirFn for testability. -func lastDirents(dir *sys.ReadDir, cookie int64) (dirents []*platform.Dirent, errno syscall.Errno) { +func lastDirents(dir *sys.ReadDir, cookie int64) (dirents []*platform.Dirent, errno Errno) { if cookie < 0 { - errno = syscall.EINVAL // invalid as we will never send a negative cookie. + errno = ErrnoInval // invalid as we will never send a negative cookie. return } entryCount := int64(len(dir.Dirents)) if entryCount == 0 { // there was no prior call if cookie != 0 { - errno = syscall.EINVAL // invalid as we haven't sent that cookie + errno = ErrnoInval // invalid as we haven't sent that cookie } return } @@ -916,9 +931,9 @@ func lastDirents(dir *sys.ReadDir, cookie int64) (dirents []*platform.Dirent, er switch { case cookiePos < 0: // cookie is asking for results outside our window. - errno = syscall.ENOSYS // we can't implement directory seeking backwards. + errno = ErrnoNosys // we can't implement directory seeking backwards. case cookiePos > entryCount: - errno = syscall.EINVAL // invalid as we read that far, yet. + errno = ErrnoInval // invalid as we read that far, yet. case cookiePos > 0: // truncate so to avoid large lists. dirents = dir.Dirents[cookiePos:] default: @@ -1037,25 +1052,25 @@ func writeDirent(buf []byte, dNext uint64, ino uint64, dNamlen uint32, dType fs. le.PutUint32(buf[20:], uint32(filetype)) // d_type } -// openedDir returns the directory and 0 if the fd points to a readable directory. -func openedDir(fsc *sys.FSContext, fd uint32) (fs.File, *sys.ReadDir, syscall.Errno) { +// openedDir returns the directory and ErrnoSuccess if the fd points to a readable directory. +func openedDir(fsc *sys.FSContext, fd uint32) (fs.File, *sys.ReadDir, Errno) { if f, ok := fsc.LookupFile(fd); !ok { - return nil, nil, syscall.EBADF + return nil, nil, ErrnoBadf } else if _, ft, err := f.CachedStat(); err != nil { - return nil, nil, platform.UnwrapOSError(err) + return nil, nil, ToErrno(err) } else if ft.Type() != fs.ModeDir { - // 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, + // fd_readdir docs don't indicate whether to return ErrnoNotdir or + // ErrnoBadf. It has been noticed that rust will crash on ErrnoNotdir, // 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, nil, syscall.EBADF + return nil, nil, ErrnoBadf } else { if f.ReadDir == nil { f.ReadDir = &sys.ReadDir{} } - return f.File, f.ReadDir, 0 + return f.File, f.ReadDir, ErrnoSuccess } } @@ -1065,16 +1080,16 @@ func openedDir(fsc *sys.FSContext, fd uint32) (fs.File, *sys.ReadDir, syscall.Er // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_renumberfd-fd-to-fd---errno var fdRenumber = newHostFunc(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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() from := uint32(params[0]) to := uint32(params[1]) - if errno := fsc.Renumber(from, to); errno != 0 { - return errno + if err := fsc.Renumber(from, to); err != nil { + return ToErrno(err) } - return 0 + return ErrnoSuccess } // fdSeek is the WASI function named FdSeekName which moves the offset of a @@ -1094,11 +1109,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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoFault: `resultNewoffset` points to an offset out of memory +// - ErrnoInval: `whence` is an invalid value +// - ErrnoIo: a file system error // // 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 @@ -1120,7 +1135,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) offset := params[1] @@ -1130,29 +1145,29 @@ func fdSeekFn(_ context.Context, mod api.Module, params []uint64) syscall.Errno var seeker io.Seeker // Check to see if the file descriptor is available if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return ErrnoBadf // fs.FS doesn't declare io.Seeker, but implementations such as os.File implement it. } else if _, ft, err := f.CachedStat(); err != nil { - return platform.UnwrapOSError(err) + return ToErrno(err) } else if ft.Type() == fs.ModeDir { - return syscall.EBADF + return ErrnoBadf } else if seeker, ok = f.File.(io.Seeker); !ok { - return syscall.EBADF + return ErrnoBadf } if whence > io.SeekEnd /* exceeds the largest valid whence */ { - return syscall.EINVAL + return ErrnoInval } newOffset, err := seeker.Seek(int64(offset), int(whence)) if err != nil { - return platform.UnwrapOSError(err) + return ToErrno(err) } if !mod.Memory().WriteUint64Le(resultNewoffset, uint64(newOffset)) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } // fdSync is the WASI function named FdSyncName which synchronizes the data @@ -1161,19 +1176,19 @@ 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(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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) // Check to see if the file descriptor is available if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return ErrnoBadf } else if syncFile, ok := f.File.(syncFile); !ok { - return syscall.EBADF // possibly a fake file + return ErrnoBadf // possibly a fake file } else if err := syncFile.Sync(); err != nil { - return platform.UnwrapOSError(err) + return ToErrno(err) } - return 0 + return ErrnoSuccess } // fdTell is the WASI function named FdTellName which returns the current @@ -1182,7 +1197,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(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) Errno { fd := params[0] offset := uint64(0) whence := uint64(io.SeekCurrent) @@ -1208,10 +1223,10 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoFault: `iovs` or `resultNwritten` point to an offset out of memory +// - ErrnoIo: 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 @@ -1256,11 +1271,11 @@ 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) Errno { return fdWriteOrPwrite(mod, params, false) } -func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) syscall.Errno { +func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) Errno { mem := mod.Memory() fsc := mod.(*wasm.CallContext).Sys.FS() @@ -1271,13 +1286,13 @@ func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) syscall.Err var resultNwritten uint32 var writer io.Writer if f, ok := fsc.LookupFile(fd); !ok { - return syscall.EBADF + return ErrnoBadf } else if isPwrite { offset := int64(params[3]) writer = sysfs.WriterAtOffset(f.File, offset) resultNwritten = uint32(params[4]) } else if writer, ok = f.File.(io.Writer); !ok { - return syscall.EBADF + return ErrnoBadf } else { resultNwritten = uint32(params[3]) } @@ -1287,7 +1302,7 @@ func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) syscall.Err iovsStop := iovsCount << 3 // iovsCount * 8 iovsBuf, ok := mem.Read(iovs, iovsStop) if !ok { - return syscall.EFAULT + return ErrnoFault } for iovsPos := uint32(0); iovsPos < iovsStop; iovsPos += 8 { @@ -1300,20 +1315,20 @@ func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) syscall.Err } else { b, ok := mem.Read(offset, l) if !ok { - return syscall.EFAULT + return ErrnoFault } n, err = writer.Write(b) if err != nil { - return platform.UnwrapOSError(err) + return ToErrno(err) } } nwritten += uint32(n) } if !mod.Memory().WriteUint32Le(resultNwritten, nwritten) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } // pathCreateDirectory is the WASI function named PathCreateDirectoryName which @@ -1327,10 +1342,10 @@ func fdWriteOrPwrite(mod api.Module, params []uint64, isPwrite bool) syscall.Err // // # 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoNoent: `path` does not exist. +// - ErrnoNotdir: `path` is a file // // # Notes // - This is similar to mkdirat in POSIX. @@ -1343,7 +1358,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) @@ -1351,15 +1366,15 @@ func pathCreateDirectoryFn(_ context.Context, mod api.Module, params []uint64) s pathLen := uint32(params[2]) preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } - if errno = preopen.Mkdir(pathName, 0o700); errno != 0 { - return errno + if err := preopen.Mkdir(pathName, 0o700); err != nil { + return ToErrno(err) } - return 0 + return ErrnoSuccess } // pathFilestatGet is the WASI function named PathFilestatGetName which @@ -1375,14 +1390,14 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoNotdir: `fd` points to a file not a directory +// - ErrnoIo: could not stat `fd` on filesystem +// - ErrnoInval: the path contained "../" +// - ErrnoNametoolong: `path` + `path_len` is out of memory +// - ErrnoFault: `resultFilestat` points to an offset out of memory +// - ErrnoNoent: could not find the path // // The rest of this implementation matches that of fdFilestatGet, so is not // repeated here. @@ -1396,7 +1411,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) @@ -1405,7 +1420,7 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) sysca pathLen := uint32(params[3]) preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } @@ -1418,24 +1433,28 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) sysca // This could be optimized by modifying Stat/Lstat to return the `Stat_t` // value instead of passing a pointer as output parameter. var st platform.Stat_t + var err error if (flags & LOOKUP_SYMLINK_FOLLOW) == 0 { - st, errno = preopen.Lstat(pathName) + st, err = preopen.Lstat(pathName) } else { - st, errno = preopen.Stat(pathName) + st, err = preopen.Stat(pathName) } - if errno != 0 { - return errno + if err != nil { + return ToErrno(err) } // Write the stat result to memory resultBuf := uint32(params[4]) buf, ok := mod.Memory().Read(resultBuf, 64) if !ok { - return syscall.EFAULT + return ErrnoFault } - return writeFilestat(buf, &st) + if err = writeFilestat(buf, &st); err != nil { + return ToErrno(err) + } + return ErrnoSuccess } // pathFilestatSetTimes is the WASI function named PathFilestatSetTimesName @@ -1448,7 +1467,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) Errno { fd := uint32(params[0]) flags := uint16(params[1]) path := uint32(params[2]) @@ -1461,17 +1480,18 @@ func pathFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) fsc := sys.FS() times, errno := toTimes(atim, mtim, fstFlags) - if errno != 0 { + if errno != ErrnoSuccess { return errno } preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } symlinkFollow := flags&LOOKUP_SYMLINK_FOLLOW != 0 - return preopen.Utimens(pathName, ×, symlinkFollow) + err := preopen.Utimens(pathName, ×, symlinkFollow) + return ToErrno(err) } // pathLink is the WASI function named PathLinkName which adjusts the @@ -1484,7 +1504,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) Errno { mem := mod.Memory() fsc := mod.(*wasm.CallContext).Sys.FS() @@ -1495,7 +1515,7 @@ func pathLinkFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn oldPathLen := uint32(params[3]) oldFS, oldName, errno := atPath(fsc, mem, oldFd, oldPath, oldPathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } @@ -1504,19 +1524,22 @@ func pathLinkFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn newPathLen := uint32(params[6]) newFS, newName, errno := atPath(fsc, mem, newFD, newPath, newPathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } if oldFS != newFS { // TODO: handle link across filesystems - return syscall.ENOSYS + return ErrnoNosys } - return oldFS.Link(oldName, newName) + if err := oldFS.Link(oldName, newName); err != nil { + return ToErrno(err) + } + return ErrnoSuccess } // 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 ErrnoBadf if the fd is invalid. // // # Parameters // @@ -1535,13 +1558,13 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoFault: `resultOpenedFd` points to an offset out of memory +// - ErrnoNoent: `path` does not exist. +// - ErrnoExist: `path` exists, while `oFlags` requires that it must not. +// - ErrnoNotdir: `path` is not a directory, while `oFlags` requires it. +// - ErrnoIo: 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", @@ -1574,7 +1597,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() preopenFD := uint32(params[0]) @@ -1596,7 +1619,7 @@ func pathOpenFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn resultOpenedFd := uint32(params[8]) preopen, pathName, errno := atPath(fsc, mod.Memory(), preopenFD, path, pathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } @@ -1604,32 +1627,32 @@ func pathOpenFn(_ context.Context, mod api.Module, params []uint64) syscall.Errn isDir := fileOpenFlags&platform.O_DIRECTORY != 0 if isDir && oflags&O_CREAT != 0 { - return syscall.EINVAL // use pathCreateDirectory! + return ErrnoInval // use pathCreateDirectory! } - newFD, errno := fsc.OpenFile(preopen, pathName, fileOpenFlags, 0o600) - if errno != 0 { - return errno + newFD, err := fsc.OpenFile(preopen, pathName, fileOpenFlags, 0o600) + if err != nil { + return ToErrno(err) } // Check any flags that require the file to evaluate. if isDir { if f, ok := fsc.LookupFile(newFD); !ok { - return syscall.EBADF // unexpected + return ErrnoBadf // unexpected } else if _, ft, err := f.CachedStat(); err != nil { _ = fsc.CloseFile(newFD) - return platform.UnwrapOSError(err) + return ToErrno(err) } else if ft.Type() != fs.ModeDir { _ = fsc.CloseFile(newFD) - return syscall.ENOTDIR + return ErrnoNotdir } } if !mod.Memory().WriteUint32Le(resultOpenedFd, newFD) { _ = fsc.CloseFile(newFD) - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } // atPath returns the pre-open specific path after verifying it is a directory. @@ -1648,34 +1671,34 @@ 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, path, pathLen uint32) (sysfs.FS, string, syscall.Errno) { +func atPath(fsc *sys.FSContext, mem api.Memory, fd, path, pathLen uint32) (sysfs.FS, string, Errno) { b, ok := mem.Read(path, pathLen) if !ok { - return nil, "", syscall.EFAULT + return nil, "", ErrnoFault } pathName := string(b) if f, ok := fsc.LookupFile(fd); !ok { - return nil, "", syscall.EBADF // closed + return nil, "", ErrnoBadf // closed } else if _, ft, err := f.CachedStat(); err != nil { - return nil, "", platform.UnwrapOSError(err) + return nil, "", ToErrno(err) } else if ft.Type() != fs.ModeDir { - return nil, "", syscall.ENOTDIR + return nil, "", ErrnoNotdir } else if f.IsPreopen { // don't append the pre-open name - return f.FS, pathName, 0 + return f.FS, pathName, ErrnoSuccess } else { // Join via concat to avoid name conflict on path.Join - return f.FS, f.Name + "/" + pathName, 0 + return f.FS, f.Name + "/" + pathName, ErrnoSuccess } } -func preopenPath(fsc *sys.FSContext, fd uint32) (string, syscall.Errno) { +func preopenPath(fsc *sys.FSContext, fd uint32) (string, Errno) { if f, ok := fsc.LookupFile(fd); !ok { - return "", syscall.EBADF // closed + return "", ErrnoBadf // closed } else if !f.IsPreopen { - return "", syscall.EBADF + return "", ErrnoBadf } else { - return f.Name, 0 + return f.Name, ErrnoSuccess } } @@ -1721,7 +1744,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) @@ -1732,28 +1755,28 @@ func pathReadlinkFn(_ context.Context, mod api.Module, params []uint64) syscall. resultBufused := uint32(params[5]) if pathLen == 0 || bufLen == 0 { - return syscall.EINVAL + return ErrnoInval } mem := mod.Memory() - preopen, p, errno := atPath(fsc, mem, fd, path, pathLen) - if errno != 0 { - return errno + preopen, p, en := atPath(fsc, mem, fd, path, pathLen) + if en != ErrnoSuccess { + return en } - dst, errno := preopen.Readlink(p) - if errno != 0 { - return errno + dst, err := preopen.Readlink(p) + if err != nil { + return ToErrno(err) } if ok := mem.WriteString(buf, dst); !ok { - return syscall.EFAULT + return ErrnoFault } if !mem.WriteUint32Le(resultBufused, uint32(len(dst))) { - return syscall.EFAULT + return ErrnoFault } - return 0 + return ErrnoSuccess } // pathRemoveDirectory is the WASI function named PathRemoveDirectoryName which @@ -1767,11 +1790,11 @@ 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoNoent: `path` does not exist. +// - ErrnoNotempty: `path` is not empty +// - ErrnoNotdir: `path` is a file // // # Notes // - This is similar to unlinkat with AT_REMOVEDIR in POSIX. @@ -1784,7 +1807,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) @@ -1792,11 +1815,15 @@ func pathRemoveDirectoryFn(_ context.Context, mod api.Module, params []uint64) s pathLen := uint32(params[2]) preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } - return preopen.Rmdir(pathName) + if err := preopen.Rmdir(pathName); err != nil { + return ToErrno(err) + } + + return ErrnoSuccess } // pathRename is the WASI function named PathRenameName which renames a file or @@ -1813,11 +1840,11 @@ 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. +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` or `new_fd` are invalid +// - ErrnoNoent: `old_path` does not exist. +// - ErrnoNotdir: `old` is a directory and `new` exists, but is a file. +// - ErrnoIsdir: `old` is a file and `new` exists, but is a directory. // // # Notes // - This is similar to unlinkat in POSIX. @@ -1830,7 +1857,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) @@ -1842,20 +1869,24 @@ func pathRenameFn(_ context.Context, mod api.Module, params []uint64) syscall.Er newPathLen := uint32(params[5]) oldFS, oldPathName, errno := atPath(fsc, mod.Memory(), fd, oldPath, oldPathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } newFS, newPathName, errno := atPath(fsc, mod.Memory(), newFD, newPath, newPathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } if oldFS != newFS { // TODO: handle renames across filesystems - return syscall.ENOSYS + return ErrnoNosys } - return oldFS.Rename(oldPathName, newPathName) + if err := oldFS.Rename(oldPathName, newPathName); err != nil { + return ToErrno(err) + } + + return ErrnoSuccess } // pathSymlink is the WASI function named PathSymlinkName which creates a @@ -1868,7 +1899,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() oldPath := uint32(params[0]) @@ -1881,33 +1912,36 @@ func pathSymlinkFn(_ context.Context, mod api.Module, params []uint64) syscall.E dir, ok := fsc.LookupFile(fd) if !ok { - return syscall.EBADF // closed + return ErrnoBadf // closed } else if _, ft, err := dir.CachedStat(); err != nil { - return platform.UnwrapOSError(err) + return ToErrno(err) } else if ft.Type() != fs.ModeDir { - return syscall.ENOTDIR + return ErrnoNotdir } if oldPathLen == 0 || newPathLen == 0 { - return syscall.EINVAL + return ErrnoInval } oldPathBuf, ok := mem.Read(oldPath, oldPathLen) if !ok { - return syscall.EFAULT + return ErrnoFault } newPathBuf, ok := mem.Read(newPath, newPathLen) if !ok { - return syscall.EFAULT + return ErrnoFault } - return dir.FS.Symlink( + if err := dir.FS.Symlink( // Do not join old path since it's only resolved when dereference the link created here. // And the dereference result depends on the opening directory's file descriptor at that point. bufToStr(oldPathBuf, int(oldPathLen)), path.Join(dir.Name, bufToStr(newPathBuf, int(newPathLen))), - ) + ); err != nil { + return ToErrno(err) + } + return ErrnoSuccess } // bufToStr converts the given byte slice as string unsafely. @@ -1930,10 +1964,10 @@ func bufToStr(buf []byte, l int) 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoBadf: `fd` is invalid +// - ErrnoNoent: `path` does not exist. +// - ErrnoIsdir: `path` is a directory // // # Notes // - This is similar to unlinkat without AT_REMOVEDIR in POSIX. @@ -1946,7 +1980,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) Errno { fsc := mod.(*wasm.CallContext).Sys.FS() fd := uint32(params[0]) @@ -1954,9 +1988,13 @@ func pathUnlinkFileFn(_ context.Context, mod api.Module, params []uint64) syscal pathLen := uint32(params[2]) preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen) - if errno != 0 { + if errno != ErrnoSuccess { return errno } - return preopen.Unlink(pathName) + if err := preopen.Unlink(pathName); err != nil { + return ToErrno(err) + } + + return ErrnoSuccess } diff --git a/imports/wasi_snapshot_preview1/fs_test.go b/imports/wasi_snapshot_preview1/fs_test.go index e7a669b1..e956db5c 100644 --- a/imports/wasi_snapshot_preview1/fs_test.go +++ b/imports/wasi_snapshot_preview1/fs_test.go @@ -11,7 +11,6 @@ import ( "path" "runtime" "strings" - "syscall" "testing" "time" @@ -57,8 +56,8 @@ func Test_fdAllocate(t *testing.T) { preopen := fsc.RootFS() defer r.Close(testCtx) - fd, errno := fsc.OpenFile(preopen, fileName, os.O_RDWR, 0) - require.Zero(t, errno) + fd, err := fsc.OpenFile(preopen, fileName, os.O_RDWR, 0) + require.NoError(t, err) f, ok := fsc.LookupFile(fd) require.True(t, ok) @@ -135,11 +134,11 @@ func Test_fdClose(t *testing.T) { fsc := mod.(*wasm.CallContext).Sys.FS() preopen := fsc.RootFS() - fdToClose, errno := fsc.OpenFile(preopen, path1, os.O_RDONLY, 0) - require.Zero(t, errno) + fdToClose, err := fsc.OpenFile(preopen, path1, os.O_RDONLY, 0) + require.NoError(t, err) - fdToKeep, errno := fsc.OpenFile(preopen, path2, os.O_RDONLY, 0) - require.Zero(t, errno) + fdToKeep, err := fsc.OpenFile(preopen, path2, os.O_RDONLY, 0) + require.NoError(t, err) // Close requireErrnoResult(t, ErrnoSuccess, mod, FdCloseName, uint64(fdToClose)) @@ -228,11 +227,11 @@ func Test_fdFdstatGet(t *testing.T) { fsc := mod.(*wasm.CallContext).Sys.FS() preopen := fsc.RootFS() - fileFD, errno := fsc.OpenFile(preopen, file, os.O_RDONLY, 0) - require.Zero(t, errno) + fileFD, err := fsc.OpenFile(preopen, file, os.O_RDONLY, 0) + require.NoError(t, err) - dirFD, errno := fsc.OpenFile(preopen, dir, os.O_RDONLY, 0) - require.Zero(t, errno) + dirFD, err := fsc.OpenFile(preopen, dir, os.O_RDONLY, 0) + require.NoError(t, err) tests := []struct { name string @@ -379,8 +378,8 @@ func Test_fdFdstatSetFlags(t *testing.T) { defer r.Close(testCtx) // First, open it with O_APPEND. - fd, errno := fsc.OpenFile(preopen, fileName, os.O_RDWR|os.O_APPEND, 0) - require.Zero(t, errno) + fd, err := fsc.OpenFile(preopen, fileName, os.O_RDWR|os.O_APPEND, 0) + require.NoError(t, err) writeWazero := func() { iovs := uint32(1) // arbitrary offset @@ -473,11 +472,11 @@ func Test_fdFilestatGet(t *testing.T) { fsc := mod.(*wasm.CallContext).Sys.FS() preopen := fsc.RootFS() - fileFD, errno := fsc.OpenFile(preopen, file, os.O_RDONLY, 0) - require.Zero(t, errno) + fileFD, err := fsc.OpenFile(preopen, file, os.O_RDONLY, 0) + require.NoError(t, err) - dirFD, errno := fsc.OpenFile(preopen, dir, os.O_RDONLY, 0) - require.Zero(t, errno) + dirFD, err := fsc.OpenFile(preopen, dir, os.O_RDONLY, 0) + require.NoError(t, err) tests := []struct { name string @@ -1811,9 +1810,9 @@ var ( panic(err) } defer d.Close() - dirents, errno := platform.Readdir(d, -1) - if errno != 0 { - panic(errno) + dirents, err := platform.Readdir(d, -1) + if err != nil { + panic(err) } dots := []*platform.Dirent{ {Name: ".", Type: fs.ModeDir}, @@ -1885,8 +1884,8 @@ func Test_fdReaddir(t *testing.T) { fsc := mod.(*wasm.CallContext).Sys.FS() preopen := fsc.RootFS() - fd, errno := fsc.OpenFile(preopen, "dir", os.O_RDONLY, 0) - require.Zero(t, errno) + fd, err := fsc.OpenFile(preopen, "dir", os.O_RDONLY, 0) + require.NoError(t, err) tests := []struct { name string @@ -1971,8 +1970,8 @@ func Test_fdReaddir(t *testing.T) { dir: func() *sys.FileEntry { dir, err := fstest.FS.Open("dir") require.NoError(t, err) - dirent, errno := platform.Readdir(dir, 1) - require.Zero(t, errno) + dirent, err := platform.Readdir(dir, 1) + require.NoError(t, err) return &sys.FileEntry{ File: dir, @@ -1996,8 +1995,8 @@ func Test_fdReaddir(t *testing.T) { dir: func() *sys.FileEntry { dir, err := fstest.FS.Open("dir") require.NoError(t, err) - dirent, errno := platform.Readdir(dir, 1) - require.Zero(t, errno) + dirent, err := platform.Readdir(dir, 1) + require.NoError(t, err) return &sys.FileEntry{ File: dir, @@ -2022,8 +2021,8 @@ func Test_fdReaddir(t *testing.T) { dir: func() *sys.FileEntry { dir, err := fstest.FS.Open("dir") require.NoError(t, err) - dirent, errno := platform.Readdir(dir, 1) - require.Zero(t, errno) + dirent, err := platform.Readdir(dir, 1) + require.NoError(t, err) return &sys.FileEntry{ File: dir, @@ -2047,8 +2046,8 @@ func Test_fdReaddir(t *testing.T) { dir: func() *sys.FileEntry { dir, err := fstest.FS.Open("dir") require.NoError(t, err) - dirent, errno := platform.Readdir(dir, 1) - require.Zero(t, errno) + dirent, err := platform.Readdir(dir, 1) + require.NoError(t, err) return &sys.FileEntry{ File: dir, @@ -2072,8 +2071,8 @@ func Test_fdReaddir(t *testing.T) { dir: func() *sys.FileEntry { dir, err := fstest.FS.Open("dir") require.NoError(t, err) - two, errno := platform.Readdir(dir, 2) - require.Zero(t, errno) + two, err := platform.Readdir(dir, 2) + require.NoError(t, err) return &sys.FileEntry{ File: dir, @@ -2097,8 +2096,8 @@ func Test_fdReaddir(t *testing.T) { dir: func() *sys.FileEntry { dir, err := fstest.FS.Open("dir") require.NoError(t, err) - two, errno := platform.Readdir(dir, 2) - require.Zero(t, errno) + two, err := platform.Readdir(dir, 2) + require.NoError(t, err) return &sys.FileEntry{ File: dir, @@ -2122,7 +2121,7 @@ func Test_fdReaddir(t *testing.T) { dir: func() *sys.FileEntry { dir, err := fstest.FS.Open("dir") require.NoError(t, err) - _, errno = platform.Readdir(dir, 3) + _, err = platform.Readdir(dir, 3) require.NoError(t, err) return &sys.FileEntry{ @@ -2190,8 +2189,8 @@ func Test_fdReaddir_Rewind(t *testing.T) { fsc := mod.(*wasm.CallContext).Sys.FS() - fd, errno := fsc.OpenFile(fsc.RootFS(), "dir", os.O_RDONLY, 0) - require.Zero(t, errno) + fd, err := fsc.OpenFile(fsc.RootFS(), "dir", os.O_RDONLY, 0) + require.NoError(t, err) mem := mod.Memory() const resultBufused, buf, bufSize = 0, 8, 200 @@ -2254,11 +2253,11 @@ func Test_fdReaddir_Errors(t *testing.T) { fsc := mod.(*wasm.CallContext).Sys.FS() preopen := fsc.RootFS() - fileFD, errno := fsc.OpenFile(preopen, "animals.txt", os.O_RDONLY, 0) - require.Zero(t, errno) + fileFD, err := fsc.OpenFile(preopen, "animals.txt", os.O_RDONLY, 0) + require.NoError(t, err) - dirFD, errno := fsc.OpenFile(preopen, "dir", os.O_RDONLY, 0) - require.Zero(t, errno) + dirFD, err := fsc.OpenFile(preopen, "dir", os.O_RDONLY, 0) + require.NoError(t, err) tests := []struct { name string @@ -2454,12 +2453,12 @@ func Test_fdRenumber(t *testing.T) { preopen := fsc.RootFS() // Sanity check of the file descriptor assignment. - fileFDAssigned, errno := fsc.OpenFile(preopen, "animals.txt", os.O_RDONLY, 0) - require.Zero(t, errno) + fileFDAssigned, err := fsc.OpenFile(preopen, "animals.txt", os.O_RDONLY, 0) + require.NoError(t, err) require.Equal(t, uint32(fileFD), fileFDAssigned) - dirFDAssigned, errno := fsc.OpenFile(preopen, "dir", os.O_RDONLY, 0) - require.Zero(t, errno) + dirFDAssigned, err := fsc.OpenFile(preopen, "dir", os.O_RDONLY, 0) + require.NoError(t, err) require.Equal(t, uint32(dirFD), dirFDAssigned) requireErrnoResult(t, tc.expectedErrno, mod, FdRenumberName, uint64(tc.from), uint64(tc.to)) @@ -2566,7 +2565,7 @@ func Test_fdSeek_Errors(t *testing.T) { defer r.Close(testCtx) fsc := mod.(*wasm.CallContext).Sys.FS() - require.Zero(t, fsc.RootFS().Mkdir("dir", 0o0700)) + require.NoError(t, fsc.RootFS().Mkdir("dir", 0o0700)) dirFD := requireOpenFD(t, mod, "dir") memorySize := mod.Memory().Size() @@ -3552,10 +3551,10 @@ func Test_pathFilestatSetTimes(t *testing.T) { fsc := sys.FS() var oldSt platform.Stat_t - var errno syscall.Errno + var err error if tc.expectedErrno == ErrnoSuccess { - oldSt, errno = fsc.RootFS().Stat(pathName) - require.Zero(t, errno) + oldSt, err = fsc.RootFS().Stat(pathName) + require.NoError(t, err) } requireErrnoResult(t, tc.expectedErrno, mod, PathFilestatSetTimesName, uint64(fd), uint64(tc.flags), @@ -3566,8 +3565,8 @@ func Test_pathFilestatSetTimes(t *testing.T) { return } - newSt, errno := fsc.RootFS().Stat(pathName) - require.Zero(t, errno) + newSt, err := fsc.RootFS().Stat(pathName) + require.NoError(t, err) if platform.CompilerSupported() { if tc.fstFlags&FstflagsAtim != 0 { @@ -3605,13 +3604,13 @@ func Test_pathLink(t *testing.T) { newDirPath := joinPath(tmpDir, newDirName) require.NoError(t, os.MkdirAll(joinPath(tmpDir, newDirName), 0o700)) fsc := mod.(*wasm.CallContext).Sys.FS() - newFd, errno := fsc.OpenFile(fsc.RootFS(), newDirName, 0o600, 0) - require.Zero(t, errno) + newFd, err := fsc.OpenFile(fsc.RootFS(), newDirName, 0o600, 0) + require.NoError(t, err) mem := mod.Memory() fileName := "file" - err := os.WriteFile(joinPath(oldDirPath, fileName), []byte{1, 2, 3, 4}, 0o700) + err = os.WriteFile(joinPath(oldDirPath, fileName), []byte{1, 2, 3, 4}, 0o700) require.NoError(t, err) file := uint32(0xff) @@ -3642,8 +3641,8 @@ func Test_pathLink(t *testing.T) { require.NoError(t, f.Close()) }() - st, errno := platform.StatFile(f) - require.Zero(t, errno) + st, err := platform.StatFile(f) + require.NoError(t, err) require.False(t, st.Mode&os.ModeSymlink == os.ModeSymlink) require.Equal(t, uint64(2), st.Nlink) }) @@ -3762,7 +3761,7 @@ func Test_pathOpen(t *testing.T) { contents := []byte("hello") _, err := sys.WriterForFile(fsc, expectedOpenedFd).Write(contents) require.NoError(t, err) - require.Zero(t, fsc.CloseFile(expectedOpenedFd)) + require.NoError(t, fsc.CloseFile(expectedOpenedFd)) // verify the contents were appended b := readFile(t, dir, appendName) @@ -3794,7 +3793,7 @@ func Test_pathOpen(t *testing.T) { contents := []byte("hello") _, err := sys.WriterForFile(fsc, expectedOpenedFd).Write(contents) require.NoError(t, err) - require.Zero(t, fsc.CloseFile(expectedOpenedFd)) + require.NoError(t, fsc.CloseFile(expectedOpenedFd)) // verify the contents were written b := readFile(t, dir, "creat") @@ -3826,7 +3825,7 @@ func Test_pathOpen(t *testing.T) { contents := []byte("hello") _, err := sys.WriterForFile(fsc, expectedOpenedFd).Write(contents) require.NoError(t, err) - require.Zero(t, fsc.CloseFile(expectedOpenedFd)) + require.NoError(t, fsc.CloseFile(expectedOpenedFd)) // verify the contents were written b := readFile(t, dir, joinPath(dirName, "O_CREAT-O_TRUNC")) @@ -3891,7 +3890,7 @@ func Test_pathOpen(t *testing.T) { contents := []byte("hello") _, err := sys.WriterForFile(fsc, expectedOpenedFd).Write(contents) require.NoError(t, err) - require.Zero(t, fsc.CloseFile(expectedOpenedFd)) + require.NoError(t, fsc.CloseFile(expectedOpenedFd)) // verify the contents were truncated b := readFile(t, dir, "trunc") @@ -3959,8 +3958,8 @@ func requireOpenFD(t *testing.T, mod api.Module, path string) uint32 { fsc := mod.(*wasm.CallContext).Sys.FS() preopen := fsc.RootFS() - fd, errno := fsc.OpenFile(preopen, path, os.O_RDONLY, 0) - require.Zero(t, errno) + fd, err := fsc.OpenFile(preopen, path, os.O_RDONLY, 0) + require.NoError(t, err) return fd } @@ -4839,8 +4838,8 @@ func requireOpenFile(t *testing.T, tmpDir string, pathName string, data []byte, fsc := mod.(*wasm.CallContext).Sys.FS() preopen := fsc.RootFS() - fd, errno := fsc.OpenFile(preopen, pathName, oflags, 0) - require.Zero(t, errno) + fd, err := fsc.OpenFile(preopen, pathName, oflags, 0) + require.NoError(t, err) return mod, fd, log, r } @@ -4868,12 +4867,12 @@ func Test_fdReaddir_dotEntriesHaveRealInodes(t *testing.T) { uint64(sys.FdPreopen), uint64(0), uint64(len(readDirTarget))) // Open the directory, before writing files! - fd, errno := fsc.OpenFile(preopen, readDirTarget, os.O_RDONLY, 0) - require.Zero(t, errno) + fd, err := fsc.OpenFile(preopen, readDirTarget, os.O_RDONLY, 0) + require.NoError(t, err) // get the real inode of the current directory - st, errno := preopen.Stat(readDirTarget) - require.Zero(t, errno) + st, err := preopen.Stat(readDirTarget) + require.NoError(t, err) dirents := []byte{1, 0, 0, 0, 0, 0, 0, 0} // d_next = 1 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 1, 0, 0, 0) // d_namlen = 1 character @@ -4881,8 +4880,8 @@ func Test_fdReaddir_dotEntriesHaveRealInodes(t *testing.T) { dirents = append(dirents, '.') // name // get the real inode of the parent directory - st, errno = preopen.Stat(".") - require.Zero(t, errno) + st, err = preopen.Stat(".") + require.NoError(t, err) dirents = append(dirents, 2, 0, 0, 0, 0, 0, 0, 0) // d_next = 2 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters @@ -4927,8 +4926,8 @@ func Test_fdReaddir_opened_file_written(t *testing.T) { uint64(sys.FdPreopen), uint64(0), uint64(len(dirName))) // Open the directory, before writing files! - dirFD, errno := fsc.OpenFile(preopen, dirName, os.O_RDONLY, 0) - require.Zero(t, errno) + dirFD, err := fsc.OpenFile(preopen, dirName, os.O_RDONLY, 0) + require.NoError(t, err) // Then write a file to the directory. f, err := os.Create(joinPath(dirPath, "file")) @@ -4936,8 +4935,8 @@ func Test_fdReaddir_opened_file_written(t *testing.T) { defer f.Close() // get the real inode of the current directory - st, errno := preopen.Stat(dirName) - require.Zero(t, errno) + st, err := preopen.Stat(dirName) + require.NoError(t, err) dirents := []byte{1, 0, 0, 0, 0, 0, 0, 0} // d_next = 1 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 1, 0, 0, 0) // d_namlen = 1 character @@ -4945,8 +4944,8 @@ func Test_fdReaddir_opened_file_written(t *testing.T) { dirents = append(dirents, '.') // name // get the real inode of the parent directory - st, errno = preopen.Stat(".") - require.Zero(t, errno) + st, err = preopen.Stat(".") + require.NoError(t, err) dirents = append(dirents, 2, 0, 0, 0, 0, 0, 0, 0) // d_next = 2 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 2, 0, 0, 0) // d_namlen = 2 characters @@ -4954,8 +4953,8 @@ func Test_fdReaddir_opened_file_written(t *testing.T) { dirents = append(dirents, '.', '.') // name // get the real inode of the file - st, errno = platform.StatFile(f) - require.Zero(t, errno) + st, err = platform.StatFile(f) + require.NoError(t, err) dirents = append(dirents, 3, 0, 0, 0, 0, 0, 0, 0) // d_next = 3 dirents = append(dirents, u64.LeBytes(st.Ino)...) // d_ino dirents = append(dirents, 4, 0, 0, 0) // d_namlen = 4 characters diff --git a/imports/wasi_snapshot_preview1/fs_unit_test.go b/imports/wasi_snapshot_preview1/fs_unit_test.go index 5d8f12d6..dcb01fa0 100644 --- a/imports/wasi_snapshot_preview1/fs_unit_test.go +++ b/imports/wasi_snapshot_preview1/fs_unit_test.go @@ -19,7 +19,7 @@ func Test_fdRead_shouldContinueRead(t *testing.T) { n, l uint32 err error expectedOk bool - expectedErrno syscall.Errno + expectedErrno Errno }{ { name: "break when nothing to read", @@ -66,13 +66,13 @@ func Test_fdRead_shouldContinueRead(t *testing.T) { { name: "return ErrnoIo on error on nothing to read", err: io.ErrClosedPipe, - expectedErrno: syscall.EIO, + expectedErrno: ErrnoIo, }, { name: "return ErrnoIo on error on nothing read", l: 4, err: io.ErrClosedPipe, - expectedErrno: syscall.EIO, + expectedErrno: ErrnoIo, }, { // Special case, allows processing data before err name: "break on error on partial read", @@ -93,7 +93,7 @@ func Test_fdRead_shouldContinueRead(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ok, errno := fdRead_shouldContinueRead(tc.n, tc.l, tc.err) require.Equal(t, tc.expectedOk, ok) - require.EqualErrno(t, tc.expectedErrno, errno) + require.Equal(t, tc.expectedErrno, errno) }) } } @@ -104,7 +104,7 @@ func Test_lastDirents(t *testing.T) { f *sys.ReadDir cookie int64 expectedDirents []*platform.Dirent - expectedErrno syscall.Errno + expectedErrno Errno }{ { name: "no prior call", @@ -112,7 +112,7 @@ func Test_lastDirents(t *testing.T) { { name: "no prior call, but passed a cookie", cookie: 1, - expectedErrno: syscall.EINVAL, + expectedErrno: ErrnoInval, }, { name: "cookie is negative", @@ -121,7 +121,7 @@ func Test_lastDirents(t *testing.T) { Dirents: testDirents, }, cookie: -1, - expectedErrno: syscall.EINVAL, + expectedErrno: ErrnoInval, }, { name: "cookie is greater than last d_next", @@ -130,7 +130,7 @@ func Test_lastDirents(t *testing.T) { Dirents: testDirents, }, cookie: 5, - expectedErrno: syscall.EINVAL, + expectedErrno: ErrnoInval, }, { name: "cookie is last pos", @@ -157,7 +157,7 @@ func Test_lastDirents(t *testing.T) { Dirents: testDirents, }, cookie: 1, - expectedErrno: syscall.ENOSYS, // not implemented + expectedErrno: ErrnoNosys, // not implemented }, { name: "read from the beginning (cookie=0)", @@ -179,7 +179,7 @@ func Test_lastDirents(t *testing.T) { f = &sys.ReadDir{} } entries, errno := lastDirents(f, tc.cookie) - require.EqualErrno(t, tc.expectedErrno, errno) + require.Equal(t, tc.expectedErrno, errno) require.Equal(t, tc.expectedDirents, entries) }) } @@ -286,9 +286,9 @@ var ( panic(err) } defer dir.Close() - dirents, errno := platform.Readdir(dir, -1) - if errno != 0 { - panic(errno) + dirents, err := platform.Readdir(dir, -1) + if err != nil { + panic(err) } return dirents }() diff --git a/imports/wasi_snapshot_preview1/poll.go b/imports/wasi_snapshot_preview1/poll.go index 291a859e..38230839 100644 --- a/imports/wasi_snapshot_preview1/poll.go +++ b/imports/wasi_snapshot_preview1/poll.go @@ -2,7 +2,6 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/platform" @@ -18,20 +17,20 @@ 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 ErrnoInval. // - 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 +// The return value is ErrnoSuccess except the following error conditions: +// - ErrnoInval: the parameters are invalid +// - ErrnoNotsup: a parameters is valid, but not yet supported. +// - ErrnoFault: there is not enough memory to read the subscriptions or // write results. // // # Notes // -// - Since the `out` pointer nests Errno, the result is always 0. +// - Since the `out` pointer nests Errno, the result is always ErrnoSuccess. // - This is similar to `poll` in POSIX. // // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#poll_oneoff @@ -42,14 +41,14 @@ var pollOneoff = newHostFunc( "in", "out", "nsubscriptions", "result.nevents", ) -func pollOneoffFn(ctx context.Context, mod api.Module, params []uint64) syscall.Errno { +func pollOneoffFn(ctx context.Context, mod api.Module, params []uint64) Errno { in := uint32(params[0]) out := uint32(params[1]) nsubscriptions := uint32(params[2]) resultNevents := uint32(params[3]) if nsubscriptions == 0 { - return syscall.EINVAL + return ErrnoInval } mem := mod.Memory() @@ -57,17 +56,17 @@ func pollOneoffFn(ctx context.Context, mod api.Module, params []uint64) syscall. // Ensure capacity prior to the read loop to reduce error handling. inBuf, ok := mem.Read(in, nsubscriptions*48) if !ok { - return syscall.EFAULT + return ErrnoFault } outBuf, ok := mem.Read(out, nsubscriptions*32) if !ok { - return syscall.EFAULT + return ErrnoFault } // 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 ErrnoFault } // Loop through all subscriptions and write their output. @@ -79,7 +78,7 @@ func pollOneoffFn(ctx context.Context, mod api.Module, params []uint64) syscall. outOffset := i * 32 eventType := inBuf[inOffset+8] // +8 past userdata - var errno syscall.Errno // errno for this specific event (1-byte) + var errno Errno // errno for this specific event (1-byte) switch eventType { case EventTypeClock: // handle later // +8 past userdata +8 contents_offset @@ -88,23 +87,23 @@ func pollOneoffFn(ctx context.Context, mod api.Module, params []uint64) syscall. // +8 past userdata +8 contents_offset errno = processFDEvent(mod, eventType, inBuf[inOffset+8+8:]) default: - return syscall.EINVAL + return ErrnoInval } // Write the event corresponding to the processed subscription. // https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-event-struct - copy(outBuf, inBuf[inOffset:inOffset+8]) // userdata - outBuf[outOffset+8] = byte(ToErrno(errno)) // uint16, but safe as < 255 + copy(outBuf, inBuf[inOffset:inOffset+8]) // userdata + outBuf[outOffset+8] = byte(errno) // uint16, but safe as < 255 outBuf[outOffset+9] = 0 le.PutUint32(outBuf[outOffset+10:], uint32(eventType)) // TODO: When FD events are supported, write outOffset+16 } - return 0 + return ErrnoSuccess } // 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(_ context.Context, mod api.Module, inBuf []byte) syscall.Errno { +func processClockEvent(_ context.Context, mod api.Module, inBuf []byte) 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 @@ -114,9 +113,9 @@ func processClockEvent(_ context.Context, mod api.Module, inBuf []byte) syscall. switch flags { case 0: // relative time case 1: // subscription_clock_abstime - return syscall.ENOTSUP + return ErrnoNotsup default: // subclockflags has only one flag defined. - return syscall.EINVAL + return ErrnoInval } // https://linux.die.net/man/3/clock_settime says relative timers are @@ -125,29 +124,29 @@ func processClockEvent(_ context.Context, mod api.Module, inBuf []byte) syscall. sysCtx := mod.(*wasm.CallContext).Sys sysCtx.Nanosleep(int64(timeout)) - return 0 + return ErrnoSuccess } -// processFDEvent returns a validation error or syscall.ENOTSUP as file or socket +// processFDEvent returns a validation error or ErrnoNotsup as file or socket // subscriptions are not yet supported. -func processFDEvent(mod api.Module, eventType byte, inBuf []byte) syscall.Errno { +func processFDEvent(mod api.Module, eventType byte, inBuf []byte) Errno { fd := le.Uint32(inBuf) fsc := mod.(*wasm.CallContext).Sys.FS() // Choose the best error, which falls back to unsupported, until we support // files. - errno := syscall.ENOTSUP + errno := ErrnoNotsup if eventType == EventTypeFdRead { if _, ok := fsc.LookupFile(fd); ok { // If fd is a pipe, IsTerminal is false. if platform.IsTerminal(uintptr(fd)) { - errno = syscall.EBADF + errno = ErrnoBadf } } else { - errno = syscall.EBADF + errno = ErrnoBadf } } else if eventType == EventTypeFdWrite && internalsys.WriterForFile(fsc, fd) == nil { - errno = syscall.EBADF + errno = ErrnoBadf } return errno diff --git a/imports/wasi_snapshot_preview1/random.go b/imports/wasi_snapshot_preview1/random.go index 73ed1d72..0e841afd 100644 --- a/imports/wasi_snapshot_preview1/random.go +++ b/imports/wasi_snapshot_preview1/random.go @@ -3,7 +3,6 @@ package wasi_snapshot_preview1 import ( "context" "io" - "syscall" "github.com/tetratelabs/wazero/api" . "github.com/tetratelabs/wazero/internal/wasi_snapshot_preview1" @@ -21,8 +20,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 +// - ErrnoFault: `buf` or `bufLen` point to an offset out of memory +// - ErrnoIo: a file system error // // For example, if underlying random source was seeded like // `rand.NewSource(42)`, we expect api.Memory to contain: @@ -36,20 +35,20 @@ import ( // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-bufLen-size---errno var randomGet = newHostFunc(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) Errno { sysCtx := mod.(*wasm.CallContext).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 ErrnoFault } // 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 ErrnoIo } - return 0 + return ErrnoSuccess } diff --git a/imports/wasi_snapshot_preview1/sched.go b/imports/wasi_snapshot_preview1/sched.go index d9c7151d..3a135814 100644 --- a/imports/wasi_snapshot_preview1/sched.go +++ b/imports/wasi_snapshot_preview1/sched.go @@ -2,7 +2,6 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" . "github.com/tetratelabs/wazero/internal/wasi_snapshot_preview1" @@ -15,8 +14,8 @@ import ( // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sched_yield---errno var schedYield = newHostFunc(SchedYieldName, schedYieldFn, nil) -func schedYieldFn(_ context.Context, mod api.Module, _ []uint64) syscall.Errno { +func schedYieldFn(_ context.Context, mod api.Module, _ []uint64) Errno { sysCtx := mod.(*wasm.CallContext).Sys sysCtx.Osyield() - return 0 + return ErrnoSuccess } diff --git a/imports/wasi_snapshot_preview1/wasi.go b/imports/wasi_snapshot_preview1/wasi.go index e17638ec..9c3ed19a 100644 --- a/imports/wasi_snapshot_preview1/wasi.go +++ b/imports/wasi_snapshot_preview1/wasi.go @@ -19,7 +19,6 @@ package wasi_snapshot_preview1 import ( "context" "encoding/binary" - "syscall" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" @@ -219,18 +218,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) 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 ErrnoFault } bytesBuf, ok := mem.Read(bytes, bytesLen) if !ok { - return syscall.EFAULT + return ErrnoFault } // Loop through the values, first writing the location of its data to @@ -253,7 +252,7 @@ func writeOffsetsAndNullTerminatedValues(mem api.Memory, values [][]byte, offset bI++ } - return 0 + return ErrnoSuccess } func newHostFunc( @@ -275,12 +274,12 @@ 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) Errno // Call implements the same method as documented on api.GoModuleFunction. func (f wasiFunc) Call(ctx context.Context, mod api.Module, stack []uint64) { // Write the result back onto the stack - stack[0] = uint64(ToErrno(f(ctx, mod, stack))) + stack[0] = uint64(f(ctx, mod, stack)) } // stubFunction stubs for GrainLang per #271. diff --git a/imports/wasi_snapshot_preview1/wasi_bench_test.go b/imports/wasi_snapshot_preview1/wasi_bench_test.go index e0313b4e..8a08c5c0 100644 --- a/imports/wasi_snapshot_preview1/wasi_bench_test.go +++ b/imports/wasi_snapshot_preview1/wasi_bench_test.go @@ -192,9 +192,9 @@ func Benchmark_fdReaddir(b *testing.B) { // Open the root directory as a file-descriptor. fsc := mod.(*wasm.CallContext).Sys.FS() - fd, errno := fsc.OpenFile(fsc.RootFS(), ".", os.O_RDONLY, 0) - if errno != 0 { - b.Fatal(errno) + fd, err := fsc.OpenFile(fsc.RootFS(), ".", os.O_RDONLY, 0) + if err != nil { + b.Fatal(err) } f, ok := fsc.LookupFile(fd) if !ok { @@ -216,8 +216,8 @@ func Benchmark_fdReaddir(b *testing.B) { if err = f.File.Close(); err != nil { b.Fatal(err) } - if f.File, errno = fsc.RootFS().OpenFile(".", os.O_RDONLY, 0); errno != 0 { - b.Fatal(errno) + if f.File, err = fsc.RootFS().OpenFile(".", os.O_RDONLY, 0); err != nil { + b.Fatal(err) } f.ReadDir = nil @@ -318,9 +318,9 @@ func Benchmark_pathFilestat(b *testing.B) { fd := sys.FdPreopen if bc.fd != sys.FdPreopen { fsc := mod.(*wasm.CallContext).Sys.FS() - fd, errno := fsc.OpenFile(fsc.RootFS(), "zig", os.O_RDONLY, 0) - if errno != 0 { - b.Fatal(errno) + fd, err = fsc.OpenFile(fsc.RootFS(), "zig", os.O_RDONLY, 0) + if err != nil { + b.Fatal(err) } defer fsc.CloseFile(fd) //nolint } diff --git a/internal/fstest/fstest.go b/internal/fstest/fstest.go index a9f81eca..aac0c564 100644 --- a/internal/fstest/fstest.go +++ b/internal/fstest/fstest.go @@ -100,9 +100,9 @@ func WriteTestFiles(tmpDir string) (err error) { } // os.Stat uses GetFileInformationByHandle internally. - st, errno := platform.Stat(path) - if errno != 0 { - return errno + var st platform.Stat_t + if st, err = platform.Stat(path); err != nil { + return err } if st.Mtim == info.ModTime().UnixNano() { return nil // synced! diff --git a/internal/gojs/errno.go b/internal/gojs/errno.go index afc6ee07..24d8b179 100644 --- a/internal/gojs/errno.go +++ b/internal/gojs/errno.go @@ -30,8 +30,6 @@ var ( ErrnoBadf = &Errno{"EBADF"} // ErrnoExist File exists. ErrnoExist = &Errno{"EEXIST"} - // ErrnoFault Bad address. - ErrnoFault = &Errno{"EFAULT"} // ErrnoIntr Interrupted function. ErrnoIntr = &Errno{"EINTR"} // ErrnoInval Invalid argument. @@ -79,8 +77,6 @@ func ToErrno(err error) *Errno { return ErrnoBadf case syscall.EEXIST: return ErrnoExist - case syscall.EFAULT: - return ErrnoFault case syscall.EINTR: return ErrnoIntr case syscall.EINVAL: diff --git a/internal/gojs/errno_test.go b/internal/gojs/errno_test.go index 40a380d9..266a6e0b 100644 --- a/internal/gojs/errno_test.go +++ b/internal/gojs/errno_test.go @@ -39,11 +39,6 @@ func TestToErrno(t *testing.T) { input: syscall.EEXIST, expected: ErrnoExist, }, - { - name: "syscall.EFAULT", - input: syscall.EFAULT, - expected: ErrnoFault, - }, { name: "syscall.EINTR", input: syscall.EINTR, diff --git a/internal/gojs/fs.go b/internal/gojs/fs.go index 1737c7c0..dd93aa55 100644 --- a/internal/gojs/fs.go +++ b/internal/gojs/fs.go @@ -110,9 +110,9 @@ func (o *jsfsOpen) invoke(ctx context.Context, mod api.Module, args ...interface fsc := mod.(*wasm.CallContext).Sys.FS() - fd, errno := fsc.OpenFile(fsc.RootFS(), path, int(flags), perm) + fd, err := fsc.OpenFile(fsc.RootFS(), path, int(flags), perm) - return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(errno), fd) // note: error first + return callback.invoke(ctx, mod, goos.RefJsfs, err, fd) // note: error first } // jsfsStat implements jsFn for syscall.Stat @@ -134,8 +134,8 @@ func (s *jsfsStat) invoke(ctx context.Context, mod api.Module, args ...interface func syscallStat(mod api.Module, path string) (*jsSt, error) { fsc := mod.(*wasm.CallContext).Sys.FS() - if st, errno := fsc.RootFS().Stat(path); errno != 0 { - return nil, errno + if st, err := fsc.RootFS().Stat(path); err != nil { + return nil, err } else { return newJsSt(st), nil } @@ -161,8 +161,8 @@ func (l *jsfsLstat) invoke(ctx context.Context, mod api.Module, args ...interfac func syscallLstat(mod api.Module, path string) (*jsSt, error) { fsc := mod.(*wasm.CallContext).Sys.FS() - if st, errno := fsc.RootFS().Lstat(path); errno != 0 { - return nil, errno + if st, err := fsc.RootFS().Lstat(path); err != nil { + return nil, err } else { return newJsSt(st), nil } @@ -191,7 +191,7 @@ func syscallFstat(fsc *internalsys.FSContext, fd uint32) (*jsSt, error) { } if st, err := f.Stat(); err != nil { - return nil, platform.UnwrapOSError(err) + return nil, err } else { return newJsSt(st), nil } @@ -222,9 +222,9 @@ func (jsfsClose) invoke(ctx context.Context, mod api.Module, args ...interface{} fd := goos.ValueToUint32(args[0]) callback := args[1].(funcWrapper) - errno := fsc.CloseFile(fd) + err := fsc.CloseFile(fd) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsRead implements jsFn for syscall.Read and syscall.Pread, called by @@ -345,14 +345,14 @@ func syscallReaddir(_ context.Context, mod api.Module, name string) (*objectArra fsc := mod.(*wasm.CallContext).Sys.FS() // don't allocate a file descriptor - f, errno := fsc.RootFS().OpenFile(name, os.O_RDONLY, 0) - if errno != 0 { - return nil, errno + f, err := fsc.RootFS().OpenFile(name, os.O_RDONLY, 0) + if err != nil { + return nil, err } defer f.Close() //nolint - if names, errno := platform.Readdirnames(f, -1); errno != 0 { - return nil, errno + if names, err := platform.Readdirnames(f, -1); err != nil { + return nil, err } else { entries := make([]interface{}, 0, len(names)) for _, e := range names { @@ -378,16 +378,16 @@ func (m *jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interfac root := fsc.RootFS() var fd uint32 - var errno syscall.Errno + var err error // We need at least read access to open the file descriptor if perm == 0 { perm = 0o0500 } - if errno = root.Mkdir(path, perm); errno == 0 { - fd, errno = fsc.OpenFile(root, path, os.O_RDONLY, 0) + if err = root.Mkdir(path, perm); err == nil { + fd, err = fsc.OpenFile(root, path, os.O_RDONLY, 0) } - return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(errno), fd) // note: error first + return callback.invoke(ctx, mod, goos.RefJsfs, err, fd) // note: error first } // jsfsRmdir implements jsFn for the following @@ -402,9 +402,9 @@ func (r *jsfsRmdir) invoke(ctx context.Context, mod api.Module, args ...interfac callback := args[1].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Rmdir(path) + err := fsc.RootFS().Rmdir(path) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsRename implements jsFn for the following @@ -421,9 +421,9 @@ func (r *jsfsRename) invoke(ctx context.Context, mod api.Module, args ...interfa callback := args[2].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Rename(from, to) + err := fsc.RootFS().Rename(from, to) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsUnlink implements jsFn for the following @@ -438,9 +438,9 @@ func (u *jsfsUnlink) invoke(ctx context.Context, mod api.Module, args ...interfa callback := args[1].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Unlink(path) + err := fsc.RootFS().Unlink(path) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsUtimes implements jsFn for the following @@ -460,9 +460,9 @@ func (u *jsfsUtimes) invoke(ctx context.Context, mod api.Module, args ...interfa times := [2]syscall.Timespec{ syscall.NsecToTimespec(atimeSec * 1e9), syscall.NsecToTimespec(mtimeSec * 1e9), } - errno := fsc.RootFS().Utimens(path, ×, true) + err := fsc.RootFS().Utimens(path, ×, true) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsChmod implements jsFn for the following @@ -478,9 +478,9 @@ func (c *jsfsChmod) invoke(ctx context.Context, mod api.Module, args ...interfac callback := args[2].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Chmod(path, mode) + err := fsc.RootFS().Chmod(path, mode) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsFchmod implements jsFn for the following @@ -495,16 +495,16 @@ func (jsfsFchmod) invoke(ctx context.Context, mod api.Module, args ...interface{ // Check to see if the file descriptor is available fsc := mod.(*wasm.CallContext).Sys.FS() - var errno syscall.Errno + var err error if f, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + err = syscall.EBADF } else if chmodFile, ok := f.File.(chmodFile); !ok { - errno = syscall.EBADF // possibly a fake file + err = syscall.EBADF // possibly a fake file } else { - errno = platform.UnwrapOSError(chmodFile.Chmod(mode)) + err = chmodFile.Chmod(mode) } - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsChown implements jsFn for the following @@ -521,9 +521,9 @@ func (c *jsfsChown) invoke(ctx context.Context, mod api.Module, args ...interfac callback := args[3].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Chown(path, int(uid), int(gid)) + err := fsc.RootFS().Chown(path, int(uid), int(gid)) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsFchown implements jsFn for the following @@ -539,14 +539,14 @@ func (jsfsFchown) invoke(ctx context.Context, mod api.Module, args ...interface{ // Check to see if the file descriptor is available fsc := mod.(*wasm.CallContext).Sys.FS() - var errno syscall.Errno + var err error if f, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + err = syscall.EBADF } else { - errno = platform.ChownFile(f.File, int(uid), int(gid)) + err = platform.ChownFile(f.File, int(uid), int(gid)) } - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsLchown implements jsFn for the following @@ -563,9 +563,9 @@ func (l *jsfsLchown) invoke(ctx context.Context, mod api.Module, args ...interfa callback := args[3].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Lchown(path, int(uid), int(gid)) + err := fsc.RootFS().Lchown(path, int(uid), int(gid)) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsTruncate implements jsFn for the following @@ -581,9 +581,9 @@ func (t *jsfsTruncate) invoke(ctx context.Context, mod api.Module, args ...inter callback := args[2].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Truncate(path, length) + err := fsc.RootFS().Truncate(path, length) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsFtruncate implements jsFn for the following @@ -598,16 +598,16 @@ func (jsfsFtruncate) invoke(ctx context.Context, mod api.Module, args ...interfa // Check to see if the file descriptor is available fsc := mod.(*wasm.CallContext).Sys.FS() - var errno syscall.Errno + var err error if f, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + err = syscall.EBADF } else if truncateFile, ok := f.File.(truncateFile); !ok { - errno = syscall.EBADF // possibly a fake file + err = syscall.EBADF // possibly a fake file } else { - errno = platform.UnwrapOSError(truncateFile.Truncate(length)) + err = truncateFile.Truncate(length) } - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsReadlink implements jsFn for syscall.Readlink @@ -622,9 +622,9 @@ func (r *jsfsReadlink) invoke(ctx context.Context, mod api.Module, args ...inter callback := args[1].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - dst, errno := fsc.RootFS().Readlink(path) + dst, err := fsc.RootFS().Readlink(path) - return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(errno), dst) // note: error first + return callback.invoke(ctx, mod, goos.RefJsfs, err, dst) // note: error first } // jsfsLink implements jsFn for the following @@ -641,9 +641,9 @@ func (l *jsfsLink) invoke(ctx context.Context, mod api.Module, args ...interface callback := args[2].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Link(path, link) + err := fsc.RootFS().Link(path, link) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsSymlink implements jsFn for the following @@ -659,9 +659,9 @@ func (s *jsfsSymlink) invoke(ctx context.Context, mod api.Module, args ...interf callback := args[2].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() - errno := fsc.RootFS().Symlink(dst, link) + err := fsc.RootFS().Symlink(dst, link) - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsfsFsync implements jsFn for the following @@ -675,16 +675,16 @@ func (jsfsFsync) invoke(ctx context.Context, mod api.Module, args ...interface{} // Check to see if the file descriptor is available fsc := mod.(*wasm.CallContext).Sys.FS() - var errno syscall.Errno + var err error if f, ok := fsc.LookupFile(fd); !ok { - errno = syscall.EBADF + err = syscall.EBADF } else if syncFile, ok := f.File.(syncFile); !ok { - errno = syscall.EBADF // possibly a fake file + err = syscall.EBADF // possibly a fake file } else { - errno = platform.UnwrapOSError(syncFile.Sync()) + err = syncFile.Sync() } - return jsfsInvoke(ctx, mod, callback, errno) + return jsfsInvoke(ctx, mod, callback, err) } // jsSt is pre-parsed from fs_js.go setStat to avoid thrashing @@ -751,13 +751,6 @@ 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) { - return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(err), err == 0) // note: error first -} - -func maybeError(errno syscall.Errno) error { - if errno != 0 { - return errno - } - return nil +func jsfsInvoke(ctx context.Context, mod api.Module, callback funcWrapper, err error) (interface{}, error) { + return callback.invoke(ctx, mod, goos.RefJsfs, err, err == nil) // note: error first } diff --git a/internal/platform/bench_test.go b/internal/platform/bench_test.go index 9a6b0122..51cbdfee 100644 --- a/internal/platform/bench_test.go +++ b/internal/platform/bench_test.go @@ -28,7 +28,7 @@ func Benchmark_UtimensFile(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - if err := UtimensFile(f, times); err != 0 { + if err := UtimensFile(f, times); err != nil { b.Fatal(err) } } diff --git a/internal/platform/chown.go b/internal/platform/chown.go index d8c273d5..fd169025 100644 --- a/internal/platform/chown.go +++ b/internal/platform/chown.go @@ -8,22 +8,20 @@ import ( // Chown is like os.Chown, except it returns a syscall.Errno, not a // fs.PathError. For example, this returns syscall.ENOENT if the path doesn't -// exist. A syscall.Errno of zero is success. +// exist. See https://linux.die.net/man/3/chown // // Note: This always returns syscall.ENOSYS on windows. -// See https://linux.die.net/man/3/chown -func Chown(path string, uid, gid int) syscall.Errno { +func Chown(path string, uid, gid int) error { err := os.Chown(path, uid, gid) return UnwrapOSError(err) } // Lchown is like os.Lchown, except it returns a syscall.Errno, not a // fs.PathError. For example, this returns syscall.ENOENT if the path doesn't -// exist. A syscall.Errno of zero is success. +// exist. See https://linux.die.net/man/3/lchown // // Note: This always returns syscall.ENOSYS on windows. -// See https://linux.die.net/man/3/lchown -func Lchown(path string, uid, gid int) syscall.Errno { +func Lchown(path string, uid, gid int) error { err := os.Lchown(path, uid, gid) return UnwrapOSError(err) } @@ -33,9 +31,10 @@ func Lchown(path string, uid, gid int) syscall.Errno { // or directory was closed. See https://linux.die.net/man/3/fchown // // Note: This always returns syscall.ENOSYS on windows. -func ChownFile(f fs.File, uid, gid int) syscall.Errno { +func ChownFile(f fs.File, uid, gid int) error { if f, ok := f.(fdFile); ok { - return fchown(f.Fd(), uid, gid) + err := fchown(f.Fd(), uid, gid) + return UnwrapOSError(err) } return syscall.ENOSYS } diff --git a/internal/platform/chown_unix.go b/internal/platform/chown_unix.go index 5e0753da..69bdf9a4 100644 --- a/internal/platform/chown_unix.go +++ b/internal/platform/chown_unix.go @@ -4,6 +4,6 @@ package platform import "syscall" -func fchown(fd uintptr, uid, gid int) syscall.Errno { - return UnwrapOSError(syscall.Fchown(int(fd), uid, gid)) +func fchown(fd uintptr, uid, gid int) error { + return syscall.Fchown(int(fd), uid, gid) } diff --git a/internal/platform/chown_unix_test.go b/internal/platform/chown_unix_test.go index 7cc47dae..6f499c72 100644 --- a/internal/platform/chown_unix_test.go +++ b/internal/platform/chown_unix_test.go @@ -17,10 +17,8 @@ func TestChown(t *testing.T) { dir := path.Join(tmpDir, "dir") require.NoError(t, os.Mkdir(dir, 0o0777)) - - dirF, errno := OpenFile(dir, syscall.O_RDONLY, 0) - require.Zero(t, errno) - + dirF, err := OpenFile(dir, syscall.O_RDONLY, 0) + require.NoError(t, err) dirStat, err := dirF.Stat() require.NoError(t, err) dirSys := dirStat.Sys().(*syscall.Stat_t) @@ -32,12 +30,12 @@ func TestChown(t *testing.T) { require.NoError(t, err) t.Run("-1 parameters means leave alone", func(t *testing.T) { - require.Zero(t, Chown(dir, -1, -1)) + require.NoError(t, Chown(dir, -1, -1)) checkUidGid(t, dir, dirSys.Uid, dirSys.Gid) }) t.Run("change gid, but not uid", func(t *testing.T) { - require.Zero(t, Chown(dir, -1, gid)) + require.NoError(t, Chown(dir, -1, gid)) checkUidGid(t, dir, dirSys.Uid, uint32(gid)) }) @@ -46,11 +44,11 @@ func TestChown(t *testing.T) { g := g t.Run(fmt.Sprintf("change to gid %d", g), func(t *testing.T) { // Test using our Chown - require.Zero(t, Chown(dir, -1, g)) + require.NoError(t, Chown(dir, -1, g)) checkUidGid(t, dir, dirSys.Uid, uint32(g)) // Revert back with os.File.Chown - require.NoError(t, dirF.(*os.File).Chown(-1, gid)) + require.NoError(t, dirF.Chown(-1, gid)) checkUidGid(t, dir, dirSys.Uid, uint32(gid)) }) } @@ -65,13 +63,10 @@ func TestChownFile(t *testing.T) { dir := path.Join(tmpDir, "dir") require.NoError(t, os.Mkdir(dir, 0o0777)) - - dirF, errno := OpenFile(dir, syscall.O_RDONLY, 0) - require.Zero(t, errno) - + dirF, err := OpenFile(dir, syscall.O_RDONLY, 0) + require.NoError(t, err) dirStat, err := dirF.Stat() require.NoError(t, err) - dirSys := dirStat.Sys().(*syscall.Stat_t) // Similar to TestChownFile in os_unix_test.go, we can't expect to change @@ -81,12 +76,12 @@ func TestChownFile(t *testing.T) { require.NoError(t, err) t.Run("-1 parameters means leave alone", func(t *testing.T) { - require.Zero(t, ChownFile(dirF, -1, -1)) + require.NoError(t, ChownFile(dirF, -1, -1)) checkUidGid(t, dir, dirSys.Uid, dirSys.Gid) }) t.Run("change gid, but not uid", func(t *testing.T) { - require.Zero(t, ChownFile(dirF, -1, gid)) + require.NoError(t, ChownFile(dirF, -1, gid)) checkUidGid(t, dir, dirSys.Uid, uint32(gid)) }) @@ -95,11 +90,11 @@ func TestChownFile(t *testing.T) { g := g t.Run(fmt.Sprintf("change to gid %d", g), func(t *testing.T) { // Test using our ChownFile - require.Zero(t, ChownFile(dirF, -1, g)) + require.NoError(t, ChownFile(dirF, -1, g)) checkUidGid(t, dir, dirSys.Uid, uint32(g)) // Revert back with os.File.Chown - require.NoError(t, dirF.(*os.File).Chown(-1, gid)) + require.NoError(t, dirF.Chown(-1, gid)) checkUidGid(t, dir, dirSys.Uid, uint32(gid)) }) } @@ -115,24 +110,18 @@ func TestLchown(t *testing.T) { dir := path.Join(tmpDir, "dir") require.NoError(t, os.Mkdir(dir, 0o0777)) - - dirF, errno := OpenFile(dir, syscall.O_RDONLY, 0) - require.Zero(t, errno) - + dirF, err := OpenFile(dir, syscall.O_RDONLY, 0) + require.NoError(t, err) dirStat, err := dirF.Stat() require.NoError(t, err) - dirSys := dirStat.Sys().(*syscall.Stat_t) link := path.Join(tmpDir, "link") require.NoError(t, os.Symlink(dir, link)) - - linkF, errno := OpenFile(link, syscall.O_RDONLY, 0) - require.Zero(t, errno) - + linkF, err := OpenFile(link, syscall.O_RDONLY, 0) + require.NoError(t, err) linkStat, err := linkF.Stat() require.NoError(t, err) - linkSys := linkStat.Sys().(*syscall.Stat_t) // Similar to TestLchown in os_unix_test.go, we can't expect to change @@ -142,12 +131,12 @@ func TestLchown(t *testing.T) { require.NoError(t, err) t.Run("-1 parameters means leave alone", func(t *testing.T) { - require.Zero(t, Lchown(link, -1, -1)) + require.NoError(t, Lchown(link, -1, -1)) checkUidGid(t, link, linkSys.Uid, linkSys.Gid) }) t.Run("change gid, but not uid", func(t *testing.T) { - require.Zero(t, Chown(dir, -1, gid)) + require.NoError(t, Chown(dir, -1, gid)) checkUidGid(t, link, linkSys.Uid, uint32(gid)) // Make sure the target didn't change. checkUidGid(t, dir, dirSys.Uid, dirSys.Gid) @@ -158,7 +147,7 @@ func TestLchown(t *testing.T) { g := g t.Run(fmt.Sprintf("change to gid %d", g), func(t *testing.T) { // Test using our Lchown - require.Zero(t, Lchown(link, -1, g)) + require.NoError(t, Lchown(link, -1, g)) checkUidGid(t, link, linkSys.Uid, uint32(g)) // Make sure the target didn't change. checkUidGid(t, dir, dirSys.Uid, dirSys.Gid) diff --git a/internal/platform/chown_unsupported.go b/internal/platform/chown_unsupported.go index 38b7187d..1fec3803 100644 --- a/internal/platform/chown_unsupported.go +++ b/internal/platform/chown_unsupported.go @@ -6,6 +6,6 @@ import "syscall" // fchown is not supported on windows. For example, syscall.Fchown returns // syscall.EWINDOWS, which is the same as syscall.ENOSYS. -func fchown(fd uintptr, uid, gid int) syscall.Errno { +func fchown(fd uintptr, uid, gid int) error { return syscall.ENOSYS } diff --git a/internal/platform/dir.go b/internal/platform/dir.go index b58386df..d9c338e3 100644 --- a/internal/platform/dir.go +++ b/internal/platform/dir.go @@ -16,22 +16,23 @@ import ( // // # Errors // -// A zero syscall.Errno is success. +// If the error is non-nil, it is a syscall.Errno. io.EOF is not returned +// because implementations return that value inconsistently. // -// For portability reasons, no error is returned on io.EOF, when the file is -// closed or removed while open. +// For portability reasons, no error is returned when the file is closed or +// removed while open. // See https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs.zig#L635-L637 -func Readdirnames(f fs.File, n int) (names []string, errno syscall.Errno) { +func Readdirnames(f fs.File, n int) (names []string, err error) { switch f := f.(type) { case readdirnamesFile: - var err error names, err = f.Readdirnames(n) - if errno = adjustReaddirErr(err); errno != 0 { + if err = adjustReaddirErr(err); err != nil { return } case fs.ReadDirFile: - entries, err := f.ReadDir(n) - if errno = adjustReaddirErr(err); errno != 0 { + var entries []fs.DirEntry + entries, err = f.ReadDir(n) + if err = adjustReaddirErr(err); err != nil { return } names = make([]string, 0, len(entries)) @@ -39,8 +40,9 @@ func Readdirnames(f fs.File, n int) (names []string, errno syscall.Errno) { names = append(names, e.Name()) } default: - errno = syscall.ENOTDIR + err = syscall.ENOTDIR } + err = UnwrapOSError(err) return } @@ -81,18 +83,20 @@ func (d *Dirent) IsDir() bool { // // # Errors // -// A zero syscall.Errno is success. +// If the error is non-nil, it is a syscall.Errno. io.EOF is not returned +// because implementations return that value inconsistently. // -// For portability reasons, no error is returned on io.EOF, when the file is -// closed or removed while open. +// For portability reasons, no error is returned when the file is closed or +// removed while open. // See https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs.zig#L635-L637 -func Readdir(f fs.File, n int) (dirents []*Dirent, errno syscall.Errno) { +func Readdir(f fs.File, n int) (dirents []*Dirent, err error) { // ^^ case format is to match POSIX and similar to os.File.Readdir switch f := f.(type) { case readdirFile: - fis, e := f.Readdir(n) - if errno = adjustReaddirErr(e); errno != 0 { + var fis []fs.FileInfo + fis, err = f.Readdir(n) + if err = adjustReaddirErr(err); err != nil { return } dirents = make([]*Dirent, 0, len(fis)) @@ -100,14 +104,15 @@ func Readdir(f fs.File, n int) (dirents []*Dirent, errno syscall.Errno) { // linux/darwin won't have to fan out to lstat, but windows will. var ino uint64 for _, t := range fis { - if ino, errno = inoFromFileInfo(f, t); errno != 0 { + if ino, err = inoFromFileInfo(f, t); err != nil { return } dirents = append(dirents, &Dirent{Name: t.Name(), Ino: ino, Type: t.Mode().Type()}) } case fs.ReadDirFile: - entries, e := f.ReadDir(n) - if errno = adjustReaddirErr(e); errno != 0 { + var entries []fs.DirEntry + entries, err = f.ReadDir(n) + if err = adjustReaddirErr(err); err != nil { return } dirents = make([]*Dirent, 0, len(entries)) @@ -116,23 +121,23 @@ func Readdir(f fs.File, n int) (dirents []*Dirent, errno syscall.Errno) { dirents = append(dirents, &Dirent{Name: e.Name(), Type: e.Type()}) } default: - errno = syscall.ENOTDIR + err = syscall.ENOTDIR } return } -func adjustReaddirErr(err error) syscall.Errno { +func adjustReaddirErr(err error) error { if err == io.EOF { - return 0 // e.g. Readdir on darwin returns io.EOF, but linux doesn't. - } else if errno := UnwrapOSError(err); errno != 0 { + return nil // e.g. Readdir on darwin returns io.EOF, but linux doesn't. + } else if err = UnwrapOSError(err); err != nil { // Ignore errors when the file was closed or removed. - switch errno { + switch err { case syscall.EIO, syscall.EBADF: // closed while open - return 0 + return nil case syscall.ENOENT: // Linux error when removed while open - return 0 + return nil } - return errno + return err } - return 0 + return err } diff --git a/internal/platform/dir_test.go b/internal/platform/dir_test.go index 2a3607ce..5e506b29 100644 --- a/internal/platform/dir_test.go +++ b/internal/platform/dir_test.go @@ -38,22 +38,21 @@ func TestReaddirnames(t *testing.T) { defer dotF.Close() t.Run("dir", func(t *testing.T) { - names, errno := platform.Readdirnames(dotF, -1) - require.Zero(t, errno) - + names, err := platform.Readdirnames(dotF, -1) + require.NoError(t, err) sort.Strings(names) require.Equal(t, []string{"animals.txt", "dir", "empty.txt", "emptydir", "sub"}, names) // read again even though it is exhausted - _, errno = platform.Readdirnames(dotF, 100) - require.Zero(t, errno) + _, err = platform.Readdirnames(dotF, 100) + require.NoError(t, err) }) // Don't err if something else closed the directory while reading. t.Run("closed dir", func(t *testing.T) { require.NoError(t, dotF.Close()) - _, errno := platform.Readdir(dotF, -1) - require.Zero(t, errno) + _, err := platform.Readdir(dotF, -1) + require.NoError(t, err) }) dirF, err := tc.fs.Open("dir") @@ -61,17 +60,17 @@ func TestReaddirnames(t *testing.T) { defer dirF.Close() t.Run("partial", func(t *testing.T) { - names1, errno := platform.Readdirnames(dirF, 1) - require.Zero(t, errno) + names1, err := platform.Readdirnames(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(names1)) - names2, errno := platform.Readdirnames(dirF, 1) - require.Zero(t, errno) + names2, err := platform.Readdirnames(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(names2)) // read exactly the last entry - names3, errno := platform.Readdirnames(dirF, 1) - require.Zero(t, errno) + names3, err := platform.Readdirnames(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(names3)) names := []string{names1[0], names2[0], names3[0]} @@ -80,8 +79,8 @@ func TestReaddirnames(t *testing.T) { require.Equal(t, []string{"-", "a-", "ab-"}, names) // no error reading an exhausted directory - _, errno = platform.Readdirnames(dirF, 1) - require.Zero(t, errno) + _, err = platform.Readdirnames(dirF, 1) + require.NoError(t, err) }) fileF, err := tc.fs.Open("empty.txt") @@ -89,8 +88,8 @@ func TestReaddirnames(t *testing.T) { defer fileF.Close() t.Run("file", func(t *testing.T) { - _, errno := platform.Readdirnames(fileF, -1) - require.EqualErrno(t, syscall.ENOTDIR, errno) + _, err := platform.Readdirnames(fileF, -1) + require.EqualErrno(t, syscall.ENOTDIR, err) }) subdirF, err := tc.fs.Open("sub") @@ -98,9 +97,8 @@ func TestReaddirnames(t *testing.T) { defer subdirF.Close() t.Run("subdir", func(t *testing.T) { - names, errno := platform.Readdirnames(subdirF, -1) - require.Zero(t, errno) - + names, err := platform.Readdirnames(subdirF, -1) + require.NoError(t, err) require.Equal(t, []string{"test.txt"}, names) }) }) @@ -132,8 +130,8 @@ func TestReaddir(t *testing.T) { defer dotF.Close() t.Run("dir", func(t *testing.T) { - dirents, errno := platform.Readdir(dotF, -1) - require.Zero(t, errno) // no io.EOF when -1 is used + dirents, err := platform.Readdir(dotF, -1) + require.NoError(t, err) // no io.EOF when -1 is used sort.Slice(dirents, func(i, j int) bool { return dirents[i].Name < dirents[j].Name }) requireIno(t, dirents, tc.expectIno) @@ -147,16 +145,16 @@ func TestReaddir(t *testing.T) { }, dirents) // read again even though it is exhausted - dirents, errno = platform.Readdir(dotF, 100) - require.Zero(t, errno) + dirents, err = platform.Readdir(dotF, 100) + require.NoError(t, err) require.Zero(t, len(dirents)) }) // Don't err if something else closed the directory while reading. t.Run("closed dir", func(t *testing.T) { require.NoError(t, dotF.Close()) - _, errno := platform.Readdir(dotF, -1) - require.Zero(t, errno) + _, err := platform.Readdir(dotF, -1) + require.NoError(t, err) }) fileF, err := tc.fs.Open("empty.txt") @@ -164,8 +162,8 @@ func TestReaddir(t *testing.T) { defer fileF.Close() t.Run("file", func(t *testing.T) { - _, errno := platform.Readdir(fileF, -1) - require.EqualErrno(t, syscall.ENOTDIR, errno) + _, err := platform.Readdir(fileF, -1) + require.EqualErrno(t, syscall.ENOTDIR, err) }) dirF, err := tc.fs.Open("dir") @@ -173,17 +171,17 @@ func TestReaddir(t *testing.T) { defer dirF.Close() t.Run("partial", func(t *testing.T) { - dirents1, errno := platform.Readdir(dirF, 1) - require.Zero(t, errno) + dirents1, err := platform.Readdir(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(dirents1)) - dirents2, errno := platform.Readdir(dirF, 1) - require.Zero(t, errno) + dirents2, err := platform.Readdir(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(dirents2)) // read exactly the last entry - dirents3, errno := platform.Readdir(dirF, 1) - require.Zero(t, errno) + dirents3, err := platform.Readdir(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(dirents3)) dirents := []*platform.Dirent{dirents1[0], dirents2[0], dirents3[0]} @@ -198,8 +196,8 @@ func TestReaddir(t *testing.T) { }, dirents) // no error reading an exhausted directory - _, errno = platform.Readdir(dirF, 1) - require.Zero(t, errno) + _, err = platform.Readdir(dirF, 1) + require.NoError(t, err) }) subdirF, err := tc.fs.Open("sub") @@ -207,8 +205,8 @@ func TestReaddir(t *testing.T) { defer subdirF.Close() t.Run("subdir", func(t *testing.T) { - dirents, errno := platform.Readdir(subdirF, -1) - require.Zero(t, errno) + dirents, err := platform.Readdir(subdirF, -1) + require.NoError(t, err) sort.Slice(dirents, func(i, j int) bool { return dirents[i].Name < dirents[j].Name }) require.Equal(t, 1, len(dirents)) @@ -224,8 +222,8 @@ func TestReaddir(t *testing.T) { require.NoError(t, err) defer dirF.Close() - dirents, errno := platform.Readdir(dirF, 1) - require.Zero(t, errno) + dirents, err := platform.Readdir(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(dirents)) // Speculatively try to remove even if it won't likely work @@ -237,8 +235,8 @@ func TestReaddir(t *testing.T) { require.NoError(t, err) } - _, errno = platform.Readdir(dirF, 1) - require.Zero(t, errno) + _, err = platform.Readdir(dirF, 1) + require.NoError(t, err) // don't validate the contents as due to caching it might be present. }) } diff --git a/internal/platform/errno.go b/internal/platform/errno.go index 43e03426..c2941934 100644 --- a/internal/platform/errno.go +++ b/internal/platform/errno.go @@ -2,8 +2,6 @@ package platform -import "syscall" - -func adjustErrno(err syscall.Errno) syscall.Errno { +func adjustErrno(err error) error { return err } diff --git a/internal/platform/errno_windows.go b/internal/platform/errno_windows.go index 7cba50d4..a860ac6a 100644 --- a/internal/platform/errno_windows.go +++ b/internal/platform/errno_windows.go @@ -42,7 +42,7 @@ const ( ERROR_PRIVILEGE_NOT_HELD = syscall.Errno(0x522) ) -func adjustErrno(err syscall.Errno) syscall.Errno { +func adjustErrno(err syscall.Errno) error { // Note: In windows, ERROR_PATH_NOT_FOUND(0x3) maps to syscall.ENOTDIR switch err { case ERROR_ALREADY_EXISTS: diff --git a/internal/platform/error.go b/internal/platform/error.go index 82ed9b6c..7730715a 100644 --- a/internal/platform/error.go +++ b/internal/platform/error.go @@ -1,16 +1,15 @@ package platform 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 a syscall.Errno or nil if the input is nil. +func UnwrapOSError(err error) error { if err == nil { - return 0 + return nil } err = underlyingError(err) if se, ok := err.(syscall.Errno); ok { @@ -20,8 +19,7 @@ func UnwrapOSError(err error) syscall.Errno { // // Note: Once we have our own file type, we should never see these. switch err { - case nil, io.EOF: - return 0 + case nil: case fs.ErrInvalid: return syscall.EINVAL case fs.ErrPermission: diff --git a/internal/platform/error_test.go b/internal/platform/error_test.go index 81a071fb..f24b90ea 100644 --- a/internal/platform/error_test.go +++ b/internal/platform/error_test.go @@ -3,7 +3,6 @@ package platform import ( "errors" "fmt" - "io" "io/fs" "os" "syscall" @@ -18,11 +17,6 @@ func TestUnwrapOSError(t *testing.T) { input error expected syscall.Errno }{ - { - name: "io.EOF is not an error", - input: io.EOF, - expected: 0, - }, { name: "LinkError ErrInvalid", input: &os.LinkError{Err: fs.ErrInvalid}, @@ -83,7 +77,7 @@ func TestUnwrapOSError(t *testing.T) { }) } - t.Run("nil -> zero", func(t *testing.T) { - require.Zero(t, UnwrapOSError(nil)) + t.Run("nil", func(t *testing.T) { + require.Nil(t, UnwrapOSError(nil)) }) } diff --git a/internal/platform/futimens.go b/internal/platform/futimens.go index 0b1419c0..16db9975 100644 --- a/internal/platform/futimens.go +++ b/internal/platform/futimens.go @@ -45,7 +45,7 @@ const ( // values UTIME_NOW or UTIME_NOW. // - This is like `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) error { err := utimens(path, times, symlinkFollow) return UnwrapOSError(err) } @@ -58,7 +58,7 @@ func Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscal // cannot use this to update timestamps on a directory (syscall.EPERM). // - This is like the function `futimens` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html -func UtimensFile(f fs.File, times *[2]syscall.Timespec) syscall.Errno { +func UtimensFile(f fs.File, times *[2]syscall.Timespec) error { if f, ok := f.(fdFile); ok { err := futimens(f.Fd(), times) return UnwrapOSError(err) @@ -102,16 +102,16 @@ func utimensPortable(path string, times *[2]syscall.Timespec, symlinkFollow bool // Now, either one of the inputs is a special value, or neither. This means // we don't have a risk of re-reading the clock or re-doing stat. - if atim, err := normalizeTimespec(path, times, 0); err != 0 { + if atim, err := normalizeTimespec(path, times, 0); err != nil { return err - } else if mtim, err := normalizeTimespec(path, times, 1); err != 0 { + } else if mtim, err := normalizeTimespec(path, times, 1); err != nil { return err } else { return syscall.UtimesNano(path, []syscall.Timespec{atim, mtim}) } } -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 error) { //nolint:unused switch times[i].Nsec { case UTIME_NOW: // declined in Go per golang/go#31880. ts = nowTimespec() @@ -122,7 +122,7 @@ func normalizeTimespec(path string, times *[2]syscall.Timespec, i int) (ts sysca // - https://github.com/golang/go/issues/32558. // - https://go-review.googlesource.com/c/go/+/219638 (unmerged) var st Stat_t - if st, err = stat(path); err != 0 { + if st, err = stat(path); err != nil { return } switch i { diff --git a/internal/platform/futimens_test.go b/internal/platform/futimens_test.go index 673e5178..94f1d1c2 100644 --- a/internal/platform/futimens_test.go +++ b/internal/platform/futimens_test.go @@ -15,7 +15,6 @@ 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) - err = Utimens("nope", nil, false) if SupportsSymlinkNoFollow { require.EqualErrno(t, syscall.ENOENT, err) @@ -147,8 +146,8 @@ func testFutimens(t *testing.T, usePath bool) { panic(tc) } - oldSt, errno := Lstat(statPath) - require.Zero(t, errno) + oldSt, err := Lstat(statPath) + require.NoError(t, err) if usePath { err = Utimens(path, tc.times, !symlinkNoFollow) @@ -156,7 +155,7 @@ func testFutimens(t *testing.T, usePath bool) { require.EqualErrno(t, syscall.ENOSYS, err) return } - require.Zero(t, err) + require.NoError(t, err) } else { flag := syscall.O_RDWR if path == dir { @@ -166,17 +165,15 @@ func testFutimens(t *testing.T, usePath bool) { t.Skip("windows cannot update timestamps on a dir") } } - - f, errno := OpenFile(path, flag, 0) - require.Zero(t, errno) - - errno = UtimensFile(f, tc.times) + f, err := OpenFile(path, flag, 0) + require.NoError(t, err) + err = UtimensFile(f, tc.times) require.NoError(t, f.Close()) - require.Zero(t, errno) + require.NoError(t, err) } - newSt, errno := Lstat(statPath) - require.Zero(t, errno) + newSt, err := Lstat(statPath) + require.NoError(t, err) if CompilerSupported() { if tc.times != nil && tc.times[0].Nsec == UTIME_OMIT { @@ -221,22 +218,20 @@ func TestUtimensFile(t *testing.T) { file := path.Join(t.TempDir(), "file") err := os.WriteFile(file, []byte{}, 0o700) require.NoError(t, err) - - fileF, errno := OpenFile(file, syscall.O_RDWR, 0) - require.Zero(t, errno) + fileF, err := OpenFile(file, syscall.O_RDWR, 0) + require.NoError(t, err) require.NoError(t, fileF.Close()) - errno = UtimensFile(fileF, nil) - require.EqualErrno(t, syscall.EBADF, errno) + err = UtimensFile(fileF, nil) + require.EqualErrno(t, syscall.EBADF, err) }) t.Run("closed dir", func(t *testing.T) { dir := path.Join(t.TempDir(), "dir") err := os.Mkdir(dir, 0o700) require.NoError(t, err) - - dirF, errno := OpenFile(dir, syscall.O_RDONLY, 0) - require.Zero(t, errno) + dirF, err := OpenFile(dir, syscall.O_RDONLY, 0) + require.NoError(t, err) require.NoError(t, dirF.Close()) err = UtimensFile(dirF, nil) diff --git a/internal/platform/open_file.go b/internal/platform/open_file.go index 2430c3f7..cff965a3 100644 --- a/internal/platform/open_file.go +++ b/internal/platform/open_file.go @@ -15,9 +15,9 @@ const ( O_NOFOLLOW = syscall.O_NOFOLLOW ) -// 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) (File, syscall.Errno) { - f, err := os.OpenFile(path, flag, perm) - return f, UnwrapOSError(err) +// OpenFile is like os.OpenFile except it returns syscall.Errno +func OpenFile(name string, flag int, perm fs.FileMode) (f *os.File, err error) { + f, err = os.OpenFile(name, flag, perm) + err = UnwrapOSError(err) + return } diff --git a/internal/platform/open_file_js.go b/internal/platform/open_file_js.go index 182cb481..4b799d99 100644 --- a/internal/platform/open_file_js.go +++ b/internal/platform/open_file_js.go @@ -3,7 +3,6 @@ package platform import ( "io/fs" "os" - "syscall" ) // See the comments on the same constants in open_file_windows.go @@ -12,8 +11,9 @@ const ( O_NOFOLLOW = 1 << 30 ) -func OpenFile(path string, flag int, perm fs.FileMode) (File, syscall.Errno) { +func OpenFile(name string, flag int, perm fs.FileMode) (f *os.File, err error) { flag &= ^(O_DIRECTORY | O_NOFOLLOW) // erase placeholders - f, err := os.OpenFile(path, flag, perm) - return f, UnwrapOSError(err) + f, err = os.OpenFile(name, flag, perm) + err = UnwrapOSError(err) + return } diff --git a/internal/platform/open_file_test.go b/internal/platform/open_file_test.go index fc2a1c09..468c03d3 100644 --- a/internal/platform/open_file_test.go +++ b/internal/platform/open_file_test.go @@ -16,8 +16,8 @@ func TestOpenFile(t *testing.T) { // from os.TestDirFSPathsValid if runtime.GOOS != "windows" { t.Run("strange name", func(t *testing.T) { - f, errno := OpenFile(path.Join(tmpDir, `e:xperi\ment.txt`), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) - require.Zero(t, errno) + f, err := OpenFile(path.Join(tmpDir, `e:xperi\ment.txt`), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) + require.NoError(t, err) require.NoError(t, f.Close()) }) } @@ -27,30 +27,30 @@ func TestOpenFile_Errors(t *testing.T) { tmpDir := t.TempDir() 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) + _, err := OpenFile(path.Join(tmpDir, "not-really-exist.txt"), os.O_RDONLY, 0o600) + require.EqualErrno(t, syscall.ENOENT, err) }) // This is the same as https://github.com/ziglang/zig/blob/d24ebf1d12cf66665b52136a2807f97ff021d78d/lib/std/os/test.zig#L105-L112 t.Run("try creating on existing file must be EEXIST", func(t *testing.T) { filepath := path.Join(tmpDir, "file.txt") - f, errno := OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666) - require.Zero(t, errno) + f, err := OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666) defer require.NoError(t, f.Close()) + require.NoError(t, err) - _, errno = OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666) - require.EqualErrno(t, syscall.EEXIST, errno) + _, err = OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666) + require.EqualErrno(t, syscall.EEXIST, err) }) t.Run("writing to a read-only file is EBADF", func(t *testing.T) { path := path.Join(tmpDir, "file") require.NoError(t, os.WriteFile(path, nil, 0o600)) - f, errno := OpenFile(path, os.O_RDONLY, 0) + f, err := OpenFile(path, os.O_RDONLY, 0) defer require.NoError(t, f.Close()) - require.Zero(t, errno) + require.NoError(t, err) - _, err := f.Write([]byte{1, 2, 3, 4}) + _, err = f.Write([]byte{1, 2, 3, 4}) require.EqualErrno(t, syscall.EBADF, UnwrapOSError(err)) }) @@ -58,11 +58,11 @@ func TestOpenFile_Errors(t *testing.T) { path := path.Join(tmpDir, "diragain") require.NoError(t, os.Mkdir(path, 0o755)) - f, errno := OpenFile(path, os.O_RDONLY, 0) + f, err := OpenFile(path, os.O_RDONLY, 0) defer require.NoError(t, f.Close()) - require.Zero(t, errno) + require.NoError(t, err) - _, err := f.Write([]byte{1, 2, 3, 4}) + _, err = f.Write([]byte{1, 2, 3, 4}) require.EqualErrno(t, syscall.EBADF, UnwrapOSError(err)) }) @@ -74,10 +74,10 @@ func TestOpenFile_Errors(t *testing.T) { err := os.Symlink(target, symlink) require.NoError(t, err) - _, errno := OpenFile(symlink, O_DIRECTORY|O_NOFOLLOW, 0o0666) - require.EqualErrno(t, syscall.ENOTDIR, errno) + _, err = OpenFile(symlink, O_DIRECTORY|O_NOFOLLOW, 0o0666) + require.EqualErrno(t, syscall.ENOTDIR, err) - _, errno = OpenFile(symlink, O_NOFOLLOW, 0o0666) - require.EqualErrno(t, syscall.ELOOP, errno) + _, err = OpenFile(symlink, O_NOFOLLOW, 0o0666) + require.EqualErrno(t, syscall.ELOOP, err) }) } diff --git a/internal/platform/open_file_windows.go b/internal/platform/open_file_windows.go index 07037856..ab27e603 100644 --- a/internal/platform/open_file_windows.go +++ b/internal/platform/open_file_windows.go @@ -27,36 +27,35 @@ const ( O_NOFOLLOW = 1 << 30 ) -func OpenFile(path string, flag int, perm fs.FileMode) (File, syscall.Errno) { - if f, errno := OpenFile(path, flag, perm); err != 0 { - return nil, UnwrapOSError(err) +func OpenFile(path string, flag int, perm fs.FileMode) (File, error) { + if f, err := openFile(path, flag, perm); err != nil { + return nil, err } else { - return &windowsWrappedFile{File: f, path: path, flag: flag, perm: perm}, 0 + return &windowsWrappedFile{File: f, path: path, flag: flag, perm: perm}, 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, error) { isDir := flag&O_DIRECTORY > 0 flag &= ^(O_DIRECTORY | O_NOFOLLOW) // erase placeholders // TODO: document why we are opening twice fd, err := open(path, flag|syscall.O_CLOEXEC, uint32(perm)) if err == nil { - return os.NewFile(uintptr(fd), path), 0 + return os.NewFile(uintptr(fd), path), nil } // TODO: Set FILE_SHARE_DELETE for directory as well. f, err := os.OpenFile(path, flag, perm) - errno := UnwrapOSError(err) - if errno == 0 { - return f, 0 + if err = UnwrapOSError(err); err == nil { + return f, nil } - switch errno { + switch err { // To match expectations of WASI, e.g. TinyGo TestStatBadDir, return // ENOENT, not ENOTDIR. case syscall.ENOTDIR: - errno = syscall.ENOENT + err = syscall.ENOENT case syscall.ENOENT: if isSymlink(path) { // Either symlink or hard link not found. We change the returned @@ -64,13 +63,13 @@ func openFile(path string, flag int, perm fs.FileMode) (*os.File, syscall.Errno) // behavior across OSes. if isDir { // Dangling symlink dir must raise ENOTDIR. - errno = syscall.ENOTDIR + err = syscall.ENOTDIR } else { - errno = syscall.ELOOP + err = syscall.ELOOP } } } - return f, errno + return f, err } func isSymlink(path string) bool { diff --git a/internal/platform/rename.go b/internal/platform/rename.go index 2b537fae..6c39a556 100644 --- a/internal/platform/rename.go +++ b/internal/platform/rename.go @@ -4,9 +4,9 @@ package platform import "syscall" -func Rename(from, to string) syscall.Errno { +func Rename(from, to string) error { if from == to { - return 0 + return nil } - return UnwrapOSError(syscall.Rename(from, to)) + return syscall.Rename(from, to) } diff --git a/internal/platform/rename_test.go b/internal/platform/rename_test.go index 25bbf2c0..e2f29bc4 100644 --- a/internal/platform/rename_test.go +++ b/internal/platform/rename_test.go @@ -30,8 +30,8 @@ func TestRename(t *testing.T) { require.NoError(t, err) file2Path := path.Join(tmpDir, "file2") - errno := Rename(file1Path, file2Path) - require.Zero(t, errno) + err = Rename(file1Path, file2Path) + require.NoError(t, err) // Show the prior path no longer exists _, err = os.Stat(file1Path) @@ -48,12 +48,12 @@ func TestRename(t *testing.T) { require.NoError(t, os.Mkdir(dir1Path, 0o700)) dir2Path := path.Join(tmpDir, "dir2") - errno := Rename(dir1Path, dir2Path) - require.Zero(t, errno) + err := Rename(dir1Path, dir2Path) + require.NoError(t, err) // Show the prior path no longer exists - _, err := os.Stat(dir1Path) - require.EqualErrno(t, syscall.ENOENT, UnwrapOSError(err)) + _, err = os.Stat(dir1Path) + require.EqualErrno(t, syscall.ENOENT, errors.Unwrap(err)) s, err := os.Stat(dir2Path) require.NoError(t, err) @@ -107,8 +107,8 @@ func TestRename(t *testing.T) { dir2Path := path.Join(tmpDir, "dir2") require.NoError(t, os.Mkdir(dir2Path, 0o700)) - errno := Rename(dir1Path, dir2Path) - require.Zero(t, errno) + err = Rename(dir1Path, dir2Path) + require.NoError(t, err) // Show the prior path no longer exists _, err = os.Stat(dir1Path) @@ -158,8 +158,8 @@ func TestRename(t *testing.T) { err = os.WriteFile(file2Path, file2Contents, 0o600) require.NoError(t, err) - errno := Rename(file1Path, file2Path) - require.Zero(t, errno) + err = Rename(file1Path, file2Path) + require.NoError(t, err) // Show the prior path no longer exists _, err = os.Stat(file1Path) @@ -176,8 +176,8 @@ func TestRename(t *testing.T) { dir1Path := path.Join(tmpDir, "dir1") require.NoError(t, os.Mkdir(dir1Path, 0o700)) - errno := Rename(dir1Path, dir1Path) - require.Zero(t, errno) + err := Rename(dir1Path, dir1Path) + require.NoError(t, err) s, err := os.Stat(dir1Path) require.NoError(t, err) @@ -191,8 +191,8 @@ func TestRename(t *testing.T) { err := os.WriteFile(file1Path, file1Contents, 0o600) require.NoError(t, err) - errno := Rename(file1Path, file1Path) - require.Zero(t, errno) + err = Rename(file1Path, file1Path) + require.NoError(t, err) b, err := os.ReadFile(file1Path) require.NoError(t, err) diff --git a/internal/platform/stat.go b/internal/platform/stat.go index b6858a24..059ded7c 100644 --- a/internal/platform/stat.go +++ b/internal/platform/stat.go @@ -58,32 +58,34 @@ type Stat_t struct { // The primary difference between this and Stat is, when the path is a // symbolic link, the stat is about the link, not its target, such as directory // listings. -func Lstat(path string) (Stat_t, syscall.Errno) { - return lstat(path) // extracted to override more expensively in windows +func Lstat(path string) (Stat_t, error) { + st, err := lstat(path) // extracted to override more expensively in windows + return st, UnwrapOSError(err) } // Stat is like syscall.Stat. This returns syscall.ENOENT if the path doesn't // exist. -func Stat(path string) (Stat_t, syscall.Errno) { - return stat(path) // extracted to override more expensively in windows +func Stat(path string) (Stat_t, error) { + st, err := stat(path) // extracted to override more expensively in windows + return st, UnwrapOSError(err) } // StatFile is like syscall.Fstat, but for fs.File instead of a file // descriptor. This returns syscall.EBADF if the file or directory was closed. // Note: windows allows you to stat a closed directory. -func StatFile(f fs.File) (Stat_t, syscall.Errno) { - st, errno := statFile(f) - if errno == syscall.EIO { - errno = syscall.EBADF +func StatFile(f fs.File) (Stat_t, error) { + st, err := statFile(f) + if err = UnwrapOSError(err); err == syscall.EIO { + err = syscall.EBADF } - return st, errno + return st, err } -func defaultStatFile(f fs.File) (Stat_t, syscall.Errno) { +func defaultStatFile(f fs.File) (Stat_t, error) { if t, err := f.Stat(); err != nil { - return Stat_t{}, UnwrapOSError(err) + return Stat_t{}, err } else { - return statFromFileInfo(t), 0 + return statFromFileInfo(t), nil } } diff --git a/internal/platform/stat_bsd.go b/internal/platform/stat_bsd.go index 90959f8f..f3640b44 100644 --- a/internal/platform/stat_bsd.go +++ b/internal/platform/stat_bsd.go @@ -8,27 +8,27 @@ import ( "syscall" ) -func lstat(path string) (Stat_t, syscall.Errno) { +func lstat(path string) (Stat_t, error) { if t, err := os.Lstat(path); err != nil { - return Stat_t{}, UnwrapOSError(err) + return Stat_t{}, err } else { - return statFromFileInfo(t), 0 + return statFromFileInfo(t), nil } } -func stat(path string) (Stat_t, syscall.Errno) { +func stat(path string) (Stat_t, error) { if t, err := os.Stat(path); err != nil { - return Stat_t{}, UnwrapOSError(err) + return Stat_t{}, err } else { - return statFromFileInfo(t), 0 + return statFromFileInfo(t), nil } } -func statFile(f fs.File) (Stat_t, syscall.Errno) { +func statFile(f fs.File) (Stat_t, error) { return defaultStatFile(f) } -func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err syscall.Errno) { +func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err error) { if d, ok := t.Sys().(*syscall.Stat_t); ok { ino = d.Ino } diff --git a/internal/platform/stat_linux.go b/internal/platform/stat_linux.go index 136081ef..19a82438 100644 --- a/internal/platform/stat_linux.go +++ b/internal/platform/stat_linux.go @@ -11,27 +11,27 @@ import ( "syscall" ) -func lstat(path string) (Stat_t, syscall.Errno) { +func lstat(path string) (Stat_t, error) { if t, err := os.Lstat(path); err != nil { - return Stat_t{}, UnwrapOSError(err) + return Stat_t{}, err } else { - return statFromFileInfo(t), 0 + return statFromFileInfo(t), nil } } -func stat(path string) (Stat_t, syscall.Errno) { +func stat(path string) (Stat_t, error) { if t, err := os.Stat(path); err != nil { - return Stat_t{}, UnwrapOSError(err) + return Stat_t{}, err } else { - return statFromFileInfo(t), 0 + return statFromFileInfo(t), nil } } -func statFile(f fs.File) (Stat_t, syscall.Errno) { +func statFile(f fs.File) (Stat_t, error) { return defaultStatFile(f) } -func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err syscall.Errno) { +func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err error) { if d, ok := t.Sys().(*syscall.Stat_t); ok { ino = d.Ino } diff --git a/internal/platform/stat_test.go b/internal/platform/stat_test.go index 53437167..ca455c26 100644 --- a/internal/platform/stat_test.go +++ b/internal/platform/stat_test.go @@ -15,16 +15,15 @@ import ( func TestLstat(t *testing.T) { tmpDir := t.TempDir() - _, errno := Lstat(path.Join(tmpDir, "cat")) - require.EqualErrno(t, syscall.ENOENT, errno) - _, errno = Lstat(path.Join(tmpDir, "sub/cat")) - require.EqualErrno(t, syscall.ENOENT, errno) + _, err := Lstat(path.Join(tmpDir, "cat")) + require.EqualErrno(t, syscall.ENOENT, err) + _, err = Lstat(path.Join(tmpDir, "sub/cat")) + require.EqualErrno(t, syscall.ENOENT, err) var st Stat_t t.Run("dir", func(t *testing.T) { - st, errno = Lstat(tmpDir) - require.Zero(t, errno) - + st, err = Lstat(tmpDir) + require.NoError(t, err) require.True(t, st.Mode.IsDir()) require.NotEqual(t, uint64(0), st.Ino) }) @@ -34,9 +33,8 @@ func TestLstat(t *testing.T) { t.Run("file", func(t *testing.T) { require.NoError(t, os.WriteFile(file, []byte{1, 2}, 0o400)) - stFile, errno = Lstat(file) - require.Zero(t, errno) - + stFile, err = Lstat(file) + require.NoError(t, err) require.Zero(t, stFile.Mode.Type()) require.Equal(t, int64(2), stFile.Size) require.NotEqual(t, uint64(0), stFile.Ino) @@ -51,9 +49,8 @@ func TestLstat(t *testing.T) { t.Run("subdir", func(t *testing.T) { require.NoError(t, os.Mkdir(subdir, 0o500)) - stSubdir, errno = Lstat(subdir) - require.Zero(t, errno) - + stSubdir, err = Lstat(subdir) + require.NoError(t, err) require.True(t, stSubdir.Mode.IsDir()) require.NotEqual(t, uint64(0), stSubdir.Ino) }) @@ -64,8 +61,8 @@ func TestLstat(t *testing.T) { t.Run("link to dir link", func(t *testing.T) { pathLink := subdir + "-link" - stLink, errno := Lstat(pathLink) - require.Zero(t, errno) + stLink, err := Lstat(pathLink) + require.NoError(t, err) requireLinkStat(t, pathLink, stLink) }) @@ -75,8 +72,8 @@ func requireLinkStat(t *testing.T, path string, stat Stat_t) { link := path + "-link" require.NoError(t, os.Symlink(path, link)) - stLink, errno := Lstat(link) - require.Zero(t, errno) + stLink, err := Lstat(link) + require.NoError(t, err) require.NotEqual(t, uint64(0), stLink.Ino) require.NotEqual(t, stat.Ino, stLink.Ino) // inodes are not equal @@ -94,17 +91,16 @@ func requireLinkStat(t *testing.T, path string, stat Stat_t) { func TestStat(t *testing.T) { tmpDir := t.TempDir() - _, errno := Stat(path.Join(tmpDir, "cat")) - require.EqualErrno(t, syscall.ENOENT, errno) - _, errno = Stat(path.Join(tmpDir, "sub/cat")) - require.EqualErrno(t, syscall.ENOENT, errno) + _, err := Stat(path.Join(tmpDir, "cat")) + require.EqualErrno(t, syscall.ENOENT, err) + _, err = Stat(path.Join(tmpDir, "sub/cat")) + require.EqualErrno(t, syscall.ENOENT, err) var st Stat_t t.Run("dir", func(t *testing.T) { - st, errno = Stat(tmpDir) - require.Zero(t, errno) - + st, err = Stat(tmpDir) + require.NoError(t, err) require.True(t, st.Mode.IsDir()) require.NotEqual(t, uint64(0), st.Ino) }) @@ -115,9 +111,8 @@ func TestStat(t *testing.T) { t.Run("file", func(t *testing.T) { require.NoError(t, os.WriteFile(file, nil, 0o400)) - stFile, errno = Stat(file) - require.Zero(t, errno) - + stFile, err = Stat(file) + require.NoError(t, err) require.False(t, stFile.Mode.IsDir()) require.NotEqual(t, uint64(0), st.Ino) }) @@ -126,9 +121,8 @@ func TestStat(t *testing.T) { link := path.Join(tmpDir, "file-link") require.NoError(t, os.Symlink(file, link)) - stLink, errno := Stat(link) - require.Zero(t, errno) - + stLink, err := Stat(link) + require.NoError(t, err) require.Equal(t, stFile, stLink) // resolves to the file }) @@ -137,9 +131,8 @@ func TestStat(t *testing.T) { t.Run("subdir", func(t *testing.T) { require.NoError(t, os.Mkdir(subdir, 0o500)) - stSubdir, errno = Stat(subdir) - require.Zero(t, errno) - + stSubdir, err = Stat(subdir) + require.NoError(t, err) require.True(t, stSubdir.Mode.IsDir()) require.NotEqual(t, uint64(0), st.Ino) }) @@ -148,9 +141,8 @@ func TestStat(t *testing.T) { link := path.Join(tmpDir, "dir-link") require.NoError(t, os.Symlink(subdir, link)) - stLink, errno := Stat(link) - require.Zero(t, errno) - + stLink, err := Stat(link) + require.NoError(t, err) require.Equal(t, stSubdir, stLink) // resolves to the dir }) } @@ -160,14 +152,13 @@ func TestStatFile(t *testing.T) { var st Stat_t - tmpDirF, errno := OpenFile(tmpDir, syscall.O_RDONLY, 0) - require.Zero(t, errno) + tmpDirF, err := OpenFile(tmpDir, syscall.O_RDONLY, 0) + require.NoError(t, err) defer tmpDirF.Close() t.Run("dir", func(t *testing.T) { - st, errno = StatFile(tmpDirF) - require.Zero(t, errno) - + st, err = StatFile(tmpDirF) + require.NoError(t, err) require.True(t, st.Mode.IsDir()) requireDirectoryDevIno(t, st) }) @@ -177,41 +168,39 @@ func TestStatFile(t *testing.T) { if runtime.GOOS != "windows" { t.Run("closed dir", func(t *testing.T) { require.NoError(t, tmpDirF.Close()) - st, errno = StatFile(tmpDirF) - require.EqualErrno(t, syscall.EBADF, errno) + st, err = StatFile(tmpDirF) + require.EqualErrno(t, syscall.EBADF, err) }) } file := path.Join(tmpDir, "file") require.NoError(t, os.WriteFile(file, nil, 0o400)) - fileF, errno := OpenFile(file, syscall.O_RDONLY, 0) - require.Zero(t, errno) + fileF, err := OpenFile(file, syscall.O_RDONLY, 0) + require.NoError(t, err) defer fileF.Close() t.Run("file", func(t *testing.T) { - st, errno = StatFile(fileF) - require.Zero(t, errno) - + st, err = StatFile(fileF) + require.NoError(t, err) require.False(t, st.Mode.IsDir()) require.NotEqual(t, uint64(0), st.Ino) }) t.Run("closed file", func(t *testing.T) { require.NoError(t, fileF.Close()) - _, errno = StatFile(fileF) - require.EqualErrno(t, syscall.EBADF, errno) + _, err = StatFile(fileF) + require.EqualErrno(t, syscall.EBADF, err) }) subdir := path.Join(tmpDir, "sub") require.NoError(t, os.Mkdir(subdir, 0o500)) - subdirF, errno := OpenFile(subdir, syscall.O_RDONLY, 0) - require.Zero(t, errno) + subdirF, err := OpenFile(subdir, syscall.O_RDONLY, 0) + require.NoError(t, err) defer subdirF.Close() t.Run("subdir", func(t *testing.T) { - st, errno = StatFile(subdirF) - require.Zero(t, errno) - + st, err = StatFile(subdirF) + require.NoError(t, err) require.True(t, st.Mode.IsDir()) requireDirectoryDevIno(t, st) }) @@ -219,8 +208,8 @@ func TestStatFile(t *testing.T) { if runtime.GOOS != "windows" { // windows allows you to stat a closed dir t.Run("closed subdir", func(t *testing.T) { require.NoError(t, subdirF.Close()) - st, errno = StatFile(subdirF) - require.EqualErrno(t, syscall.EBADF, errno) + st, err = StatFile(subdirF) + require.EqualErrno(t, syscall.EBADF, err) }) } } @@ -268,9 +257,8 @@ func Test_StatFile_times(t *testing.T) { require.NoError(t, err) defer f.Close() - st, errno := StatFile(f) - require.Zero(t, errno) - + st, err := StatFile(f) + require.NoError(t, err) require.Equal(t, st.Atim, tc.atimeNsec) require.Equal(t, st.Mtim, tc.mtimeNsec) }) @@ -301,20 +289,19 @@ func TestStatFile_dev_inode(t *testing.T) { defer l2.Close() // First, stat the directory - st1, errno := StatFile(d) - require.Zero(t, errno) - + st1, err := StatFile(d) + require.NoError(t, err) requireDirectoryDevIno(t, st1) // Now, stat the files in it - st1, errno = StatFile(f1) - require.Zero(t, errno) + st1, err = StatFile(f1) + require.NoError(t, err) - st2, errno := StatFile(f2) - require.Zero(t, errno) + st2, err := StatFile(f2) + require.NoError(t, err) - st3, errno := StatFile(l2) - require.Zero(t, errno) + st3, err := StatFile(l2) + require.NoError(t, err) // The files should be on the same device, but different inodes require.Equal(t, st1.Dev, st2.Dev) @@ -322,8 +309,8 @@ func TestStatFile_dev_inode(t *testing.T) { require.Equal(t, st2, st3) // stat on a link is for its target // Redoing stat should result in the same inodes - st1Again, errno := StatFile(f1) - require.Zero(t, errno) + st1Again, err := StatFile(f1) + require.NoError(t, err) require.Equal(t, st1.Dev, st1Again.Dev) // On Windows, we cannot rename while opening. @@ -333,13 +320,13 @@ func TestStatFile_dev_inode(t *testing.T) { require.NoError(t, l2.Close()) // Renaming a file shouldn't change its inodes. - require.Zero(t, Rename(path1, path2)) + require.NoError(t, Rename(path1, path2)) f1, err = os.Open(path2) require.NoError(t, err) defer f1.Close() - st1Again, errno = StatFile(f1) - require.Zero(t, errno) + st1Again, err = StatFile(f1) + require.NoError(t, err) require.Equal(t, st1.Dev, st1Again.Dev) require.Equal(t, st1.Ino, st1Again.Ino) } @@ -372,11 +359,10 @@ func TestStat_uid_gid(t *testing.T) { tmpDir := t.TempDir() dir := path.Join(tmpDir, "dir") require.NoError(t, os.Mkdir(dir, 0o0700)) - require.Zero(t, chgid(dir, gid)) - - st, errno := Stat(dir) - require.Zero(t, errno) + require.NoError(t, chgid(dir, gid)) + st, err := Stat(dir) + require.NoError(t, err) require.Equal(t, uid, st.Uid) require.Equal(t, gid, st.Gid) }) @@ -385,11 +371,10 @@ func TestStat_uid_gid(t *testing.T) { tmpDir := t.TempDir() link := path.Join(tmpDir, "link") require.NoError(t, os.Symlink(tmpDir, link)) - require.Zero(t, chgid(link, gid)) - - st, errno := Lstat(link) - require.Zero(t, errno) + require.NoError(t, chgid(link, gid)) + st, err := Lstat(link) + require.NoError(t, err) require.Equal(t, uid, st.Uid) require.Equal(t, gid, st.Gid) }) @@ -398,11 +383,10 @@ func TestStat_uid_gid(t *testing.T) { tmpDir := t.TempDir() file := path.Join(tmpDir, "file") require.NoError(t, os.WriteFile(file, nil, 0o0600)) - require.Zero(t, chgid(file, gid)) - - st, errno := Lstat(file) - require.Zero(t, errno) + require.NoError(t, chgid(file, gid)) + st, err := Lstat(file) + require.NoError(t, err) require.Equal(t, uid, st.Uid) require.Equal(t, gid, st.Gid) }) diff --git a/internal/platform/stat_unsupported.go b/internal/platform/stat_unsupported.go index 32e94438..d2c73b9c 100644 --- a/internal/platform/stat_unsupported.go +++ b/internal/platform/stat_unsupported.go @@ -5,32 +5,29 @@ package platform import ( "io/fs" "os" - "syscall" ) -func lstat(path string) (Stat_t, syscall.Errno) { +func lstat(path string) (Stat_t, error) { t, err := os.Lstat(path) - if errno := UnwrapOSError(err); errno == 0 { - return statFromFileInfo(t), 0 - } else { - return Stat_t{}, errno + if err = UnwrapOSError(err); err != nil { + return statFromFileInfo(t), nil } + return Stat_t{}, err } -func stat(path string) (Stat_t, syscall.Errno) { +func stat(path string) (Stat_t, error) { t, err := os.Stat(path) - if errno := UnwrapOSError(err); errno == 0 { - return statFromFileInfo(t), 0 - } else { - return Stat_t{}, errno + if err = UnwrapOSError(err); err == nil { + return statFromFileInfo(t), nil } + return Stat_t{}, err } -func statFile(f fs.File) (Stat_t, syscall.Errno) { +func statFile(f fs.File) (Stat_t, error) { return defaultStatFile(f) } -func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, err syscall.Errno) { +func inoFromFileInfo(readdirFile, fs.FileInfo) (ino uint64, err error) { return } diff --git a/internal/platform/stat_windows.go b/internal/platform/stat_windows.go index 86db40bb..e0a517ba 100644 --- a/internal/platform/stat_windows.go +++ b/internal/platform/stat_windows.go @@ -8,7 +8,7 @@ import ( "syscall" ) -func lstat(path string) (Stat_t, syscall.Errno) { +func lstat(path string) (Stat_t, error) { 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 @@ -16,12 +16,12 @@ func lstat(path string) (Stat_t, syscall.Errno) { return statPath(attrs, path) } -func stat(path string) (Stat_t, syscall.Errno) { +func stat(path string) (Stat_t, error) { attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) return statPath(attrs, path) } -func statPath(createFileAttrs uint32, path string) (Stat_t, syscall.Errno) { +func statPath(createFileAttrs uint32, path string) (Stat_t, error) { if len(path) == 0 { return Stat_t{}, syscall.ENOENT } @@ -46,7 +46,7 @@ func statPath(createFileAttrs uint32, path string) (Stat_t, syscall.Errno) { return statHandle(h) } -func statFile(f fs.File) (Stat_t, syscall.Errno) { +func statFile(f fs.File) (Stat_t, error) { if of, ok := f.(fdFile); ok { // Attempt to get the stat by handle, which works for normal files st, err := statHandle(syscall.Handle(of.Fd())) @@ -61,11 +61,10 @@ func statFile(f fs.File) (Stat_t, syscall.Errno) { } // inoFromFileInfo uses stat to get the inode information of the file. -func inoFromFileInfo(_ readdirFile, t fs.FileInfo) (ino uint64, errno syscall.Errno) { +func inoFromFileInfo(f readdirFile, t fs.FileInfo) (ino uint64, err error) { if pf, ok := f.(PathFile); ok { inoPath := path.Clean(path.Join(pf.Path(), t.Name())) - var st Stat_t - if st, errno = Lstat(inoPath); errno == 0 { + if st, err := lstat(inoPath); err == nil { ino = st.Ino } } @@ -89,7 +88,7 @@ func statFromFileInfo(t fs.FileInfo) Stat_t { } } -func statHandle(h syscall.Handle) (Stat_t, syscall.Errno) { +func statHandle(h syscall.Handle) (Stat_t, error) { winFt, err := syscall.GetFileType(h) if err != nil { return Stat_t{}, err diff --git a/internal/platform/sync.go b/internal/platform/sync.go index a93e24ba..cf00b0fe 100644 --- a/internal/platform/sync.go +++ b/internal/platform/sync.go @@ -1,14 +1,11 @@ package platform -import ( - "io/fs" - "syscall" -) +import "io/fs" // Fdatasync is like syscall.Fdatasync except that's only defined in linux. // // Note: This returns with no error instead of syscall.ENOSYS when // unimplemented. This prevents fake filesystems from erring. -func Fdatasync(f fs.File) syscall.Errno { +func Fdatasync(f fs.File) (err error) { return fdatasync(f) } diff --git a/internal/platform/sync_linux.go b/internal/platform/sync_linux.go index 41ae44c8..c634431d 100644 --- a/internal/platform/sync_linux.go +++ b/internal/platform/sync_linux.go @@ -7,14 +7,14 @@ import ( "syscall" ) -func fdatasync(f fs.File) syscall.Errno { +func fdatasync(f fs.File) (err error) { if fd, ok := f.(fdFile); ok { - return UnwrapOSError(syscall.Fdatasync(int(fd.Fd()))) + return syscall.Fdatasync(int(fd.Fd())) } // Attempt to sync everything, even if we only need to sync the data. if s, ok := f.(syncFile); ok { - return UnwrapOSError(s.Sync()) + err = s.Sync() } - return 0 + return } diff --git a/internal/platform/sync_test.go b/internal/platform/sync_test.go index ac2afb00..f50380b7 100644 --- a/internal/platform/sync_test.go +++ b/internal/platform/sync_test.go @@ -14,30 +14,30 @@ import ( // 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 Test_Fdatasync(t *testing.T) { - f, errno := os.CreateTemp("", t.Name()) - require.NoError(t, errno) + f, err := os.CreateTemp("", t.Name()) + require.NoError(t, err) defer f.Close() expected := "hello world!" // Write the expected data - _, errno = f.Write([]byte(expected)) - require.NoError(t, errno) + _, err = f.Write([]byte(expected)) + require.NoError(t, err) // Sync the data. - if errno = Fdatasync(f); errno == syscall.ENOSYS { + if err = Fdatasync(f); err == syscall.ENOSYS { return // don't continue if it isn't supported. } - require.Zero(t, errno) + require.NoError(t, err) // Rewind while the file is still open. - _, err := f.Seek(0, io.SeekStart) + _, err = f.Seek(0, io.SeekStart) require.NoError(t, err) // Read data from the file var buf bytes.Buffer - _, errno = io.Copy(&buf, f) - require.NoError(t, errno) + _, err = io.Copy(&buf, f) + require.NoError(t, err) // It may be the case that sync worked. require.Equal(t, expected, buf.String()) diff --git a/internal/platform/sync_unsupported.go b/internal/platform/sync_unsupported.go index 38284aa5..8fc60518 100644 --- a/internal/platform/sync_unsupported.go +++ b/internal/platform/sync_unsupported.go @@ -4,13 +4,12 @@ package platform import ( "io/fs" - "syscall" ) -func fdatasync(f fs.File) syscall.Errno { +func fdatasync(f fs.File) error { // Attempt to sync everything, even if we only need to sync the data. if s, ok := f.(syncFile); ok { - return UnwrapOSError(s.Sync()) + return s.Sync() } - return 0 + return nil } diff --git a/internal/platform/unlink.go b/internal/platform/unlink.go index 9555da51..898d4b4c 100644 --- a/internal/platform/unlink.go +++ b/internal/platform/unlink.go @@ -4,10 +4,10 @@ package platform import "syscall" -func Unlink(name string) (errno syscall.Errno) { +func Unlink(name string) error { err := syscall.Unlink(name) - if errno = UnwrapOSError(err); errno == syscall.EPERM { - errno = syscall.EISDIR + if err = UnwrapOSError(err); err == syscall.EPERM { + err = syscall.EISDIR } - return errno + return err } diff --git a/internal/platform/unlink_test.go b/internal/platform/unlink_test.go index e74bca15..3e9d8d29 100644 --- a/internal/platform/unlink_test.go +++ b/internal/platform/unlink_test.go @@ -12,8 +12,8 @@ import ( 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) + err := Unlink(name) + require.EqualErrno(t, syscall.ENOENT, err) }) t.Run("target: dir", func(t *testing.T) { @@ -22,8 +22,8 @@ func TestUnlink(t *testing.T) { dir := path.Join(tmpDir, "dir") require.NoError(t, os.Mkdir(dir, 0o700)) - errno := Unlink(dir) - require.EqualErrno(t, syscall.EISDIR, errno) + err := Unlink(dir) + require.EqualErrno(t, syscall.EISDIR, err) require.NoError(t, os.Remove(dir)) }) @@ -40,8 +40,8 @@ func TestUnlink(t *testing.T) { require.NoError(t, os.Symlink("subdir", symlinkName)) // Unlinking the symlink should suceed. - errno := Unlink(symlinkName) - require.Zero(t, errno) + err := Unlink(symlinkName) + require.NoError(t, err) }) t.Run("file exists", func(t *testing.T) { @@ -51,7 +51,7 @@ func TestUnlink(t *testing.T) { require.NoError(t, os.WriteFile(name, []byte{}, 0o600)) - require.Zero(t, Unlink(name)) + require.NoError(t, Unlink(name)) _, err := os.Stat(name) require.Error(t, err) }) diff --git a/internal/platform/wrap_windows.go b/internal/platform/wrap_windows.go index b63c6deb..ba7d4c8f 100644 --- a/internal/platform/wrap_windows.go +++ b/internal/platform/wrap_windows.go @@ -88,7 +88,7 @@ func (w *windowsWrappedFile) maybeInitDir() error { if err := w.File.Close(); err != nil { return err } - newW, errno := OpenFile(w.path, w.flag, w.perm) + newW, err := openFile(w.path, w.flag, w.perm) if err != nil { return &fs.PathError{Op: "OpenFile", Path: w.path, Err: err} } @@ -120,7 +120,7 @@ func (w *windowsWrappedFile) requireFile(op string, readOnly, isDir bool) error // getFileType caches the file type as this cannot change on an open file. func (w *windowsWrappedFile) getFileType() (fs.FileMode, error) { if w.fileType == nil { - st, errno := StatFile(w.File) + st, err := StatFile(w.File) if err != nil { return 0, nil } diff --git a/internal/sys/fs.go b/internal/sys/fs.go index a26ef683..5f87d783 100644 --- a/internal/sys/fs.go +++ b/internal/sys/fs.go @@ -115,26 +115,26 @@ type lazyDir struct { // Stat implements fs.File func (r *lazyDir) Stat() (fs.FileInfo, error) { - if f, err := r.file(); err != 0 { + if f, err := r.file(); err != nil { return nil, err } else { return f.Stat() } } -func (r *lazyDir) file() (f fs.File, errno syscall.Errno) { +func (r *lazyDir) file() (f fs.File, err error) { if f = r.f; r.f != nil { return } - r.f, errno = r.fs.OpenFile(".", os.O_RDONLY, 0) + r.f, err = r.fs.OpenFile(".", os.O_RDONLY, 0) f = r.f return } // Read implements fs.File func (r *lazyDir) Read(p []byte) (n int, err error) { - if f, errno := r.file(); errno != 0 { - return 0, errno + if f, err := r.file(); err != nil { + return 0, err } else { return f.Read(p) } @@ -142,14 +142,10 @@ func (r *lazyDir) Read(p []byte) (n int, err error) { // Close implements fs.File func (r *lazyDir) Close() error { - f, errno := r.file() - switch errno { - case 0: - return f.Close() - case syscall.ENOENT: + if f, err := r.file(); err != nil { return nil - default: - return errno + } else { + return f.Close() } } @@ -203,19 +199,16 @@ func (f *FileEntry) CachedStat() (ino uint64, fileType fs.FileMode, err error) { // Stat returns the underlying stat of this file. func (f *FileEntry) Stat() (st platform.Stat_t, err error) { - var errno syscall.Errno if ld, ok := f.File.(*lazyDir); ok { var sf fs.File - if sf, errno = ld.file(); errno == 0 { - st, errno = platform.StatFile(sf) + if sf, err = ld.file(); err == nil { + st, err = platform.StatFile(sf) } } else { - st, errno = platform.StatFile(f.File) + st, err = platform.StatFile(f.File) } - if errno != 0 { - err = errno - } else { + if err == nil { f.cachedStat = &cachedStat{Ino: st.Ino, Type: st.Mode & fs.ModeType} } return @@ -332,9 +325,9 @@ func (c *FSContext) RootFS() sysfs.FS { // 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 sysfs.FS, path string, flag int, perm fs.FileMode) (uint32, syscall.Errno) { - if f, errno := fs.OpenFile(path, flag, perm); errno != 0 { - return 0, errno +func (c *FSContext) OpenFile(fs sysfs.FS, path string, flag int, perm fs.FileMode) (uint32, error) { + if f, err := fs.OpenFile(path, flag, perm); err != nil { + return 0, err } else { fe := &FileEntry{openPath: path, FS: fs, File: f, openFlag: flag, openPerm: perm} if path == "/" || path == "." { @@ -343,54 +336,54 @@ func (c *FSContext) OpenFile(fs sysfs.FS, path string, flag int, perm fs.FileMod fe.Name = path } newFD := c.openedFiles.Insert(fe) - return newFD, 0 + return newFD, nil } } // ReOpenDir re-opens the directory while keeping the same file descriptor. // TODO: this might not be necessary once we have our own File type. -func (c *FSContext) ReOpenDir(fd uint32) (*FileEntry, syscall.Errno) { +func (c *FSContext) ReOpenDir(fd uint32) (*FileEntry, error) { f, ok := c.openedFiles.Lookup(fd) if !ok { return nil, syscall.EBADF } else if _, ft, err := f.CachedStat(); err != nil { - return nil, platform.UnwrapOSError(err) + return nil, err } else if ft.Type() != fs.ModeDir { return nil, syscall.EISDIR } - if errno := c.reopen(f); errno != 0 { - return nil, errno + if err := c.reopen(f); err != nil { + return f, err } f.ReadDir.CountRead, f.ReadDir.Dirents = 0, nil - return f, 0 + return f, nil } -func (c *FSContext) reopen(f *FileEntry) syscall.Errno { +func (c *FSContext) reopen(f *FileEntry) error { if err := f.File.Close(); err != nil { - return platform.UnwrapOSError(err) + return err } // Re-opens with the same parameters as before. - opened, errno := f.FS.OpenFile(f.openPath, f.openFlag, f.openPerm) - if errno != 0 { - return errno + opened, err := f.FS.OpenFile(f.openPath, f.openFlag, f.openPerm) + if err != nil { + return err } // Reset the state. f.File = opened - return 0 + return nil } // ChangeOpenFlag changes the open flag of the given opened file pointed by `fd`. // Currently, this only supports the change of syscall.O_APPEND flag. -func (c *FSContext) ChangeOpenFlag(fd uint32, flag int) syscall.Errno { +func (c *FSContext) ChangeOpenFlag(fd uint32, flag int) error { f, ok := c.LookupFile(fd) if !ok { return syscall.EBADF } else if _, ft, err := f.CachedStat(); err != nil { - return platform.UnwrapOSError(err) + return err } else if ft.Type() == fs.ModeDir { return syscall.EISDIR } @@ -411,7 +404,10 @@ func (c *FSContext) ChangeOpenFlag(fd uint32, flag int) syscall.Errno { // // Therefore, here we re-open the file while keeping the file descriptor. // TODO: this might be improved once we have our own File type. - return c.reopen(f) + if err := c.reopen(f); err != nil { + return err + } + return nil } // LookupFile returns a file if it is in the table. @@ -421,7 +417,7 @@ func (c *FSContext) LookupFile(fd uint32) (*FileEntry, bool) { } // Renumber assigns the file pointed by the descriptor `from` to `to`. -func (c *FSContext) Renumber(from, to uint32) syscall.Errno { +func (c *FSContext) Renumber(from, to uint32) error { fromFile, ok := c.openedFiles.Lookup(from) if !ok { return syscall.EBADF @@ -443,11 +439,11 @@ func (c *FSContext) Renumber(from, to uint32) syscall.Errno { c.openedFiles.Delete(from) c.openedFiles.InsertAt(fromFile, to) - return 0 + return nil } // CloseFile returns any error closing the existing file. -func (c *FSContext) CloseFile(fd uint32) syscall.Errno { +func (c *FSContext) CloseFile(fd uint32) error { f, ok := c.openedFiles.Lookup(fd) if !ok { return syscall.EBADF @@ -457,7 +453,7 @@ func (c *FSContext) CloseFile(fd uint32) syscall.Errno { return syscall.ENOTSUP } c.openedFiles.Delete(fd) - return platform.UnwrapOSError(f.File.Close()) + return f.File.Close() } // Close implements api.Closer diff --git a/internal/sys/fs_test.go b/internal/sys/fs_test.go index 3e2d875d..b35de3bf 100644 --- a/internal/sys/fs_test.go +++ b/internal/sys/fs_test.go @@ -76,8 +76,8 @@ func TestNewFSContext(t *testing.T) { // Verify that each call to OpenFile returns a different file // descriptor. - f1, errno := fsc.OpenFile(preopenedDir.FS, preopenedDir.Name, 0, 0) - require.Zero(t, errno) + f1, err := fsc.OpenFile(preopenedDir.FS, preopenedDir.Name, 0, 0) + require.NoError(t, err) require.NotEqual(t, FdPreopen, f1) // Verify that file descriptors are reused. @@ -88,9 +88,9 @@ func TestNewFSContext(t *testing.T) { // test to ensure that our implementation properly reuses descriptor // numbers but if we were to change the reuse strategy, this test // would likely break and need to be updated. - require.Zero(t, fsc.CloseFile(f1)) - f2, errno := fsc.OpenFile(preopenedDir.FS, preopenedDir.Name, 0, 0) - require.Zero(t, errno) + require.NoError(t, fsc.CloseFile(f1)) + f2, err := fsc.OpenFile(preopenedDir.FS, preopenedDir.Name, 0, 0) + require.NoError(t, err) require.Equal(t, f1, f2) }) } @@ -105,14 +105,14 @@ func TestFSContext_CloseFile(t *testing.T) { require.NoError(t, err) defer fsc.Close(testCtx) - fdToClose, errno := fsc.OpenFile(testFS, "empty.txt", os.O_RDONLY, 0) - require.Zero(t, errno) + fdToClose, err := fsc.OpenFile(testFS, "empty.txt", os.O_RDONLY, 0) + require.NoError(t, err) - fdToKeep, errno := fsc.OpenFile(testFS, "test.txt", os.O_RDONLY, 0) - require.Zero(t, errno) + fdToKeep, err := fsc.OpenFile(testFS, "test.txt", os.O_RDONLY, 0) + require.NoError(t, err) // Close - require.Zero(t, fsc.CloseFile(fdToClose)) + require.NoError(t, fsc.CloseFile(fdToClose)) // Verify fdToClose is closed and removed from the opened FDs. _, ok := fsc.LookupFile(fdToClose) @@ -123,10 +123,10 @@ 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.Equal(t, syscall.EBADF, fsc.CloseFile(42)) // 42 is an arbitrary invalid FD }) t.Run("ENOTSUP for a preopen", func(t *testing.T) { - require.EqualErrno(t, syscall.ENOTSUP, fsc.CloseFile(FdPreopen)) // 42 is an arbitrary invalid FD + require.Equal(t, syscall.ENOTSUP, fsc.CloseFile(FdPreopen)) // 42 is an arbitrary invalid FD }) } @@ -187,8 +187,8 @@ func TestContext_Close(t *testing.T) { // Verify base case require.Equal(t, 1+FdPreopen, uint32(fsc.openedFiles.Len())) - _, errno := fsc.OpenFile(testFS, "foo", os.O_RDONLY, 0) - require.Zero(t, errno) + _, err = fsc.OpenFile(testFS, "foo", os.O_RDONLY, 0) + require.NoError(t, err) require.Equal(t, 2+FdPreopen, uint32(fsc.openedFiles.Len())) // Closing should not err. @@ -210,8 +210,8 @@ func TestContext_Close_Error(t *testing.T) { require.NoError(t, err) // open another file - _, errno := fsc.OpenFile(testFS, "foo", os.O_RDONLY, 0) - require.Zero(t, errno) + _, err = fsc.OpenFile(testFS, "foo", os.O_RDONLY, 0) + require.NoError(t, err) require.EqualError(t, fsc.Close(testCtx), "error closing") @@ -224,8 +224,8 @@ func TestFSContext_ReOpenDir(t *testing.T) { dirFs := sysfs.NewDirFS(tmpDir) const dirName = "dir" - errno := dirFs.Mkdir(dirName, 0o700) - require.Zero(t, errno) + err := dirFs.Mkdir(dirName, 0o700) + require.NoError(t, err) fsc, err := NewFSContext(nil, nil, nil, dirFs) require.NoError(t, err) @@ -234,8 +234,8 @@ func TestFSContext_ReOpenDir(t *testing.T) { }() t.Run("ok", func(t *testing.T) { - dirFd, errno := fsc.OpenFile(dirFs, dirName, os.O_RDONLY, 0o600) - require.Zero(t, errno) + dirFd, err := fsc.OpenFile(dirFs, dirName, os.O_RDONLY, 0o600) + require.NoError(t, err) ent, ok := fsc.LookupFile(dirFd) require.True(t, ok) @@ -244,25 +244,24 @@ func TestFSContext_ReOpenDir(t *testing.T) { ent.ReadDir = &ReadDir{Dirents: make([]*platform.Dirent, 10), CountRead: 12345} // Then reopen the same file descriptor. - ent, errno = fsc.ReOpenDir(dirFd) - require.Zero(t, errno) + ent, err = fsc.ReOpenDir(dirFd) + require.NoError(t, err) // Verify the read dir state has been reset. require.Equal(t, &ReadDir{}, ent.ReadDir) }) t.Run("non existing ", func(t *testing.T) { - _, errno = fsc.ReOpenDir(12345) - require.EqualErrno(t, syscall.EBADF, errno) + _, err = fsc.ReOpenDir(12345) + require.ErrorIs(t, err, syscall.EBADF) }) t.Run("not dir", func(t *testing.T) { const fileName = "dog" - fd, errno := fsc.OpenFile(dirFs, fileName, os.O_CREATE, 0o600) - require.Zero(t, errno) - - _, errno = fsc.ReOpenDir(fd) - require.EqualErrno(t, syscall.EISDIR, errno) + fd, err := fsc.OpenFile(dirFs, fileName, os.O_CREATE, 0o600) + require.NoError(t, err) + _, err = fsc.ReOpenDir(fd) + require.ErrorIs(t, err, syscall.EISDIR) }) } @@ -271,8 +270,8 @@ func TestFSContext_Renumber(t *testing.T) { dirFs := sysfs.NewDirFS(tmpDir) const dirName = "dir" - errno := dirFs.Mkdir(dirName, 0o700) - require.Zero(t, errno) + err := dirFs.Mkdir(dirName, 0o700) + require.NoError(t, err) c, err := NewFSContext(nil, nil, nil, dirFs) require.NoError(t, err) @@ -281,13 +280,13 @@ func TestFSContext_Renumber(t *testing.T) { }() for _, toFd := range []uint32{10, 100, 100} { - fromFd, errno := c.OpenFile(dirFs, dirName, os.O_RDONLY, 0) - require.Zero(t, errno) + fromFd, err := c.OpenFile(dirFs, dirName, os.O_RDONLY, 0) + require.NoError(t, err) prevDirFile, ok := c.LookupFile(fromFd) require.True(t, ok) - require.Zero(t, c.Renumber(fromFd, toFd)) + require.Equal(t, nil, c.Renumber(fromFd, toFd)) renumberedDirFile, ok := c.LookupFile(toFd) require.True(t, ok) @@ -323,28 +322,27 @@ func TestFSContext_ChangeOpenFlag(t *testing.T) { const fileName = "dir" require.NoError(t, os.WriteFile(path.Join(tmpDir, fileName), []byte("0123456789"), 0o600)) - c, errno := NewFSContext(nil, nil, nil, dirFs) - require.NoError(t, errno) + c, err := NewFSContext(nil, nil, nil, dirFs) + require.NoError(t, err) defer func() { require.NoError(t, c.Close(context.Background())) }() // Without APPEND. - fd, errno := c.OpenFile(dirFs, fileName, os.O_RDWR, 0o600) - require.Zero(t, errno) - + fd, err := c.OpenFile(dirFs, fileName, os.O_RDWR, 0o600) + require.NoError(t, err) f0, ok := c.openedFiles.Lookup(fd) require.True(t, ok) require.Equal(t, f0.openFlag&syscall.O_APPEND, 0) // Set the APPEND flag. - require.Zero(t, c.ChangeOpenFlag(fd, syscall.O_APPEND)) + require.NoError(t, c.ChangeOpenFlag(fd, syscall.O_APPEND)) f1, ok := c.openedFiles.Lookup(fd) require.True(t, ok) require.Equal(t, f1.openFlag&syscall.O_APPEND, syscall.O_APPEND) // Remove the APPEND flag. - require.Zero(t, c.ChangeOpenFlag(fd, 0)) + require.NoError(t, c.ChangeOpenFlag(fd, 0)) f2, ok := c.openedFiles.Lookup(fd) require.True(t, ok) require.Equal(t, f2.openFlag&syscall.O_APPEND, 0) diff --git a/internal/sys/sys_test.go b/internal/sys/sys_test.go index b8c4903e..b2dbab68 100644 --- a/internal/sys/sys_test.go +++ b/internal/sys/sys_test.go @@ -88,8 +88,8 @@ func TestFileEntry_cachedStat(t *testing.T) { dirFS := sysfs.NewDirFS(tmpDir) // get the expected inode - st, errno := platform.Stat(tmpDir) - require.Zero(t, errno) + st, err := platform.Stat(tmpDir) + require.NoError(t, err) tests := []struct { name string diff --git a/internal/sysfs/adapter.go b/internal/sysfs/adapter.go index 8ccd227b..cc5b253c 100644 --- a/internal/sysfs/adapter.go +++ b/internal/sysfs/adapter.go @@ -7,7 +7,6 @@ import ( "path" "runtime" "strings" - "syscall" "github.com/tetratelabs/wazero/internal/platform" ) @@ -45,14 +44,14 @@ func (a *adapter) Open(name string) (fs.File, error) { } // OpenFile implements FS.OpenFile -func (a *adapter) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, syscall.Errno) { +func (a *adapter) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, error) { path = cleanPath(path) f, err := a.fs.Open(path) return f, platform.UnwrapOSError(err) } // Stat implements FS.Stat -func (a *adapter) Stat(path string) (platform.Stat_t, syscall.Errno) { +func (a *adapter) Stat(path string) (platform.Stat_t, error) { name := cleanPath(path) f, err := a.fs.Open(name) if err != nil { @@ -63,7 +62,7 @@ func (a *adapter) Stat(path string) (platform.Stat_t, syscall.Errno) { } // Lstat implements FS.Lstat -func (a *adapter) Lstat(path string) (platform.Stat_t, syscall.Errno) { +func (a *adapter) Lstat(path string) (platform.Stat_t, error) { // At this time, we make the assumption that fs.FS instances do not support // symbolic links, therefore Lstat is the same as Stat. This is obviously // not true but until fs.FS has a solid story for how to handle symlinks we @@ -104,7 +103,7 @@ func fsOpen(f FS, name string) (fs.File, error) { } } - if f, err := f.OpenFile(name, os.O_RDONLY, 0); err != 0 { + if f, err := f.OpenFile(name, os.O_RDONLY, 0); err != nil { return nil, &fs.PathError{Op: "open", Path: name, Err: err} } else { return f, nil diff --git a/internal/sysfs/adapter_test.go b/internal/sysfs/adapter_test.go index 56384165..75aa7c7e 100644 --- a/internal/sysfs/adapter_test.go +++ b/internal/sysfs/adapter_test.go @@ -125,9 +125,8 @@ func TestAdapt_Lstat(t *testing.T) { fullPath := joinPath(tmpDir, path) linkPath := joinPath(tmpDir, path+"-link") require.NoError(t, os.Symlink(fullPath, linkPath)) - - _, errno := testFS.Lstat(filepath.Base(linkPath)) - require.Zero(t, errno) + _, err := testFS.Lstat(filepath.Base(linkPath)) + require.NoError(t, err) } } diff --git a/internal/sysfs/dirfs.go b/internal/sysfs/dirfs.go index 92c81b52..e209a8aa 100644 --- a/internal/sysfs/dirfs.go +++ b/internal/sysfs/dirfs.go @@ -41,95 +41,96 @@ func (d *dirFS) Open(name string) (fs.File, error) { } // OpenFile implements FS.OpenFile -func (d *dirFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, syscall.Errno) { +func (d *dirFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, error) { return platform.OpenFile(d.join(path), flag, perm) } // Lstat implements FS.Lstat -func (d *dirFS) Lstat(path string) (platform.Stat_t, syscall.Errno) { +func (d *dirFS) Lstat(path string) (platform.Stat_t, error) { return platform.Lstat(d.join(path)) } // Stat implements FS.Stat -func (d *dirFS) Stat(path string) (platform.Stat_t, syscall.Errno) { +func (d *dirFS) Stat(path string) (platform.Stat_t, error) { return platform.Stat(d.join(path)) } // Mkdir implements FS.Mkdir -func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno syscall.Errno) { - err := os.Mkdir(d.join(path), perm) - if errno = platform.UnwrapOSError(err); errno == syscall.ENOTDIR { - errno = syscall.ENOENT +func (d *dirFS) Mkdir(path string, perm fs.FileMode) (err error) { + err = os.Mkdir(d.join(path), perm) + if err = platform.UnwrapOSError(err); err == syscall.ENOTDIR { + err = syscall.ENOENT } return } // Chmod implements FS.Chmod -func (d *dirFS) Chmod(path string, perm fs.FileMode) syscall.Errno { +func (d *dirFS) Chmod(path string, perm fs.FileMode) error { err := os.Chmod(d.join(path), perm) return platform.UnwrapOSError(err) } // Chown implements FS.Chown -func (d *dirFS) Chown(path string, uid, gid int) syscall.Errno { +func (d *dirFS) Chown(path string, uid, gid int) error { return platform.Chown(d.join(path), uid, gid) } // Lchown implements FS.Lchown -func (d *dirFS) Lchown(path string, uid, gid int) syscall.Errno { +func (d *dirFS) Lchown(path string, uid, gid int) error { return platform.Lchown(d.join(path), uid, gid) } // Rename implements FS.Rename -func (d *dirFS) Rename(from, to string) syscall.Errno { +func (d *dirFS) Rename(from, to string) error { from, to = d.join(from), d.join(to) - return platform.Rename(from, to) + err := platform.Rename(from, to) + return platform.UnwrapOSError(err) } // Readlink implements FS.Readlink -func (d *dirFS) Readlink(path string) (string, syscall.Errno) { +func (d *dirFS) Readlink(path string) (string, error) { // 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 platform.ToPosixPath(dst), 0 + return platform.ToPosixPath(dst), nil } // Link implements FS.Link. -func (d *dirFS) Link(oldName, newName string) syscall.Errno { +func (d *dirFS) Link(oldName, newName string) error { err := os.Link(d.join(oldName), d.join(newName)) return platform.UnwrapOSError(err) } // Rmdir implements FS.Rmdir -func (d *dirFS) Rmdir(path string) syscall.Errno { +func (d *dirFS) Rmdir(path string) error { err := syscall.Rmdir(d.join(path)) return platform.UnwrapOSError(err) } // Unlink implements FS.Unlink -func (d *dirFS) Unlink(path string) (err syscall.Errno) { +func (d *dirFS) Unlink(path string) (err error) { return platform.Unlink(d.join(path)) } // Symlink implements FS.Symlink -func (d *dirFS) Symlink(oldName, link string) syscall.Errno { +func (d *dirFS) Symlink(oldName, link string) (err error) { // 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)) + err = os.Symlink(oldName, d.join(link)) return platform.UnwrapOSError(err) } // Utimens implements FS.Utimens -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) error { return platform.Utimens(d.join(path), times, symlinkFollow) } // Truncate implements FS.Truncate -func (d *dirFS) Truncate(path string, size int64) syscall.Errno { +func (d *dirFS) Truncate(path string, size int64) error { // Use os.Truncate as syscall.Truncate doesn't exist on Windows. err := os.Truncate(d.join(path), size) return platform.UnwrapOSError(err) diff --git a/internal/sysfs/dirfs_test.go b/internal/sysfs/dirfs_test.go index 76cd41e2..8da4fcb5 100644 --- a/internal/sysfs/dirfs_test.go +++ b/internal/sysfs/dirfs_test.go @@ -19,22 +19,22 @@ func TestNewDirFS(t *testing.T) { testFS := NewDirFS(".") // Guest can look up / - f, errno := testFS.OpenFile("/", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := testFS.OpenFile("/", os.O_RDONLY, 0) + require.NoError(t, err) require.NoError(t, f.Close()) 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) + _, err = testFS.OpenFile(".", os.O_RDONLY, 0) + require.EqualErrno(t, syscall.ENOENT, err) }) 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. testFS := NewDirFS(arg0) - d, errno := testFS.OpenFile(".", os.O_RDONLY, 0) - require.Zero(t, errno) - _, err := d.(fs.ReadDirFile).ReadDir(-1) + d, err := testFS.OpenFile(".", os.O_RDONLY, 0) + require.NoError(t, err) + _, err = d.(fs.ReadDirFile).ReadDir(-1) require.EqualErrno(t, syscall.ENOTDIR, platform.UnwrapOSError(err)) }) } @@ -66,7 +66,7 @@ func TestDirFS_Lstat(t *testing.T) { testFS := NewDirFS(tmpDir) for _, path := range []string{"animals.txt", "sub", "sub-link"} { - require.Zero(t, testFS.Symlink(path, path+"-link")) + require.NoError(t, testFS.Symlink(path, path+"-link")) } testLstat(t, testFS) @@ -80,11 +80,9 @@ func TestDirFS_MkDir(t *testing.T) { realPath := path.Join(tmpDir, name) t.Run("doesn't exist", func(t *testing.T) { - require.Zero(t, testFS.Mkdir(name, fs.ModeDir)) - + require.NoError(t, testFS.Mkdir(name, fs.ModeDir)) stat, err := os.Stat(realPath) require.NoError(t, err) - require.Equal(t, name, stat.Name()) require.True(t, stat.IsDir()) }) @@ -131,20 +129,19 @@ func testChmod(t *testing.T, testFS FS, path string) { requireMode(t, testFS, path, 0o444) // Test adding write, using 0o666 not 0o600 for read-back on windows. - require.Zero(t, testFS.Chmod(path, 0o666)) + require.NoError(t, testFS.Chmod(path, 0o666)) requireMode(t, testFS, path, 0o666) if runtime.GOOS != "windows" { // Test clearing group and world, setting owner read+execute. - require.Zero(t, testFS.Chmod(path, 0o500)) + require.NoError(t, testFS.Chmod(path, 0o500)) requireMode(t, testFS, path, 0o500) } } func requireMode(t *testing.T, testFS FS, path string, mode fs.FileMode) { - st, errno := testFS.Stat(path) - require.Zero(t, errno) - + st, err := testFS.Stat(path) + require.NoError(t, err) require.Equal(t, mode, st.Mode.Perm()) } @@ -168,17 +165,17 @@ func TestDirFS_Rename(t *testing.T) { file1 := "file1" file1Path := path.Join(tmpDir, file1) file1Contents := []byte{1} - errno := os.WriteFile(file1Path, file1Contents, 0o600) - require.NoError(t, errno) + err := os.WriteFile(file1Path, file1Contents, 0o600) + require.NoError(t, err) file2 := "file2" file2Path := path.Join(tmpDir, file2) - errno = testFS.Rename(file1, file2) - require.Zero(t, errno) + err = testFS.Rename(file1, file2) + require.NoError(t, err) // Show the prior path no longer exists - _, errno = os.Stat(file1Path) - require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(errno)) + _, err = os.Stat(file1Path) + require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(err)) s, err := os.Stat(file2Path) require.NoError(t, err) @@ -194,11 +191,11 @@ func TestDirFS_Rename(t *testing.T) { dir2 := "dir2" dir2Path := path.Join(tmpDir, dir2) - errrno := testFS.Rename(dir1, dir2) - require.Zero(t, errrno) + err := testFS.Rename(dir1, dir2) + require.NoError(t, err) // Show the prior path no longer exists - _, err := os.Stat(dir1Path) + _, err = os.Stat(dir1Path) require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(err)) s, err := os.Stat(dir2Path) @@ -221,8 +218,8 @@ func TestDirFS_Rename(t *testing.T) { require.NoError(t, err) require.NoError(t, f.Close()) - errno := testFS.Rename(dir1, dir2) - require.EqualErrno(t, syscall.ENOTDIR, errno) + err = testFS.Rename(dir1, dir2) + require.EqualErrno(t, syscall.ENOTDIR, err) }) t.Run("file to dir", func(t *testing.T) { tmpDir := t.TempDir() @@ -238,8 +235,8 @@ func TestDirFS_Rename(t *testing.T) { dir1Path := path.Join(tmpDir, dir1) require.NoError(t, os.Mkdir(dir1Path, 0o700)) - errno := testFS.Rename(file1, dir1) - require.EqualErrno(t, syscall.EISDIR, errno) + err = testFS.Rename(file1, dir1) + require.EqualErrno(t, syscall.EISDIR, err) }) // Similar to https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs/test.zig#L567-L582 @@ -262,8 +259,8 @@ func TestDirFS_Rename(t *testing.T) { dir2Path := path.Join(tmpDir, dir2) require.NoError(t, os.Mkdir(dir2Path, 0o700)) - errno := testFS.Rename(dir1, dir2) - require.Zero(t, errno) + err = testFS.Rename(dir1, dir2) + require.NoError(t, err) // Show the prior path no longer exists _, err = os.Stat(dir1Path) @@ -299,8 +296,8 @@ func TestDirFS_Rename(t *testing.T) { err = os.WriteFile(path.Join(dir2Path, "existing.txt"), []byte("any thing"), 0o600) require.NoError(t, err) - errno := testFS.Rename(dir1, dir2) - require.EqualErrno(t, syscall.ENOTEMPTY, errno) + err = testFS.Rename(dir1, dir2) + require.EqualErrno(t, syscall.ENOTEMPTY, err) }) t.Run("file to file", func(t *testing.T) { @@ -319,8 +316,8 @@ func TestDirFS_Rename(t *testing.T) { err = os.WriteFile(file2Path, file2Contents, 0o600) require.NoError(t, err) - errno := testFS.Rename(file1, file2) - require.Zero(t, errno) + err = testFS.Rename(file1, file2) + require.NoError(t, err) // Show the prior path no longer exists _, err = os.Stat(file1Path) @@ -339,8 +336,8 @@ func TestDirFS_Rename(t *testing.T) { dir1Path := path.Join(tmpDir, dir1) require.NoError(t, os.Mkdir(dir1Path, 0o700)) - errno := testFS.Rename(dir1, dir1) - require.Zero(t, errno) + err := testFS.Rename(dir1, dir1) + require.NoError(t, err) s, err := os.Stat(dir1Path) require.NoError(t, err) @@ -356,8 +353,8 @@ func TestDirFS_Rename(t *testing.T) { err := os.WriteFile(file1Path, file1Contents, 0o600) require.NoError(t, err) - errno := testFS.Rename(file1, file1) - require.Zero(t, errno) + err = testFS.Rename(file1, file1) + require.NoError(t, err) b, err := os.ReadFile(file1Path) require.NoError(t, err) @@ -407,8 +404,8 @@ func TestDirFS_Rmdir(t *testing.T) { require.NoError(t, os.Remove(fileInDir)) // After deletion, try removing directory. - errno := testFS.Rmdir(name) - require.Zero(t, errno) + err := testFS.Rmdir(name) + require.NoError(t, err) }) t.Run("dir empty", func(t *testing.T) { @@ -418,7 +415,7 @@ func TestDirFS_Rmdir(t *testing.T) { name := "rmdir" realPath := path.Join(tmpDir, name) require.NoError(t, os.Mkdir(realPath, 0o700)) - require.Zero(t, testFS.Rmdir(name)) + require.NoError(t, testFS.Rmdir(name)) _, err := os.Stat(realPath) require.Error(t, err) }) @@ -431,14 +428,14 @@ func TestDirFS_Rmdir(t *testing.T) { realPath := path.Join(tmpDir, name) require.NoError(t, os.Mkdir(realPath, 0o700)) - f, errno := testFS.OpenFile(name, platform.O_DIRECTORY, 0o700) - require.Zero(t, errno) + f, err := testFS.OpenFile(name, platform.O_DIRECTORY, 0o700) + require.NoError(t, err) defer func() { require.NoError(t, f.Close()) }() - require.Zero(t, testFS.Rmdir(name)) - _, err := os.Stat(realPath) + require.NoError(t, testFS.Rmdir(name)) + _, err = os.Stat(realPath) require.Error(t, err) }) @@ -494,11 +491,11 @@ func TestDirFS_Unlink(t *testing.T) { // Create a symlink to the subdirectory. const symlinkName = "symlink-to-dir" - require.Zero(t, testFS.Symlink("subdir", symlinkName)) + require.NoError(t, testFS.Symlink("subdir", symlinkName)) // Unlinking the symlink should suceed. err := testFS.Unlink(symlinkName) - require.Zero(t, err) + require.NoError(t, err) }) t.Run("file exists", func(t *testing.T) { @@ -510,8 +507,7 @@ func TestDirFS_Unlink(t *testing.T) { require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600)) - require.Zero(t, testFS.Unlink(name)) - + require.NoError(t, testFS.Unlink(name)) _, err := os.Stat(realPath) require.Error(t, err) }) @@ -614,15 +610,15 @@ func TestDirFS_Utimesns(t *testing.T) { testFS := NewDirFS(tmpDir) file := path.Join(tmpDir, "file") - errno := os.WriteFile(file, []byte{}, 0o700) - require.NoError(t, errno) + err := os.WriteFile(file, []byte{}, 0o700) + require.NoError(t, err) link := file + "-link" require.NoError(t, os.Symlink(file, link)) dir := path.Join(tmpDir, "dir") - errno = os.Mkdir(dir, 0o700) - require.NoError(t, errno) + err = os.Mkdir(dir, 0o700) + require.NoError(t, err) var path, statPath string switch fileType { @@ -642,18 +638,18 @@ func TestDirFS_Utimesns(t *testing.T) { panic(tc) } - oldSt, errno := testFS.Lstat(statPath) - require.Zero(t, errno) + oldSt, err := testFS.Lstat(statPath) + require.NoError(t, err) - errno = testFS.Utimens(path, tc.times, !symlinkNoFollow) + err = testFS.Utimens(path, tc.times, !symlinkNoFollow) if symlinkNoFollow && !platform.SupportsSymlinkNoFollow { - require.EqualErrno(t, syscall.ENOSYS, errno) + require.EqualErrno(t, syscall.ENOSYS, err) return } - require.Zero(t, errno) + require.NoError(t, err) - newSt, errno := testFS.Lstat(statPath) - require.Zero(t, errno) + newSt, err := testFS.Lstat(statPath) + require.NoError(t, err) if platform.CompilerSupported() { if tc.times != nil && tc.times[0].Nsec == platform.UTIME_OMIT { @@ -715,8 +711,8 @@ func TestDirFS_Stat(t *testing.T) { name := `e:xperi\ment.txt` require.NoError(t, os.WriteFile(path.Join(tmpDir, name), nil, 0o600)) - _, errno := testFS.Stat(name) - require.Zero(t, errno) + _, err := testFS.Stat(name) + require.NoError(t, err) }) } } @@ -762,8 +758,8 @@ func TestDirFS_Truncate(t *testing.T) { realPath := path.Join(tmpDir, name) require.NoError(t, os.WriteFile(realPath, content, 0o0600)) - errno := testFS.Truncate(name, tc.size) - require.Zero(t, errno) + err := testFS.Truncate(name, tc.size) + require.NoError(t, err) actual, err := os.ReadFile(realPath) require.NoError(t, err) @@ -825,12 +821,12 @@ func Test_fdReaddir_opened_file_written(t *testing.T) { testFS := NewDirFS(root) const readDirTarget = "dir" - errno := testFS.Mkdir(readDirTarget, 0o700) - require.Zero(t, errno) + err := testFS.Mkdir(readDirTarget, 0o700) + require.NoError(t, err) // Open the directory, before writing files! - dirFile, errno := testFS.OpenFile(readDirTarget, os.O_RDONLY, 0) - require.Zero(t, errno) + dirFile, err := testFS.OpenFile(readDirTarget, os.O_RDONLY, 0) + require.NoError(t, err) defer dirFile.Close() // Then write a file to the directory. @@ -857,12 +853,12 @@ 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.Zero(t, testFS.Link("sub/test.txt", "foo")) + require.ErrorIs(t, testFS.Link("cat", ""), syscall.ENOENT) + require.ErrorIs(t, testFS.Link("sub/test.txt", "sub/test.txt"), syscall.EEXIST) + require.ErrorIs(t, testFS.Link("sub/test.txt", "."), syscall.EEXIST) + require.ErrorIs(t, testFS.Link("sub/test.txt", ""), syscall.EEXIST) + require.ErrorIs(t, testFS.Link("sub/test.txt", "/"), syscall.EEXIST) + require.NoError(t, testFS.Link("sub/test.txt", "foo")) } func TestDirFS_Symlink(t *testing.T) { @@ -874,10 +870,10 @@ func TestDirFS_Symlink(t *testing.T) { testFS := NewDirFS(tmpDir) - require.EqualErrno(t, testFS.Symlink("sub/test.txt", "sub/test.txt"), syscall.EEXIST) + require.ErrorIs(t, testFS.Symlink("sub/test.txt", "sub/test.txt"), syscall.EEXIST) // Non-existing old name is allowed. - require.Zero(t, testFS.Symlink("non-existing", "aa")) - require.Zero(t, testFS.Symlink("sub/", "symlinked-subdir")) + require.NoError(t, testFS.Symlink("non-existing", "aa")) + require.NoError(t, testFS.Symlink("sub/", "symlinked-subdir")) st, err := os.Lstat(path.Join(tmpDir, "aa")) require.NoError(t, err) diff --git a/internal/sysfs/dirfs_unix_test.go b/internal/sysfs/dirfs_unix_test.go index a3bbea38..2cc7ef97 100644 --- a/internal/sysfs/dirfs_unix_test.go +++ b/internal/sysfs/dirfs_unix_test.go @@ -17,13 +17,11 @@ func TestDirFS_Chown(t *testing.T) { tmpDir := t.TempDir() testFS := NewDirFS(tmpDir) - require.Zero(t, testFS.Mkdir("dir", 0o0777)) - dirF, errno := testFS.OpenFile("dir", syscall.O_RDONLY, 0) - require.Zero(t, errno) - + require.NoError(t, testFS.Mkdir("dir", 0o0777)) + dirF, err := testFS.OpenFile("dir", syscall.O_RDONLY, 0) + require.NoError(t, err) dirStat, err := dirF.Stat() require.NoError(t, err) - dirSys := dirStat.Sys().(*syscall.Stat_t) // Similar to TestChown in os_unix_test.go, we can't expect to change @@ -33,12 +31,12 @@ func TestDirFS_Chown(t *testing.T) { require.NoError(t, err) t.Run("-1 parameters means leave alone", func(t *testing.T) { - require.Zero(t, testFS.Chown("dir", -1, -1)) + require.NoError(t, testFS.Chown("dir", -1, -1)) checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, dirSys.Gid) }) t.Run("change gid, but not uid", func(t *testing.T) { - require.Zero(t, testFS.Chown("dir", -1, gid)) + require.NoError(t, testFS.Chown("dir", -1, gid)) checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(gid)) }) @@ -47,11 +45,11 @@ func TestDirFS_Chown(t *testing.T) { g := g t.Run(fmt.Sprintf("change to gid %d", g), func(t *testing.T) { // Test using our Chown - require.Zero(t, testFS.Chown("dir", -1, g)) + require.NoError(t, testFS.Chown("dir", -1, g)) checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(g)) // Revert back with platform.ChownFile - require.Zero(t, platform.ChownFile(dirF, -1, gid)) + require.NoError(t, platform.ChownFile(dirF, -1, gid)) checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(gid)) }) } @@ -65,22 +63,18 @@ func TestDirFS_Lchown(t *testing.T) { tmpDir := t.TempDir() testFS := NewDirFS(tmpDir) - require.Zero(t, testFS.Mkdir("dir", 0o0777)) - dirF, errno := testFS.OpenFile("dir", syscall.O_RDONLY, 0) - require.Zero(t, errno) - + require.NoError(t, testFS.Mkdir("dir", 0o0777)) + dirF, err := testFS.OpenFile("dir", syscall.O_RDONLY, 0) + require.NoError(t, err) dirStat, err := dirF.Stat() require.NoError(t, err) - dirSys := dirStat.Sys().(*syscall.Stat_t) - require.Zero(t, testFS.Symlink("dir", "link")) - linkF, errno := testFS.OpenFile("link", syscall.O_RDONLY, 0) - require.Zero(t, errno) - + require.NoError(t, testFS.Symlink("dir", "link")) + linkF, err := testFS.OpenFile("link", syscall.O_RDONLY, 0) + require.NoError(t, err) linkStat, err := linkF.Stat() require.NoError(t, err) - linkSys := linkStat.Sys().(*syscall.Stat_t) // Similar to TestLchown in os_unix_test.go, we can't expect to change @@ -90,12 +84,12 @@ func TestDirFS_Lchown(t *testing.T) { require.NoError(t, err) t.Run("-1 parameters means leave alone", func(t *testing.T) { - require.Zero(t, testFS.Lchown("link", -1, -1)) + require.NoError(t, testFS.Lchown("link", -1, -1)) checkUidGid(t, path.Join(tmpDir, "link"), linkSys.Uid, linkSys.Gid) }) t.Run("change gid, but not uid", func(t *testing.T) { - require.Zero(t, testFS.Chown("dir", -1, gid)) + require.NoError(t, testFS.Chown("dir", -1, gid)) checkUidGid(t, path.Join(tmpDir, "link"), linkSys.Uid, uint32(gid)) // Make sure the target didn't change. checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, dirSys.Gid) @@ -106,7 +100,7 @@ func TestDirFS_Lchown(t *testing.T) { g := g t.Run(fmt.Sprintf("change to gid %d", g), func(t *testing.T) { // Test using our Lchown - require.Zero(t, testFS.Lchown("link", -1, g)) + require.NoError(t, testFS.Lchown("link", -1, g)) checkUidGid(t, path.Join(tmpDir, "link"), linkSys.Uid, uint32(g)) // Make sure the target didn't change. checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, dirSys.Gid) diff --git a/internal/sysfs/readfs.go b/internal/sysfs/readfs.go index bec2dc3d..1d18a9d0 100644 --- a/internal/sysfs/readfs.go +++ b/internal/sysfs/readfs.go @@ -37,7 +37,7 @@ func (r *readFS) Open(name string) (fs.File, error) { } // OpenFile implements FS.OpenFile -func (r *readFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, syscall.Errno) { +func (r *readFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, error) { // 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. @@ -63,11 +63,11 @@ func (r *readFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, sys default: // os.O_RDONLY so we are ok! } - f, errno := r.fs.OpenFile(path, flag, perm) - if errno != 0 { - return nil, errno + f, err := r.fs.OpenFile(path, flag, perm) + if err != nil { + return nil, err } - return maskForReads(f), 0 + return maskForReads(f), nil } // maskForReads masks the file with read-only interfaces used by wazero. @@ -141,71 +141,71 @@ func maskForReads(f fs.File) fs.File { } // Lstat implements FS.Lstat -func (r *readFS) Lstat(path string) (platform.Stat_t, syscall.Errno) { +func (r *readFS) Lstat(path string) (platform.Stat_t, error) { return r.fs.Lstat(path) } // Stat implements FS.Stat -func (r *readFS) Stat(path string) (platform.Stat_t, syscall.Errno) { +func (r *readFS) Stat(path string) (platform.Stat_t, error) { return r.fs.Stat(path) } // Readlink implements FS.Readlink -func (r *readFS) Readlink(path string) (dst string, err syscall.Errno) { +func (r *readFS) Readlink(path string) (dst string, err error) { return r.fs.Readlink(path) } // Mkdir implements FS.Mkdir -func (r *readFS) Mkdir(path string, perm fs.FileMode) syscall.Errno { +func (r *readFS) Mkdir(path string, perm fs.FileMode) error { return syscall.EROFS } // Chmod implements FS.Chmod -func (r *readFS) Chmod(path string, perm fs.FileMode) syscall.Errno { +func (r *readFS) Chmod(path string, perm fs.FileMode) error { return syscall.EROFS } // Chown implements FS.Chown -func (r *readFS) Chown(path string, uid, gid int) syscall.Errno { +func (r *readFS) Chown(path string, uid, gid int) error { return syscall.EROFS } // Lchown implements FS.Lchown -func (r *readFS) Lchown(path string, uid, gid int) syscall.Errno { +func (r *readFS) Lchown(path string, uid, gid int) error { return syscall.EROFS } // Rename implements FS.Rename -func (r *readFS) Rename(from, to string) syscall.Errno { +func (r *readFS) Rename(from, to string) error { return syscall.EROFS } // Rmdir implements FS.Rmdir -func (r *readFS) Rmdir(path string) syscall.Errno { +func (r *readFS) Rmdir(path string) error { return syscall.EROFS } // Link implements FS.Link -func (r *readFS) Link(_, _ string) syscall.Errno { +func (r *readFS) Link(_, _ string) error { return syscall.EROFS } // Symlink implements FS.Symlink -func (r *readFS) Symlink(_, _ string) syscall.Errno { +func (r *readFS) Symlink(_, _ string) error { return syscall.EROFS } // Unlink implements FS.Unlink -func (r *readFS) Unlink(path string) syscall.Errno { +func (r *readFS) Unlink(path string) error { return syscall.EROFS } // Utimens implements FS.Utimens -func (r *readFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { +func (r *readFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) error { return syscall.EROFS } // Truncate implements FS.Truncate -func (r *readFS) Truncate(string, int64) syscall.Errno { +func (r *readFS) Truncate(string, int64) error { return syscall.EROFS } diff --git a/internal/sysfs/readfs_test.go b/internal/sysfs/readfs_test.go index e053f335..5c9b5b68 100644 --- a/internal/sysfs/readfs_test.go +++ b/internal/sysfs/readfs_test.go @@ -41,7 +41,7 @@ func TestReadFS_Lstat(t *testing.T) { writeable := NewDirFS(tmpDir) for _, path := range []string{"animals.txt", "sub", "sub-link"} { - require.Zero(t, writeable.Symlink(path, path+"-link")) + require.NoError(t, writeable.Symlink(path, path+"-link")) } testFS := NewReadFS(writeable) diff --git a/internal/sysfs/rootfs.go b/internal/sysfs/rootfs.go index cdac5e76..3a9ff181 100644 --- a/internal/sysfs/rootfs.go +++ b/internal/sysfs/rootfs.go @@ -123,11 +123,11 @@ func (c *CompositeFS) Open(name string) (fs.File, error) { } // OpenFile implements FS.OpenFile -func (c *CompositeFS) OpenFile(path string, flag int, perm fs.FileMode) (f fs.File, err syscall.Errno) { +func (c *CompositeFS) OpenFile(path string, flag int, perm fs.FileMode) (f fs.File, err error) { matchIndex, relativePath := c.chooseFS(path) f, err = c.fs[matchIndex].OpenFile(relativePath, flag, perm) - if err != 0 { + if err != nil { return } @@ -192,7 +192,7 @@ func (d *openRootDir) readDir() (err error) { } func (d *openRootDir) rootEntry(name string, fsI int) (fs.DirEntry, error) { - if st, err := d.c.fs[fsI].Stat("."); err != 0 { + if st, err := d.c.fs[fsI].Stat("."); err != nil { return nil, err } else { return &dirInfo{name, st}, nil @@ -246,43 +246,43 @@ func (d *openRootDir) ReadDir(count int) ([]fs.DirEntry, error) { } // Lstat implements FS.Lstat -func (c *CompositeFS) Lstat(path string) (platform.Stat_t, syscall.Errno) { +func (c *CompositeFS) Lstat(path string) (platform.Stat_t, error) { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Lstat(relativePath) } // Stat implements FS.Stat -func (c *CompositeFS) Stat(path string) (platform.Stat_t, syscall.Errno) { +func (c *CompositeFS) Stat(path string) (platform.Stat_t, error) { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Stat(relativePath) } // Mkdir implements FS.Mkdir -func (c *CompositeFS) Mkdir(path string, perm fs.FileMode) syscall.Errno { +func (c *CompositeFS) Mkdir(path string, perm fs.FileMode) error { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Mkdir(relativePath, perm) } // Chmod implements FS.Chmod -func (c *CompositeFS) Chmod(path string, perm fs.FileMode) syscall.Errno { +func (c *CompositeFS) Chmod(path string, perm fs.FileMode) error { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Chmod(relativePath, perm) } // Chown implements FS.Chown -func (c *CompositeFS) Chown(path string, uid, gid int) syscall.Errno { +func (c *CompositeFS) Chown(path string, uid, gid int) error { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Chown(relativePath, uid, gid) } // Lchown implements FS.Lchown -func (c *CompositeFS) Lchown(path string, uid, gid int) syscall.Errno { +func (c *CompositeFS) Lchown(path string, uid, gid int) error { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Lchown(relativePath, uid, gid) } // Rename implements FS.Rename -func (c *CompositeFS) Rename(from, to string) syscall.Errno { +func (c *CompositeFS) Rename(from, to string) error { fromFS, fromPath := c.chooseFS(from) toFS, toPath := c.chooseFS(to) if fromFS != toFS { @@ -292,13 +292,13 @@ func (c *CompositeFS) Rename(from, to string) syscall.Errno { } // Readlink implements FS.Readlink -func (c *CompositeFS) Readlink(path string) (string, syscall.Errno) { +func (c *CompositeFS) Readlink(path string) (string, error) { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Readlink(relativePath) } // Link implements FS.Link. -func (c *CompositeFS) Link(oldName, newName string) syscall.Errno { +func (c *CompositeFS) Link(oldName, newName string) error { fromFS, oldNamePath := c.chooseFS(oldName) toFS, newNamePath := c.chooseFS(newName) if fromFS != toFS { @@ -308,13 +308,13 @@ func (c *CompositeFS) Link(oldName, newName string) syscall.Errno { } // Utimens implements FS.Utimens -func (c *CompositeFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { +func (c *CompositeFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) error { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Utimens(relativePath, times, symlinkFollow) } // Symlink implements FS.Symlink -func (c *CompositeFS) Symlink(oldName, link string) (err syscall.Errno) { +func (c *CompositeFS) Symlink(oldName, link string) (err error) { fromFS, oldNamePath := c.chooseFS(oldName) toFS, linkPath := c.chooseFS(link) if fromFS != toFS { @@ -324,19 +324,19 @@ func (c *CompositeFS) Symlink(oldName, link string) (err syscall.Errno) { } // Truncate implements FS.Truncate -func (c *CompositeFS) Truncate(path string, size int64) syscall.Errno { +func (c *CompositeFS) Truncate(path string, size int64) error { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Truncate(relativePath, size) } // Rmdir implements FS.Rmdir -func (c *CompositeFS) Rmdir(path string) syscall.Errno { +func (c *CompositeFS) Rmdir(path string) error { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Rmdir(relativePath) } // Unlink implements FS.Unlink -func (c *CompositeFS) Unlink(path string) syscall.Errno { +func (c *CompositeFS) Unlink(path string) error { matchIndex, relativePath := c.chooseFS(path) return c.fs[matchIndex].Unlink(relativePath) } @@ -479,10 +479,10 @@ loop: type fakeRootFS struct{ UnimplementedFS } // OpenFile implements FS.OpenFile -func (*fakeRootFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, syscall.Errno) { +func (*fakeRootFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, error) { switch path { case ".", "/", "": - return fakeRootDir{}, 0 + return fakeRootDir{}, nil } return nil, syscall.ENOENT } diff --git a/internal/sysfs/rootfs_test.go b/internal/sysfs/rootfs_test.go index 49074111..744d00b9 100644 --- a/internal/sysfs/rootfs_test.go +++ b/internal/sysfs/rootfs_test.go @@ -48,14 +48,13 @@ func TestNewRootFS(t *testing.T) { require.Equal(t, "[.:/tmp]", rootFS.String()) // Guest can look up /tmp - f, errno := rootFS.OpenFile("/tmp", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := rootFS.OpenFile("/tmp", os.O_RDONLY, 0) + require.NoError(t, err) require.NoError(t, f.Close()) // Guest can look up / and see "/tmp" in it - f, errno = rootFS.OpenFile("/", os.O_RDONLY, 0) - require.Zero(t, errno) - + f, err = rootFS.OpenFile("/", os.O_RDONLY, 0) + require.NoError(t, err) dirents, err := f.(fs.ReadDirFile).ReadDir(-1) require.NoError(t, err) require.Equal(t, 1, len(dirents)) @@ -96,10 +95,9 @@ func TestNewRootFS(t *testing.T) { require.NotEqual(t, testFS2, rootFS) t.Run("last wins", func(t *testing.T) { - f, errno := rootFS.OpenFile("/tmp/a", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := rootFS.OpenFile("/tmp/a", os.O_RDONLY, 0) + require.NoError(t, err) defer f.Close() - b, err := io.ReadAll(f) require.NoError(t, err) require.Equal(t, []byte{2}, b) @@ -107,8 +105,8 @@ func TestNewRootFS(t *testing.T) { // This test is covered by fstest.TestFS, but doing again here t.Run("root includes prefix mount", func(t *testing.T) { - f, errno := rootFS.OpenFile(".", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := rootFS.OpenFile(".", os.O_RDONLY, 0) + require.NoError(t, err) defer f.Close() require.Equal(t, []string{"a", "tmp"}, readDirNames(t, f)) @@ -117,8 +115,8 @@ func TestNewRootFS(t *testing.T) { } func readDirNames(t *testing.T, f fs.File) []string { - names, errno := platform.Readdirnames(f, -1) - require.Zero(t, errno) + names, err := platform.Readdirnames(f, -1) + require.NoError(t, err) sort.Strings(names) return names } @@ -281,8 +279,8 @@ func TestRootFS_examples(t *testing.T) { require.NoError(t, err) for _, p := range tc.expected { - f, errno := root.OpenFile(p, os.O_RDONLY, 0) - require.Zero(t, errno, p) + f, err := root.OpenFile(p, os.O_RDONLY, 0) + require.NoError(t, err, p) require.NoError(t, f.Close(), p) } diff --git a/internal/sysfs/sysfs.go b/internal/sysfs/sysfs.go index 41869723..e677c120 100644 --- a/internal/sysfs/sysfs.go +++ b/internal/sysfs/sysfs.go @@ -19,20 +19,7 @@ import ( // Implementations should embed UnimplementedFS for forward compatability. Any // unsupported method or parameter should return syscall.ENOSYS. // -// # Errors -// -// All methods that can return an error return a syscall.Errno, which is zero -// on success. -// -// Restricting to syscall.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. -// -// # Notes -// -// A writable filesystem abstraction is not yet implemented as of Go 1.20. See -// https://github.com/golang/go/issues/45757 +// See https://github.com/golang/go/issues/45757 type FS interface { // String should return a human-readable format of the filesystem // @@ -44,8 +31,7 @@ type FS interface { String() string // OpenFile is similar to os.OpenFile, except the path is relative to this - // file system, and syscall.Errno are returned instead of an os.PathError. - // A zero syscall.Errno is success. + // file system, and syscall.Errno are returned instead of a os.PathError. // // # Errors // @@ -71,7 +57,7 @@ type FS interface { // - flag are the same as OpenFile, for example, os.O_CREATE. // - Implications of permissions when os.O_CREATE are described in Chmod // notes. - OpenFile(path string, flag int, perm fs.FileMode) (fs.File, syscall.Errno) + OpenFile(path string, flag int, perm fs.FileMode) (fs.File, error) // ^^ TODO: Consider syscall.Open, though this implies defining and // coercing flags and perms similar to what is done in os.OpenFile. @@ -89,7 +75,7 @@ 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) (platform.Stat_t, syscall.Errno) + Lstat(path string) (platform.Stat_t, error) // Stat is similar to syscall.Stat, except the path is relative to this // file system. @@ -105,11 +91,10 @@ 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) (platform.Stat_t, syscall.Errno) + Stat(path string) (platform.Stat_t, error) // Mkdir is similar to os.Mkdir, except the path is relative to this file - // system, and syscall.Errno are returned instead of a os.PathError. A zero - // syscall.Errno is success. + // system, and syscall.Errno are returned instead of a os.PathError. // // # Errors // @@ -121,13 +106,12 @@ type FS interface { // # Notes // // - Implications of permissions are described in Chmod notes. - Mkdir(path string, perm fs.FileMode) syscall.Errno + Mkdir(path string, perm fs.FileMode) error // ^^ TODO: Consider syscall.Mkdir, though this implies defining and // coercing flags and perms similar to what is done in os.Mkdir. // Chmod is similar to os.Chmod, except the path is relative to this file - // system, and syscall.Errno are returned instead of a os.PathError. A zero - // syscall.Errno is success. + // system, and syscall.Errno are returned instead of a os.PathError. // // # Errors // @@ -140,13 +124,12 @@ 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) error // ^^ TODO: Consider syscall.Chmod, though this implies defining and // coercing flags and perms similar to what is done in os.Chmod. // Chown is like os.Chown except the path is relative to this file // system, and syscall.Errno are returned instead of an os.PathError. - // A zero syscall.Errno is success. // // # Errors // @@ -158,11 +141,11 @@ type FS interface { // // - Windows will always return syscall.ENOSYS // - This is similar to https://linux.die.net/man/3/chown - Chown(path string, uid, gid int) syscall.Errno + Chown(path string, uid, gid int) error // Lchown is like os.Lchown except the path is relative to this file - // system, and syscall.Errno are returned instead of an os.PathError. A - // zero syscall.Errno is success. + // system, and syscall.Errno are returned instead of a os.PathError. + // See https://linux.die.net/man/3/lchown // // # Errors // @@ -174,7 +157,7 @@ type FS interface { // // - Windows will always return syscall.ENOSYS // - This is similar to https://linux.die.net/man/3/lchown - Lchown(path string, uid, gid int) syscall.Errno + Lchown(path string, uid, gid int) error // Rename is similar to syscall.Rename, except the path is relative to this // file system. @@ -192,7 +175,7 @@ type FS interface { // # Notes // // - Windows doesn't let you overwrite an existing directory. - Rename(from, to string) syscall.Errno + Rename(from, to string) error // Rmdir is similar to syscall.Rmdir, except the path is relative to this // file system. @@ -208,7 +191,7 @@ type FS interface { // # Notes // // - As of Go 1.19, Windows maps syscall.ENOTDIR to syscall.ENOENT. - Rmdir(path string) syscall.Errno + Rmdir(path string) error // Unlink is similar to syscall.Unlink, except the path is relative to this // file system. @@ -225,7 +208,7 @@ 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) error // Link is similar to syscall.Link, except the path is relative to this // file system. This creates "hard" link from oldPath to newPath, in @@ -237,7 +220,7 @@ type FS interface { // - syscall.EPERM: `oldPath` is invalid. // - syscall.ENOENT: `oldPath` doesn't exist. // - syscall.EISDIR: `newPath` exists, but is a directory. - Link(oldPath, newPath string) syscall.Errno + Link(oldPath, newPath string) error // Symlink is similar to syscall.Symlink, except the `oldPath` is relative // to this file system. This creates "soft" link from oldPath to newPath, @@ -259,7 +242,7 @@ type FS interface { // - Symlinks in Windows requires `SeCreateSymbolicLinkPrivilege`. // Otherwise, syscall.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) error // Readlink is similar to syscall.Readlink, except the path is relative to // this file system. @@ -273,7 +256,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, error) // Truncate is similar to syscall.Truncate, except the path is relative to // this file system. @@ -284,7 +267,7 @@ type FS interface { // - syscall.EINVAL: `path` is invalid or size is negative. // - syscall.ENOENT: `path` doesn't exist // - syscall.EACCES: `path` doesn't have write access. - Truncate(path string, size int64) syscall.Errno + Truncate(path string, size int64) error // Utimens set file access and modification times on a path relative to // this file system, at nanosecond precision. @@ -313,7 +296,7 @@ type FS interface { // values UTIME_NOW or UTIME_NOW. // - This is like `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) error } // ReaderAtOffset gets an io.Reader from a fs.File that reads from an offset, @@ -334,7 +317,7 @@ func ReaderAtOffset(f fs.File, offset int64) io.Reader { } // FileDatasync is like syscall.Fdatasync except that's only defined in linux. -func FileDatasync(f fs.File) (err syscall.Errno) { +func FileDatasync(f fs.File) (err error) { return platform.Fdatasync(f) } diff --git a/internal/sysfs/sysfs_test.go b/internal/sysfs/sysfs_test.go index 865c94ee..aef35713 100644 --- a/internal/sysfs/sysfs_test.go +++ b/internal/sysfs/sysfs_test.go @@ -24,8 +24,8 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS FS) { err := os.WriteFile(realPath, []byte{}, 0o600) require.NoError(t, err) - f, errno := testFS.OpenFile(file, os.O_RDWR, 0) - require.Zero(t, errno) + f, err := testFS.OpenFile(file, os.O_RDWR, 0) + require.NoError(t, err) defer f.Close() w, ok := f.(io.Writer) @@ -46,8 +46,8 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS FS) { // re-create as read-only, using 0444 to allow read-back on windows. require.NoError(t, os.Remove(realPath)) - f, errno = testFS.OpenFile(file, os.O_RDONLY|os.O_CREATE, 0o444) - require.Zero(t, errno) + f, err = testFS.OpenFile(file, os.O_RDONLY|os.O_CREATE, 0o444) + require.NoError(t, err) defer f.Close() w, ok = f.(io.Writer) @@ -67,27 +67,27 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS FS) { // from os.TestDirFSPathsValid if runtime.GOOS != "windows" { t.Run("strange name", func(t *testing.T) { - f, errno = testFS.OpenFile(`e:xperi\ment.txt`, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) - require.Zero(t, errno) + f, err := testFS.OpenFile(`e:xperi\ment.txt`, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) + require.NoError(t, err) defer f.Close() - _, errno = platform.StatFile(f) - require.Zero(t, errno) + _, err = platform.StatFile(f) + require.NoError(t, err) }) } } func testOpen_Read(t *testing.T, testFS FS, expectIno bool) { t.Run("doesn't exist", func(t *testing.T) { - _, errno := testFS.OpenFile("nope", os.O_RDONLY, 0) + _, err := 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, syscall.ENOENT, err) }) t.Run("readdir . opens root", func(t *testing.T) { - f, errno := testFS.OpenFile(".", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := testFS.OpenFile(".", os.O_RDONLY, 0) + require.NoError(t, err) defer f.Close() dirents := requireReaddir(t, f, -1, expectIno) @@ -102,8 +102,8 @@ func testOpen_Read(t *testing.T, testFS FS, expectIno bool) { }) t.Run("readdirnames . opens root", func(t *testing.T) { - f, errno := testFS.OpenFile(".", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := testFS.OpenFile(".", os.O_RDONLY, 0) + require.NoError(t, err) defer f.Close() names := requireReaddirnames(t, f, -1) @@ -111,8 +111,8 @@ func testOpen_Read(t *testing.T, testFS FS, expectIno bool) { }) t.Run("readdir empty", func(t *testing.T) { - f, errno := testFS.OpenFile("emptydir", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := testFS.OpenFile("emptydir", os.O_RDONLY, 0) + require.NoError(t, err) defer f.Close() entries := requireReaddir(t, f, -1, expectIno) @@ -120,8 +120,8 @@ func testOpen_Read(t *testing.T, testFS FS, expectIno bool) { }) t.Run("readdirnames empty", func(t *testing.T) { - f, errno := testFS.OpenFile("emptydir", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := testFS.OpenFile("emptydir", os.O_RDONLY, 0) + require.NoError(t, err) defer f.Close() names := requireReaddirnames(t, f, -1) @@ -129,21 +129,21 @@ func testOpen_Read(t *testing.T, testFS FS, expectIno bool) { }) t.Run("readdir partial", func(t *testing.T) { - dirF, errno := testFS.OpenFile("dir", os.O_RDONLY, 0) - require.Zero(t, errno) + dirF, err := testFS.OpenFile("dir", os.O_RDONLY, 0) + require.NoError(t, err) defer dirF.Close() - dirents1, errno := platform.Readdir(dirF, 1) - require.Zero(t, errno) + dirents1, err := platform.Readdir(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(dirents1)) - dirents2, errno := platform.Readdir(dirF, 1) - require.Zero(t, errno) + dirents2, err := platform.Readdir(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(dirents2)) // read exactly the last entry - dirents3, errno := platform.Readdir(dirF, 1) - require.Zero(t, errno) + dirents3, err := platform.Readdir(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(dirents3)) dirents := []*platform.Dirent{dirents1[0], dirents2[0], dirents3[0]} @@ -158,28 +158,28 @@ func testOpen_Read(t *testing.T, testFS FS, expectIno bool) { }, dirents) // no error reading an exhausted directory - _, errno = platform.Readdir(dirF, 1) - require.Zero(t, errno) + _, err = platform.Readdir(dirF, 1) + require.NoError(t, err) }) // TODO: consolidate duplicated tests from platform once we have our own // file type t.Run("readdirnames partial", func(t *testing.T) { - dirF, errno := testFS.OpenFile("dir", os.O_RDONLY, 0) - require.Zero(t, errno) + dirF, err := testFS.OpenFile("dir", os.O_RDONLY, 0) + require.NoError(t, err) defer dirF.Close() - names1, errno := platform.Readdirnames(dirF, 1) - require.Zero(t, errno) + names1, err := platform.Readdirnames(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(names1)) - names2, errno := platform.Readdirnames(dirF, 1) - require.Zero(t, errno) + names2, err := platform.Readdirnames(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(names2)) // read exactly the last entry - names3, errno := platform.Readdirnames(dirF, 1) - require.Zero(t, errno) + names3, err := platform.Readdirnames(dirF, 1) + require.NoError(t, err) require.Equal(t, 1, len(names3)) names := []string{names1[0], names2[0], names3[0]} @@ -188,13 +188,13 @@ func testOpen_Read(t *testing.T, testFS FS, expectIno bool) { require.Equal(t, []string{"-", "a-", "ab-"}, names) // no error reading an exhausted directory - _, errno = platform.Readdirnames(dirF, 1) - require.Zero(t, errno) + _, err = platform.Readdirnames(dirF, 1) + require.NoError(t, err) }) t.Run("file exists", func(t *testing.T) { - f, errno := testFS.OpenFile("animals.txt", os.O_RDONLY, 0) - require.Zero(t, errno) + f, err := testFS.OpenFile("animals.txt", os.O_RDONLY, 0) + require.NoError(t, err) defer f.Close() fileContents := []byte(`bear @@ -230,18 +230,18 @@ human // currently supported in WASI or GOOS=js const O_NOATIME = 0x40000 - f, errno := testFS.OpenFile("animals.txt", os.O_RDONLY|O_NOATIME, 0) - require.Zero(t, errno) + f, err := testFS.OpenFile("animals.txt", os.O_RDONLY|O_NOATIME, 0) + require.NoError(t, err) defer f.Close() }) t.Run("writing to a read-only file is EBADF", func(t *testing.T) { - f, errno := testFS.OpenFile("animals.txt", os.O_RDONLY, 0) + f, err := testFS.OpenFile("animals.txt", os.O_RDONLY, 0) defer require.NoError(t, f.Close()) - require.Zero(t, errno) + require.NoError(t, err) if w, ok := f.(io.Writer); ok { - _, err := w.Write([]byte{1, 2, 3, 4}) + _, err = w.Write([]byte{1, 2, 3, 4}) require.EqualErrno(t, syscall.EBADF, platform.UnwrapOSError(err)) } else { t.Skip("not an io.Writer") @@ -249,12 +249,12 @@ human }) t.Run("writing to a directory is EBADF", func(t *testing.T) { - f, errno := testFS.OpenFile("sub", os.O_RDONLY, 0) + f, err := testFS.OpenFile("sub", os.O_RDONLY, 0) defer require.NoError(t, f.Close()) - require.Zero(t, errno) + require.NoError(t, err) if w, ok := f.(io.Writer); ok { - _, err := w.Write([]byte{1, 2, 3, 4}) + _, err = w.Write([]byte{1, 2, 3, 4}) require.EqualErrno(t, syscall.EBADF, platform.UnwrapOSError(err)) } else { t.Skip("not an io.Writer") @@ -263,16 +263,16 @@ human } func testLstat(t *testing.T, testFS FS) { - _, errno := testFS.Lstat("cat") - require.EqualErrno(t, syscall.ENOENT, errno) - _, errno = testFS.Lstat("sub/cat") - require.EqualErrno(t, syscall.ENOENT, errno) + _, err := testFS.Lstat("cat") + require.EqualErrno(t, syscall.ENOENT, err) + _, err = testFS.Lstat("sub/cat") + require.EqualErrno(t, syscall.ENOENT, err) var st platform.Stat_t t.Run("dir", func(t *testing.T) { - st, errno = testFS.Lstat(".") - require.Zero(t, errno) + st, err = testFS.Lstat(".") + require.NoError(t, err) require.True(t, st.Mode.IsDir()) require.NotEqual(t, uint64(0), st.Ino) }) @@ -280,9 +280,8 @@ func testLstat(t *testing.T, testFS FS) { var stFile platform.Stat_t t.Run("file", func(t *testing.T) { - stFile, errno = testFS.Lstat("animals.txt") - require.Zero(t, errno) - + stFile, err = testFS.Lstat("animals.txt") + require.NoError(t, err) require.Zero(t, stFile.Mode.Type()) require.Equal(t, int64(30), stFile.Size) require.NotEqual(t, uint64(0), st.Ino) @@ -294,9 +293,8 @@ func testLstat(t *testing.T, testFS FS) { var stSubdir platform.Stat_t t.Run("subdir", func(t *testing.T) { - stSubdir, errno = testFS.Lstat("sub") - require.Zero(t, errno) - + stSubdir, err = testFS.Lstat("sub") + require.NoError(t, err) require.True(t, stSubdir.Mode.IsDir()) require.NotEqual(t, uint64(0), st.Ino) }) @@ -307,8 +305,8 @@ func testLstat(t *testing.T, testFS FS) { t.Run("link to dir link", func(t *testing.T) { pathLink := "sub-link" - stLink, errno := testFS.Lstat(pathLink) - require.Zero(t, errno) + stLink, err := testFS.Lstat(pathLink) + require.NoError(t, err) requireLinkStat(t, testFS, pathLink, stLink) }) @@ -316,9 +314,8 @@ func testLstat(t *testing.T, testFS FS) { func requireLinkStat(t *testing.T, testFS FS, path string, stat platform.Stat_t) { link := path + "-link" - stLink, errno := testFS.Lstat(link) - require.Zero(t, errno) - + stLink, err := testFS.Lstat(link) + require.NoError(t, err) require.NotEqual(t, stat.Ino, stLink.Ino) // inodes are not equal require.Equal(t, fs.ModeSymlink, stLink.Mode.Type()) // From https://linux.die.net/man/2/lstat: @@ -332,21 +329,19 @@ func requireLinkStat(t *testing.T, testFS FS, path string, stat platform.Stat_t) } func testStat(t *testing.T, testFS FS) { - _, errno := testFS.Stat("cat") - require.EqualErrno(t, syscall.ENOENT, errno) - _, errno = testFS.Stat("sub/cat") - require.EqualErrno(t, syscall.ENOENT, errno) - - st, errno := testFS.Stat("sub/test.txt") - require.Zero(t, errno) + _, err := testFS.Stat("cat") + require.EqualErrno(t, syscall.ENOENT, err) + _, err = testFS.Stat("sub/cat") + require.EqualErrno(t, syscall.ENOENT, err) + st, err := testFS.Stat("sub/test.txt") + require.NoError(t, err) require.False(t, st.Mode.IsDir()) require.NotEqual(t, uint64(0), st.Dev) require.NotEqual(t, uint64(0), st.Ino) - st, errno = testFS.Stat("sub") - require.Zero(t, errno) - + st, err = testFS.Stat("sub") + require.NoError(t, err) require.True(t, st.Mode.IsDir()) // windows before go 1.20 has trouble reading the inode information on directories. if runtime.GOOS != "windows" || platform.IsGo120 { @@ -358,9 +353,8 @@ func testStat(t *testing.T, testFS FS) { // requireReaddir ensures the input file is a directory, and returns its // entries. func requireReaddir(t *testing.T, f fs.File, n int, expectIno bool) []*platform.Dirent { - entries, errno := platform.Readdir(f, n) - require.Zero(t, errno) - + entries, err := platform.Readdir(f, n) + require.NoError(t, err) sort.Slice(entries, func(i, j int) bool { return entries[i].Name < entries[j].Name }) if _, ok := f.(*openRootDir); ok { // TODO: get inodes to work on the root directory of a composite FS @@ -374,8 +368,8 @@ func requireReaddir(t *testing.T, f fs.File, n int, expectIno bool) []*platform. // requireReaddirnames ensures the input file is a directory, and returns its // entries. func requireReaddirnames(t *testing.T, f fs.File, n int) []string { - names, errno := platform.Readdirnames(f, n) - require.Zero(t, errno) + names, err := platform.Readdirnames(f, n) + require.NoError(t, err) sort.Strings(names) return names } @@ -394,11 +388,11 @@ func testReadlink(t *testing.T, readFS, writeFS FS) { } for _, tl := range testLinks { - errno := writeFS.Symlink(tl.old, tl.dst) // not os.Symlink for windows compat - require.Zero(t, errno, "%v", tl) + err := writeFS.Symlink(tl.old, tl.dst) // not os.Symlink for windows compat + require.NoError(t, err, "%v", tl) - dst, errno := readFS.Readlink(tl.dst) - require.Zero(t, errno) + dst, err := readFS.Readlink(tl.dst) + require.NoError(t, err) require.Equal(t, tl.old, dst) } @@ -580,8 +574,8 @@ func TestWriterAtOffset(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - f, errno := tc.fs.OpenFile(readerAtFile, os.O_RDWR|os.O_CREATE, 0o600) - require.Zero(t, errno) + f, err := tc.fs.OpenFile(readerAtFile, os.O_RDWR|os.O_CREATE, 0o600) + require.NoError(t, err) defer f.Close() w := f.(io.Writer) @@ -646,8 +640,8 @@ func TestWriterAtOffset_empty(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - f, errno := tc.fs.OpenFile(emptyFile, os.O_RDWR|os.O_CREATE, 0o600) - require.Zero(t, errno) + f, err := tc.fs.OpenFile(emptyFile, os.O_RDWR|os.O_CREATE, 0o600) + require.NoError(t, err) defer f.Close() r := f.(io.Writer) @@ -674,15 +668,15 @@ func TestWriterAtOffset_Unsupported(t *testing.T) { tmpDir := t.TempDir() dirFS := NewDirFS(tmpDir) - f, errno := dirFS.OpenFile(readerAtFile, os.O_RDWR|os.O_CREATE, 0o600) - require.Zero(t, errno) + f, err := dirFS.OpenFile(readerAtFile, os.O_RDWR|os.O_CREATE, 0o600) + require.NoError(t, err) defer f.Close() // mask both io.WriterAt and io.Seeker ra := WriterAtOffset(struct{ fs.File }{f}, 0) buf := make([]byte, 3) - _, err := ra.Write(buf) + _, err = ra.Write(buf) require.Equal(t, syscall.ENOSYS, err) } @@ -690,8 +684,8 @@ func TestWriterAtOffset_Unsupported(t *testing.T) { // sync anyway. There is no test in Go for os.File Sync, but closest is similar // to below. Effectively, this only tests that things don't error. func Test_FileSync(t *testing.T) { - testSync(t, func(f fs.File) syscall.Errno { - return platform.UnwrapOSError(f.(interface{ Sync() error }).Sync()) + testSync(t, func(f fs.File) error { + return f.(interface{ Sync() error }).Sync() }) } @@ -700,7 +694,7 @@ func Test_FileDatasync(t *testing.T) { testSync(t, FileDatasync) } -func testSync(t *testing.T, sync func(fs.File) syscall.Errno) { +func testSync(t *testing.T, sync func(fs.File) error) { f, err := os.CreateTemp("", t.Name()) require.NoError(t, err) defer f.Close() @@ -712,7 +706,7 @@ func testSync(t *testing.T, sync func(fs.File) syscall.Errno) { require.NoError(t, err) // Sync the data. - require.Zero(t, sync(f)) + require.NoError(t, sync(f)) // Rewind while the file is still open. _, err = f.Seek(0, io.SeekStart) diff --git a/internal/sysfs/unsupported.go b/internal/sysfs/unsupported.go index 6fb9114f..7e5a4bc7 100644 --- a/internal/sysfs/unsupported.go +++ b/internal/sysfs/unsupported.go @@ -22,76 +22,76 @@ func (UnimplementedFS) Open(name string) (fs.File, error) { } // OpenFile implements FS.OpenFile -func (UnimplementedFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, syscall.Errno) { +func (UnimplementedFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, error) { return nil, syscall.ENOSYS } // Lstat implements FS.Lstat -func (UnimplementedFS) Lstat(path string) (platform.Stat_t, syscall.Errno) { +func (UnimplementedFS) Lstat(path string) (platform.Stat_t, error) { return platform.Stat_t{}, syscall.ENOSYS } // Stat implements FS.Stat -func (UnimplementedFS) Stat(path string) (platform.Stat_t, syscall.Errno) { +func (UnimplementedFS) Stat(path string) (platform.Stat_t, error) { return platform.Stat_t{}, syscall.ENOSYS } // Readlink implements FS.Readlink -func (UnimplementedFS) Readlink(path string) (string, syscall.Errno) { +func (UnimplementedFS) Readlink(path string) (string, error) { return "", syscall.ENOSYS } // Mkdir implements FS.Mkdir -func (UnimplementedFS) Mkdir(path string, perm fs.FileMode) syscall.Errno { +func (UnimplementedFS) Mkdir(path string, perm fs.FileMode) error { return syscall.ENOSYS } // Chmod implements FS.Chmod -func (UnimplementedFS) Chmod(path string, perm fs.FileMode) syscall.Errno { +func (UnimplementedFS) Chmod(path string, perm fs.FileMode) error { return syscall.ENOSYS } // Chown implements FS.Chown -func (UnimplementedFS) Chown(path string, uid, gid int) syscall.Errno { +func (UnimplementedFS) Chown(path string, uid, gid int) error { return syscall.ENOSYS } // Lchown implements FS.Lchown -func (UnimplementedFS) Lchown(path string, uid, gid int) syscall.Errno { +func (UnimplementedFS) Lchown(path string, uid, gid int) error { return syscall.ENOSYS } // Rename implements FS.Rename -func (UnimplementedFS) Rename(from, to string) syscall.Errno { +func (UnimplementedFS) Rename(from, to string) error { return syscall.ENOSYS } // Rmdir implements FS.Rmdir -func (UnimplementedFS) Rmdir(path string) syscall.Errno { +func (UnimplementedFS) Rmdir(path string) error { return syscall.ENOSYS } // Link implements FS.Link -func (UnimplementedFS) Link(_, _ string) syscall.Errno { +func (UnimplementedFS) Link(_, _ string) error { return syscall.ENOSYS } // Symlink implements FS.Symlink -func (UnimplementedFS) Symlink(_, _ string) syscall.Errno { +func (UnimplementedFS) Symlink(_, _ string) error { return syscall.ENOSYS } // Unlink implements FS.Unlink -func (UnimplementedFS) Unlink(path string) syscall.Errno { +func (UnimplementedFS) Unlink(path string) error { return syscall.ENOSYS } // Utimens implements FS.Utimens -func (UnimplementedFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) syscall.Errno { +func (UnimplementedFS) Utimens(path string, times *[2]syscall.Timespec, symlinkFollow bool) error { return syscall.ENOSYS } // Truncate implements FS.Truncate -func (UnimplementedFS) Truncate(string, int64) syscall.Errno { +func (UnimplementedFS) Truncate(string, int64) error { return syscall.ENOSYS } diff --git a/internal/wasi_snapshot_preview1/errno.go b/internal/wasi_snapshot_preview1/errno.go index 6537120b..d29ea10c 100644 --- a/internal/wasi_snapshot_preview1/errno.go +++ b/internal/wasi_snapshot_preview1/errno.go @@ -2,7 +2,10 @@ package wasi_snapshot_preview1 import ( "fmt" + "io" "syscall" + + "github.com/tetratelabs/wazero/internal/platform" ) // Errno is neither uint16 nor an alias for parity with wasm.ValueType. @@ -263,10 +266,13 @@ 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(err error) Errno { + if err == nil || err == io.EOF { + return ErrnoSuccess // io.EOF has no value in WASI, and isn't an error. + } + errno := platform.UnwrapOSError(err) + switch errno { - case 0: - return ErrnoSuccess case syscall.EACCES: return ErrnoAcces case syscall.EAGAIN: @@ -275,8 +281,6 @@ func ToErrno(errno syscall.Errno) Errno { return ErrnoBadf case syscall.EEXIST: return ErrnoExist - case syscall.EFAULT: - return ErrnoFault case syscall.EINTR: return ErrnoIntr case syscall.EINVAL: diff --git a/internal/wasi_snapshot_preview1/errno_test.go b/internal/wasi_snapshot_preview1/errno_test.go index 08746e81..4ac7e341 100644 --- a/internal/wasi_snapshot_preview1/errno_test.go +++ b/internal/wasi_snapshot_preview1/errno_test.go @@ -1,6 +1,7 @@ package wasi_snapshot_preview1 import ( + "io" "syscall" "testing" ) @@ -8,11 +9,16 @@ import ( func TestToErrno(t *testing.T) { tests := []struct { name string - input syscall.Errno + input error expected Errno }{ { - name: "zero is not an error", + name: "nil is not an error", + expected: ErrnoSuccess, + }, + { + name: "io.EOF is not an error", + input: io.EOF, expected: ErrnoSuccess, }, { @@ -35,11 +41,6 @@ func TestToErrno(t *testing.T) { input: syscall.EEXIST, expected: ErrnoExist, }, - { - name: "syscall.EFAULT", - input: syscall.EFAULT, - expected: ErrnoFault, - }, { name: "syscall.EINTR", input: syscall.EINTR, diff --git a/internal/wasm/call_context_test.go b/internal/wasm/call_context_test.go index 99619410..cd6174b1 100644 --- a/internal/wasm/call_context_test.go +++ b/internal/wasm/call_context_test.go @@ -109,8 +109,8 @@ func TestCallContext_Close(t *testing.T) { sysCtx := internalsys.DefaultContext(testFS) fsCtx := sysCtx.FS() - _, errno := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0) - require.Zero(t, errno) + _, err := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0) + require.NoError(t, err) m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil) require.NoError(t, err) @@ -144,8 +144,8 @@ func TestCallContext_Close(t *testing.T) { sysCtx := internalsys.DefaultContext(testFS) fsCtx := sysCtx.FS() - _, errno := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0) - require.Zero(t, errno) + _, err := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0) + require.NoError(t, err) m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil) require.NoError(t, err) @@ -213,8 +213,8 @@ func TestCallContext_CallDynamic(t *testing.T) { sysCtx := internalsys.DefaultContext(testFS) fsCtx := sysCtx.FS() - _, errno := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0) - require.Zero(t, errno) + _, err := fsCtx.OpenFile(testFS, "/foo", os.O_RDONLY, 0) + require.NoError(t, err) m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil) require.NoError(t, err) @@ -242,8 +242,8 @@ func TestCallContext_CallDynamic(t *testing.T) { fsCtx := sysCtx.FS() path := "/foo" - _, errno := fsCtx.OpenFile(testFS, path, os.O_RDONLY, 0) - require.Zero(t, errno) + _, err := fsCtx.OpenFile(testFS, path, os.O_RDONLY, 0) + require.NoError(t, err) m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil) require.NoError(t, err)