Revert "sysfs: requires all methods to return syscall.Errno"

This reverts commit 4ea9d1a7b5.
This commit is contained in:
Adrian Cole
2023-03-21 22:22:36 +08:00
parent 4ea9d1a7b5
commit a43a0f11e4
65 changed files with 1279 additions and 1349 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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
}()

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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
}

View File

@@ -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!

View File

@@ -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:

View File

@@ -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,

View File

@@ -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, &times, true)
err := fsc.RootFS().Utimens(path, &times, 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
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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.
})
}

View File

@@ -2,8 +2,6 @@
package platform
import "syscall"
func adjustErrno(err syscall.Errno) syscall.Errno {
func adjustErrno(err error) error {
return err
}

View File

@@ -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:

View File

@@ -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:

View File

@@ -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))
})
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
})
}

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
})

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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())

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
})

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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:

View File

@@ -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,

View File

@@ -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)