sysfs: Makes ReadFS and AdaptFS embeddable (#1607)

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-08-02 08:27:50 +08:00
committed by GitHub
parent 1f8c908f1c
commit 2f2b6a9d2c
21 changed files with 249 additions and 271 deletions
+2 -2
View File
@@ -419,9 +419,9 @@ func validateMounts(mounts sliceFlag, stdErr logging.Writer) (rc int, rootPath s
fmt.Fprintf(stdErr, "invalid mount: path %q is not a directory\n", dir) fmt.Fprintf(stdErr, "invalid mount: path %q is not a directory\n", dir)
} }
root := sysfs.NewDirFS(dir) root := sysfs.DirFS(dir)
if readOnly { if readOnly {
root = sysfs.NewReadFS(root) root = &sysfs.ReadFS{FS: root}
} }
config = config.(sysfs.FSConfig).WithSysFSMount(root, guestPath) config = config.(sysfs.FSConfig).WithSysFSMount(root, guestPath)
+4 -3
View File
@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/api"
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/fstest" "github.com/tetratelabs/wazero/internal/fstest"
"github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/platform"
internalsys "github.com/tetratelabs/wazero/internal/sys" internalsys "github.com/tetratelabs/wazero/internal/sys"
@@ -325,7 +326,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
config := base.WithFS(testFS) config := base.WithFS(testFS)
return config, func(t *testing.T, sys *internalsys.Context) { return config, func(t *testing.T, sys *internalsys.Context) {
rootfs := sys.FS().RootFS() rootfs := sys.FS().RootFS()
require.Equal(t, sysfs.Adapt(testFS), rootfs) require.Equal(t, &sysfs.AdaptFS{FS: testFS}, rootfs)
} }
}, },
}, },
@@ -336,7 +337,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
config := base.WithFS(testFS).WithFS(testFS2) config := base.WithFS(testFS).WithFS(testFS2)
return config, func(t *testing.T, sys *internalsys.Context) { return config, func(t *testing.T, sys *internalsys.Context) {
rootfs := sys.FS().RootFS() rootfs := sys.FS().RootFS()
require.Equal(t, sysfs.Adapt(testFS2), rootfs) require.Equal(t, &sysfs.AdaptFS{FS: testFS2}, rootfs)
} }
}, },
}, },
@@ -346,7 +347,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
config := base.WithFS(nil) config := base.WithFS(nil)
return config, func(t *testing.T, sys *internalsys.Context) { return config, func(t *testing.T, sys *internalsys.Context) {
rootfs := sys.FS().RootFS() rootfs := sys.FS().RootFS()
require.Equal(t, sysfs.Adapt(nil), rootfs) require.Equal(t, experimentalsys.UnimplementedFS{}, rootfs)
} }
}, },
}, },
-15
View File
@@ -10,16 +10,6 @@ import (
// This should be embedded to have forward compatible implementations. // This should be embedded to have forward compatible implementations.
type UnimplementedFS struct{} type UnimplementedFS struct{}
// String implements fmt.Stringer
func (UnimplementedFS) String() string {
return "Unimplemented:/"
}
// Open implements the same method as documented on fs.FS
func (UnimplementedFS) Open(name string) (fs.File, error) {
return nil, &fs.PathError{Op: "open", Path: name, Err: ENOSYS}
}
// OpenFile implements FS.OpenFile // OpenFile implements FS.OpenFile
func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) { func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) {
return nil, ENOSYS return nil, ENOSYS
@@ -80,11 +70,6 @@ func (UnimplementedFS) Utimens(path string, atim, mtim int64) Errno {
return ENOSYS return ENOSYS
} }
// Truncate implements FS.Truncate
func (UnimplementedFS) Truncate(string, int64) Errno {
return ENOSYS
}
// UnimplementedFile is a File that returns ENOSYS for all functions, // UnimplementedFile is a File that returns ENOSYS for all functions,
// except where no-op are otherwise documented. // except where no-op are otherwise documented.
// //
+20 -20
View File
@@ -10,31 +10,31 @@ import (
var moduleConfig wazero.ModuleConfig var moduleConfig wazero.ModuleConfig
// This example shows how to configure a sysfs.NewDirFS // This example shows how to adapt a fs.FS to a sys.FS
func ExampleNewDirFS() { func ExampleAdaptFS() {
root := sysfs.NewDirFS(".")
moduleConfig = wazero.NewModuleConfig().
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/"))
}
// This example shows how to configure a sysfs.NewReadFS
func ExampleNewReadFS() {
root := sysfs.NewDirFS(".")
readOnly := sysfs.NewReadFS(root)
moduleConfig = wazero.NewModuleConfig().
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(readOnly, "/"))
}
// This example shows how to adapt a fs.FS as a sys.FS
func ExampleAdapt() {
m := fstest.MapFS{ m := fstest.MapFS{
"a/b.txt": &fstest.MapFile{Mode: 0o666}, "a/b.txt": &fstest.MapFile{Mode: 0o666},
".": &fstest.MapFile{Mode: 0o777 | fs.ModeDir}, ".": &fstest.MapFile{Mode: 0o777 | fs.ModeDir},
} }
root := sysfs.Adapt(m) root := &sysfs.AdaptFS{FS: m}
moduleConfig = wazero.NewModuleConfig(). moduleConfig = wazero.NewModuleConfig().
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/")) WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/"))
} }
// This example shows how to configure a sysfs.DirFS
func ExampleDirFS() {
root := sysfs.DirFS(".")
moduleConfig = wazero.NewModuleConfig().
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/"))
}
// This example shows how to configure a sysfs.ReadFS
func ExampleReadFS() {
root := sysfs.DirFS(".")
readOnly := &sysfs.ReadFS{FS: root}
moduleConfig = wazero.NewModuleConfig().
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(readOnly, "/"))
}
+14 -17
View File
@@ -8,32 +8,29 @@
package sysfs package sysfs
import ( import (
"io/fs"
experimentalsys "github.com/tetratelabs/wazero/experimental/sys" experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/sysfs" "github.com/tetratelabs/wazero/internal/sysfs"
) )
// Adapt adapts the input to sys.FS unless it is already one. Use NewDirFS // AdaptFS adapts the input to sys.FS. Use DirFS instead of adapting an
// instead of os.DirFS as it handles interop issues such as windows support. // os.DirFS as it handles interop issues such as windows support.
// //
// Note: This performs no flag verification on OpenFile. fs.FS cannot read // Note: This performs no flag verification on OpenFile. sys.FS cannot read
// flags as there is no parameter to pass them through with. Moreover, fs.FS // flags as there is no parameter to pass them through with. Moreover, sys.FS
// documentation does not require the file to be present. In summary, we can't // documentation does not require the file to be present. In summary, we can't
// enforce flag behavior. // enforce flag behavior.
func Adapt(fs fs.FS) experimentalsys.FS { type AdaptFS = sysfs.AdaptFS
return sysfs.Adapt(fs)
// DirFS is like os.DirFS except it returns sys.FS, which has more features.
func DirFS(dir string) experimentalsys.FS {
return sysfs.DirFS(dir)
} }
// NewReadFS is used to mask an existing sys.FS for reads. Notably, this allows // ReadFS is used to mask an existing sys.FS for reads. Notably, this allows
// the CLI to do read-only mounts of directories the host user can write, but // the CLI to do read-only mounts of directories the host user can write, but
// doesn't want the guest wasm to. For example, Python libraries shouldn't be // doesn't want the guest wasm to. For example, Python libraries shouldn't be
// written to at runtime by the python wasm file. // written to at runtime by the python wasm file.
func NewReadFS(fs experimentalsys.FS) experimentalsys.FS { //
return sysfs.NewReadFS(fs) // Note: This implements read-only by returning sys.EROFS or sys.EBADF,
} // depending on the operation that require write access.
type ReadFS = sysfs.ReadFS
// NewDirFS is like os.DirFS except it returns sys.FS, which has more features.
func NewDirFS(dir string) experimentalsys.FS {
return sysfs.NewDirFS(dir)
}
+8 -4
View File
@@ -165,17 +165,21 @@ func (c *fsConfig) clone() *fsConfig {
// WithDirMount implements FSConfig.WithDirMount // WithDirMount implements FSConfig.WithDirMount
func (c *fsConfig) WithDirMount(dir, guestPath string) FSConfig { func (c *fsConfig) WithDirMount(dir, guestPath string) FSConfig {
return c.WithSysFSMount(sysfs.NewDirFS(dir), guestPath) return c.WithSysFSMount(sysfs.DirFS(dir), guestPath)
} }
// WithReadOnlyDirMount implements FSConfig.WithReadOnlyDirMount // WithReadOnlyDirMount implements FSConfig.WithReadOnlyDirMount
func (c *fsConfig) WithReadOnlyDirMount(dir, guestPath string) FSConfig { func (c *fsConfig) WithReadOnlyDirMount(dir, guestPath string) FSConfig {
return c.WithSysFSMount(sysfs.NewReadFS(sysfs.NewDirFS(dir)), guestPath) return c.WithSysFSMount(&sysfs.ReadFS{FS: sysfs.DirFS(dir)}, guestPath)
} }
// WithFSMount implements FSConfig.WithFSMount // WithFSMount implements FSConfig.WithFSMount
func (c *fsConfig) WithFSMount(fs fs.FS, guestPath string) FSConfig { func (c *fsConfig) WithFSMount(fs fs.FS, guestPath string) FSConfig {
return c.WithSysFSMount(sysfs.Adapt(fs), guestPath) var adapted experimentalsys.FS
if fs != nil {
adapted = &sysfs.AdaptFS{FS: fs}
}
return c.WithSysFSMount(adapted, guestPath)
} }
// WithSysFSMount implements sysfs.FSConfig // WithSysFSMount implements sysfs.FSConfig
@@ -188,7 +192,7 @@ func (c *fsConfig) WithSysFSMount(fs experimentalsys.FS, guestPath string) FSCon
if i, ok := ret.guestPathToFS[cleaned]; ok { if i, ok := ret.guestPathToFS[cleaned]; ok {
ret.fs[i] = fs ret.fs[i] = fs
ret.guestPaths[i] = guestPath ret.guestPaths[i] = guestPath
} else { } else if fs != nil {
ret.guestPathToFS[cleaned] = len(ret.fs) ret.guestPathToFS[cleaned] = len(ret.fs)
ret.fs = append(ret.fs, fs) ret.fs = append(ret.fs, fs)
ret.guestPaths = append(ret.guestPaths, guestPath) ret.guestPaths = append(ret.guestPaths, guestPath)
+4 -4
View File
@@ -29,13 +29,13 @@ func TestFSConfig(t *testing.T) {
{ {
name: "WithFSMount", name: "WithFSMount",
input: base.WithFSMount(testFS, "/"), input: base.WithFSMount(testFS, "/"),
expectedFS: []sys.FS{sysfs.Adapt(testFS)}, expectedFS: []sys.FS{&sysfs.AdaptFS{FS: testFS}},
expectedGuestPaths: []string{"/"}, expectedGuestPaths: []string{"/"},
}, },
{ {
name: "WithFSMount overwrites", name: "WithFSMount overwrites",
input: base.WithFSMount(testFS, "/").WithFSMount(testFS2, "/"), input: base.WithFSMount(testFS, "/").WithFSMount(testFS2, "/"),
expectedFS: []sys.FS{sysfs.Adapt(testFS2)}, expectedFS: []sys.FS{&sysfs.AdaptFS{FS: testFS2}},
expectedGuestPaths: []string{"/"}, expectedGuestPaths: []string{"/"},
}, },
{ {
@@ -45,13 +45,13 @@ func TestFSConfig(t *testing.T) {
{ {
name: "WithDirMount overwrites", name: "WithDirMount overwrites",
input: base.WithFSMount(testFS, "/").WithDirMount(".", "/"), input: base.WithFSMount(testFS, "/").WithDirMount(".", "/"),
expectedFS: []sys.FS{sysfs.NewDirFS(".")}, expectedFS: []sys.FS{sysfs.DirFS(".")},
expectedGuestPaths: []string{"/"}, expectedGuestPaths: []string{"/"},
}, },
{ {
name: "multiple", name: "multiple",
input: base.WithReadOnlyDirMount(".", "/").WithDirMount("/tmp", "/tmp"), input: base.WithReadOnlyDirMount(".", "/").WithDirMount("/tmp", "/tmp"),
expectedFS: []sys.FS{sysfs.NewReadFS(sysfs.NewDirFS(".")), sysfs.NewDirFS("/tmp")}, expectedFS: []sys.FS{&sysfs.ReadFS{FS: sysfs.DirFS(".")}, sysfs.DirFS("/tmp")},
expectedGuestPaths: []string{"/", "/tmp"}, expectedGuestPaths: []string{"/", "/tmp"},
}, },
} }
+4 -4
View File
@@ -242,9 +242,9 @@ func Test_fdFdstatGet(t *testing.T) {
// replace stdin with a fake TTY file. // replace stdin with a fake TTY file.
// TODO: Make this easier once we have in-memory sys.File // TODO: Make this easier once we have in-memory sys.File
stdin, _ := fsc.LookupFile(sys.FdStdin) stdin, _ := fsc.LookupFile(sys.FdStdin)
stdinFile, errno := sysfs.Adapt(&gofstest.MapFS{"stdin": &gofstest.MapFile{ stdinFile, errno := (&sysfs.AdaptFS{FS: &gofstest.MapFS{"stdin": &gofstest.MapFile{
Mode: fs.ModeDevice | fs.ModeCharDevice | 0o600, Mode: fs.ModeDevice | fs.ModeCharDevice | 0o600,
}}).OpenFile("stdin", 0, 0) }}}).OpenFile("stdin", 0, 0)
require.EqualErrno(t, 0, errno) require.EqualErrno(t, 0, errno)
stdin.File = stdinFile stdin.File = stdinFile
@@ -3648,8 +3648,8 @@ func Test_pathLink(t *testing.T) {
func Test_pathOpen(t *testing.T) { func Test_pathOpen(t *testing.T) {
dir := t.TempDir() // open before loop to ensure no locking problems. dir := t.TempDir() // open before loop to ensure no locking problems.
writeFS := sysfs.NewDirFS(dir) writeFS := sysfs.DirFS(dir)
readFS := sysfs.NewReadFS(writeFS) readFS := &sysfs.ReadFS{FS: writeFS}
fileName := "file" fileName := "file"
fileContents := []byte("012") fileContents := []byte("012")
+12 -12
View File
@@ -24,7 +24,7 @@ func TestNewFSContext(t *testing.T) {
embedFS, err := fs.Sub(testdata, "testdata") embedFS, err := fs.Sub(testdata, "testdata")
require.NoError(t, err) require.NoError(t, err)
dirfs := sysfs.NewDirFS(".") dirfs := sysfs.DirFS(".")
// Test various usual configuration for the file system. // Test various usual configuration for the file system.
tests := []struct { tests := []struct {
@@ -33,21 +33,21 @@ func TestNewFSContext(t *testing.T) {
}{ }{
{ {
name: "embed.FS", name: "embed.FS",
fs: sysfs.Adapt(embedFS), fs: &sysfs.AdaptFS{FS: embedFS},
}, },
{ {
name: "NewDirFS", name: "DirFS",
// Don't use "testdata" because it may not be present in // Don't use "testdata" because it may not be present in
// cross-architecture (a.k.a. scratch) build containers. // cross-architecture (a.k.a. scratch) build containers.
fs: dirfs, fs: dirfs,
}, },
{ {
name: "NewReadFS", name: "ReadFS",
fs: sysfs.NewReadFS(dirfs), fs: &sysfs.ReadFS{FS: dirfs},
}, },
{ {
name: "fstest.MapFS", name: "fstest.MapFS",
fs: sysfs.Adapt(gofstest.MapFS{}), fs: &sysfs.AdaptFS{FS: gofstest.MapFS{}},
}, },
} }
@@ -96,7 +96,7 @@ func TestNewFSContext(t *testing.T) {
func TestFSContext_CloseFile(t *testing.T) { func TestFSContext_CloseFile(t *testing.T) {
embedFS, err := fs.Sub(testdata, "testdata") embedFS, err := fs.Sub(testdata, "testdata")
require.NoError(t, err) require.NoError(t, err)
testFS := sysfs.Adapt(embedFS) testFS := &sysfs.AdaptFS{FS: embedFS}
c := Context{} c := Context{}
err = c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil) err = c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
@@ -154,7 +154,7 @@ func TestFSContext_noPreopens(t *testing.T) {
} }
func TestContext_Close(t *testing.T) { func TestContext_Close(t *testing.T) {
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{}}) testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": &testfs.File{}}}
c := Context{} c := Context{}
err := c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil) err := c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
@@ -181,7 +181,7 @@ func TestContext_Close(t *testing.T) {
func TestContext_Close_Error(t *testing.T) { func TestContext_Close_Error(t *testing.T) {
file := &testfs.File{CloseErr: errors.New("error closing")} file := &testfs.File{CloseErr: errors.New("error closing")}
testFS := sysfs.Adapt(testfs.FS{"foo": file}) testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": file}}
c := Context{} c := Context{}
err := c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil) err := c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
@@ -201,7 +201,7 @@ func TestContext_Close_Error(t *testing.T) {
func TestFSContext_Renumber(t *testing.T) { func TestFSContext_Renumber(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
dirFS := sysfs.NewDirFS(tmpDir) dirFS := sysfs.DirFS(tmpDir)
const dirName = "dir" const dirName = "dir"
errno := dirFS.Mkdir(dirName, 0o700) errno := dirFS.Mkdir(dirName, 0o700)
@@ -252,7 +252,7 @@ func TestFSContext_Renumber(t *testing.T) {
func TestDirentCache_Read(t *testing.T) { func TestDirentCache_Read(t *testing.T) {
c := Context{} c := Context{}
err := c.InitFSContext(nil, nil, nil, []sys.FS{sysfs.Adapt(fstest.FS)}, []string{"/"}, nil) err := c.InitFSContext(nil, nil, nil, []sys.FS{&sysfs.AdaptFS{FS: fstest.FS}}, []string{"/"}, nil)
require.NoError(t, err) require.NoError(t, err)
fsc := c.fsc fsc := c.fsc
defer fsc.Close() defer fsc.Close()
@@ -430,7 +430,7 @@ func TestDirentCache_ReadNewFile(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
c := Context{} c := Context{}
err := c.InitFSContext(nil, nil, nil, []sys.FS{sysfs.NewDirFS(tmpDir)}, []string{"/"}, nil) err := c.InitFSContext(nil, nil, nil, []sys.FS{sysfs.DirFS(tmpDir)}, []string{"/"}, nil)
require.NoError(t, err) require.NoError(t, err)
fsc := c.fsc fsc := c.fsc
defer fsc.Close() defer fsc.Close()
+1 -1
View File
@@ -20,7 +20,7 @@ func TestContext_WalltimeNanos(t *testing.T) {
} }
func TestDefaultSysContext(t *testing.T) { func TestDefaultSysContext(t *testing.T) {
testFS := sysfs.Adapt(fstest.FS) testFS := &sysfs.AdaptFS{FS: fstest.FS}
sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil, nil, []experimentalsys.FS{testFS}, []string{"/"}, nil) sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil, nil, []experimentalsys.FS{testFS}, []string{"/"}, nil)
require.NoError(t, err) require.NoError(t, err)
+63 -36
View File
@@ -9,40 +9,35 @@ import (
"github.com/tetratelabs/wazero/sys" "github.com/tetratelabs/wazero/sys"
) )
// Adapt adapts the input to sys.FS unless it is already one. Use NewDirFS instead type AdaptFS struct {
// of os.DirFS as it handles interop issues such as windows support. FS fs.FS
//
// Note: This performs no flag verification on OpenFile. sys.FS cannot read
// flags as there is no parameter to pass them through with. Moreover, sys.FS
// documentation does not require the file to be present. In summary, we can't
// enforce flag behavior.
func Adapt(fs fs.FS) experimentalsys.FS {
if fs == nil {
return experimentalsys.UnimplementedFS{}
}
if sys, ok := fs.(experimentalsys.FS); ok {
return sys
}
return &adapter{fs: fs}
}
type adapter struct {
experimentalsys.UnimplementedFS
fs fs.FS
} }
// String implements fmt.Stringer // String implements fmt.Stringer
func (a *adapter) String() string { func (a *AdaptFS) String() string {
return fmt.Sprintf("%v", a.fs) return fmt.Sprintf("%v", a.FS)
} }
// OpenFile implements the same method as documented on sys.FS // OpenFile implements the same method as documented on sys.FS
func (a *adapter) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { func (a *AdaptFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
return OpenFSFile(a.fs, cleanPath(path), flag, perm) return OpenFSFile(a.FS, cleanPath(path), flag, perm)
}
// Lstat implements the same method as documented on sys.FS
func (a *AdaptFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
// At this time, we make the assumption sys.FS instances do not support
// symbolic links, therefore Lstat is the same as Stat. This is obviously
// not true, but until FS.FS has a solid story for how to handle symlinks,
// we are better off not making a decision that would be difficult to
// revert later on.
//
// For further discussions on the topic, see:
// https://github.com/golang/go/issues/49580
return a.Stat(path)
} }
// Stat implements the same method as documented on sys.FS // Stat implements the same method as documented on sys.FS
func (a *adapter) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { func (a *AdaptFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
f, errno := a.OpenFile(path, experimentalsys.O_RDONLY, 0) f, errno := a.OpenFile(path, experimentalsys.O_RDONLY, 0)
if errno != 0 { if errno != 0 {
return sys.Stat_t{}, errno return sys.Stat_t{}, errno
@@ -51,17 +46,49 @@ func (a *adapter) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
return f.Stat() return f.Stat()
} }
// Lstat implements the same method as documented on sys.FS // Readlink implements the same method as documented on sys.FS
func (a *adapter) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { func (a *AdaptFS) Readlink(string) (string, experimentalsys.Errno) {
// At this time, we make the assumption that sys.FS instances do not support return "", experimentalsys.ENOSYS
// symbolic links, therefore Lstat is the same as Stat. This is obviously }
// not true but until sys.FS has a solid story for how to handle symlinks we
// are better off not making a decision that would be difficult to revert // Mkdir implements the same method as documented on sys.FS
// later on. func (a *AdaptFS) Mkdir(string, fs.FileMode) experimentalsys.Errno {
// return experimentalsys.ENOSYS
// For further discussions on the topic, see: }
// https://github.com/golang/go/issues/49580
return a.Stat(path) // Chmod implements the same method as documented on sys.FS
func (a *AdaptFS) Chmod(string, fs.FileMode) experimentalsys.Errno {
return experimentalsys.ENOSYS
}
// Rename implements the same method as documented on sys.FS
func (a *AdaptFS) Rename(string, string) experimentalsys.Errno {
return experimentalsys.ENOSYS
}
// Rmdir implements the same method as documented on sys.FS
func (a *AdaptFS) Rmdir(string) experimentalsys.Errno {
return experimentalsys.ENOSYS
}
// Link implements the same method as documented on sys.FS
func (a *AdaptFS) Link(string, string) experimentalsys.Errno {
return experimentalsys.ENOSYS
}
// Symlink implements the same method as documented on sys.FS
func (a *AdaptFS) Symlink(string, string) experimentalsys.Errno {
return experimentalsys.ENOSYS
}
// Unlink implements the same method as documented on sys.FS
func (a *AdaptFS) Unlink(string) experimentalsys.Errno {
return experimentalsys.ENOSYS
}
// Utimens implements the same method as documented on sys.FS
func (a *AdaptFS) Utimens(string, int64, int64) experimentalsys.Errno {
return experimentalsys.ENOSYS
} }
func cleanPath(name string) string { func cleanPath(name string) string {
+24 -30
View File
@@ -17,29 +17,23 @@ import (
"github.com/tetratelabs/wazero/sys" "github.com/tetratelabs/wazero/sys"
) )
func TestAdapt_nil(t *testing.T) { func TestAdaptFS_MkDir(t *testing.T) {
testFS := Adapt(nil) testFS := &AdaptFS{FS: os.DirFS(t.TempDir())}
_, ok := testFS.(experimentalsys.UnimplementedFS)
require.True(t, ok)
}
func TestAdapt_MkDir(t *testing.T) {
testFS := Adapt(os.DirFS(t.TempDir()))
err := testFS.Mkdir("mkdir", fs.ModeDir) err := testFS.Mkdir("mkdir", fs.ModeDir)
require.EqualErrno(t, experimentalsys.ENOSYS, err) require.EqualErrno(t, experimentalsys.ENOSYS, err)
} }
func TestAdapt_Chmod(t *testing.T) { func TestAdaptFS_Chmod(t *testing.T) {
testFS := Adapt(os.DirFS(t.TempDir())) testFS := &AdaptFS{FS: os.DirFS(t.TempDir())}
err := testFS.Chmod("chmod", fs.ModeDir) err := testFS.Chmod("chmod", fs.ModeDir)
require.EqualErrno(t, experimentalsys.ENOSYS, err) require.EqualErrno(t, experimentalsys.ENOSYS, err)
} }
func TestAdapt_Rename(t *testing.T) { func TestAdaptFS_Rename(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := Adapt(os.DirFS(tmpDir)) testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
file1 := "file1" file1 := "file1"
file1Path := joinPath(tmpDir, file1) file1Path := joinPath(tmpDir, file1)
@@ -57,9 +51,9 @@ func TestAdapt_Rename(t *testing.T) {
require.EqualErrno(t, experimentalsys.ENOSYS, err) require.EqualErrno(t, experimentalsys.ENOSYS, err)
} }
func TestAdapt_Rmdir(t *testing.T) { func TestAdaptFS_Rmdir(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := Adapt(os.DirFS(tmpDir)) testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
path := "rmdir" path := "rmdir"
realPath := joinPath(tmpDir, path) realPath := joinPath(tmpDir, path)
@@ -69,9 +63,9 @@ func TestAdapt_Rmdir(t *testing.T) {
require.EqualErrno(t, experimentalsys.ENOSYS, err) require.EqualErrno(t, experimentalsys.ENOSYS, err)
} }
func TestAdapt_Unlink(t *testing.T) { func TestAdaptFS_Unlink(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := Adapt(os.DirFS(tmpDir)) testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
path := "unlink" path := "unlink"
realPath := joinPath(tmpDir, path) realPath := joinPath(tmpDir, path)
@@ -81,9 +75,9 @@ func TestAdapt_Unlink(t *testing.T) {
require.EqualErrno(t, experimentalsys.ENOSYS, err) require.EqualErrno(t, experimentalsys.ENOSYS, err)
} }
func TestAdapt_UtimesNano(t *testing.T) { func TestAdaptFS_UtimesNano(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := Adapt(os.DirFS(tmpDir)) testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
path := "utimes" path := "utimes"
realPath := joinPath(tmpDir, path) realPath := joinPath(tmpDir, path)
@@ -93,13 +87,13 @@ func TestAdapt_UtimesNano(t *testing.T) {
require.EqualErrno(t, experimentalsys.ENOSYS, err) require.EqualErrno(t, experimentalsys.ENOSYS, err)
} }
func TestAdapt_Open_Read(t *testing.T) { func TestAdaptFS_Open_Read(t *testing.T) {
// Create a subdirectory, so we can test reads outside the sys.FS root.
tmpDir := t.TempDir() tmpDir := t.TempDir()
// Create a subdirectory, so we can test reads outside the sys.FS root.
tmpDir = joinPath(tmpDir, t.Name()) tmpDir = joinPath(tmpDir, t.Name())
require.NoError(t, os.Mkdir(tmpDir, 0o700)) require.NoError(t, os.Mkdir(tmpDir, 0o700))
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := Adapt(os.DirFS(tmpDir)) testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
// We can't correct operating system portability issues with os.DirFS on // We can't correct operating system portability issues with os.DirFS on
// windows. Use syscall.DirFS instead! // windows. Use syscall.DirFS instead!
@@ -113,10 +107,10 @@ func TestAdapt_Open_Read(t *testing.T) {
}) })
} }
func TestAdapt_Lstat(t *testing.T) { func TestAdaptFS_Lstat(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := Adapt(os.DirFS(tmpDir))
for _, path := range []string{"animals.txt", "sub", "sub-link"} { for _, path := range []string{"animals.txt", "sub", "sub-link"} {
fullPath := joinPath(tmpDir, path) fullPath := joinPath(tmpDir, path)
@@ -128,15 +122,15 @@ func TestAdapt_Lstat(t *testing.T) {
} }
} }
func TestAdapt_Stat(t *testing.T) { func TestAdaptFS_Stat(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := Adapt(os.DirFS(tmpDir))
testStat(t, testFS) testStat(t, testFS)
} }
// hackFS cheats the api.FS contract by opening for write (sys.O_RDWR). // hackFS cheats the fs.FS contract by opening for write (sys.O_RDWR).
// //
// Until we have an alternate public interface for filesystems, some users will // Until we have an alternate public interface for filesystems, some users will
// rely on this. Via testing, we ensure we don't accidentally break them. // rely on this. Via testing, we ensure we don't accidentally break them.
@@ -156,11 +150,11 @@ func (dir hackFS) Open(name string) (fs.File, error) {
} }
} }
// TestAdapt_HackedWrites ensures we allow writes even if they violate the // TestAdaptFS_HackedWrites ensures we allow writes even if they violate the
// api.FS contract. // api.FS contract.
func TestAdapt_HackedWrites(t *testing.T) { func TestAdaptFS_HackedWrites(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := Adapt(hackFS(tmpDir)) testFS := &AdaptFS{FS: hackFS(tmpDir)}
testOpen_O_RDWR(t, tmpDir, testFS) testOpen_O_RDWR(t, tmpDir, testFS)
} }
@@ -237,7 +231,7 @@ func (i *sysFileInfo) Sys() any {
// to zero). // to zero).
// //
// A fs.File implementing this should be functionally equivalent to an os.File, // A fs.File implementing this should be functionally equivalent to an os.File,
// even if both are less ideal than using NewDirFS directly, especially on // even if both are less ideal than using DirFS directly, especially on
// Windows. // Windows.
// //
// For example, on Windows, we cannot reliably read the inode for a // For example, on Windows, we cannot reliably read the inode for a
+4 -1
View File
@@ -9,7 +9,7 @@ import (
"github.com/tetratelabs/wazero/sys" "github.com/tetratelabs/wazero/sys"
) )
func NewDirFS(dir string) experimentalsys.FS { func DirFS(dir string) experimentalsys.FS {
return &dirFS{ return &dirFS{
dir: dir, dir: dir,
cleanedDir: ensureTrailingPathSeparator(dir), cleanedDir: ensureTrailingPathSeparator(dir),
@@ -23,8 +23,11 @@ func ensureTrailingPathSeparator(dir string) string {
return dir return dir
} }
// dirFS is not exported because the input fields must be maintained together.
// This is likely why os.DirFS doesn't, either!
type dirFS struct { type dirFS struct {
experimentalsys.UnimplementedFS experimentalsys.UnimplementedFS
dir string dir string
// cleanedDir is for easier OS-specific concatenation, as it always has // cleanedDir is for easier OS-specific concatenation, as it always has
// a trailing path separator. // a trailing path separator.
+37 -37
View File
@@ -16,8 +16,8 @@ import (
"github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/internal/testing/require"
) )
func TestNewDirFS(t *testing.T) { func TestDirFS(t *testing.T) {
testFS := NewDirFS(".") testFS := DirFS(".")
// Guest can look up / // Guest can look up /
f, errno := testFS.OpenFile("/", sys.O_RDONLY, 0) f, errno := testFS.OpenFile("/", sys.O_RDONLY, 0)
@@ -25,14 +25,14 @@ func TestNewDirFS(t *testing.T) {
require.EqualErrno(t, 0, f.Close()) require.EqualErrno(t, 0, f.Close())
t.Run("host path not found", func(t *testing.T) { t.Run("host path not found", func(t *testing.T) {
testFS := NewDirFS("a") testFS := DirFS("a")
_, errno = testFS.OpenFile(".", sys.O_RDONLY, 0) _, errno = testFS.OpenFile(".", sys.O_RDONLY, 0)
require.EqualErrno(t, sys.ENOENT, errno) require.EqualErrno(t, sys.ENOENT, errno)
}) })
t.Run("host path not a directory", func(t *testing.T) { t.Run("host path not a directory", func(t *testing.T) {
arg0 := os.Args[0] // should be safe in scratch tests which don't have the source mounted. arg0 := os.Args[0] // should be safe in scratch tests which don't have the source mounted.
testFS := NewDirFS(arg0) testFS := DirFS(arg0)
d, errno := testFS.OpenFile(".", sys.O_RDONLY, 0) d, errno := testFS.OpenFile(".", sys.O_RDONLY, 0)
require.EqualErrno(t, 0, errno) require.EqualErrno(t, 0, errno)
_, errno = d.Readdir(-1) _, errno = d.Readdir(-1)
@@ -41,13 +41,13 @@ func TestNewDirFS(t *testing.T) {
} }
func TestDirFS_join(t *testing.T) { func TestDirFS_join(t *testing.T) {
testFS := NewDirFS("/").(*dirFS) testFS := DirFS("/").(*dirFS)
require.Equal(t, "/", testFS.join("")) require.Equal(t, "/", testFS.join(""))
require.Equal(t, "/", testFS.join(".")) require.Equal(t, "/", testFS.join("."))
require.Equal(t, "/", testFS.join("/")) require.Equal(t, "/", testFS.join("/"))
require.Equal(t, "/tmp", testFS.join("tmp")) require.Equal(t, "/tmp", testFS.join("tmp"))
testFS = NewDirFS(".").(*dirFS) testFS = DirFS(".").(*dirFS)
require.Equal(t, ".", testFS.join("")) require.Equal(t, ".", testFS.join(""))
require.Equal(t, ".", testFS.join(".")) require.Equal(t, ".", testFS.join("."))
require.Equal(t, ".", testFS.join("/")) require.Equal(t, ".", testFS.join("/"))
@@ -55,7 +55,7 @@ func TestDirFS_join(t *testing.T) {
} }
func TestDirFS_String(t *testing.T) { func TestDirFS_String(t *testing.T) {
testFS := NewDirFS(".") testFS := DirFS(".")
// String has the name of the path entered // String has the name of the path entered
require.Equal(t, ".", testFS.(fmt.Stringer).String()) require.Equal(t, ".", testFS.(fmt.Stringer).String())
@@ -65,7 +65,7 @@ func TestDirFS_Lstat(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
for _, path := range []string{"animals.txt", "sub", "sub-link"} { for _, path := range []string{"animals.txt", "sub", "sub-link"} {
require.EqualErrno(t, 0, testFS.Symlink(path, path+"-link")) require.EqualErrno(t, 0, testFS.Symlink(path, path+"-link"))
} }
@@ -75,7 +75,7 @@ func TestDirFS_Lstat(t *testing.T) {
func TestDirFS_MkDir(t *testing.T) { func TestDirFS_MkDir(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "mkdir" name := "mkdir"
realPath := path.Join(tmpDir, name) realPath := path.Join(tmpDir, name)
@@ -152,7 +152,7 @@ func requireMode(t *testing.T, testFS sys.FS, path string, mode fs.FileMode) {
func TestDirFS_Rename(t *testing.T) { func TestDirFS_Rename(t *testing.T) {
t.Run("from doesn't exist", func(t *testing.T) { t.Run("from doesn't exist", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
file1 := "file1" file1 := "file1"
file1Path := path.Join(tmpDir, file1) file1Path := path.Join(tmpDir, file1)
@@ -164,7 +164,7 @@ func TestDirFS_Rename(t *testing.T) {
}) })
t.Run("file to non-exist", func(t *testing.T) { t.Run("file to non-exist", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
file1 := "file1" file1 := "file1"
file1Path := path.Join(tmpDir, file1) file1Path := path.Join(tmpDir, file1)
@@ -187,7 +187,7 @@ func TestDirFS_Rename(t *testing.T) {
}) })
t.Run("dir to non-exist", func(t *testing.T) { t.Run("dir to non-exist", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
dir1 := "dir1" dir1 := "dir1"
dir1Path := path.Join(tmpDir, dir1) dir1Path := path.Join(tmpDir, dir1)
@@ -208,7 +208,7 @@ func TestDirFS_Rename(t *testing.T) {
}) })
t.Run("dir to file", func(t *testing.T) { t.Run("dir to file", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
dir1 := "dir1" dir1 := "dir1"
dir1Path := path.Join(tmpDir, dir1) dir1Path := path.Join(tmpDir, dir1)
@@ -227,7 +227,7 @@ func TestDirFS_Rename(t *testing.T) {
}) })
t.Run("file to dir", func(t *testing.T) { t.Run("file to dir", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
file1 := "file1" file1 := "file1"
file1Path := path.Join(tmpDir, file1) file1Path := path.Join(tmpDir, file1)
@@ -246,7 +246,7 @@ func TestDirFS_Rename(t *testing.T) {
// Similar to https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs/test.zig#L567-L582 // Similar to https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs/test.zig#L567-L582
t.Run("dir to empty dir should be fine", func(t *testing.T) { t.Run("dir to empty dir should be fine", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
dir1 := "dir1" dir1 := "dir1"
dir1Path := path.Join(tmpDir, dir1) dir1Path := path.Join(tmpDir, dir1)
@@ -279,7 +279,7 @@ func TestDirFS_Rename(t *testing.T) {
// Similar to https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs/test.zig#L584-L604 // Similar to https://github.com/ziglang/zig/blob/0.10.1/lib/std/fs/test.zig#L584-L604
t.Run("dir to non empty dir should be EXIST", func(t *testing.T) { t.Run("dir to non empty dir should be EXIST", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
dir1 := "dir1" dir1 := "dir1"
dir1Path := path.Join(tmpDir, dir1) dir1Path := path.Join(tmpDir, dir1)
@@ -306,7 +306,7 @@ func TestDirFS_Rename(t *testing.T) {
t.Run("file to file", func(t *testing.T) { t.Run("file to file", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
file1 := "file1" file1 := "file1"
file1Path := path.Join(tmpDir, file1) file1Path := path.Join(tmpDir, file1)
@@ -334,7 +334,7 @@ func TestDirFS_Rename(t *testing.T) {
}) })
t.Run("dir to itself", func(t *testing.T) { t.Run("dir to itself", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
dir1 := "dir1" dir1 := "dir1"
dir1Path := path.Join(tmpDir, dir1) dir1Path := path.Join(tmpDir, dir1)
@@ -349,7 +349,7 @@ func TestDirFS_Rename(t *testing.T) {
}) })
t.Run("file to itself", func(t *testing.T) { t.Run("file to itself", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
file1 := "file1" file1 := "file1"
file1Path := path.Join(tmpDir, file1) file1Path := path.Join(tmpDir, file1)
@@ -369,7 +369,7 @@ func TestDirFS_Rename(t *testing.T) {
func TestDirFS_Rmdir(t *testing.T) { func TestDirFS_Rmdir(t *testing.T) {
t.Run("doesn't exist", func(t *testing.T) { t.Run("doesn't exist", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "rmdir" name := "rmdir"
@@ -379,7 +379,7 @@ func TestDirFS_Rmdir(t *testing.T) {
t.Run("dir not empty", func(t *testing.T) { t.Run("dir not empty", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "rmdir" name := "rmdir"
realPath := path.Join(tmpDir, name) realPath := path.Join(tmpDir, name)
@@ -396,7 +396,7 @@ func TestDirFS_Rmdir(t *testing.T) {
t.Run("dir previously not empty", func(t *testing.T) { t.Run("dir previously not empty", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "rmdir" name := "rmdir"
realPath := path.Join(tmpDir, name) realPath := path.Join(tmpDir, name)
@@ -414,7 +414,7 @@ func TestDirFS_Rmdir(t *testing.T) {
t.Run("dir empty", func(t *testing.T) { t.Run("dir empty", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "rmdir" name := "rmdir"
realPath := path.Join(tmpDir, name) realPath := path.Join(tmpDir, name)
@@ -426,7 +426,7 @@ func TestDirFS_Rmdir(t *testing.T) {
t.Run("dir empty while opening", func(t *testing.T) { t.Run("dir empty while opening", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "rmdir" name := "rmdir"
realPath := path.Join(tmpDir, name) realPath := path.Join(tmpDir, name)
@@ -443,7 +443,7 @@ func TestDirFS_Rmdir(t *testing.T) {
t.Run("not directory", func(t *testing.T) { t.Run("not directory", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "rmdir" name := "rmdir"
realPath := path.Join(tmpDir, name) realPath := path.Join(tmpDir, name)
@@ -460,7 +460,7 @@ func TestDirFS_Rmdir(t *testing.T) {
func TestDirFS_Unlink(t *testing.T) { func TestDirFS_Unlink(t *testing.T) {
t.Run("doesn't exist", func(t *testing.T) { t.Run("doesn't exist", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "unlink" name := "unlink"
err := testFS.Unlink(name) err := testFS.Unlink(name)
@@ -469,7 +469,7 @@ func TestDirFS_Unlink(t *testing.T) {
t.Run("target: dir", func(t *testing.T) { t.Run("target: dir", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
dir := "dir" dir := "dir"
realPath := path.Join(tmpDir, dir) realPath := path.Join(tmpDir, dir)
@@ -484,7 +484,7 @@ func TestDirFS_Unlink(t *testing.T) {
t.Run("target: symlink to dir", func(t *testing.T) { t.Run("target: symlink to dir", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
// Create link target dir. // Create link target dir.
subDirName := "subdir" subDirName := "subdir"
@@ -502,7 +502,7 @@ func TestDirFS_Unlink(t *testing.T) {
t.Run("file exists", func(t *testing.T) { t.Run("file exists", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "unlink" name := "unlink"
realPath := path.Join(tmpDir, name) realPath := path.Join(tmpDir, name)
@@ -518,7 +518,7 @@ func TestDirFS_Unlink(t *testing.T) {
func TestDirFS_Utimesns(t *testing.T) { func TestDirFS_Utimesns(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
file := "file" file := "file"
err := os.WriteFile(path.Join(tmpDir, file), []byte{}, 0o700) err := os.WriteFile(path.Join(tmpDir, file), []byte{}, 0o700)
@@ -574,7 +574,7 @@ func TestDirFS_Utimesns(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
file := path.Join(tmpDir, "file") file := path.Join(tmpDir, "file")
errno := os.WriteFile(file, []byte{}, 0o700) errno := os.WriteFile(file, []byte{}, 0o700)
@@ -638,7 +638,7 @@ func TestDirFS_OpenFile(t *testing.T) {
require.NoError(t, os.Mkdir(tmpDir, 0o700)) require.NoError(t, os.Mkdir(tmpDir, 0o700))
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
testOpen_Read(t, testFS, statSetsIno(), true) testOpen_Read(t, testFS, statSetsIno(), true)
@@ -656,7 +656,7 @@ func TestDirFS_Stat(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
testStat(t, testFS) testStat(t, testFS)
// from os.TestDirFSPathsValid // from os.TestDirFSPathsValid
@@ -673,7 +673,7 @@ func TestDirFS_Stat(t *testing.T) {
func TestDirFS_Readdir(t *testing.T) { func TestDirFS_Readdir(t *testing.T) {
root := t.TempDir() root := t.TempDir()
testFS := NewDirFS(root) testFS := DirFS(root)
const readDirTarget = "dir" const readDirTarget = "dir"
errno := testFS.Mkdir(readDirTarget, 0o700) errno := testFS.Mkdir(readDirTarget, 0o700)
@@ -728,7 +728,7 @@ func TestDirFS_Link(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
require.EqualErrno(t, testFS.Link("cat", ""), sys.ENOENT) require.EqualErrno(t, testFS.Link("cat", ""), sys.ENOENT)
require.EqualErrno(t, testFS.Link("sub/test.txt", "sub/test.txt"), sys.EEXIST) require.EqualErrno(t, testFS.Link("sub/test.txt", "sub/test.txt"), sys.EEXIST)
@@ -745,7 +745,7 @@ func TestDirFS_Symlink(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
require.EqualErrno(t, sys.EEXIST, testFS.Symlink("sub/test.txt", "sub/test.txt")) require.EqualErrno(t, sys.EEXIST, testFS.Symlink("sub/test.txt", "sub/test.txt"))
// Non-existing old name is allowed. // Non-existing old name is allowed.
@@ -767,6 +767,6 @@ func TestDirFS_Readlink(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
testReadlink(t, testFS, testFS) testReadlink(t, testFS, testFS)
} }
+4 -4
View File
@@ -60,7 +60,7 @@ func OpenFSFile(fs fs.FS, path string, flag experimentalsys.Oflag, perm fs.FileM
return nil, errno return nil, errno
} }
// Don't return an os.File because the path is not absolute. osFile needs // Don't return an os.File because the path is not absolute. osFile needs
// the path to be real and certain fs.File impls are subrooted. // the path to be real and certain FS.File impls are subrooted.
return &fsFile{fs: fs, name: path, file: f}, 0 return &fsFile{fs: fs, name: path, file: f}, 0
} }
@@ -261,7 +261,7 @@ func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno experime
// Notably, this uses readdirFile or fs.ReadDirFile if available. This does not // Notably, this uses readdirFile or fs.ReadDirFile if available. This does not
// return inodes on windows. // return inodes on windows.
func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) { func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
// Windows lets you Readdir after close, fs.File also may not implement // Windows lets you Readdir after close, FS.File also may not implement
// close in a meaningful way. read our closed field to return consistent // close in a meaningful way. read our closed field to return consistent
// results. // results.
if f.closed { if f.closed {
@@ -286,8 +286,8 @@ func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experim
return return
} }
// Try with fs.ReadDirFile which is available on api.FS implementations // Try with FS.ReadDirFile which is available on api.FS implementations
// like embed:fs. // like embed:FS.
if rdf, ok := f.file.(fs.ReadDirFile); ok { if rdf, ok := f.file.(fs.ReadDirFile); ok {
entries, e := rdf.ReadDir(n) entries, e := rdf.ReadDir(n)
if errno = adjustReaddirErr(f, f.closed, e); errno != 0 { if errno = adjustReaddirErr(f, f.closed, e); errno != 0 {
+1 -1
View File
@@ -832,7 +832,7 @@ func testSync_NoError(t *testing.T, sync func(experimentalsys.File) experimental
f experimentalsys.File f experimentalsys.File
}{ }{
{name: "UnimplementedFile", f: experimentalsys.UnimplementedFile{}}, {name: "UnimplementedFile", f: experimentalsys.UnimplementedFile{}},
{name: "File of read-only fs.File", f: ro}, {name: "File of read-only FS.File", f: ro},
{name: "File of os.File", f: rw}, {name: "File of os.File", f: rw},
} }
+1 -1
View File
@@ -14,7 +14,7 @@ func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno exp
return v.Ino, 0 return v.Ino, 0
} }
if dirPath == "" { if dirPath == "" {
// This is a fs.File backed implementation which doesn't have access to // This is a FS.File backed implementation which doesn't have access to
// the original file path. // the original file path.
return return
} }
+13 -43
View File
@@ -6,51 +6,21 @@ import (
experimentalsys "github.com/tetratelabs/wazero/experimental/sys" experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
) )
// NewReadFS is used to mask an existing sys.FS for reads. Notably, this allows type ReadFS struct {
// the CLI to do read-only mounts of directories the host user can write, but
// doesn't want the guest wasm to. For example, Python libraries shouldn't be
// written to at runtime by the python wasm file.
func NewReadFS(fs experimentalsys.FS) experimentalsys.FS {
if _, ok := fs.(*readFS); ok {
return fs
} else if _, ok = fs.(experimentalsys.UnimplementedFS); ok {
return fs // unimplemented is read-only
}
return &readFS{fs}
}
type readFS struct {
experimentalsys.FS experimentalsys.FS
} }
// OpenFile implements the same method as documented on sys.FS // OpenFile implements the same method as documented on sys.FS
func (r *readFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { func (r *ReadFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
// TODO: Once the real implementation is complete, move the below to // Mask the mutually exclusive bits as they determine write mode.
// /RATIONALE.md. Doing this while the type is unstable creates
// documentation drift as we expect a lot of reshaping meanwhile.
//
// Callers of this function expect to either open a valid file handle, or
// get an error, if they can't. We want to return ENOSYS if opened for
// anything except reads.
//
// Instead, we could return a fake no-op file on O_WRONLY. However, this
// hurts observability because a later write error to that file will be on
// a different source code line than the root cause which is opening with
// an unsupported flag.
//
// The tricky part is os.RD_ONLY is typically defined as zero, so while the
// parameter is named flag, the part about opening read vs write isn't a
// typical bitflag. We can't compare against zero anyway, because even if
// there isn't a current flag to OR in with that, there may be in the
// future. What we do instead is mask the flags about read/write mode and
// check if they are the opposite of read or not.
switch flag & (experimentalsys.O_RDONLY | experimentalsys.O_WRONLY | experimentalsys.O_RDWR) { switch flag & (experimentalsys.O_RDONLY | experimentalsys.O_WRONLY | experimentalsys.O_RDWR) {
case experimentalsys.O_WRONLY, experimentalsys.O_RDWR: case experimentalsys.O_WRONLY, experimentalsys.O_RDWR:
// Return the correct error if a directory was opened for write.
if flag&experimentalsys.O_DIRECTORY != 0 { if flag&experimentalsys.O_DIRECTORY != 0 {
return nil, experimentalsys.EISDIR return nil, experimentalsys.EISDIR
} }
return nil, experimentalsys.ENOSYS return nil, experimentalsys.ENOSYS
default: // sys.O_RDONLY (or no flag) so we are ok! default: // sys.O_RDONLY (integer zero) so we are ok!
} }
f, errno := r.FS.OpenFile(path, flag, perm) f, errno := r.FS.OpenFile(path, flag, perm)
@@ -61,42 +31,42 @@ func (r *readFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileM
} }
// Mkdir implements the same method as documented on sys.FS // Mkdir implements the same method as documented on sys.FS
func (r *readFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno { func (r *ReadFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno {
return experimentalsys.EROFS return experimentalsys.EROFS
} }
// Chmod implements the same method as documented on sys.FS // Chmod implements the same method as documented on sys.FS
func (r *readFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { func (r *ReadFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
return experimentalsys.EROFS return experimentalsys.EROFS
} }
// Rename implements the same method as documented on sys.FS // Rename implements the same method as documented on sys.FS
func (r *readFS) Rename(from, to string) experimentalsys.Errno { func (r *ReadFS) Rename(from, to string) experimentalsys.Errno {
return experimentalsys.EROFS return experimentalsys.EROFS
} }
// Rmdir implements the same method as documented on sys.FS // Rmdir implements the same method as documented on sys.FS
func (r *readFS) Rmdir(path string) experimentalsys.Errno { func (r *ReadFS) Rmdir(path string) experimentalsys.Errno {
return experimentalsys.EROFS return experimentalsys.EROFS
} }
// Link implements the same method as documented on sys.FS // Link implements the same method as documented on sys.FS
func (r *readFS) Link(_, _ string) experimentalsys.Errno { func (r *ReadFS) Link(_, _ string) experimentalsys.Errno {
return experimentalsys.EROFS return experimentalsys.EROFS
} }
// Symlink implements the same method as documented on sys.FS // Symlink implements the same method as documented on sys.FS
func (r *readFS) Symlink(_, _ string) experimentalsys.Errno { func (r *ReadFS) Symlink(_, _ string) experimentalsys.Errno {
return experimentalsys.EROFS return experimentalsys.EROFS
} }
// Unlink implements the same method as documented on sys.FS // Unlink implements the same method as documented on sys.FS
func (r *readFS) Unlink(path string) experimentalsys.Errno { func (r *ReadFS) Unlink(path string) experimentalsys.Errno {
return experimentalsys.EROFS return experimentalsys.EROFS
} }
// Utimens implements the same method as documented on sys.FS // Utimens implements the same method as documented on sys.FS
func (r *readFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno { func (r *ReadFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno {
return experimentalsys.EROFS return experimentalsys.EROFS
} }
+28 -31
View File
@@ -14,16 +14,13 @@ import (
func TestNewReadFS(t *testing.T) { func TestNewReadFS(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
// Doesn't double-wrap file systems that are already read-only
require.Equal(t, sys.UnimplementedFS{}, NewReadFS(sys.UnimplementedFS{}))
// Wraps a sys.FS because it allows access to Write // Wraps a sys.FS because it allows access to Write
adapted := Adapt(os.DirFS(tmpDir)) adapted := &AdaptFS{FS: os.DirFS(tmpDir)}
require.NotEqual(t, adapted, NewReadFS(adapted)) require.NotEqual(t, adapted, &ReadFS{FS: adapted})
// Wraps a writeable file system // Wraps a writeable file system
writeable := NewDirFS(tmpDir) writeable := DirFS(tmpDir)
readFS := NewReadFS(writeable) readFS := &ReadFS{FS: writeable}
require.NotEqual(t, writeable, readFS) require.NotEqual(t, writeable, readFS)
} }
@@ -31,27 +28,27 @@ func TestReadFS_Lstat(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
writeable := NewDirFS(tmpDir) writeable := DirFS(tmpDir)
for _, path := range []string{"animals.txt", "sub", "sub-link"} { for _, path := range []string{"animals.txt", "sub", "sub-link"} {
require.EqualErrno(t, 0, writeable.Symlink(path, path+"-link")) require.EqualErrno(t, 0, writeable.Symlink(path, path+"-link"))
} }
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
testLstat(t, testFS) testLstat(t, testFS)
} }
func TestReadFS_MkDir(t *testing.T) { func TestReadFS_MkDir(t *testing.T) {
writeable := NewDirFS(t.TempDir()) writeable := DirFS(t.TempDir())
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
err := testFS.Mkdir("mkdir", fs.ModeDir) err := testFS.Mkdir("mkdir", fs.ModeDir)
require.EqualErrno(t, sys.EROFS, err) require.EqualErrno(t, sys.EROFS, err)
} }
func TestReadFS_Chmod(t *testing.T) { func TestReadFS_Chmod(t *testing.T) {
writeable := NewDirFS(t.TempDir()) writeable := DirFS(t.TempDir())
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
err := testFS.Chmod("chmod", fs.ModeDir) err := testFS.Chmod("chmod", fs.ModeDir)
require.EqualErrno(t, sys.EROFS, err) require.EqualErrno(t, sys.EROFS, err)
@@ -59,8 +56,8 @@ func TestReadFS_Chmod(t *testing.T) {
func TestReadFS_Rename(t *testing.T) { func TestReadFS_Rename(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
writeable := NewDirFS(tmpDir) writeable := DirFS(tmpDir)
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
file1 := "file1" file1 := "file1"
file1Path := joinPath(tmpDir, file1) file1Path := joinPath(tmpDir, file1)
@@ -80,8 +77,8 @@ func TestReadFS_Rename(t *testing.T) {
func TestReadFS_Rmdir(t *testing.T) { func TestReadFS_Rmdir(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
writeable := NewDirFS(tmpDir) writeable := DirFS(tmpDir)
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
path := "rmdir" path := "rmdir"
realPath := joinPath(tmpDir, path) realPath := joinPath(tmpDir, path)
@@ -93,8 +90,8 @@ func TestReadFS_Rmdir(t *testing.T) {
func TestReadFS_Unlink(t *testing.T) { func TestReadFS_Unlink(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
writeable := NewDirFS(tmpDir) writeable := DirFS(tmpDir)
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
path := "unlink" path := "unlink"
realPath := joinPath(tmpDir, path) realPath := joinPath(tmpDir, path)
@@ -106,8 +103,8 @@ func TestReadFS_Unlink(t *testing.T) {
func TestReadFS_UtimesNano(t *testing.T) { func TestReadFS_UtimesNano(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
writeable := NewDirFS(tmpDir) writeable := DirFS(tmpDir)
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
path := "utimes" path := "utimes"
realPath := joinPath(tmpDir, path) realPath := joinPath(tmpDir, path)
@@ -129,7 +126,7 @@ func TestReadFS_Open_Read(t *testing.T) {
{ {
name: "DirFS", name: "DirFS",
fs: func(tmpDir string) sys.FS { fs: func(tmpDir string) sys.FS {
return NewDirFS(tmpDir) return DirFS(tmpDir)
}, },
expectFileIno: true, expectFileIno: true,
expectDirIno: true, expectDirIno: true,
@@ -137,7 +134,7 @@ func TestReadFS_Open_Read(t *testing.T) {
{ {
name: "fstest.MapFS", name: "fstest.MapFS",
fs: func(tmpDir string) sys.FS { fs: func(tmpDir string) sys.FS {
return Adapt(fstest.FS) return &AdaptFS{FS: fstest.FS}
}, },
expectFileIno: false, expectFileIno: false,
expectDirIno: false, expectDirIno: false,
@@ -145,7 +142,7 @@ func TestReadFS_Open_Read(t *testing.T) {
{ {
name: "os.DirFS", name: "os.DirFS",
fs: func(tmpDir string) sys.FS { fs: func(tmpDir string) sys.FS {
return Adapt(os.DirFS(tmpDir)) return &AdaptFS{FS: os.DirFS(tmpDir)}
}, },
expectFileIno: statSetsIno(), expectFileIno: statSetsIno(),
expectDirIno: runtime.GOOS != "windows", expectDirIno: runtime.GOOS != "windows",
@@ -153,7 +150,7 @@ func TestReadFS_Open_Read(t *testing.T) {
{ {
name: "mask(os.DirFS)", name: "mask(os.DirFS)",
fs: func(tmpDir string) sys.FS { fs: func(tmpDir string) sys.FS {
return Adapt(&MaskOsFS{Fs: os.DirFS(tmpDir)}) return &AdaptFS{FS: &MaskOsFS{Fs: os.DirFS(tmpDir)}}
}, },
expectFileIno: statSetsIno(), expectFileIno: statSetsIno(),
expectDirIno: runtime.GOOS != "windows", expectDirIno: runtime.GOOS != "windows",
@@ -161,7 +158,7 @@ func TestReadFS_Open_Read(t *testing.T) {
{ {
name: "mask(os.DirFS) ZeroIno", name: "mask(os.DirFS) ZeroIno",
fs: func(tmpDir string) sys.FS { fs: func(tmpDir string) sys.FS {
return Adapt(&MaskOsFS{Fs: os.DirFS(tmpDir), ZeroIno: true}) return &AdaptFS{FS: &MaskOsFS{Fs: os.DirFS(tmpDir), ZeroIno: true}}
}, },
expectFileIno: false, expectFileIno: false,
expectDirIno: false, expectDirIno: false,
@@ -178,7 +175,7 @@ func TestReadFS_Open_Read(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
testOpen_Read(t, NewReadFS(tc.fs(tmpDir)), tc.expectFileIno, tc.expectDirIno) testOpen_Read(t, &ReadFS{FS: tc.fs(tmpDir)}, tc.expectFileIno, tc.expectDirIno)
}) })
} }
} }
@@ -187,8 +184,8 @@ func TestReadFS_Stat(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
writeable := NewDirFS(tmpDir) writeable := DirFS(tmpDir)
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
testStat(t, testFS) testStat(t, testFS)
} }
@@ -196,7 +193,7 @@ func TestReadFS_Readlink(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
require.NoError(t, fstest.WriteTestFiles(tmpDir)) require.NoError(t, fstest.WriteTestFiles(tmpDir))
writeable := NewDirFS(tmpDir) writeable := DirFS(tmpDir)
testFS := NewReadFS(writeable) testFS := &ReadFS{FS: writeable}
testReadlink(t, testFS, writeable) testReadlink(t, testFS, writeable)
} }
+1 -1
View File
@@ -70,7 +70,7 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS experimentalsys.FS) {
t.Run("O_TRUNC", func(t *testing.T) { t.Run("O_TRUNC", func(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFS := NewDirFS(tmpDir) testFS := DirFS(tmpDir)
name := "truncate" name := "truncate"
realPath := path.Join(tmpDir, name) realPath := path.Join(tmpDir, name)
+4 -4
View File
@@ -112,7 +112,7 @@ func TestModuleInstance_Close(t *testing.T) {
} }
t.Run("calls Context.Close()", func(t *testing.T) { t.Run("calls Context.Close()", func(t *testing.T) {
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{}}) testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": &testfs.File{}}}
sysCtx := internalsys.DefaultContext(testFS) sysCtx := internalsys.DefaultContext(testFS)
fsCtx := sysCtx.FS() fsCtx := sysCtx.FS()
@@ -147,7 +147,7 @@ func TestModuleInstance_Close(t *testing.T) {
t.Run("error closing", func(t *testing.T) { t.Run("error closing", func(t *testing.T) {
// Right now, the only way to err closing the sys context is if a File.Close erred. // Right now, the only way to err closing the sys context is if a File.Close erred.
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{CloseErr: errors.New("error closing")}}) testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": &testfs.File{CloseErr: errors.New("error closing")}}}
sysCtx := internalsys.DefaultContext(testFS) sysCtx := internalsys.DefaultContext(testFS)
fsCtx := sysCtx.FS() fsCtx := sysCtx.FS()
@@ -217,7 +217,7 @@ func TestModuleInstance_CallDynamic(t *testing.T) {
} }
t.Run("calls Context.Close()", func(t *testing.T) { t.Run("calls Context.Close()", func(t *testing.T) {
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{}}) testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": &testfs.File{}}}
sysCtx := internalsys.DefaultContext(testFS) sysCtx := internalsys.DefaultContext(testFS)
fsCtx := sysCtx.FS() fsCtx := sysCtx.FS()
@@ -245,7 +245,7 @@ func TestModuleInstance_CallDynamic(t *testing.T) {
t.Run("error closing", func(t *testing.T) { t.Run("error closing", func(t *testing.T) {
// Right now, the only way to err closing the sys context is if a File.Close erred. // Right now, the only way to err closing the sys context is if a File.Close erred.
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{CloseErr: errors.New("error closing")}}) testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": &testfs.File{CloseErr: errors.New("error closing")}}}
sysCtx := internalsys.DefaultContext(testFS) sysCtx := internalsys.DefaultContext(testFS)
fsCtx := sysCtx.FS() fsCtx := sysCtx.FS()