wasi: implements fd_fdstat_get and adds stdio support to fd_filestat_get (#895)
This implements fd_fdstat_get and furthers implementation of fd_fdstat_get. Notably, this does the following: * Ensures stdio are treated as character devices. * Ensures someone can stat the root FD (4). * Fixes encoding as we didn't clear zeros when encoding numbers. Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
6
Makefile
6
Makefile
@@ -70,7 +70,7 @@ build.examples.tinygo: $(tinygo_sources)
|
||||
tinygo build -o internal/testing/dwarftestdata/testdata/main.wasm -scheduler=none --target=wasi internal/testing/dwarftestdata/testdata/main.go
|
||||
|
||||
# We use zig to build C as it is easy to install and embeds a copy of zig-cc.
|
||||
c_sources := imports/wasi_snapshot_preview1/example/testdata/zig-cc/cat.c imports/wasi_snapshot_preview1/testdata/zig-cc/ls.c
|
||||
c_sources := imports/wasi_snapshot_preview1/example/testdata/zig-cc/cat.c imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.c
|
||||
.PHONY: build.examples.zig-cc
|
||||
build.examples.zig-cc: $(c_sources)
|
||||
@for f in $^; do \
|
||||
@@ -105,10 +105,10 @@ build.examples.emscripten: $(emscripten_sources)
|
||||
|
||||
%/greet.wasm : cargo_target := wasm32-unknown-unknown
|
||||
%/cat.wasm : cargo_target := wasm32-wasi
|
||||
%/ls.wasm : cargo_target := wasm32-wasi
|
||||
%/wasi.wasm : cargo_target := wasm32-wasi
|
||||
|
||||
.PHONY: build.examples.rust
|
||||
build.examples.rust: examples/allocation/rust/testdata/greet.wasm imports/wasi_snapshot_preview1/example/testdata/cargo-wasi/cat.wasm imports/wasi_snapshot_preview1/testdata/cargo-wasi/ls.wasm
|
||||
build.examples.rust: examples/allocation/rust/testdata/greet.wasm imports/wasi_snapshot_preview1/example/testdata/cargo-wasi/cat.wasm imports/wasi_snapshot_preview1/testdata/cargo-wasi/wasi.wasm
|
||||
|
||||
# Builds rust using cargo normally, or cargo-wasi.
|
||||
%.wasm: %.rs
|
||||
|
||||
@@ -2,7 +2,6 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -50,7 +49,7 @@ const (
|
||||
pathUnlinkFileName = "path_unlink_file"
|
||||
)
|
||||
|
||||
// fdAdvise is the WASI function named functionFdAdvise which provides file
|
||||
// fdAdvise is the WASI function named fdAdviseName which provides file
|
||||
// advisory information on a file descriptor.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_advisefd-fd-offset-filesize-len-filesize-advice-advice---errno
|
||||
@@ -60,7 +59,7 @@ var fdAdvise = stubFunction(
|
||||
"fd", "offset", "len", "result.advice",
|
||||
)
|
||||
|
||||
// fdAllocate is the WASI function named functionFdAllocate which forces the
|
||||
// fdAllocate is the WASI function named fdAllocateName which forces the
|
||||
// allocation of space in a file.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_allocatefd-fd-offset-filesize-len-filesize---errno
|
||||
@@ -70,7 +69,7 @@ var fdAllocate = stubFunction(
|
||||
"fd", "offset", "len",
|
||||
)
|
||||
|
||||
// fdClose is the WASI function named functionFdClose which closes a file
|
||||
// fdClose is the WASI function named fdCloseName which closes a file
|
||||
// descriptor.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -97,13 +96,13 @@ func fdCloseFn(ctx context.Context, mod api.Module, params []uint64) Errno {
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// fdDatasync is the WASI function named functionFdDatasync which synchronizes
|
||||
// fdDatasync is the WASI function named fdDatasyncName which synchronizes
|
||||
// the data of a file to disk.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_datasyncfd-fd---errno
|
||||
var fdDatasync = stubFunction(fdDatasyncName, []wasm.ValueType{i32}, "fd")
|
||||
|
||||
// fdFdstatGet is the WASI function named functionFdFdstatGet which returns the
|
||||
// fdFdstatGet is the WASI function named fdFdstatGetName which returns the
|
||||
// attributes of a file descriptor.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -143,17 +142,84 @@ var fdDatasync = stubFunction(fdDatasyncName, []wasm.ValueType{i32}, "fd")
|
||||
var fdFdstatGet = newHostFunc(fdFdstatGetName, fdFdstatGetFn, []api.ValueType{i32, i32}, "fd", "result.stat")
|
||||
|
||||
func fdFdstatGetFn(ctx context.Context, mod api.Module, params []uint64) Errno {
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
// TODO: actually write the fdstat!
|
||||
fd, _ := uint32(params[0]), uint32(params[1])
|
||||
fd, resultFdstat := uint32(params[0]), uint32(params[1])
|
||||
|
||||
if _, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok {
|
||||
// Ensure we can write the fdstat
|
||||
buf, ok := mod.Memory().Read(ctx, resultFdstat, 24)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
// Special-case the stdio character devices
|
||||
if fd <= internalsys.FdStderr {
|
||||
switch fd {
|
||||
case internalsys.FdStdin:
|
||||
copy(buf, charFdstat)
|
||||
case internalsys.FdStdout, internalsys.FdStderr:
|
||||
copy(buf, charOutFdstat)
|
||||
}
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// Otherwise, look up the file corresponding to the file descriptor.
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
file, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd)
|
||||
if !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
// see if the file is writable
|
||||
fdflags := wasiFdflagsNone
|
||||
filetype := wasiFiletypeDirectory // root
|
||||
if f := file.File; f != nil { // not root
|
||||
if _, ok := f.(io.Writer); ok {
|
||||
fdflags = wasiFdflagsAppend
|
||||
}
|
||||
|
||||
if fdstat, err := f.Stat(); err != nil {
|
||||
return ErrnoIo
|
||||
} else {
|
||||
filetype = getWasiFiletype(fdstat.Mode())
|
||||
}
|
||||
}
|
||||
|
||||
writeFdstat(buf, filetype, fdflags)
|
||||
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// fdFdstatSetFlags is the WASI function named functionFdFdstatSetFlags which
|
||||
type wasiFdflags = byte // actually 16-bit, but there aren't that many.
|
||||
const (
|
||||
wasiFdflagsNone wasiFdflags = iota
|
||||
wasiFdflagsAppend
|
||||
wasiFdflagsDsync
|
||||
wasiFdflagsNonblock
|
||||
wasiFdflagsRsync
|
||||
wasiFdflagsSync
|
||||
)
|
||||
|
||||
var charFdstat = []byte{
|
||||
byte(wasiFiletypeCharacterDevice), 0, // filetype
|
||||
0, 0, 0, 0, 0, 0, // fdflags
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
}
|
||||
|
||||
var charOutFdstat = []byte{
|
||||
byte(wasiFiletypeCharacterDevice), 0, // filetype
|
||||
wasiFdflagsAppend, 0, 0, 0, 0, 0, // fdflags
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
}
|
||||
|
||||
func writeFdstat(buf []byte, filetype wasiFiletype, fdflags wasiFdflags) {
|
||||
// memory is re-used, so ensure the result is defaulted.
|
||||
copy(buf, charFdstat)
|
||||
buf[0] = uint8(filetype)
|
||||
buf[2] = fdflags
|
||||
}
|
||||
|
||||
// fdFdstatSetFlags is the WASI function named fdFdstatSetFlagsName which
|
||||
// adjusts the flags associated with a file descriptor.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_fdstat_set_flagsfd-fd-flags-fdflags---errnoand is stubbed for GrainLang per #271
|
||||
@@ -168,7 +234,7 @@ var fdFdstatSetRights = stubFunction(
|
||||
"fd", "fs_rights_base", "fs_rights_inheriting",
|
||||
)
|
||||
|
||||
// fdFilestatGet is the WASI function named functionFdFilestatGet which returns
|
||||
// fdFilestatGet is the WASI function named fdFilestatGetName which returns
|
||||
// the stat attributes of an open file.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -208,7 +274,7 @@ var fdFdstatSetRights = stubFunction(
|
||||
// The following properties of filestat are not implemented:
|
||||
// - dev: not supported by Golang FS
|
||||
// - ino: not supported by Golang FS
|
||||
// - nlink: not supported by Golang FS
|
||||
// - nlink: not supported by Golang FS, we use 1
|
||||
// - atime: not supported by Golang FS, we use mtim for this
|
||||
// - ctim: not supported by Golang FS, we use mtim for this
|
||||
//
|
||||
@@ -222,6 +288,12 @@ type wasiFiletype uint8
|
||||
const (
|
||||
wasiFiletypeUnknown wasiFiletype = iota
|
||||
wasiFiletypeBlockDevice
|
||||
// wasiFiletypeCharacterDevice is set when the FD is a character device.
|
||||
//
|
||||
// Note: wazero currently returns this for stdio descriptors even if the
|
||||
// actual file is not a TTY, to ensure python can work. This avoids
|
||||
// dependencies needed to be more precise.
|
||||
// See https://github.com/mattn/go-isatty
|
||||
wasiFiletypeCharacterDevice
|
||||
wasiFiletypeDirectory
|
||||
wasiFiletypeRegularFile
|
||||
@@ -235,49 +307,85 @@ func fdFilestatGetFn(ctx context.Context, mod api.Module, params []uint64) Errno
|
||||
}
|
||||
|
||||
func fdFilestatGetFunc(ctx context.Context, mod api.Module, fd, resultBuf uint32) Errno {
|
||||
// Ensure we can write the filestat
|
||||
buf, ok := mod.Memory().Read(ctx, resultBuf, 64)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
// Special-case the stdio character devices
|
||||
switch fd {
|
||||
case internalsys.FdStdin, internalsys.FdStdout, internalsys.FdStderr:
|
||||
copy(buf, charFilestat)
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// Otherwise, look up the file corresponding to the file descriptor.
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
file, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd)
|
||||
if !ok {
|
||||
return ErrnoBadf
|
||||
}
|
||||
|
||||
fileStat, err := file.File.Stat()
|
||||
if err != nil {
|
||||
return ErrnoIo
|
||||
var filetype wasiFiletype
|
||||
var filesize uint64
|
||||
var mtim int64
|
||||
if f := file.File; f != nil { // not root
|
||||
if stat, err := f.Stat(); err != nil {
|
||||
return ErrnoIo
|
||||
} else {
|
||||
filetype = getWasiFiletype(stat.Mode())
|
||||
filesize = uint64(stat.Size())
|
||||
mtim = stat.ModTime().UnixNano()
|
||||
}
|
||||
} else { // root
|
||||
filetype = wasiFiletypeDirectory
|
||||
// TODO: support filesize and mtime when root
|
||||
}
|
||||
|
||||
fileMode := fileStat.Mode()
|
||||
|
||||
wasiFileMode := wasiFiletypeUnknown
|
||||
if fileMode&fs.ModeDevice != 0 {
|
||||
wasiFileMode = wasiFiletypeBlockDevice
|
||||
} else if fileMode&fs.ModeCharDevice != 0 {
|
||||
wasiFileMode = wasiFiletypeCharacterDevice
|
||||
} else if fileMode&fs.ModeDir != 0 {
|
||||
wasiFileMode = wasiFiletypeDirectory
|
||||
} else if fileMode&fs.ModeType == 0 {
|
||||
wasiFileMode = wasiFiletypeRegularFile
|
||||
} else if fileMode&fs.ModeSymlink != 0 {
|
||||
wasiFileMode = wasiFiletypeSymbolicLink
|
||||
}
|
||||
|
||||
buf, ok := mod.Memory().Read(ctx, resultBuf, 64)
|
||||
if !ok {
|
||||
return ErrnoFault
|
||||
}
|
||||
|
||||
buf[16] = uint8(wasiFileMode)
|
||||
size := uint64(fileStat.Size())
|
||||
binary.LittleEndian.PutUint64(buf[32:], size)
|
||||
mtim := uint64(fileStat.ModTime().UnixNano())
|
||||
binary.LittleEndian.PutUint64(buf[40:], mtim)
|
||||
binary.LittleEndian.PutUint64(buf[48:], mtim)
|
||||
binary.LittleEndian.PutUint64(buf[56:], mtim)
|
||||
writeFilestat(buf, filetype, filesize, mtim)
|
||||
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// fdFilestatSetSize is the WASI function named functionFdFilestatSetSize which
|
||||
func getWasiFiletype(fileMode fs.FileMode) wasiFiletype {
|
||||
wasiFileType := wasiFiletypeUnknown
|
||||
if fileMode&fs.ModeDevice != 0 {
|
||||
wasiFileType = wasiFiletypeBlockDevice
|
||||
} else if fileMode&fs.ModeCharDevice != 0 {
|
||||
wasiFileType = wasiFiletypeCharacterDevice
|
||||
} else if fileMode&fs.ModeDir != 0 {
|
||||
wasiFileType = wasiFiletypeDirectory
|
||||
} else if fileMode&fs.ModeType == 0 {
|
||||
wasiFileType = wasiFiletypeRegularFile
|
||||
} else if fileMode&fs.ModeSymlink != 0 {
|
||||
wasiFileType = wasiFiletypeSymbolicLink
|
||||
}
|
||||
return wasiFileType
|
||||
}
|
||||
|
||||
var charFilestat = []byte{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // device
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // inode
|
||||
byte(wasiFiletypeCharacterDevice), 0, 0, 0, 0, 0, 0, 0, // filetype
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // filesize
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // atim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // mtim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ctim
|
||||
}
|
||||
|
||||
func writeFilestat(buf []byte, filetype wasiFiletype, filesize uint64, mtim int64) {
|
||||
// memory is re-used, so ensure the result is defaulted.
|
||||
copy(buf, charFilestat[:32])
|
||||
buf[16] = uint8(filetype)
|
||||
le.PutUint64(buf[32:], filesize) // filesize
|
||||
le.PutUint64(buf[40:], uint64(mtim)) // atim
|
||||
le.PutUint64(buf[48:], uint64(mtim)) // mtim
|
||||
le.PutUint64(buf[56:], uint64(mtim)) // ctim
|
||||
}
|
||||
|
||||
// fdFilestatSetSize is the WASI function named fdFilestatSetSizeName which
|
||||
// adjusts the size of an open file.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_set_sizefd-fd-size-filesize---errno
|
||||
@@ -293,7 +401,7 @@ var fdFilestatSetTimes = stubFunction(
|
||||
"fd", "atim", "mtim", "fst_flags",
|
||||
)
|
||||
|
||||
// fdPread is the WASI function named functionFdPread which reads from a file
|
||||
// fdPread is the WASI function named fdPreadName which reads from a file
|
||||
// descriptor, without using and updating the file descriptor's offset.
|
||||
//
|
||||
// Except for handling offset, this implementation is identical to fdRead.
|
||||
@@ -309,7 +417,7 @@ func fdPreadFn(ctx context.Context, mod api.Module, params []uint64) Errno {
|
||||
return fdReadOrPread(ctx, mod, params, true)
|
||||
}
|
||||
|
||||
// fdPrestatGet is the WASI function named functionFdPrestatGet which returns
|
||||
// fdPrestatGet is the WASI function named fdPrestatGetName which returns
|
||||
// the prestat data of a file descriptor.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -363,7 +471,7 @@ func fdPrestatGetFn(ctx context.Context, mod api.Module, params []uint64) Errno
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// fdPrestatDirName is the WASI function named functionFdPrestatDirName which
|
||||
// fdPrestatDirName is the WASI function named fdPrestatDirNameName which
|
||||
// returns the path of the pre-opened directory of a file descriptor.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -421,7 +529,7 @@ func fdPrestatDirNameFn(ctx context.Context, mod api.Module, params []uint64) Er
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// fdPwrite is the WASI function named functionFdPwrite which writes to a file
|
||||
// fdPwrite is the WASI function named fdPwriteName which writes to a file
|
||||
// descriptor, without using and updating the file descriptor's offset.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_pwritefd-fd-iovs-ciovec_array-offset-filesize---errno-size
|
||||
@@ -431,7 +539,7 @@ var fdPwrite = stubFunction(
|
||||
"fd", "iovs", "iovs_len", "offset", "result.nwritten",
|
||||
)
|
||||
|
||||
// fdRead is the WASI function named functionFdRead which reads from a file
|
||||
// fdRead is the WASI function named fdReadName which reads from a file
|
||||
// descriptor.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -571,7 +679,7 @@ func fdRead_shouldContinueRead(n, l uint32, err error) (bool, Errno) {
|
||||
return n == l && n != 0, ErrnoSuccess
|
||||
}
|
||||
|
||||
// fdReaddir is the WASI function named functionFdReaddir which reads directory
|
||||
// fdReaddir is the WASI function named fdReaddirName which reads directory
|
||||
// entries from a directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_readdirfd-fd-buf-pointeru8-buf_len-size-cookie-dircookie---errno-size
|
||||
@@ -806,15 +914,15 @@ func writeDirents(
|
||||
|
||||
// writeDirent writes direntSize bytes
|
||||
func writeDirent(buf []byte, dNext uint64, dNamlen uint32, dType bool) {
|
||||
binary.LittleEndian.PutUint64(buf, dNext) // d_next
|
||||
binary.LittleEndian.PutUint64(buf[8:], 0) // no d_ino
|
||||
binary.LittleEndian.PutUint32(buf[16:], dNamlen) // d_namlen
|
||||
le.PutUint64(buf, dNext) // d_next
|
||||
le.PutUint64(buf[8:], 0) // no d_ino
|
||||
le.PutUint32(buf[16:], dNamlen) // d_namlen
|
||||
|
||||
filetype := wasiFiletypeRegularFile
|
||||
if dType {
|
||||
filetype = wasiFiletypeDirectory
|
||||
}
|
||||
binary.LittleEndian.PutUint32(buf[20:], uint32(filetype)) // d_type
|
||||
le.PutUint32(buf[20:], uint32(filetype)) // d_type
|
||||
}
|
||||
|
||||
// openedDir returns the directory and ErrnoSuccess if the fd points to a readable directory.
|
||||
@@ -832,13 +940,13 @@ func openedDir(ctx context.Context, mod api.Module, fd uint32) (fs.ReadDirFile,
|
||||
}
|
||||
}
|
||||
|
||||
// fdRenumber is the WASI function named functionFdRenumber which atomically
|
||||
// fdRenumber is the WASI function named fdRenumberName which atomically
|
||||
// replaces a file descriptor by renumbering another file descriptor.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_renumberfd-fd-to-fd---errno
|
||||
var fdRenumber = stubFunction(fdRenumberName, []wasm.ValueType{i32, i32}, "fd", "to")
|
||||
|
||||
// fdSeek is the WASI function named functionFdSeek which moves the offset of a
|
||||
// fdSeek is the WASI function named fdSeekName which moves the offset of a
|
||||
// file descriptor.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -911,19 +1019,19 @@ func fdSeekFn(ctx context.Context, mod api.Module, params []uint64) Errno {
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// fdSync is the WASI function named functionFdSync which synchronizes the data
|
||||
// fdSync is the WASI function named fdSyncName which synchronizes the data
|
||||
// and metadata of a file to disk.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_syncfd-fd---errno
|
||||
var fdSync = stubFunction(fdSyncName, []wasm.ValueType{i32}, "fd")
|
||||
|
||||
// fdTell is the WASI function named functionFdTell which returns the current
|
||||
// fdTell is the WASI function named fdTellName which returns the current
|
||||
// offset of a file descriptor.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_tellfd-fd---errno-filesize
|
||||
var fdTell = stubFunction(fdTellName, []wasm.ValueType{i32, i32}, "fd", "result.offset")
|
||||
|
||||
// fdWrite is the WASI function named functionFdWrite which writes to a file
|
||||
// fdWrite is the WASI function named fdWriteName which writes to a file
|
||||
// descriptor.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -1034,7 +1142,7 @@ func fdWriteFn(ctx context.Context, mod api.Module, params []uint64) Errno {
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// pathCreateDirectory is the WASI function named functionPathCreateDirectory
|
||||
// pathCreateDirectory is the WASI function named pathCreateDirectoryName
|
||||
// which creates a directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_create_directoryfd-fd-path-string---errno
|
||||
@@ -1044,7 +1152,7 @@ var pathCreateDirectory = stubFunction(
|
||||
"fd", "path", "path_len",
|
||||
)
|
||||
|
||||
// pathFilestatGet is the WASI function named functionPathFilestatGet which
|
||||
// pathFilestatGet is the WASI function named pathFilestatGetName which
|
||||
// returns the stat attributes of a file or directory.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -1119,7 +1227,7 @@ func pathFilestatGetFn(ctx context.Context, mod api.Module, params []uint64) Err
|
||||
return fdFilestatGetFunc(ctx, mod, pathFd, resultBuf)
|
||||
}
|
||||
|
||||
// pathFilestatSetTimes is the WASI function named functionPathFilestatSetTimes
|
||||
// pathFilestatSetTimes is the WASI function named pathFilestatSetTimesName
|
||||
// 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
|
||||
@@ -1129,7 +1237,7 @@ var pathFilestatSetTimes = stubFunction(
|
||||
"fd", "flags", "path", "path_len", "atim", "mtim", "fst_flags",
|
||||
)
|
||||
|
||||
// pathLink is the WASI function named functionPathLink which adjusts the
|
||||
// pathLink is the WASI function named pathLinkName which adjusts the
|
||||
// timestamps of a file or directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#path_link
|
||||
@@ -1139,7 +1247,7 @@ var pathLink = stubFunction(
|
||||
"old_fd", "old_flags", "old_path", "old_path_len", "new_fd", "new_path", "new_path_len",
|
||||
)
|
||||
|
||||
// pathOpen is the WASI function named functionPathOpen which opens a file or
|
||||
// pathOpen is the WASI function named pathOpenName which opens a file or
|
||||
// directory. This returns ErrnoBadf if the fd is invalid.
|
||||
//
|
||||
// # Parameters
|
||||
@@ -1233,7 +1341,7 @@ func pathOpenFn(ctx context.Context, mod api.Module, params []uint64) Errno {
|
||||
return ErrnoSuccess
|
||||
}
|
||||
|
||||
// pathReadlink is the WASI function named functionPathReadlink that reads the
|
||||
// pathReadlink is the WASI function named pathReadlinkName that reads the
|
||||
// contents of a symbolic link.
|
||||
//
|
||||
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_readlinkfd-fd-path-string-buf-pointeru8-buf_len-size---errno-size
|
||||
@@ -1243,7 +1351,7 @@ var pathReadlink = stubFunction(
|
||||
"fd", "path", "path_len", "buf", "buf_len", "result.bufused",
|
||||
)
|
||||
|
||||
// pathRemoveDirectory is the WASI function named functionPathRemoveDirectory
|
||||
// pathRemoveDirectory is the WASI function named pathRemoveDirectoryName
|
||||
// which removes a directory.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_remove_directoryfd-fd-path-string---errno
|
||||
@@ -1253,7 +1361,7 @@ var pathRemoveDirectory = stubFunction(
|
||||
"fd", "path", "path_len",
|
||||
)
|
||||
|
||||
// pathRename is the WASI function named functionPathRename which renames a
|
||||
// pathRename is the WASI function named pathRenameName which renames a
|
||||
// file or directory.
|
||||
var pathRename = stubFunction(
|
||||
pathRenameName,
|
||||
@@ -1261,7 +1369,7 @@ var pathRename = stubFunction(
|
||||
"fd", "old_path", "old_path_len", "new_fd", "new_path", "new_path_len",
|
||||
)
|
||||
|
||||
// pathSymlink is the WASI function named functionPathSymlink which creates a
|
||||
// pathSymlink is the WASI function named pathSymlinkName which creates a
|
||||
// symbolic link.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#path_symlink
|
||||
@@ -1271,7 +1379,7 @@ var pathSymlink = stubFunction(
|
||||
"old_path", "old_path_len", "fd", "new_path", "new_path_len",
|
||||
)
|
||||
|
||||
// pathUnlinkFile is the WASI function named functionPathUnlinkFile which
|
||||
// pathUnlinkFile is the WASI function named pathUnlinkFileName which
|
||||
// unlinks a file.
|
||||
//
|
||||
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_unlink_filefd-fd-path-string---errno
|
||||
|
||||
@@ -100,7 +100,7 @@ func Test_fdDatasync(t *testing.T) {
|
||||
|
||||
func Test_fdFdstatGet(t *testing.T) {
|
||||
file, dir := "a", "b"
|
||||
testFS := fstest.MapFS{file: {Data: make([]byte, 0)}, dir: {Mode: fs.ModeDir}}
|
||||
testFS := fstest.MapFS{file: {Data: make([]byte, 10), ModTime: time.Unix(1667482413, 0)}, dir: {Mode: fs.ModeDir, ModTime: time.Unix(1667482413, 0)}}
|
||||
|
||||
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig().WithFS(testFS))
|
||||
defer r.Close(testCtx)
|
||||
@@ -116,16 +116,85 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fd, resultStat uint32
|
||||
// TODO: expectedMem
|
||||
expectedErrno Errno
|
||||
expectedLog string
|
||||
name string
|
||||
fd, resultFdstat uint32
|
||||
expectedMemory []byte
|
||||
expectedErrno Errno
|
||||
expectedLog string
|
||||
}{
|
||||
{
|
||||
name: "stdin",
|
||||
fd: internalsys.FdStdin,
|
||||
expectedMemory: []byte{
|
||||
2, 0, // fs_filetype
|
||||
0, 0, 0, 0, 0, 0, // fs_flags
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_fdstat_get(fd=0,result.stat=0)
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=0,result.stat=0)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "stdout",
|
||||
fd: internalsys.FdStdout,
|
||||
expectedMemory: []byte{
|
||||
2, 0, // fs_filetype
|
||||
1, 0, 0, 0, 0, 0, // fs_flags
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_fdstat_get(fd=1,result.stat=0)
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=1,result.stat=0)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "stderr",
|
||||
fd: internalsys.FdStderr,
|
||||
expectedMemory: []byte{
|
||||
2, 0, // fs_filetype
|
||||
1, 0, 0, 0, 0, 0, // fs_flags
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_fdstat_get(fd=2,result.stat=0)
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=2,result.stat=0)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "root",
|
||||
fd: internalsys.FdStderr + 1,
|
||||
expectedMemory: []byte{
|
||||
3, 0, // fs_filetype
|
||||
0, 0, 0, 0, 0, 0, // fs_flags
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_fdstat_get(fd=3,result.stat=0)
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=3,result.stat=0)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
fd: fileFd,
|
||||
// TODO: expectedMem for a file
|
||||
expectedMemory: []byte{
|
||||
4, 0, // fs_filetype
|
||||
0, 0, 0, 0, 0, 0, // fs_flags
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_fdstat_get(fd=4,result.stat=0)
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=4,result.stat=0)
|
||||
@@ -136,7 +205,12 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
{
|
||||
name: "dir",
|
||||
fd: dirFd,
|
||||
// TODO: expectedMem for a dir
|
||||
expectedMemory: []byte{
|
||||
3, 0, // fs_filetype
|
||||
0, 0, 0, 0, 0, 0, // fs_flags
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_base
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // fs_rights_inheriting
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_fdstat_get(fd=5,result.stat=0)
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=5,result.stat=0)
|
||||
@@ -156,15 +230,15 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "resultStat exceeds the maximum valid address by 1",
|
||||
fd: dirFd,
|
||||
resultStat: memorySize - 24 + 1,
|
||||
// TODO: ErrnoFault
|
||||
name: "resultFdstat exceeds the maximum valid address by 1",
|
||||
fd: dirFd,
|
||||
resultFdstat: memorySize - 24 + 1,
|
||||
expectedErrno: ErrnoFault,
|
||||
expectedLog: `
|
||||
--> proxy.fd_fdstat_get(fd=5,result.stat=65513)
|
||||
==> wasi_snapshot_preview1.fd_fdstat_get(fd=5,result.stat=65513)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
<== EFAULT
|
||||
<-- 21
|
||||
`,
|
||||
},
|
||||
}
|
||||
@@ -175,8 +249,14 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer log.Reset()
|
||||
|
||||
requireErrno(t, tc.expectedErrno, mod, fdFdstatGetName, uint64(tc.fd), uint64(tc.resultStat))
|
||||
maskMemory(t, testCtx, mod, len(tc.expectedMemory))
|
||||
|
||||
requireErrno(t, tc.expectedErrno, mod, fdFdstatGetName, uint64(tc.fd), uint64(tc.resultFdstat))
|
||||
require.Equal(t, tc.expectedLog, "\n"+log.String())
|
||||
|
||||
actual, ok := mod.Memory().Read(testCtx, 0, uint32(len(tc.expectedMemory)))
|
||||
require.True(t, ok)
|
||||
require.Equal(t, tc.expectedMemory, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -227,14 +307,94 @@ func Test_fdFilestatGet(t *testing.T) {
|
||||
expectedErrno Errno
|
||||
expectedLog string
|
||||
}{
|
||||
{
|
||||
name: "stdin",
|
||||
fd: internalsys.FdStdin,
|
||||
expectedMemory: []byte{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
2, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // size
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // atim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // mtim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ctim
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_filestat_get(fd=0,result.buf=0)
|
||||
==> wasi_snapshot_preview1.fd_filestat_get(fd=0,result.buf=0)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "stdout",
|
||||
fd: internalsys.FdStdout,
|
||||
expectedMemory: []byte{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
2, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // size
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // atim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // mtim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ctim
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_filestat_get(fd=1,result.buf=0)
|
||||
==> wasi_snapshot_preview1.fd_filestat_get(fd=1,result.buf=0)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "stderr",
|
||||
fd: internalsys.FdStderr,
|
||||
expectedMemory: []byte{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
2, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // size
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // atim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // mtim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ctim
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_filestat_get(fd=2,result.buf=0)
|
||||
==> wasi_snapshot_preview1.fd_filestat_get(fd=2,result.buf=0)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "root",
|
||||
fd: internalsys.FdStderr + 1,
|
||||
expectedMemory: []byte{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
3, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // TODO: size
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // TODO: atim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // TODO: mtim
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // TODO: ctim
|
||||
},
|
||||
expectedLog: `
|
||||
--> proxy.fd_filestat_get(fd=3,result.buf=0)
|
||||
==> wasi_snapshot_preview1.fd_filestat_get(fd=3,result.buf=0)
|
||||
<== ESUCCESS
|
||||
<-- 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
fd: fileFd,
|
||||
expectedMemory: []byte{
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // dev
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // ino
|
||||
4, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
4, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
10, 0, 0, 0, 0, 0, 0, 0, // size
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
|
||||
@@ -251,10 +411,10 @@ func Test_fdFilestatGet(t *testing.T) {
|
||||
name: "dir",
|
||||
fd: dirFd,
|
||||
expectedMemory: []byte{
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // dev
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // ino
|
||||
3, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
3, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // size
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
|
||||
@@ -2167,10 +2327,10 @@ func Test_pathFilestatGet(t *testing.T) {
|
||||
resultFilestat: 2,
|
||||
expectedMemory: append(
|
||||
initialMemoryFile,
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // dev
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // ino
|
||||
4, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
4, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
10, 0, 0, 0, 0, 0, 0, 0, // size
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
|
||||
@@ -2191,10 +2351,10 @@ func Test_pathFilestatGet(t *testing.T) {
|
||||
resultFilestat: 2,
|
||||
expectedMemory: append(
|
||||
initialMemoryFile,
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // dev
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // ino
|
||||
4, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
4, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
20, 0, 0, 0, 0, 0, 0, 0, // size
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
|
||||
@@ -2215,10 +2375,10 @@ func Test_pathFilestatGet(t *testing.T) {
|
||||
resultFilestat: 2,
|
||||
expectedMemory: append(
|
||||
initialMemoryDir,
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // dev
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // ino
|
||||
3, '?', '?', '?', '?', '?', '?', '?', // filetype + padding
|
||||
'?', '?', '?', '?', '?', '?', '?', '?', // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // dev
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // ino
|
||||
3, 0, 0, 0, 0, 0, 0, 0, // filetype + padding
|
||||
1, 0, 0, 0, 0, 0, 0, 0, // nlink
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // size
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // atim
|
||||
0x0, 0x82, 0x13, 0x80, 0x6b, 0x16, 0x24, 0x17, // mtim
|
||||
|
||||
@@ -2,7 +2,6 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
internalsys "github.com/tetratelabs/wazero/internal/sys"
|
||||
@@ -104,7 +103,7 @@ func pollOneoffFn(ctx context.Context, mod api.Module, params []uint64) Errno {
|
||||
copy(outBuf, inBuf[inOffset:inOffset+8]) // userdata
|
||||
outBuf[outOffset+8] = byte(errno) // uint16, but safe as < 255
|
||||
outBuf[outOffset+9] = 0
|
||||
binary.LittleEndian.PutUint32(outBuf[outOffset+10:], uint32(eventType))
|
||||
le.PutUint32(outBuf[outOffset+10:], uint32(eventType))
|
||||
// TODO: When FD events are supported, write outOffset+16
|
||||
}
|
||||
return ErrnoSuccess
|
||||
@@ -113,10 +112,10 @@ func pollOneoffFn(ctx context.Context, mod api.Module, params []uint64) Errno {
|
||||
// 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(ctx context.Context, mod api.Module, inBuf []byte) Errno {
|
||||
_ /* ID */ = binary.LittleEndian.Uint32(inBuf[0:8]) // See below
|
||||
timeout := binary.LittleEndian.Uint64(inBuf[8:16]) // nanos if relative
|
||||
_ /* precision */ = binary.LittleEndian.Uint64(inBuf[16:24]) // Unused
|
||||
flags := binary.LittleEndian.Uint16(inBuf[24:32])
|
||||
_ /* ID */ = le.Uint32(inBuf[0:8]) // See below
|
||||
timeout := le.Uint64(inBuf[8:16]) // nanos if relative
|
||||
_ /* precision */ = le.Uint64(inBuf[16:24]) // Unused
|
||||
flags := le.Uint16(inBuf[24:32])
|
||||
|
||||
// subclockflags has only one flag defined: subscription_clock_abstime
|
||||
switch flags {
|
||||
@@ -139,7 +138,7 @@ func processClockEvent(ctx context.Context, mod api.Module, inBuf []byte) Errno
|
||||
// processFDEvent returns a validation error or ErrnoNotsup as file or socket
|
||||
// subscriptions are not yet supported.
|
||||
func processFDEvent(ctx context.Context, mod api.Module, eventType byte, inBuf []byte) Errno {
|
||||
fd := binary.LittleEndian.Uint32(inBuf)
|
||||
fd := le.Uint32(inBuf)
|
||||
sysCtx := mod.(*wasm.CallContext).Sys
|
||||
|
||||
// Choose the best error, which falls back to unsupported, until we support
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
[package]
|
||||
name = "ls"
|
||||
name = "wasi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "ls"
|
||||
path = "ls.rs"
|
||||
name = "wasi"
|
||||
path = "wasi.rs"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
use std::fs;
|
||||
|
||||
fn main() {
|
||||
for path in fs::read_dir(".").unwrap() {
|
||||
println!("{}", path.unwrap().path().display())
|
||||
}
|
||||
}
|
||||
Binary file not shown.
39
imports/wasi_snapshot_preview1/testdata/cargo-wasi/wasi.rs
vendored
Normal file
39
imports/wasi_snapshot_preview1/testdata/cargo-wasi/wasi.rs
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::process::exit;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
match args[1].as_str() {
|
||||
"ls" => {
|
||||
main_ls();
|
||||
},
|
||||
"stat" => {
|
||||
main_stat();
|
||||
},
|
||||
_ => {
|
||||
writeln!(io::stderr(), "unknown command: {}", args[1]).unwrap();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main_ls() {
|
||||
for path in fs::read_dir(".").unwrap() {
|
||||
println!("{}", path.unwrap().path().display())
|
||||
}
|
||||
}
|
||||
|
||||
extern crate libc;
|
||||
|
||||
fn main_stat() {
|
||||
unsafe{
|
||||
println!("stdin isatty: {}", libc::isatty(0) != 0);
|
||||
println!("stdout isatty: {}", libc::isatty(1) != 0);
|
||||
println!("stderr isatty: {}", libc::isatty(2) != 0);
|
||||
println!("/ isatty: {}", libc::isatty(3) != 0);
|
||||
}
|
||||
}
|
||||
BIN
imports/wasi_snapshot_preview1/testdata/cargo-wasi/wasi.wasm
vendored
Normal file
BIN
imports/wasi_snapshot_preview1/testdata/cargo-wasi/wasi.wasm
vendored
Normal file
Binary file not shown.
@@ -1,15 +0,0 @@
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
DIR *d;
|
||||
struct dirent *dir;
|
||||
d = opendir(".");
|
||||
if (d) {
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
printf("./%s\n", dir->d_name);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
Binary file not shown.
37
imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.c
vendored
Normal file
37
imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.c
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define formatBool(b) ((b) ? "true" : "false")
|
||||
|
||||
void main_ls() {
|
||||
DIR *d;
|
||||
struct dirent *dir;
|
||||
d = opendir(".");
|
||||
if (d) {
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
printf("./%s\n", dir->d_name);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
}
|
||||
|
||||
void main_stat() {
|
||||
printf("stdin isatty: %s\n", formatBool(isatty(0)));
|
||||
printf("stdout isatty: %s\n", formatBool(isatty(1)));
|
||||
printf("stderr isatty: %s\n", formatBool(isatty(2)));
|
||||
printf("/ isatty: %s\n", formatBool(isatty(3)));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (strcmp(argv[1],"ls")==0) {
|
||||
main_ls();
|
||||
} else if (strcmp(argv[1],"stat")==0) {
|
||||
main_stat();
|
||||
} else {
|
||||
fprintf(stderr, "unknown command: %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
BIN
imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.wasm
vendored
Executable file
BIN
imports/wasi_snapshot_preview1/testdata/zig-cc/wasi.wasm
vendored
Executable file
Binary file not shown.
@@ -18,6 +18,7 @@ package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
@@ -32,6 +33,8 @@ const (
|
||||
i32, i64 = wasm.ValueTypeI32, wasm.ValueTypeI64
|
||||
)
|
||||
|
||||
var le = binary.LittleEndian
|
||||
|
||||
// MustInstantiate calls Instantiate or panics on error.
|
||||
//
|
||||
// This is a simpler function for those who know the module ModuleName is not
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// package wasi_snapshot_preview1 ensures that the behavior we've implemented
|
||||
// not only matches the wasi spec, but also at least two compilers use of sdks.
|
||||
package wasi_snapshot_preview1
|
||||
|
||||
import (
|
||||
@@ -14,22 +16,20 @@ import (
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// lsWasmCargoWasi was compiled from testdata/cargo-wasi/ls.rs
|
||||
// wasmCargoWasi was compiled from testdata/cargo-wasi/wasi.rs
|
||||
//
|
||||
//go:embed testdata/cargo-wasi/ls.wasm
|
||||
var lsWasmCargoWasi []byte
|
||||
//go:embed testdata/cargo-wasi/wasi.wasm
|
||||
var wasmCargoWasi []byte
|
||||
|
||||
// lsZigCc was compiled from testdata/zig-cc/ls.c
|
||||
// wasmZigCc was compiled from testdata/zig-cc/wasi.c
|
||||
//
|
||||
//go:embed testdata/zig-cc/ls.wasm
|
||||
var lsZigCc []byte
|
||||
//go:embed testdata/zig-cc/wasi.wasm
|
||||
var wasmZigCc []byte
|
||||
|
||||
// Test_fdReaddir_ls ensures that the behavior we've implemented not only
|
||||
// matches the wasi spec, but also at least two compilers use of sdks.
|
||||
func Test_fdReaddir_ls(t *testing.T) {
|
||||
for toolchain, bin := range map[string][]byte{
|
||||
"cargo-wasi": lsWasmCargoWasi,
|
||||
"zig-cc": lsZigCc,
|
||||
"cargo-wasi": wasmCargoWasi,
|
||||
"zig-cc": wasmZigCc,
|
||||
} {
|
||||
toolchain := toolchain
|
||||
bin := bin
|
||||
@@ -40,16 +40,17 @@ func Test_fdReaddir_ls(t *testing.T) {
|
||||
}
|
||||
|
||||
func testFdReaddirLs(t *testing.T, bin []byte) {
|
||||
moduleConfig := wazero.NewModuleConfig().WithArgs("wasi", "ls")
|
||||
|
||||
t.Run("empty directory", func(t *testing.T) {
|
||||
stdout, stderr := compileAndRun(t, wazero.NewModuleConfig().
|
||||
WithFS(fstest.MapFS{}), bin)
|
||||
stdout, stderr := compileAndRun(t, moduleConfig.WithFS(fstest.MapFS{}), bin)
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.Zero(t, stdout)
|
||||
})
|
||||
|
||||
t.Run("directory with entries", func(t *testing.T) {
|
||||
stdout, stderr := compileAndRun(t, wazero.NewModuleConfig().
|
||||
stdout, stderr := compileAndRun(t, moduleConfig.
|
||||
WithFS(fstest.MapFS{
|
||||
"-": {},
|
||||
"a-": {Mode: fs.ModeDir},
|
||||
@@ -57,10 +58,11 @@ func testFdReaddirLs(t *testing.T, bin []byte) {
|
||||
}), bin)
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.Equal(t, `./-
|
||||
require.Equal(t, `
|
||||
./-
|
||||
./a-
|
||||
./ab-
|
||||
`, stdout)
|
||||
`, "\n"+stdout)
|
||||
})
|
||||
|
||||
t.Run("directory with tons of entries", func(t *testing.T) {
|
||||
@@ -69,8 +71,7 @@ func testFdReaddirLs(t *testing.T, bin []byte) {
|
||||
for i := 0; i < count; i++ {
|
||||
testFS[strconv.Itoa(i)] = &fstest.MapFile{}
|
||||
}
|
||||
stdout, stderr := compileAndRun(t, wazero.NewModuleConfig().
|
||||
WithFS(testFS), bin)
|
||||
stdout, stderr := compileAndRun(t, moduleConfig.WithFS(testFS), bin)
|
||||
|
||||
require.Zero(t, stderr)
|
||||
lines := strings.Split(stdout, "\n")
|
||||
@@ -78,6 +79,33 @@ func testFdReaddirLs(t *testing.T, bin []byte) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_fdReaddir_stat(t *testing.T) {
|
||||
for toolchain, bin := range map[string][]byte{
|
||||
"cargo-wasi": wasmCargoWasi,
|
||||
"zig-cc": wasmZigCc,
|
||||
} {
|
||||
toolchain := toolchain
|
||||
bin := bin
|
||||
t.Run(toolchain, func(t *testing.T) {
|
||||
testFdReaddirStat(t, bin)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testFdReaddirStat(t *testing.T, bin []byte) {
|
||||
moduleConfig := wazero.NewModuleConfig().WithArgs("wasi", "stat")
|
||||
|
||||
stdout, stderr := compileAndRun(t, moduleConfig.WithFS(fstest.MapFS{}), bin)
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.Equal(t, `
|
||||
stdin isatty: true
|
||||
stdout isatty: true
|
||||
stderr isatty: true
|
||||
/ isatty: false
|
||||
`, "\n"+stdout)
|
||||
}
|
||||
|
||||
func compileAndRun(t *testing.T, config wazero.ModuleConfig, bin []byte) (stdout, stderr string) {
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
|
||||
@@ -92,7 +120,7 @@ func compileAndRun(t *testing.T, config wazero.ModuleConfig, bin []byte) (stdout
|
||||
|
||||
_, err = r.InstantiateModule(testCtx, compiled, config.WithStdout(&stdoutBuf).WithStderr(&stderrBuf))
|
||||
if exitErr, ok := err.(*sys.ExitError); ok {
|
||||
require.Zero(t, exitErr.ExitCode())
|
||||
require.Zero(t, exitErr.ExitCode(), stderrBuf.String())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user