wasi: implements path_filestat_set_times (#1220)

This implements `path_filestat_set_times` which is the last remaining filesystem function in wasi we formerly stubbed.

Other minor changes:
* this removes all places we import alias path as pathutil, introducing a utility function `joinPath` where that was used to reduce name conflicts.
* this fixes places where we used different variable names for the same parameter between main and test source.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-03-13 09:46:17 +08:00
committed by GitHub
parent 19773267ba
commit 41ef17c5c2
16 changed files with 742 additions and 451 deletions

View File

@@ -6,7 +6,7 @@ import (
"io"
"io/fs"
"math"
pathutil "path"
"path"
"reflect"
"syscall"
"unsafe"
@@ -425,6 +425,8 @@ var fdFilestatSetTimes = newHostFunc(
func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) Errno {
fd := uint32(params[0])
atim := int64(params[1])
mtim := int64(params[2])
fstFlags := uint16(params[3])
sys := mod.(*wasm.CallContext).Sys
@@ -435,29 +437,9 @@ func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) Er
return ErrnoBadf
}
// times[0] == atim, times[1] == mtim
times := [2]syscall.Timespec{}
// coerce atim into a timespec
if set, now := fstFlags&FileStatAdjustFlagsAtim != 0, fstFlags&FileStatAdjustFlagsAtimNow != 0; set && now {
return ErrnoInval
} else if set {
times[0] = syscall.NsecToTimespec(int64(params[1]))
} else if now {
times[0].Nsec = platform.UTIME_NOW
} else {
times[0].Nsec = platform.UTIME_OMIT
}
// coerce mtim into a timespec
if set, now := fstFlags&FileStatAdjustFlagsMtim != 0, fstFlags&FileStatAdjustFlagsMtimNow != 0; set && now {
return ErrnoInval
} else if set {
times[1] = syscall.NsecToTimespec(int64(params[2]))
} else if now {
times[1].Nsec = platform.UTIME_NOW
} else {
times[1].Nsec = platform.UTIME_OMIT
times, errno := toTimes(atim, mtim, fstFlags)
if errno != ErrnoSuccess {
return errno
}
// Try to update the file timestamps by file-descriptor.
@@ -472,6 +454,35 @@ func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) Er
return ToErrno(err)
}
func toTimes(atim, mtime int64, fstFlags uint16) (times [2]syscall.Timespec, errno Errno) {
// times[0] == atim, times[1] == mtim
// coerce atim into a timespec
if set, now := fstFlags&FstflagsAtim != 0, fstFlags&FstflagsAtimNow != 0; set && now {
errno = ErrnoInval
return
} else if set {
times[0] = syscall.NsecToTimespec(atim)
} else if now {
times[0].Nsec = platform.UTIME_NOW
} else {
times[0].Nsec = platform.UTIME_OMIT
}
// coerce mtim into a timespec
if set, now := fstFlags&FstflagsMtim != 0, fstFlags&FstflagsMtimNow != 0; set && now {
errno = ErrnoInval
return
} else if set {
times[1] = syscall.NsecToTimespec(mtime)
} else if now {
times[1].Nsec = platform.UTIME_NOW
} else {
times[1].Nsec = platform.UTIME_OMIT
}
return
}
// fdPread is the WASI function named FdPreadName which reads from a file
// descriptor, without using and updating the file descriptor's offset.
//
@@ -882,7 +893,7 @@ func dotDirents(f *sys.FileEntry) ([]*platform.Dirent, error) {
dotDotIno := uint64(0)
if !f.IsPreopen && f.Name != "." {
var st platform.Stat_t
if err = f.FS.Stat(pathutil.Dir(f.Name), &st); err != nil {
if err = f.FS.Stat(path.Dir(f.Name), &st); err != nil {
return nil, err
}
dotDotIno = st.Ino
@@ -1349,11 +1360,11 @@ var pathCreateDirectory = newHostFunc(
func pathCreateDirectoryFn(_ context.Context, mod api.Module, params []uint64) Errno {
fsc := mod.(*wasm.CallContext).Sys.FS()
dirFD := uint32(params[0])
fd := uint32(params[0])
path := uint32(params[1])
pathLen := uint32(params[2])
preopen, pathName, errno := atPath(fsc, mod.Memory(), dirFD, path, pathLen)
preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen)
if errno != ErrnoSuccess {
return errno
}
@@ -1402,7 +1413,7 @@ var pathFilestatGet = newHostFunc(
func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) Errno {
fsc := mod.(*wasm.CallContext).Sys.FS()
dirFD := uint32(params[0])
fd := uint32(params[0])
// TODO: flags is a lookupflags and it only has one bit: symlink_follow
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#lookupflags
@@ -1411,7 +1422,7 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) Errno
path := uint32(params[2])
pathLen := uint32(params[3])
preopen, pathName, errno := atPath(fsc, mod.Memory(), dirFD, path, pathLen)
preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen)
if errno != ErrnoSuccess {
return errno
}
@@ -1439,12 +1450,39 @@ func pathFilestatGetFn(_ context.Context, mod api.Module, params []uint64) Errno
// which adjusts the timestamps of a file or directory.
//
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_filestat_set_timesfd-fd-flags-lookupflags-path-string-atim-timestamp-mtim-timestamp-fst_flags-fstflags---errno
var pathFilestatSetTimes = stubFunction(
PathFilestatSetTimesName,
var pathFilestatSetTimes = newHostFunc(
PathFilestatSetTimesName, pathFilestatSetTimesFn,
[]wasm.ValueType{i32, i32, i32, i32, i64, i64, i32},
"fd", "flags", "path", "path_len", "atim", "mtim", "fst_flags",
)
func pathFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) Errno {
fd := uint32(params[0])
flags := uint16(params[1])
path := uint32(params[2])
pathLen := uint32(params[3])
atim := int64(params[4])
mtim := int64(params[5])
fstFlags := uint16(params[6])
sys := mod.(*wasm.CallContext).Sys
fsc := sys.FS()
times, errno := toTimes(atim, mtim, fstFlags)
if errno != ErrnoSuccess {
return errno
}
preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen)
if errno != ErrnoSuccess {
return errno
}
symlinkFollow := flags&LOOKUP_SYMLINK_FOLLOW != 0
err := preopen.Utimens(pathName, &times, symlinkFollow)
return ToErrno(err)
}
// pathLink is the WASI function named PathLinkName which adjusts the
// timestamps of a file or directory.
//
@@ -1470,11 +1508,11 @@ func pathLinkFn(_ context.Context, mod api.Module, params []uint64) Errno {
return errno
}
newFd := uint32(params[4])
newFD := uint32(params[4])
newPath := uint32(params[5])
newPathLen := uint32(params[6])
newFS, newName, errno := atPath(fsc, mem, newFd, newPath, newPathLen)
newFS, newName, errno := atPath(fsc, mem, newFD, newPath, newPathLen)
if errno != ErrnoSuccess {
return errno
}
@@ -1611,24 +1649,24 @@ func pathOpenFn(_ context.Context, mod api.Module, params []uint64) Errno {
//
// Languages including Zig and Rust use only pre-opens for the FD because
// wasi-libc `__wasilibc_find_relpath` will only return a preopen. That said,
// our wasi.c example shows other languages act differently and can use dirFD
// of a non-preopen.
// our wasi.c example shows other languages act differently and can use a non
// pre-opened file descriptor.
//
// We don't handle AT_FDCWD, as that's resolved in the compiler. There's no
// We don't handle `AT_FDCWD`, as that's resolved in the compiler. There's no
// working directory function in WASI, so most assume CWD is "/". Notably, Zig
// has different behavior which assumes it is whatever the first pre-open name
// is.
//
// See https://github.com/WebAssembly/wasi-libc/blob/659ff414560721b1660a19685110e484a081c3d4/libc-bottom-half/sources/at_fdcwd.c
// See https://linux.die.net/man/2/openat
func atPath(fsc *sys.FSContext, mem api.Memory, dirFD, path, pathLen uint32) (sysfs.FS, string, Errno) {
func atPath(fsc *sys.FSContext, mem api.Memory, fd, path, pathLen uint32) (sysfs.FS, string, Errno) {
b, ok := mem.Read(path, pathLen)
if !ok {
return nil, "", ErrnoFault
}
pathName := string(b)
if f, ok := fsc.LookupFile(dirFD); !ok {
if f, ok := fsc.LookupFile(fd); !ok {
return nil, "", ErrnoBadf // closed
} else if _, ft, err := f.CachedStat(); err != nil {
return nil, "", ToErrno(err)
@@ -1637,12 +1675,13 @@ func atPath(fsc *sys.FSContext, mem api.Memory, dirFD, path, pathLen uint32) (sy
} else if f.IsPreopen { // don't append the pre-open name
return f.FS, pathName, ErrnoSuccess
} else {
return f.FS, pathutil.Join(f.Name, pathName), ErrnoSuccess
// Join via concat to avoid name conflict on path.Join
return f.FS, f.Name + "/" + pathName, ErrnoSuccess
}
}
func preopenPath(fsc *sys.FSContext, dirFD uint32) (string, Errno) {
if f, ok := fsc.LookupFile(dirFD); !ok {
func preopenPath(fsc *sys.FSContext, fd uint32) (string, Errno) {
if f, ok := fsc.LookupFile(fd); !ok {
return "", ErrnoBadf // closed
} else if !f.IsPreopen {
return "", ErrnoBadf
@@ -1692,9 +1731,9 @@ func pathReadlinkFn(_ context.Context, mod api.Module, params []uint64) Errno {
fd := uint32(params[0])
path := uint32(params[1])
pathLen := uint32(params[2])
bufPtr := uint32(params[3])
buf := uint32(params[3])
bufLen := uint32(params[4])
resultBufUsedPtr := uint32(params[5])
resultBufused := uint32(params[5])
if pathLen == 0 || bufLen == 0 {
return ErrnoInval
@@ -1711,11 +1750,11 @@ func pathReadlinkFn(_ context.Context, mod api.Module, params []uint64) Errno {
return ToErrno(err)
}
if ok := mem.WriteString(bufPtr, dst); !ok {
if ok := mem.WriteString(buf, dst); !ok {
return ErrnoFault
}
if !mem.WriteUint32Le(resultBufUsedPtr, uint32(len(dst))) {
if !mem.WriteUint32Le(resultBufused, uint32(len(dst))) {
return ErrnoFault
}
return ErrnoSuccess
@@ -1752,11 +1791,11 @@ var pathRemoveDirectory = newHostFunc(
func pathRemoveDirectoryFn(_ context.Context, mod api.Module, params []uint64) Errno {
fsc := mod.(*wasm.CallContext).Sys.FS()
dirFD := uint32(params[0])
fd := uint32(params[0])
path := uint32(params[1])
pathLen := uint32(params[2])
preopen, pathName, errno := atPath(fsc, mod.Memory(), dirFD, path, pathLen)
preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen)
if errno != ErrnoSuccess {
return errno
}
@@ -1802,20 +1841,20 @@ var pathRename = newHostFunc(
func pathRenameFn(_ context.Context, mod api.Module, params []uint64) Errno {
fsc := mod.(*wasm.CallContext).Sys.FS()
olddirFD := uint32(params[0])
fd := uint32(params[0])
oldPath := uint32(params[1])
oldPathLen := uint32(params[2])
newdirFD := uint32(params[3])
newFD := uint32(params[3])
newPath := uint32(params[4])
newPathLen := uint32(params[5])
oldFS, oldPathName, errno := atPath(fsc, mod.Memory(), olddirFD, oldPath, oldPathLen)
oldFS, oldPathName, errno := atPath(fsc, mod.Memory(), fd, oldPath, oldPathLen)
if errno != ErrnoSuccess {
return errno
}
newFS, newPathName, errno := atPath(fsc, mod.Memory(), newdirFD, newPath, newPathLen)
newFS, newPathName, errno := atPath(fsc, mod.Memory(), newFD, newPath, newPathLen)
if errno != ErrnoSuccess {
return errno
}
@@ -1846,13 +1885,13 @@ func pathSymlinkFn(_ context.Context, mod api.Module, params []uint64) Errno {
oldPath := uint32(params[0])
oldPathLen := uint32(params[1])
dirFD := uint32(params[2])
fd := uint32(params[2])
newPath := uint32(params[3])
newPathLen := uint32(params[4])
mem := mod.Memory()
dir, ok := fsc.LookupFile(dirFD)
dir, ok := fsc.LookupFile(fd)
if !ok {
return ErrnoBadf // closed
} else if _, ft, err := dir.CachedStat(); err != nil {
@@ -1879,7 +1918,7 @@ func pathSymlinkFn(_ context.Context, mod api.Module, params []uint64) Errno {
// Do not join old path since it's only resolved when dereference the link created here.
// And the dereference result depends on the opening directory's file descriptor at that point.
bufToStr(oldPathBuf, int(oldPathLen)),
pathutil.Join(dir.Name, bufToStr(newPathBuf, int(newPathLen))),
path.Join(dir.Name, bufToStr(newPathBuf, int(newPathLen))),
); err != nil {
return ToErrno(err)
}
@@ -1925,11 +1964,11 @@ var pathUnlinkFile = newHostFunc(
func pathUnlinkFileFn(_ context.Context, mod api.Module, params []uint64) Errno {
fsc := mod.(*wasm.CallContext).Sys.FS()
dirFD := uint32(params[0])
fd := uint32(params[0])
path := uint32(params[1])
pathLen := uint32(params[2])
preopen, pathName, errno := atPath(fsc, mod.Memory(), dirFD, path, pathLen)
preopen, pathName, errno := atPath(fsc, mod.Memory(), fd, path, pathLen)
if errno != ErrnoSuccess {
return errno
}

File diff suppressed because it is too large Load Diff

View File

@@ -145,5 +145,5 @@ func requireErrnoResult(t *testing.T, expectedErrno Errno, mod api.Closer, funcN
results, err := mod.(api.Module).ExportedFunction(funcName).Call(testCtx, params...)
require.NoError(t, err)
errno := Errno(results[0])
require.Equal(t, expectedErrno, errno, "want %s but got %s", ErrnoName(expectedErrno), ErrnoName(errno))
require.Equal(t, expectedErrno, errno, "want %s but have %s", ErrnoName(expectedErrno), ErrnoName(errno))
}

View File

@@ -79,6 +79,20 @@ func testFutimens(t *testing.T, usePath bool) {
{Sec: 123, Nsec: UTIME_NOW},
},
},
{
name: "a=set,m=omit",
times: &[2]syscall.Timespec{
{Sec: 123, Nsec: 4 * 1e3},
{Sec: 123, Nsec: UTIME_OMIT},
},
},
{
name: "a=omit,m=set",
times: &[2]syscall.Timespec{
{Sec: 123, Nsec: UTIME_OMIT},
{Sec: 123, Nsec: 4 * 1e3},
},
},
{
name: "a=set,m=set",
times: &[2]syscall.Timespec{

View File

@@ -2,7 +2,7 @@ package platform
import (
"os"
pathutil "path/filepath"
path "path/filepath"
"syscall"
"testing"
@@ -13,13 +13,13 @@ func TestOpenFile_Errors(t *testing.T) {
tmpDir := t.TempDir()
t.Run("not found must be ENOENT", func(t *testing.T) {
_, err := OpenFile(pathutil.Join(tmpDir, "not-really-exist.txt"), os.O_RDONLY, 0o600)
_, 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 := pathutil.Join(tmpDir, "file.txt")
filepath := path.Join(tmpDir, "file.txt")
f, err := OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666)
defer require.NoError(t, f.Close())
require.NoError(t, err)
@@ -29,7 +29,7 @@ func TestOpenFile_Errors(t *testing.T) {
})
t.Run("writing to a read-only file is EBADF", func(t *testing.T) {
path := pathutil.Join(tmpDir, "file")
path := path.Join(tmpDir, "file")
require.NoError(t, os.WriteFile(path, nil, 0o600))
f, err := OpenFile(path, os.O_RDONLY, 0)
@@ -41,7 +41,7 @@ func TestOpenFile_Errors(t *testing.T) {
})
t.Run("writing to a directory is EBADF", func(t *testing.T) {
path := pathutil.Join(tmpDir, "diragain")
path := path.Join(tmpDir, "diragain")
require.NoError(t, os.Mkdir(path, 0o755))
f, err := OpenFile(path, os.O_RDONLY, 0)
@@ -54,8 +54,8 @@ func TestOpenFile_Errors(t *testing.T) {
// This is similar to https://github.com/WebAssembly/wasi-testsuite/blob/dc7f8d27be1030cd4788ebdf07d9b57e5d23441e/tests/rust/src/bin/dangling_symlink.rs
t.Run("dangling symlinks", func(t *testing.T) {
target := pathutil.Join(tmpDir, "target")
symlink := pathutil.Join(tmpDir, "dangling_symlink_symlink.cleanup")
target := path.Join(tmpDir, "target")
symlink := path.Join(tmpDir, "dangling_symlink_symlink.cleanup")
err := os.Symlink(target, symlink)
require.NoError(t, err)

View File

@@ -3,7 +3,7 @@ package platform
import (
"io/fs"
"os"
pathutil "path"
"path"
"runtime"
"syscall"
"testing"
@@ -16,8 +16,8 @@ func TestLstat(t *testing.T) {
tmpDir := t.TempDir()
var stat Stat_t
require.EqualErrno(t, syscall.ENOENT, Lstat(pathutil.Join(tmpDir, "cat"), &stat))
require.EqualErrno(t, syscall.ENOENT, Lstat(pathutil.Join(tmpDir, "sub/cat"), &stat))
require.EqualErrno(t, syscall.ENOENT, Lstat(path.Join(tmpDir, "cat"), &stat))
require.EqualErrno(t, syscall.ENOENT, Lstat(path.Join(tmpDir, "sub/cat"), &stat))
t.Run("dir", func(t *testing.T) {
err := Lstat(tmpDir, &stat)
@@ -26,7 +26,7 @@ func TestLstat(t *testing.T) {
require.NotEqual(t, uint64(0), stat.Ino)
})
file := pathutil.Join(tmpDir, "file")
file := path.Join(tmpDir, "file")
var statFile Stat_t
t.Run("file", func(t *testing.T) {
@@ -41,7 +41,7 @@ func TestLstat(t *testing.T) {
requireLinkStat(t, file, &statFile)
})
subdir := pathutil.Join(tmpDir, "sub")
subdir := path.Join(tmpDir, "sub")
var statSubdir Stat_t
t.Run("subdir", func(t *testing.T) {
require.NoError(t, os.Mkdir(subdir, 0o500))
@@ -87,8 +87,8 @@ func TestStat(t *testing.T) {
tmpDir := t.TempDir()
var stat Stat_t
require.EqualErrno(t, syscall.ENOENT, Stat(pathutil.Join(tmpDir, "cat"), &stat))
require.EqualErrno(t, syscall.ENOENT, Stat(pathutil.Join(tmpDir, "sub/cat"), &stat))
require.EqualErrno(t, syscall.ENOENT, Stat(path.Join(tmpDir, "cat"), &stat))
require.EqualErrno(t, syscall.ENOENT, Stat(path.Join(tmpDir, "sub/cat"), &stat))
t.Run("dir", func(t *testing.T) {
err := Stat(tmpDir, &stat)
@@ -97,7 +97,7 @@ func TestStat(t *testing.T) {
require.NotEqual(t, uint64(0), stat.Ino)
})
file := pathutil.Join(tmpDir, "file")
file := path.Join(tmpDir, "file")
var statFile Stat_t
t.Run("file", func(t *testing.T) {
@@ -108,14 +108,14 @@ func TestStat(t *testing.T) {
})
t.Run("link to file", func(t *testing.T) {
link := pathutil.Join(tmpDir, "file-link")
link := path.Join(tmpDir, "file-link")
require.NoError(t, os.Symlink(file, link))
require.NoError(t, Stat(link, &stat))
require.Equal(t, statFile, stat) // resolves to the file
})
subdir := pathutil.Join(tmpDir, "sub")
subdir := path.Join(tmpDir, "sub")
var statSubdir Stat_t
t.Run("subdir", func(t *testing.T) {
require.NoError(t, os.Mkdir(subdir, 0o500))
@@ -126,7 +126,7 @@ func TestStat(t *testing.T) {
})
t.Run("link to dir", func(t *testing.T) {
link := pathutil.Join(tmpDir, "dir-link")
link := path.Join(tmpDir, "dir-link")
require.NoError(t, os.Symlink(subdir, link))
require.NoError(t, Stat(link, &stat))
@@ -159,7 +159,7 @@ func TestStatFile(t *testing.T) {
})
}
file := pathutil.Join(tmpDir, "file")
file := path.Join(tmpDir, "file")
require.NoError(t, os.WriteFile(file, nil, 0o400))
fileF, err := OpenFile(file, syscall.O_RDONLY, 0)
require.NoError(t, err)
@@ -178,7 +178,7 @@ func TestStatFile(t *testing.T) {
require.NotEqual(t, uint64(0), stat.Ino)
})
subdir := pathutil.Join(tmpDir, "sub")
subdir := path.Join(tmpDir, "sub")
require.NoError(t, os.Mkdir(subdir, 0o500))
subdirF, err := OpenFile(subdir, syscall.O_RDONLY, 0)
require.NoError(t, err)
@@ -202,7 +202,7 @@ func TestStatFile(t *testing.T) {
func Test_StatFile_times(t *testing.T) {
tmpDir := t.TempDir()
file := pathutil.Join(tmpDir, "file")
file := path.Join(tmpDir, "file")
err := os.WriteFile(file, []byte{}, 0o700)
require.NoError(t, err)
@@ -256,17 +256,17 @@ func TestStatFile_dev_inode(t *testing.T) {
require.NoError(t, err)
defer d.Close()
path1 := pathutil.Join(tmpDir, "1")
path1 := path.Join(tmpDir, "1")
f1, err := os.Create(path1)
require.NoError(t, err)
defer f1.Close()
path2 := pathutil.Join(tmpDir, "2")
path2 := path.Join(tmpDir, "2")
f2, err := os.Create(path2)
require.NoError(t, err)
defer f2.Close()
pathLink2 := pathutil.Join(tmpDir, "link2")
pathLink2 := path.Join(tmpDir, "link2")
err = os.Symlink(path2, pathLink2)
require.NoError(t, err)
l2, err := os.Open(pathLink2)

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"io/fs"
"os"
pathutil "path"
"path"
"runtime"
"strings"
@@ -70,7 +70,7 @@ func cleanPath(name string) string {
if name[0] == '/' {
cleaned = name[1:]
}
cleaned = pathutil.Clean(cleaned) // e.g. "sub/." -> "sub"
cleaned = path.Clean(cleaned) // e.g. "sub/." -> "sub"
return cleaned
}

View File

@@ -4,7 +4,6 @@ import (
"errors"
"io/fs"
"os"
pathutil "path"
"runtime"
"syscall"
"testing"
@@ -44,13 +43,13 @@ func TestAdapt_Rename(t *testing.T) {
testFS := Adapt(os.DirFS(tmpDir))
file1 := "file1"
file1Path := pathutil.Join(tmpDir, file1)
file1Path := joinPath(tmpDir, file1)
file1Contents := []byte{1}
err := os.WriteFile(file1Path, file1Contents, 0o600)
require.NoError(t, err)
file2 := "file2"
file2Path := pathutil.Join(tmpDir, file2)
file2Path := joinPath(tmpDir, file2)
file2Contents := []byte{2}
err = os.WriteFile(file2Path, file2Contents, 0o600)
require.NoError(t, err)
@@ -64,7 +63,7 @@ func TestAdapt_Rmdir(t *testing.T) {
testFS := Adapt(os.DirFS(tmpDir))
path := "rmdir"
realPath := pathutil.Join(tmpDir, path)
realPath := joinPath(tmpDir, path)
require.NoError(t, os.Mkdir(realPath, 0o700))
err := testFS.Rmdir(path)
@@ -76,7 +75,7 @@ func TestAdapt_Unlink(t *testing.T) {
testFS := Adapt(os.DirFS(tmpDir))
path := "unlink"
realPath := pathutil.Join(tmpDir, path)
realPath := joinPath(tmpDir, path)
require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600))
err := testFS.Unlink(path)
@@ -88,7 +87,7 @@ func TestAdapt_UtimesNano(t *testing.T) {
testFS := Adapt(os.DirFS(tmpDir))
path := "utimes"
realPath := pathutil.Join(tmpDir, path)
realPath := joinPath(tmpDir, path)
require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600))
err := testFS.Utimens(path, nil, true)
@@ -98,7 +97,7 @@ func TestAdapt_UtimesNano(t *testing.T) {
func TestAdapt_Open_Read(t *testing.T) {
// Create a subdirectory, so we can test reads outside the FS root.
tmpDir := t.TempDir()
tmpDir = pathutil.Join(tmpDir, t.Name())
tmpDir = joinPath(tmpDir, t.Name())
require.NoError(t, os.Mkdir(tmpDir, 0o700))
require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := Adapt(os.DirFS(tmpDir))
@@ -125,8 +124,8 @@ func TestAdapt_Lstat(t *testing.T) {
testFS := Adapt(os.DirFS(tmpDir))
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
fullPath := pathutil.Join(tmpDir, path)
linkPath := pathutil.Join(tmpDir, path+"-link")
fullPath := joinPath(tmpDir, path)
linkPath := joinPath(tmpDir, path+"-link")
require.NoError(t, os.Symlink(fullPath, linkPath))
var stat platform.Stat_t
require.EqualErrno(t, syscall.ENOSYS, testFS.Lstat(linkPath, &stat))

View File

@@ -4,7 +4,7 @@ import (
"errors"
"io/fs"
"os"
pathutil "path"
"path"
"runtime"
"syscall"
"testing"
@@ -63,7 +63,7 @@ func TestDirFS_MkDir(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "mkdir"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
t.Run("doesn't exist", func(t *testing.T) {
require.NoError(t, testFS.Mkdir(name, fs.ModeDir))
@@ -86,7 +86,7 @@ func TestDirFS_MkDir(t *testing.T) {
require.EqualErrno(t, syscall.EEXIST, err)
})
t.Run("try creating on file", func(t *testing.T) {
filePath := pathutil.Join("non-existing-dir", "foo.txt")
filePath := path.Join("non-existing-dir", "foo.txt")
err := testFS.Mkdir(filePath, fs.ModeDir)
require.EqualErrno(t, syscall.ENOENT, err)
})
@@ -137,7 +137,7 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
file1 := "file1"
file1Path := pathutil.Join(tmpDir, file1)
file1Path := path.Join(tmpDir, file1)
err := os.WriteFile(file1Path, []byte{1}, 0o600)
require.NoError(t, err)
@@ -149,13 +149,13 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
file1 := "file1"
file1Path := pathutil.Join(tmpDir, file1)
file1Path := path.Join(tmpDir, file1)
file1Contents := []byte{1}
err := os.WriteFile(file1Path, file1Contents, 0o600)
require.NoError(t, err)
file2 := "file2"
file2Path := pathutil.Join(tmpDir, file2)
file2Path := path.Join(tmpDir, file2)
err = testFS.Rename(file1, file2)
require.NoError(t, err)
@@ -172,11 +172,11 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
dir1 := "dir1"
dir1Path := pathutil.Join(tmpDir, dir1)
dir1Path := path.Join(tmpDir, dir1)
require.NoError(t, os.Mkdir(dir1Path, 0o700))
dir2 := "dir2"
dir2Path := pathutil.Join(tmpDir, dir2)
dir2Path := path.Join(tmpDir, dir2)
err := testFS.Rename(dir1, dir2)
require.NoError(t, err)
@@ -193,11 +193,11 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
dir1 := "dir1"
dir1Path := pathutil.Join(tmpDir, dir1)
dir1Path := path.Join(tmpDir, dir1)
require.NoError(t, os.Mkdir(dir1Path, 0o700))
dir2 := "dir2"
dir2Path := pathutil.Join(tmpDir, dir2)
dir2Path := path.Join(tmpDir, dir2)
// write a file to that path
f, err := os.OpenFile(dir2Path, os.O_RDWR|os.O_CREATE, 0o600)
@@ -212,13 +212,13 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
file1 := "file1"
file1Path := pathutil.Join(tmpDir, file1)
file1Path := path.Join(tmpDir, file1)
file1Contents := []byte{1}
err := os.WriteFile(file1Path, file1Contents, 0o600)
require.NoError(t, err)
dir1 := "dir1"
dir1Path := pathutil.Join(tmpDir, dir1)
dir1Path := path.Join(tmpDir, dir1)
require.NoError(t, os.Mkdir(dir1Path, 0o700))
err = testFS.Rename(file1, dir1)
@@ -231,18 +231,18 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
dir1 := "dir1"
dir1Path := pathutil.Join(tmpDir, dir1)
dir1Path := path.Join(tmpDir, dir1)
require.NoError(t, os.Mkdir(dir1Path, 0o700))
// add a file to that directory
file1 := "file1"
file1Path := pathutil.Join(dir1Path, file1)
file1Path := path.Join(dir1Path, file1)
file1Contents := []byte{1}
err := os.WriteFile(file1Path, file1Contents, 0o600)
require.NoError(t, err)
dir2 := "dir2"
dir2Path := pathutil.Join(tmpDir, dir2)
dir2Path := path.Join(tmpDir, dir2)
require.NoError(t, os.Mkdir(dir2Path, 0o700))
err = testFS.Rename(dir1, dir2)
@@ -253,7 +253,7 @@ func TestDirFS_Rename(t *testing.T) {
require.EqualErrno(t, syscall.ENOENT, platform.UnwrapOSError(err))
// Show the file inside that directory moved
s, err := os.Stat(pathutil.Join(dir2Path, file1))
s, err := os.Stat(path.Join(dir2Path, file1))
require.NoError(t, err)
require.False(t, s.IsDir())
})
@@ -264,22 +264,22 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
dir1 := "dir1"
dir1Path := pathutil.Join(tmpDir, dir1)
dir1Path := path.Join(tmpDir, dir1)
require.NoError(t, os.Mkdir(dir1Path, 0o700))
// add a file to that directory
file1 := "file1"
file1Path := pathutil.Join(dir1Path, file1)
file1Path := path.Join(dir1Path, file1)
file1Contents := []byte{1}
err := os.WriteFile(file1Path, file1Contents, 0o600)
require.NoError(t, err)
dir2 := "dir2"
dir2Path := pathutil.Join(tmpDir, dir2)
dir2Path := path.Join(tmpDir, dir2)
require.NoError(t, os.Mkdir(dir2Path, 0o700))
// Make the destination non-empty.
err = os.WriteFile(pathutil.Join(dir2Path, "existing.txt"), []byte("any thing"), 0o600)
err = os.WriteFile(path.Join(dir2Path, "existing.txt"), []byte("any thing"), 0o600)
require.NoError(t, err)
err = testFS.Rename(dir1, dir2)
@@ -291,13 +291,13 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
file1 := "file1"
file1Path := pathutil.Join(tmpDir, file1)
file1Path := path.Join(tmpDir, file1)
file1Contents := []byte{1}
err := os.WriteFile(file1Path, file1Contents, 0o600)
require.NoError(t, err)
file2 := "file2"
file2Path := pathutil.Join(tmpDir, file2)
file2Path := path.Join(tmpDir, file2)
file2Contents := []byte{2}
err = os.WriteFile(file2Path, file2Contents, 0o600)
require.NoError(t, err)
@@ -319,7 +319,7 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
dir1 := "dir1"
dir1Path := pathutil.Join(tmpDir, dir1)
dir1Path := path.Join(tmpDir, dir1)
require.NoError(t, os.Mkdir(dir1Path, 0o700))
err := testFS.Rename(dir1, dir1)
@@ -334,7 +334,7 @@ func TestDirFS_Rename(t *testing.T) {
testFS := NewDirFS(tmpDir)
file1 := "file1"
file1Path := pathutil.Join(tmpDir, file1)
file1Path := path.Join(tmpDir, file1)
file1Contents := []byte{1}
err := os.WriteFile(file1Path, file1Contents, 0o600)
require.NoError(t, err)
@@ -364,10 +364,10 @@ func TestDirFS_Rmdir(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "rmdir"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
require.NoError(t, os.Mkdir(realPath, 0o700))
fileInDir := pathutil.Join(realPath, "file")
fileInDir := path.Join(realPath, "file")
require.NoError(t, os.WriteFile(fileInDir, []byte{}, 0o600))
err := testFS.Rmdir(name)
@@ -381,11 +381,11 @@ func TestDirFS_Rmdir(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "rmdir"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
require.NoError(t, os.Mkdir(realPath, 0o700))
// Create a file and then delete it.
fileInDir := pathutil.Join(realPath, "file")
fileInDir := path.Join(realPath, "file")
require.NoError(t, os.WriteFile(fileInDir, []byte{}, 0o600))
require.NoError(t, os.Remove(fileInDir))
@@ -399,7 +399,7 @@ func TestDirFS_Rmdir(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "rmdir"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
require.NoError(t, os.Mkdir(realPath, 0o700))
require.NoError(t, testFS.Rmdir(name))
_, err := os.Stat(realPath)
@@ -411,7 +411,7 @@ func TestDirFS_Rmdir(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "rmdir"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
require.NoError(t, os.Mkdir(realPath, 0o700))
f, err := testFS.OpenFile(name, platform.O_DIRECTORY, 0o700)
@@ -430,7 +430,7 @@ func TestDirFS_Rmdir(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "rmdir"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600))
@@ -456,7 +456,7 @@ func TestDirFS_Unlink(t *testing.T) {
testFS := NewDirFS(tmpDir)
dir := "dir"
realPath := pathutil.Join(tmpDir, dir)
realPath := path.Join(tmpDir, dir)
require.NoError(t, os.Mkdir(realPath, 0o700))
@@ -472,7 +472,7 @@ func TestDirFS_Unlink(t *testing.T) {
// Create link target dir.
subDirName := "subdir"
subDirRealPath := pathutil.Join(tmpDir, subDirName)
subDirRealPath := path.Join(tmpDir, subDirName)
require.NoError(t, os.Mkdir(subDirRealPath, 0o700))
// Create a symlink to the subdirectory.
@@ -489,7 +489,7 @@ func TestDirFS_Unlink(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "unlink"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600))
@@ -504,11 +504,11 @@ func TestDirFS_Utimesns(t *testing.T) {
testFS := NewDirFS(tmpDir)
file := "file"
err := os.WriteFile(pathutil.Join(tmpDir, file), []byte{}, 0o700)
err := os.WriteFile(path.Join(tmpDir, file), []byte{}, 0o700)
require.NoError(t, err)
dir := "dir"
err = os.Mkdir(pathutil.Join(tmpDir, dir), 0o700)
err = os.Mkdir(path.Join(tmpDir, dir), 0o700)
require.NoError(t, err)
t.Run("doesn't exist", func(t *testing.T) {
@@ -595,14 +595,14 @@ func TestDirFS_Utimesns(t *testing.T) {
tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir)
file := pathutil.Join(tmpDir, "file")
file := path.Join(tmpDir, "file")
err := os.WriteFile(file, []byte{}, 0o700)
require.NoError(t, err)
link := file + "-link"
require.NoError(t, os.Symlink(file, link))
dir := pathutil.Join(tmpDir, "dir")
dir := path.Join(tmpDir, "dir")
err = os.Mkdir(dir, 0o700)
require.NoError(t, err)
@@ -666,7 +666,7 @@ func TestDirFS_OpenFile(t *testing.T) {
tmpDir := t.TempDir()
// Create a subdirectory, so we can test reads outside the FS root.
tmpDir = pathutil.Join(tmpDir, t.Name())
tmpDir = path.Join(tmpDir, t.Name())
require.NoError(t, os.Mkdir(tmpDir, 0o700))
require.NoError(t, fstest.WriteTestFiles(tmpDir))
@@ -730,7 +730,7 @@ func TestDirFS_Truncate(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "truncate"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
require.NoError(t, os.WriteFile(realPath, content, 0o0600))
err := testFS.Truncate(name, tc.size)
@@ -746,7 +746,7 @@ func TestDirFS_Truncate(t *testing.T) {
testFS := NewDirFS(tmpDir)
name := "truncate"
realPath := pathutil.Join(tmpDir, name)
realPath := path.Join(tmpDir, name)
if runtime.GOOS != "windows" {
// TODO: os.Truncate on windows can create the file even when it
@@ -805,7 +805,7 @@ func Test_fdReaddir_opened_file_written(t *testing.T) {
defer dirFile.Close()
// Then write a file to the directory.
f, err := os.Create(pathutil.Join(root, readDirTarget, "my-file"))
f, err := os.Create(path.Join(root, readDirTarget, "my-file"))
require.NoError(t, err)
defer f.Close()
@@ -850,12 +850,12 @@ func TestDirFS_Symlink(t *testing.T) {
require.NoError(t, testFS.Symlink("non-existing", "aa"))
require.NoError(t, testFS.Symlink("sub/", "symlinked-subdir"))
st, err := os.Lstat(pathutil.Join(tmpDir, "aa"))
st, err := os.Lstat(path.Join(tmpDir, "aa"))
require.NoError(t, err)
require.Equal(t, "aa", st.Name())
require.True(t, st.Mode()&fs.ModeSymlink > 0 && !st.IsDir())
st, err = os.Lstat(pathutil.Join(tmpDir, "symlinked-subdir"))
st, err = os.Lstat(path.Join(tmpDir, "symlinked-subdir"))
require.NoError(t, err)
require.Equal(t, "symlinked-subdir", st.Name())
require.True(t, st.Mode()&fs.ModeSymlink > 0)

View File

@@ -3,7 +3,6 @@ package sysfs
import (
"io/fs"
"os"
pathutil "path"
"runtime"
"syscall"
"testing"
@@ -72,13 +71,13 @@ func TestReadFS_Rename(t *testing.T) {
testFS := NewReadFS(writeable)
file1 := "file1"
file1Path := pathutil.Join(tmpDir, file1)
file1Path := joinPath(tmpDir, file1)
file1Contents := []byte{1}
err := os.WriteFile(file1Path, file1Contents, 0o600)
require.NoError(t, err)
file2 := "file2"
file2Path := pathutil.Join(tmpDir, file2)
file2Path := joinPath(tmpDir, file2)
file2Contents := []byte{2}
err = os.WriteFile(file2Path, file2Contents, 0o600)
require.NoError(t, err)
@@ -93,7 +92,7 @@ func TestReadFS_Rmdir(t *testing.T) {
testFS := NewReadFS(writeable)
path := "rmdir"
realPath := pathutil.Join(tmpDir, path)
realPath := joinPath(tmpDir, path)
require.NoError(t, os.Mkdir(realPath, 0o700))
err := testFS.Rmdir(path)
@@ -106,7 +105,7 @@ func TestReadFS_Unlink(t *testing.T) {
testFS := NewReadFS(writeable)
path := "unlink"
realPath := pathutil.Join(tmpDir, path)
realPath := joinPath(tmpDir, path)
require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600))
err := testFS.Unlink(path)
@@ -119,7 +118,7 @@ func TestReadFS_UtimesNano(t *testing.T) {
testFS := NewReadFS(writeable)
path := "utimes"
realPath := pathutil.Join(tmpDir, path)
realPath := joinPath(tmpDir, path)
require.NoError(t, os.WriteFile(realPath, []byte{}, 0o600))
err := testFS.Utimens(path, nil, true)

View File

@@ -5,7 +5,7 @@ import (
"io"
"io/fs"
"os"
pathutil "path"
"path"
"sort"
"strings"
"syscall"
@@ -76,12 +76,12 @@ func TestNewRootFS(t *testing.T) {
t.Run("multiple matches", func(t *testing.T) {
tmpDir1 := t.TempDir()
testFS1 := NewDirFS(tmpDir1)
require.NoError(t, os.Mkdir(pathutil.Join(tmpDir1, "tmp"), 0o700))
require.NoError(t, os.WriteFile(pathutil.Join(tmpDir1, "a"), []byte{1}, 0o600))
require.NoError(t, os.Mkdir(path.Join(tmpDir1, "tmp"), 0o700))
require.NoError(t, os.WriteFile(path.Join(tmpDir1, "a"), []byte{1}, 0o600))
tmpDir2 := t.TempDir()
testFS2 := NewDirFS(tmpDir2)
require.NoError(t, os.WriteFile(pathutil.Join(tmpDir2, "a"), []byte{2}, 0o600))
require.NoError(t, os.WriteFile(path.Join(tmpDir2, "a"), []byte{2}, 0o600))
rootFS, err := NewRootFS([]FS{testFS2, testFS1}, []string{"/tmp", "/"})
require.NoError(t, err)
@@ -135,7 +135,7 @@ func TestRootFS_Open(t *testing.T) {
tmpDir := t.TempDir()
// Create a subdirectory, so we can test reads outside the FS root.
tmpDir = pathutil.Join(tmpDir, t.Name())
tmpDir = path.Join(tmpDir, t.Name())
require.NoError(t, os.Mkdir(tmpDir, 0o700))
require.NoError(t, fstest.WriteTestFiles(tmpDir))
@@ -175,13 +175,13 @@ func TestRootFS_TestFS(t *testing.T) {
// move one directory outside the other
tmpDir2 := t.TempDir()
require.NoError(t, os.Rename(pathutil.Join(tmpDir1, "dir"), pathutil.Join(tmpDir2, "dir")))
require.NoError(t, os.Rename(path.Join(tmpDir1, "dir"), path.Join(tmpDir2, "dir")))
// Create a root mount
testFS1 := NewDirFS(tmpDir1)
// Create a dir mount
testFS2 := NewDirFS(pathutil.Join(tmpDir2, "dir"))
testFS2 := NewDirFS(path.Join(tmpDir2, "dir"))
testFS, err := NewRootFS([]FS{testFS1, testFS2}, []string{"/", "/dir"})
require.NoError(t, err)

View File

@@ -713,3 +713,9 @@ func requireIno(t *testing.T, dirents []*platform.Dirent, expectIno bool) {
}
}
}
// joinPath avoids us having to rename fields just to avoid conflict with the
// path package.
func joinPath(dirName, baseName string) string {
return path.Join(dirName, baseName)
}

View File

@@ -136,12 +136,23 @@ var filetypeToString = [...]string{
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fstflags
const (
FileStatAdjustFlagsAtim uint16 = 1 << iota
FileStatAdjustFlagsAtimNow
FileStatAdjustFlagsMtim
FileStatAdjustFlagsMtimNow
FstflagsAtim uint16 = 1 << iota
FstflagsAtimNow
FstflagsMtim
FstflagsMtimNow
)
var fstflagNames = [...]string{
"ATIM",
"ATIM_NOW",
"MTIM",
"MTIM_NOW",
}
func FstflagsString(fdflags int) string {
return flagsString(fstflagNames[:], fdflags)
}
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-advice-enumu8
const (
FdAdviceNormal byte = iota

View File

@@ -96,7 +96,8 @@ func Config(fnd api.FunctionDefinition) (pSampler logging.ParamSampler, pLoggers
var logger logging.ParamLogger
if isLookupFlags(fnd, name) {
logger = (&logLookupflags{name, idx}).Log
lf := &logLookupflags{name, idx}
logger = lf.Log
pLoggers = append(pLoggers, logger)
continue
}
@@ -139,6 +140,8 @@ func Config(fnd api.FunctionDefinition) (pSampler logging.ParamSampler, pLoggers
switch name {
case "fdflags":
logger = logFdflags(idx).Log
case "fst_flags":
logger = logFstflags(idx).Log
case "oflags":
logger = logOflags(idx).Log
case "fs_rights_base":
@@ -333,6 +336,13 @@ func (i logOflags) Log(_ context.Context, _ api.Module, w logging.Writer, params
w.WriteString(OflagsString(int(params[i]))) //nolint
}
type logFstflags int
func (i logFstflags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
w.WriteString("fst_flags=") //nolint
w.WriteString(FstflagsString(int(params[i]))) //nolint
}
func resultParamName(name string) string {
return name[7:] // without "result."
}

View File

@@ -22,7 +22,7 @@ brevity.
When Rust compiles a `%.rs` file with a `wasm32-*` target, the output `%.wasm`
depends on a subset of features in the [WebAssembly 1.0 Core specification]
({{< ref "/specs#core" >}}). The `wasm32-wasi` target depends on [WASI]
({{< ref "/specs#core" >}}). The [wasm32-wasi][15] target depends on [WASI]
({{< ref "/specs#wasi" >}}) host functions as well.
Unlike some compilers, Rust also supports importing custom host functions and
@@ -227,3 +227,4 @@ source code may reduce binary size further.
[12]: https://github.com/rustwasm/wee_alloc
[13]: https://doc.rust-lang.org/cargo/reference/profiles.html#profile-settings
[14]: https://github.com/bytecodealliance/cargo-wasi
[15]: https://github.com/rust-lang/rust/tree/1.68.0/library/std/src/sys/wasi

View File

@@ -76,8 +76,9 @@ complete the below chart. If you desire something not yet implemented, please
your use case (ex which language you are using to compile, a.k.a. target Wasm).
Notes:
* C (via clang) supports the maximum WASI functions due to [wasi-libc][16]
* AssemblyScript has its own ABI which can optionally use [wasi-shim][17]
* C (via clang) supports the maximum WASI functions due to [wasi-libc][16]
* Rust supports WASI via its [wasm32-wasi][18] target.
<details><summary>Click to see the full list of supported WASI functions</summary>
<p>
@@ -113,7 +114,7 @@ Notes:
| fd_write | ✅ | Rust,TinyGo,Zig |
| path_create_directory | ✅ | Rust,TinyGo,Zig |
| path_filestat_get | ✅ | Rust,TinyGo,Zig |
| path_filestat_set_times | | |
| path_filestat_set_times | | Rust,libc |
| path_link | ✅ | Rust,Zig |
| path_open | ✅ | Rust,TinyGo,Zig |
| path_readlink | ✅ | Rust,Zig |
@@ -152,3 +153,4 @@ Note: 💀 means the function was later removed from WASI.
[15]: https://github.com/WebAssembly/WASI/pull/458
[16]: https://github.com/WebAssembly/wasi-libc
[17]: https://github.com/AssemblyScript/wasi-shim
[18]: https://github.com/rust-lang/rust/tree/1.68.0/library/std/src/sys/wasi