From 2c21f3aa8f87cb8c96c72d2a2235ecf6747a64b4 Mon Sep 17 00:00:00 2001 From: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com> Date: Thu, 6 Jul 2023 16:19:24 +0800 Subject: [PATCH] wasi: adds Go readdir integration tests for `GOOS=wasip1` and TinyGo (#1562) Signed-off-by: Adrian Cole --- Makefile | 2 +- imports/wasi_snapshot_preview1/fs.go | 4 +- imports/wasi_snapshot_preview1/fs_test.go | 2 +- imports/wasi_snapshot_preview1/poll_test.go | 10 +- .../testdata/gotip/wasi.go | 178 ++++++++++++------ .../testdata/tinygo/wasi.go | 86 +++++++++ .../testdata/tinygo/wasi.wasm | Bin 0 -> 109235 bytes .../wasi_stdlib_test.go | 159 +++++++++------- internal/sys/lazy.go | 24 +-- internal/sys/stdio.go | 20 +- internal/wasm/module_instance_test.go | 4 +- 11 files changed, 336 insertions(+), 153 deletions(-) create mode 100644 imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go create mode 100755 imports/wasi_snapshot_preview1/testdata/tinygo/wasi.wasm diff --git a/Makefile b/Makefile index 5d69367f..494b49e9 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ build.examples.as: build.examples.zig: examples/allocation/zig/testdata/greet.wasm imports/wasi_snapshot_preview1/example/testdata/zig/cat.wasm imports/wasi_snapshot_preview1/testdata/zig/wasi.wasm @cd internal/testing/dwarftestdata/testdata/zig; zig build; mv zig-out/*/main.wasm ./ # Need DWARF custom sections. -tinygo_sources := examples/basic/testdata/add.go examples/allocation/tinygo/testdata/greet.go examples/cli/testdata/cli.go imports/wasi_snapshot_preview1/example/testdata/tinygo/cat.go +tinygo_sources := examples/basic/testdata/add.go examples/allocation/tinygo/testdata/greet.go examples/cli/testdata/cli.go imports/wasi_snapshot_preview1/example/testdata/tinygo/cat.go imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go .PHONY: build.examples.tinygo build.examples.tinygo: $(tinygo_sources) @for f in $^; do \ diff --git a/imports/wasi_snapshot_preview1/fs.go b/imports/wasi_snapshot_preview1/fs.go index 946d0f09..efb1be63 100644 --- a/imports/wasi_snapshot_preview1/fs.go +++ b/imports/wasi_snapshot_preview1/fs.go @@ -747,7 +747,7 @@ type preader struct { offset int64 } -// Read implements the same function as documented on internalapi.File. +// Read implements the same function as documented on fsapi.File. func (w *preader) Read(buf []byte) (n int, errno syscall.Errno) { if len(buf) == 0 { return 0, 0 // less overhead on zero-length reads. @@ -1235,7 +1235,7 @@ type pwriter struct { offset int64 } -// Write implements the same function as documented on internalapi.File. +// Write implements the same function as documented on fsapi.File. func (w *pwriter) Write(buf []byte) (n int, errno syscall.Errno) { if len(buf) == 0 { return 0, 0 // less overhead on zero-length writes. diff --git a/imports/wasi_snapshot_preview1/fs_test.go b/imports/wasi_snapshot_preview1/fs_test.go index 4391f918..3de05b0a 100644 --- a/imports/wasi_snapshot_preview1/fs_test.go +++ b/imports/wasi_snapshot_preview1/fs_test.go @@ -241,7 +241,7 @@ func Test_fdFdstatGet(t *testing.T) { preopen := fsc.RootFS() // replace stdin with a fake TTY file. - // TODO: Make this easier once we have in-memory internalapi.File + // TODO: Make this easier once we have in-memory fsapi.File stdin, _ := fsc.LookupFile(sys.FdStdin) stdinFile, errno := sysfs.Adapt(&gofstest.MapFS{"stdin": &gofstest.MapFile{ Mode: fs.ModeDevice | fs.ModeCharDevice | 0o600, diff --git a/imports/wasi_snapshot_preview1/poll_test.go b/imports/wasi_snapshot_preview1/poll_test.go index f0920328..b0c0c3cb 100644 --- a/imports/wasi_snapshot_preview1/poll_test.go +++ b/imports/wasi_snapshot_preview1/poll_test.go @@ -528,12 +528,14 @@ func fdReadSubFd(fd byte) []byte { // subscription for an EventTypeFdRead on stdin var fdReadSub = fdReadSubFd(byte(sys.FdStdin)) -// ttyStat returns fs.ModeCharDevice as an approximation for isatty. +// ttyStat returns fs.ModeCharDevice | fs.ModeCharDevice as an approximation +// for isatty. +// // See go-isatty for a more specific approach: // https://github.com/mattn/go-isatty/blob/v0.0.18/isatty_tcgets.go#LL11C1-L12C1 type ttyStat struct{} -// Stat implements the same method as documented on internalapi.File +// Stat implements the same method as documented on fsapi.File func (ttyStat) Stat() (fsapi.Stat_t, syscall.Errno) { return fsapi.Stat_t{ Mode: fs.ModeDevice | fs.ModeCharDevice, @@ -551,7 +553,7 @@ type neverReadyTtyStdinFile struct { ttyStat } -// PollRead implements the same method as documented on internalapi.File +// PollRead implements the same method as documented on fsapi.File func (neverReadyTtyStdinFile) PollRead(timeout *time.Duration) (ready bool, errno syscall.Errno) { time.Sleep(*timeout) return false, 0 @@ -563,7 +565,7 @@ type pollStdinFile struct { ready bool } -// PollRead implements the same method as documented on internalapi.File +// PollRead implements the same method as documented on fsapi.File func (p *pollStdinFile) PollRead(*time.Duration) (ready bool, errno syscall.Errno) { return p.ready, 0 } diff --git a/imports/wasi_snapshot_preview1/testdata/gotip/wasi.go b/imports/wasi_snapshot_preview1/testdata/gotip/wasi.go index 42199727..d71e15a0 100644 --- a/imports/wasi_snapshot_preview1/testdata/gotip/wasi.go +++ b/imports/wasi_snapshot_preview1/testdata/gotip/wasi.go @@ -1,8 +1,10 @@ package main import ( + "errors" "fmt" "io" + "io/fs" "net" "net/http" "os" @@ -13,18 +15,38 @@ import ( func main() { switch os.Args[1] { + case "ls": + var repeat bool + if len(os.Args) == 4 { + repeat = os.Args[3] == "repeat" + } + // Go doesn't open with O_DIRECTORY, so we don't end up with ENOTDIR, + // rather EBADF trying to read the directory later. + if err := mainLs(os.Args[2], repeat); errors.Is(err, syscall.EBADF) { + fmt.Println("ENOTDIR") + } else if err != nil { + panic(err) + } + case "stat": + if err := mainStat(); err != nil { + panic(err) + } case "sock": if err := mainSock(); err != nil { panic(err) } - case "http": - if err := mainHTTP(); err != nil { - panic(err) - } case "nonblock": if err := mainNonblock(os.Args[2], os.Args[3:]); err != nil { panic(err) } + } + + // Handle go-specific additions + switch os.Args[1] { + case "http": + if err := mainHTTP(); err != nil { + panic(err) + } case "stdin": if err := mainStdin(); err != nil { panic(err) @@ -32,7 +54,57 @@ func main() { case "stdout": mainStdout() } +} +func mainLs(path string, repeat bool) error { + d, err := os.Open(path) + if err != nil { + return err + } + defer d.Close() + + if err = printFileNames(d); err != nil { + return err + } else if repeat { + // rewind + if _, err = d.Seek(0, io.SeekStart); err != nil { + return err + } + return printFileNames(d) + } + return nil +} + +func printFileNames(d *os.File) error { + if names, err := d.Readdirnames(-1); err != nil { + return err + } else { + for _, n := range names { + fmt.Println("./" + n) + } + } + return nil +} + +func mainStat() error { + var isatty = func(name string, fd uintptr) error { + f := os.NewFile(fd, "") + if st, err := f.Stat(); err != nil { + return err + } else { + ttyMode := fs.ModeDevice | fs.ModeCharDevice + isatty := st.Mode()&ttyMode == ttyMode + fmt.Println(name, "isatty:", isatty) + return nil + } + } + + for fd, name := range []string{"stdin", "stdout", "stderr", "/"} { + if err := isatty(name, uintptr(fd)); err != nil { + return err + } + } + return nil } // mainSock is an explicit test of a blocking socket. @@ -65,55 +137,6 @@ func mainSock() error { return nil } -// mainHTTP implicitly tests non-blocking sockets, as they are needed for -// middleware. -func mainHTTP() error { - // Get the file representing a pre-opened TCP socket. - // The socket (listener) is the first pre-open, with a file-descriptor of - // 3 because the host didn't add any pre-opened files. - listenerFD := 3 - f := os.NewFile(uintptr(listenerFD), "") - - // Wasm runs similarly to GOMAXPROCS=1, so multiple goroutines cannot work - // in parallel. non-blocking allows the poller to park the go-routine - // accepting connections while work is done on one. - if err := syscall.SetNonblock(listenerFD, true); err != nil { - return err - } - - // Convert the file representing the pre-opened socket to a listener, so - // that we can integrate it with HTTP middleware. - ln, err := net.FileListener(f) - defer f.Close() - if err != nil { - return err - } - defer ln.Close() - - // Serve middleware that echos the request body to the response once, then quits. - h := &echoOnce{ch: make(chan struct{}, 1)} - go http.Serve(ln, h) - <-h.ch - return nil -} - -type echoOnce struct { - ch chan struct{} -} - -func (e echoOnce) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Copy up to 32 bytes from the request to the response, appending a newline. - // Note: the test should write: "wazero", so that's all we should read. - var buf [32]byte - if n, err := r.Body.Read(buf[:]); err != nil && err != io.EOF { - panic(err) - } else if n, err = w.Write(append(buf[:n], '\n')); err != nil { - panic(err) - } - // Once one request was served, close the channel. - close(e.ch) -} - // Adapted from nonblock.go // https://github.com/golang/go/blob/0fcc70ecd56e3b5c214ddaee4065ea1139ae16b5/src/runtime/internal/wasitest/testdata/nonblock.go func mainNonblock(mode string, files []string) error { @@ -169,6 +192,55 @@ func mainNonblock(mode string, files []string) error { return nil } +// mainHTTP implicitly tests non-blocking sockets, as they are needed for +// middleware. +func mainHTTP() error { + // Get the file representing a pre-opened TCP socket. + // The socket (listener) is the first pre-open, with a file-descriptor of + // 3 because the host didn't add any pre-opened files. + listenerFD := 3 + f := os.NewFile(uintptr(listenerFD), "") + + // Wasm runs similarly to GOMAXPROCS=1, so multiple goroutines cannot work + // in parallel. non-blocking allows the poller to park the go-routine + // accepting connections while work is done on one. + if err := syscall.SetNonblock(listenerFD, true); err != nil { + return err + } + + // Convert the file representing the pre-opened socket to a listener, so + // that we can integrate it with HTTP middleware. + ln, err := net.FileListener(f) + defer f.Close() + if err != nil { + return err + } + defer ln.Close() + + // Serve middleware that echos the request body to the response once, then quits. + h := &echoOnce{ch: make(chan struct{}, 1)} + go http.Serve(ln, h) + <-h.ch + return nil +} + +type echoOnce struct { + ch chan struct{} +} + +func (e echoOnce) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Copy up to 32 bytes from the request to the response, appending a newline. + // Note: the test should write: "wazero", so that's all we should read. + var buf [32]byte + if n, err := r.Body.Read(buf[:]); err != nil && err != io.EOF { + panic(err) + } else if n, err = w.Write(append(buf[:n], '\n')); err != nil { + panic(err) + } + // Once one request was served, close the channel. + close(e.ch) +} + // Reproducer for https://github.com/tetratelabs/wazero/issues/1538 func mainStdin() error { go func() { diff --git a/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go b/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go new file mode 100644 index 00000000..a4a22059 --- /dev/null +++ b/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go @@ -0,0 +1,86 @@ +package main + +import ( + "errors" + "fmt" + "io" + "io/fs" + "os" + "syscall" +) + +func main() { + switch os.Args[1] { + case "ls": + var repeat bool + if len(os.Args) == 4 { + repeat = os.Args[3] == "repeat" + } + // Go doesn't open with O_DIRECTORY, so we don't end up with ENOTDIR, + // rather EBADF trying to read the directory later. + if err := mainLs(os.Args[2], repeat); errors.Is(err, syscall.EBADF) { + fmt.Println("ENOTDIR") + } else if err != nil { + panic(err) + } + case "stat": + if err := mainStat(); err != nil { + panic(err) + } + case "sock": + // TODO: undefined: net.FileListener + case "nonblock": + // TODO: undefined: syscall.SetNonblock + } +} + +func mainLs(path string, repeat bool) error { + d, err := os.Open(path) + if err != nil { + return err + } + defer d.Close() + + if err = printFileNames(d); err != nil { + return err + } else if repeat { + // rewind + if _, err = d.Seek(0, io.SeekStart); err != nil { + return err + } + return printFileNames(d) + } + return nil +} + +func printFileNames(d *os.File) error { + if names, err := d.Readdirnames(-1); err != nil { + return err + } else { + for _, n := range names { + fmt.Println("./" + n) + } + } + return nil +} + +func mainStat() error { + var isatty = func(name string, fd uintptr) error { + f := os.NewFile(fd, "") + if st, err := f.Stat(); err != nil { + return err + } else { + ttyMode := fs.ModeDevice | fs.ModeCharDevice + isatty := st.Mode()&ttyMode == ttyMode + fmt.Println(name, "isatty:", isatty) + return nil + } + } + + for fd, name := range []string{"stdin", "stdout", "stderr", "/"} { + if err := isatty(name, uintptr(fd)); err != nil { + return err + } + } + return nil +} diff --git a/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.wasm b/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.wasm new file mode 100755 index 0000000000000000000000000000000000000000..d7dfb28d32ebedf2bfeacf61001e24253ca08b20 GIT binary patch literal 109235 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�L-oJ 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 literal 0 HcmV?d00001 diff --git a/imports/wasi_snapshot_preview1/wasi_stdlib_test.go b/imports/wasi_snapshot_preview1/wasi_stdlib_test.go index 72c130eb..556fc1d6 100644 --- a/imports/wasi_snapshot_preview1/wasi_stdlib_test.go +++ b/imports/wasi_snapshot_preview1/wasi_stdlib_test.go @@ -5,15 +5,16 @@ import ( "context" _ "embed" "io" - "io/fs" "net" "net/http" "os" + "path" "runtime" + "sort" "strconv" "strings" "testing" - "testing/fstest" + gofstest "testing/fstest" "time" "github.com/tetratelabs/wazero" @@ -21,6 +22,7 @@ import ( experimentalsock "github.com/tetratelabs/wazero/experimental/sock" "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" "github.com/tetratelabs/wazero/internal/fsapi" + "github.com/tetratelabs/wazero/internal/fstest" internalsys "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/sys" @@ -38,6 +40,14 @@ var sleepALittle = func() { time.Sleep(500 * time.Millisecond) } //go:embed testdata/cargo-wasi/wasi.wasm var wasmCargoWasi []byte +// wasmGotip is conditionally compiled from testdata/gotip/wasi.go +var wasmGotip []byte + +// wasmTinyGo was compiled from testdata/tinygo/wasi.go +// +//go:embed testdata/tinygo/wasi.wasm +var wasmTinyGo []byte + // wasmZigCc was compiled from testdata/zig-cc/wasi.c // //go:embed testdata/zig-cc/wasi.wasm @@ -48,37 +58,52 @@ var wasmZigCc []byte //go:embed testdata/zig/wasi.wasm var wasmZig []byte -// wasmGotip is conditionally compiled from testdata/gotip/wasi.go -var wasmGotip []byte - func Test_fdReaddir_ls(t *testing.T) { - for toolchain, bin := range map[string][]byte{ + toolchains := map[string][]byte{ "cargo-wasi": wasmCargoWasi, + "tinygo": wasmTinyGo, "zig-cc": wasmZigCc, "zig": wasmZig, - } { + } + if wasmGotip != nil { + toolchains["gotip"] = wasmGotip + } + + tmpDir := t.TempDir() + require.NoError(t, fstest.WriteTestFiles(tmpDir)) + + tons := path.Join(tmpDir, "tons") + require.NoError(t, os.Mkdir(tons, 0o0777)) + for i := 0; i < direntCountTons; i++ { + require.NoError(t, os.WriteFile(path.Join(tons, strconv.Itoa(i)), nil, 0o0666)) + } + + for toolchain, bin := range toolchains { toolchain := toolchain bin := bin t.Run(toolchain, func(t *testing.T) { - expectDots := toolchain == "zig-cc" - testFdReaddirLs(t, bin, expectDots) + var expectDots int + if toolchain == "zig-cc" { + expectDots = 1 + } + testFdReaddirLs(t, bin, toolchain, tmpDir, expectDots) }) } } -func testFdReaddirLs(t *testing.T, bin []byte, expectDots bool) { - // TODO: make a subfs +const direntCountTons = 8096 + +func testFdReaddirLs(t *testing.T, bin []byte, toolchain, rootDir string, expectDots int) { + t.Helper() + moduleConfig := wazero.NewModuleConfig(). - WithFS(fstest.MapFS{ - "-": {}, - "a-": {Mode: fs.ModeDir}, - "ab-": {}, - }) + WithFSConfig(wazero.NewFSConfig(). + WithReadOnlyDirMount(path.Join(rootDir, "dir"), "/")) t.Run("empty directory", func(t *testing.T) { console := compileAndRun(t, testCtx, moduleConfig.WithArgs("wasi", "ls", "./a-"), bin) - requireLsOut(t, "\n", expectDots, console) + requireLsOut(t, nil, expectDots, console) }) t.Run("not a directory", func(t *testing.T) { @@ -91,75 +116,73 @@ ENOTDIR t.Run("directory with entries", func(t *testing.T) { console := compileAndRun(t, testCtx, moduleConfig.WithArgs("wasi", "ls", "."), bin) - requireLsOut(t, ` -./- -./a- -./ab- -`, expectDots, console) + requireLsOut(t, []string{ + "./-", + "./a-", + "./ab-", + }, expectDots, console) }) t.Run("directory with entries - read twice", func(t *testing.T) { - console := compileAndRun(t, testCtx, moduleConfig.WithArgs("wasi", "ls", ".", "repeat"), bin) - if expectDots { - require.Equal(t, ` -./. -./.. -./- -./a- -./ab- -./. -./.. -./- -./a- -./ab- -`, "\n"+console) - } else { - require.Equal(t, ` -./- -./a- -./ab- -./- -./a- -./ab- -`, "\n"+console) + if toolchain == "tinygo" { + t.Skip("https://github.com/tinygo-org/tinygo/issues/3823") } + + console := compileAndRun(t, testCtx, moduleConfig.WithArgs("wasi", "ls", ".", "repeat"), bin) + requireLsOut(t, []string{ + "./-", + "./a-", + "./ab-", + "./-", + "./a-", + "./ab-", + }, expectDots*2, console) }) t.Run("directory with tons of entries", func(t *testing.T) { - testFS := fstest.MapFS{} - count := 8096 - for i := 0; i < count; i++ { - testFS[strconv.Itoa(i)] = &fstest.MapFile{} - } - config := wazero.NewModuleConfig().WithFS(testFS).WithArgs("wasi", "ls", ".") - console := compileAndRun(t, testCtx, config, bin) + moduleConfig = wazero.NewModuleConfig(). + WithFSConfig(wazero.NewFSConfig(). + WithReadOnlyDirMount(path.Join(rootDir, "tons"), "/")). + WithArgs("wasi", "ls", ".") + + console := compileAndRun(t, testCtx, moduleConfig, bin) lines := strings.Split(console, "\n") - expected := count + 1 /* trailing newline */ - if expectDots { - expected += 2 - } + expected := direntCountTons + 1 /* trailing newline */ + expected += expectDots * 2 require.Equal(t, expected, len(lines)) }) } -func requireLsOut(t *testing.T, expected string, expectDots bool, console string) { - dots := ` -./. -./.. -` - if expectDots { - expected = dots + expected[1:] +func requireLsOut(t *testing.T, expected []string, expectDots int, console string) { + for i := 0; i < expectDots; i++ { + expected = append(expected, "./.", "./..") + } + + actual := strings.Split(console, "\n") + sort.Strings(actual) // os directories are not lexicographic order + actual = actual[1:] // trailing newline + + sort.Strings(expected) + if len(actual) == 0 { + require.Nil(t, expected) + } else { + require.Equal(t, expected, actual) } - require.Equal(t, expected, "\n"+console) } func Test_fdReaddir_stat(t *testing.T) { - for toolchain, bin := range map[string][]byte{ + toolchains := map[string][]byte{ "cargo-wasi": wasmCargoWasi, + "tinygo": wasmTinyGo, "zig-cc": wasmZigCc, "zig": wasmZig, - } { + } + if wasmGotip != nil { + toolchains["gotip"] = wasmGotip + } + + for toolchain, bin := range toolchains { toolchain := toolchain bin := bin t.Run(toolchain, func(t *testing.T) { @@ -171,7 +194,7 @@ func Test_fdReaddir_stat(t *testing.T) { func testFdReaddirStat(t *testing.T, bin []byte) { moduleConfig := wazero.NewModuleConfig().WithArgs("wasi", "stat") - console := compileAndRun(t, testCtx, moduleConfig.WithFS(fstest.MapFS{}), bin) + console := compileAndRun(t, testCtx, moduleConfig.WithFS(gofstest.MapFS{}), bin) // TODO: switch this to a real stat test require.Equal(t, ` @@ -200,7 +223,7 @@ func testPreopen(t *testing.T, bin []byte) { console := compileAndRun(t, testCtx, moduleConfig. WithFSConfig(wazero.NewFSConfig(). WithDirMount(".", "/"). - WithFSMount(fstest.MapFS{}, "/tmp")), bin) + WithFSMount(gofstest.MapFS{}, "/tmp")), bin) require.Equal(t, ` 0: stdin diff --git a/internal/sys/lazy.go b/internal/sys/lazy.go index df68b51b..5ce6ae22 100644 --- a/internal/sys/lazy.go +++ b/internal/sys/lazy.go @@ -8,7 +8,7 @@ import ( "github.com/tetratelabs/wazero/internal/fsapi" ) -// compile-time check to ensure lazyDir implements internalapi.File. +// compile-time check to ensure lazyDir implements fsapi.File. var _ fsapi.File = (*lazyDir)(nil) type lazyDir struct { @@ -18,7 +18,7 @@ type lazyDir struct { f fsapi.File } -// Ino implements the same method as documented on internalapi.File +// Ino implements the same method as documented on fsapi.File func (r *lazyDir) Ino() (uint64, syscall.Errno) { if f, ok := r.file(); !ok { return 0, syscall.EBADF @@ -27,17 +27,17 @@ func (r *lazyDir) Ino() (uint64, syscall.Errno) { } } -// IsAppend implements the same method as documented on internalapi.File +// IsAppend implements the same method as documented on fsapi.File func (r *lazyDir) IsAppend() bool { return false } -// SetAppend implements the same method as documented on internalapi.File +// SetAppend implements the same method as documented on fsapi.File func (r *lazyDir) SetAppend(bool) syscall.Errno { return syscall.EISDIR } -// Seek implements the same method as documented on internalapi.File +// Seek implements the same method as documented on fsapi.File func (r *lazyDir) Seek(offset int64, whence int) (newOffset int64, errno syscall.Errno) { if f, ok := r.file(); !ok { return 0, syscall.EBADF @@ -46,7 +46,7 @@ func (r *lazyDir) Seek(offset int64, whence int) (newOffset int64, errno syscall } } -// Stat implements the same method as documented on internalapi.File +// Stat implements the same method as documented on fsapi.File func (r *lazyDir) Stat() (fsapi.Stat_t, syscall.Errno) { if f, ok := r.file(); !ok { return fsapi.Stat_t{}, syscall.EBADF @@ -55,7 +55,7 @@ func (r *lazyDir) Stat() (fsapi.Stat_t, syscall.Errno) { } } -// Readdir implements the same method as documented on internalapi.File +// Readdir implements the same method as documented on fsapi.File func (r *lazyDir) Readdir(n int) (dirents []fsapi.Dirent, errno syscall.Errno) { if f, ok := r.file(); !ok { return nil, syscall.EBADF @@ -64,7 +64,7 @@ func (r *lazyDir) Readdir(n int) (dirents []fsapi.Dirent, errno syscall.Errno) { } } -// Sync implements the same method as documented on internalapi.File +// Sync implements the same method as documented on fsapi.File func (r *lazyDir) Sync() syscall.Errno { if f, ok := r.file(); !ok { return syscall.EBADF @@ -73,7 +73,7 @@ func (r *lazyDir) Sync() syscall.Errno { } } -// Datasync implements the same method as documented on internalapi.File +// Datasync implements the same method as documented on fsapi.File func (r *lazyDir) Datasync() syscall.Errno { if f, ok := r.file(); !ok { return syscall.EBADF @@ -82,7 +82,7 @@ func (r *lazyDir) Datasync() syscall.Errno { } } -// Chmod implements the same method as documented on internalapi.File +// Chmod implements the same method as documented on fsapi.File func (r *lazyDir) Chmod(mode fs.FileMode) syscall.Errno { if f, ok := r.file(); !ok { return syscall.EBADF @@ -91,7 +91,7 @@ func (r *lazyDir) Chmod(mode fs.FileMode) syscall.Errno { } } -// Chown implements the same method as documented on internalapi.File +// Chown implements the same method as documented on fsapi.File func (r *lazyDir) Chown(uid, gid int) syscall.Errno { if f, ok := r.file(); !ok { return syscall.EBADF @@ -100,7 +100,7 @@ func (r *lazyDir) Chown(uid, gid int) syscall.Errno { } } -// Utimens implements the same method as documented on internalapi.File +// Utimens implements the same method as documented on fsapi.File func (r *lazyDir) Utimens(times *[2]syscall.Timespec) syscall.Errno { if f, ok := r.file(); !ok { return syscall.EBADF diff --git a/internal/sys/stdio.go b/internal/sys/stdio.go index c9e852c5..8b3c8815 100644 --- a/internal/sys/stdio.go +++ b/internal/sys/stdio.go @@ -19,7 +19,7 @@ type StdinFile struct { io.Reader } -// Read implements the same method as documented on internalapi.File +// Read implements the same method as documented on fsapi.File func (f *StdinFile) Read(buf []byte) (int, syscall.Errno) { n, err := f.Reader.Read(buf) return n, platform.UnwrapOSError(err) @@ -31,7 +31,7 @@ type writerFile struct { w io.Writer } -// Write implements the same method as documented on internalapi.File +// Write implements the same method as documented on fsapi.File func (f *writerFile) Write(buf []byte) (int, syscall.Errno) { n, err := f.w.Write(buf) return n, platform.UnwrapOSError(err) @@ -44,17 +44,17 @@ type noopStdinFile struct { noopStdioFile } -// AccessMode implements the same method as documented on internalapi.File +// AccessMode implements the same method as documented on fsapi.File func (noopStdinFile) AccessMode() int { return syscall.O_RDONLY } -// Read implements the same method as documented on internalapi.File +// Read implements the same method as documented on fsapi.File func (noopStdinFile) Read([]byte) (int, syscall.Errno) { return 0, 0 // Always EOF } -// PollRead implements the same method as documented on internalapi.File +// PollRead implements the same method as documented on fsapi.File func (noopStdinFile) PollRead(*time.Duration) (ready bool, errno syscall.Errno) { return true, 0 // always ready to read nothing } @@ -65,12 +65,12 @@ type noopStdoutFile struct { noopStdioFile } -// AccessMode implements the same method as documented on internalapi.File +// AccessMode implements the same method as documented on fsapi.File func (noopStdoutFile) AccessMode() int { return syscall.O_WRONLY } -// Write implements the same method as documented on internalapi.File +// Write implements the same method as documented on fsapi.File func (noopStdoutFile) Write(buf []byte) (int, syscall.Errno) { return len(buf), 0 // same as io.Discard } @@ -79,17 +79,17 @@ type noopStdioFile struct { fsapi.UnimplementedFile } -// Stat implements the same method as documented on internalapi.File +// Stat implements the same method as documented on fsapi.File func (noopStdioFile) Stat() (fsapi.Stat_t, syscall.Errno) { return fsapi.Stat_t{Mode: modeDevice, Nlink: 1}, 0 } -// IsDir implements the same method as documented on internalapi.File +// IsDir implements the same method as documented on fsapi.File func (noopStdioFile) IsDir() (bool, syscall.Errno) { return false, 0 } -// Close implements the same method as documented on internalapi.File +// Close implements the same method as documented on fsapi.File func (noopStdioFile) Close() (errno syscall.Errno) { return } func stdinFileEntry(r io.Reader) (*FileEntry, error) { diff --git a/internal/wasm/module_instance_test.go b/internal/wasm/module_instance_test.go index 3699f930..44b1998b 100644 --- a/internal/wasm/module_instance_test.go +++ b/internal/wasm/module_instance_test.go @@ -155,7 +155,7 @@ func TestModuleInstance_Close(t *testing.T) { m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil) require.NoError(t, err) - // In internalapi.FS, non syscall errors map to syscall.EIO. + // In fsapi.FS, non syscall errors map to syscall.EIO. require.EqualErrno(t, syscall.EIO, m.Close(testCtx)) // Verify our intended side-effect @@ -254,7 +254,7 @@ func TestModuleInstance_CallDynamic(t *testing.T) { m, err := s.Instantiate(testCtx, &Module{}, t.Name(), sysCtx, nil) require.NoError(t, err) - // In internalapi.FS, non syscall errors map to syscall.EIO. + // In fsapi.FS, non syscall errors map to syscall.EIO. require.EqualErrno(t, syscall.EIO, m.Close(testCtx)) // Verify our intended side-effect