mirror of
https://github.com/wazero/wazero
synced 2026-06-21 14:12:37 +00:00
sysfs: Makes ReadFS and AdaptFS embeddable (#1607)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
root := sysfs.NewDirFS(dir)
|
||||
root := sysfs.DirFS(dir)
|
||||
if readOnly {
|
||||
root = sysfs.NewReadFS(root)
|
||||
root = &sysfs.ReadFS{FS: root}
|
||||
}
|
||||
|
||||
config = config.(sysfs.FSConfig).WithSysFSMount(root, guestPath)
|
||||
|
||||
+4
-3
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fstest"
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
internalsys "github.com/tetratelabs/wazero/internal/sys"
|
||||
@@ -325,7 +326,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
|
||||
config := base.WithFS(testFS)
|
||||
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||
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)
|
||||
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||
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)
|
||||
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||
rootfs := sys.FS().RootFS()
|
||||
require.Equal(t, sysfs.Adapt(nil), rootfs)
|
||||
require.Equal(t, experimentalsys.UnimplementedFS{}, rootfs)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,16 +10,6 @@ import (
|
||||
// This should be embedded to have forward compatible implementations.
|
||||
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
|
||||
func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) {
|
||||
return nil, ENOSYS
|
||||
@@ -80,11 +70,6 @@ func (UnimplementedFS) Utimens(path string, atim, mtim int64) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// Truncate implements FS.Truncate
|
||||
func (UnimplementedFS) Truncate(string, int64) Errno {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// UnimplementedFile is a File that returns ENOSYS for all functions,
|
||||
// except where no-op are otherwise documented.
|
||||
//
|
||||
|
||||
@@ -10,31 +10,31 @@ import (
|
||||
|
||||
var moduleConfig wazero.ModuleConfig
|
||||
|
||||
// This example shows how to configure a sysfs.NewDirFS
|
||||
func ExampleNewDirFS() {
|
||||
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() {
|
||||
// This example shows how to adapt a fs.FS to a sys.FS
|
||||
func ExampleAdaptFS() {
|
||||
m := fstest.MapFS{
|
||||
"a/b.txt": &fstest.MapFile{Mode: 0o666},
|
||||
".": &fstest.MapFile{Mode: 0o777 | fs.ModeDir},
|
||||
}
|
||||
root := sysfs.Adapt(m)
|
||||
root := &sysfs.AdaptFS{FS: m}
|
||||
|
||||
moduleConfig = wazero.NewModuleConfig().
|
||||
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
@@ -8,32 +8,29 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/sysfs"
|
||||
)
|
||||
|
||||
// Adapt adapts the input to sys.FS unless it is already one. Use NewDirFS
|
||||
// instead of os.DirFS as it handles interop issues such as windows support.
|
||||
// AdaptFS adapts the input to sys.FS. Use DirFS instead of adapting an
|
||||
// os.DirFS as it handles interop issues such as windows support.
|
||||
//
|
||||
// Note: This performs no flag verification on OpenFile. fs.FS cannot read
|
||||
// flags as there is no parameter to pass them through with. Moreover, 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 {
|
||||
return sysfs.Adapt(fs)
|
||||
type AdaptFS = sysfs.AdaptFS
|
||||
|
||||
// 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
|
||||
// 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 {
|
||||
return sysfs.NewReadFS(fs)
|
||||
}
|
||||
|
||||
// NewDirFS is like os.DirFS except it returns sys.FS, which has more features.
|
||||
func NewDirFS(dir string) experimentalsys.FS {
|
||||
return sysfs.NewDirFS(dir)
|
||||
}
|
||||
//
|
||||
// Note: This implements read-only by returning sys.EROFS or sys.EBADF,
|
||||
// depending on the operation that require write access.
|
||||
type ReadFS = sysfs.ReadFS
|
||||
|
||||
+8
-4
@@ -165,17 +165,21 @@ func (c *fsConfig) clone() *fsConfig {
|
||||
|
||||
// WithDirMount implements FSConfig.WithDirMount
|
||||
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
|
||||
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
|
||||
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
|
||||
@@ -188,7 +192,7 @@ func (c *fsConfig) WithSysFSMount(fs experimentalsys.FS, guestPath string) FSCon
|
||||
if i, ok := ret.guestPathToFS[cleaned]; ok {
|
||||
ret.fs[i] = fs
|
||||
ret.guestPaths[i] = guestPath
|
||||
} else {
|
||||
} else if fs != nil {
|
||||
ret.guestPathToFS[cleaned] = len(ret.fs)
|
||||
ret.fs = append(ret.fs, fs)
|
||||
ret.guestPaths = append(ret.guestPaths, guestPath)
|
||||
|
||||
+4
-4
@@ -29,13 +29,13 @@ func TestFSConfig(t *testing.T) {
|
||||
{
|
||||
name: "WithFSMount",
|
||||
input: base.WithFSMount(testFS, "/"),
|
||||
expectedFS: []sys.FS{sysfs.Adapt(testFS)},
|
||||
expectedFS: []sys.FS{&sysfs.AdaptFS{FS: testFS}},
|
||||
expectedGuestPaths: []string{"/"},
|
||||
},
|
||||
{
|
||||
name: "WithFSMount overwrites",
|
||||
input: base.WithFSMount(testFS, "/").WithFSMount(testFS2, "/"),
|
||||
expectedFS: []sys.FS{sysfs.Adapt(testFS2)},
|
||||
expectedFS: []sys.FS{&sysfs.AdaptFS{FS: testFS2}},
|
||||
expectedGuestPaths: []string{"/"},
|
||||
},
|
||||
{
|
||||
@@ -45,13 +45,13 @@ func TestFSConfig(t *testing.T) {
|
||||
{
|
||||
name: "WithDirMount overwrites",
|
||||
input: base.WithFSMount(testFS, "/").WithDirMount(".", "/"),
|
||||
expectedFS: []sys.FS{sysfs.NewDirFS(".")},
|
||||
expectedFS: []sys.FS{sysfs.DirFS(".")},
|
||||
expectedGuestPaths: []string{"/"},
|
||||
},
|
||||
{
|
||||
name: "multiple",
|
||||
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"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -242,9 +242,9 @@ func Test_fdFdstatGet(t *testing.T) {
|
||||
// replace stdin with a fake TTY file.
|
||||
// TODO: Make this easier once we have in-memory sys.File
|
||||
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,
|
||||
}}).OpenFile("stdin", 0, 0)
|
||||
}}}).OpenFile("stdin", 0, 0)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
|
||||
stdin.File = stdinFile
|
||||
@@ -3648,8 +3648,8 @@ func Test_pathLink(t *testing.T) {
|
||||
|
||||
func Test_pathOpen(t *testing.T) {
|
||||
dir := t.TempDir() // open before loop to ensure no locking problems.
|
||||
writeFS := sysfs.NewDirFS(dir)
|
||||
readFS := sysfs.NewReadFS(writeFS)
|
||||
writeFS := sysfs.DirFS(dir)
|
||||
readFS := &sysfs.ReadFS{FS: writeFS}
|
||||
|
||||
fileName := "file"
|
||||
fileContents := []byte("012")
|
||||
|
||||
+12
-12
@@ -24,7 +24,7 @@ func TestNewFSContext(t *testing.T) {
|
||||
embedFS, err := fs.Sub(testdata, "testdata")
|
||||
require.NoError(t, err)
|
||||
|
||||
dirfs := sysfs.NewDirFS(".")
|
||||
dirfs := sysfs.DirFS(".")
|
||||
|
||||
// Test various usual configuration for the file system.
|
||||
tests := []struct {
|
||||
@@ -33,21 +33,21 @@ func TestNewFSContext(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
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
|
||||
// cross-architecture (a.k.a. scratch) build containers.
|
||||
fs: dirfs,
|
||||
},
|
||||
{
|
||||
name: "NewReadFS",
|
||||
fs: sysfs.NewReadFS(dirfs),
|
||||
name: "ReadFS",
|
||||
fs: &sysfs.ReadFS{FS: dirfs},
|
||||
},
|
||||
{
|
||||
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) {
|
||||
embedFS, err := fs.Sub(testdata, "testdata")
|
||||
require.NoError(t, err)
|
||||
testFS := sysfs.Adapt(embedFS)
|
||||
testFS := &sysfs.AdaptFS{FS: embedFS}
|
||||
|
||||
c := Context{}
|
||||
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) {
|
||||
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{}})
|
||||
testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": &testfs.File{}}}
|
||||
|
||||
c := Context{}
|
||||
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) {
|
||||
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{}
|
||||
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) {
|
||||
tmpDir := t.TempDir()
|
||||
dirFS := sysfs.NewDirFS(tmpDir)
|
||||
dirFS := sysfs.DirFS(tmpDir)
|
||||
|
||||
const dirName = "dir"
|
||||
errno := dirFS.Mkdir(dirName, 0o700)
|
||||
@@ -252,7 +252,7 @@ func TestFSContext_Renumber(t *testing.T) {
|
||||
|
||||
func TestDirentCache_Read(t *testing.T) {
|
||||
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)
|
||||
fsc := c.fsc
|
||||
defer fsc.Close()
|
||||
@@ -430,7 +430,7 @@ func TestDirentCache_ReadNewFile(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
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)
|
||||
fsc := c.fsc
|
||||
defer fsc.Close()
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestContext_WalltimeNanos(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)
|
||||
require.NoError(t, err)
|
||||
|
||||
+63
-36
@@ -9,40 +9,35 @@ import (
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// Adapt adapts the input to sys.FS unless it is already one. Use NewDirFS instead
|
||||
// of os.DirFS as it handles interop issues such as windows support.
|
||||
//
|
||||
// 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
|
||||
type AdaptFS struct {
|
||||
FS fs.FS
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (a *adapter) String() string {
|
||||
return fmt.Sprintf("%v", a.fs)
|
||||
func (a *AdaptFS) String() string {
|
||||
return fmt.Sprintf("%v", a.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) {
|
||||
return OpenFSFile(a.fs, cleanPath(path), flag, perm)
|
||||
func (a *AdaptFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
|
||||
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
|
||||
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)
|
||||
if errno != 0 {
|
||||
return sys.Stat_t{}, errno
|
||||
@@ -51,17 +46,49 @@ func (a *adapter) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
return f.Stat()
|
||||
}
|
||||
|
||||
// Lstat implements the same method as documented on sys.FS
|
||||
func (a *adapter) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
// At this time, we make the assumption that sys.FS instances do not support
|
||||
// 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
|
||||
// later on.
|
||||
//
|
||||
// For further discussions on the topic, see:
|
||||
// https://github.com/golang/go/issues/49580
|
||||
return a.Stat(path)
|
||||
// Readlink implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Readlink(string) (string, experimentalsys.Errno) {
|
||||
return "", experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Mkdir implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Mkdir(string, fs.FileMode) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -17,29 +17,23 @@ import (
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func TestAdapt_nil(t *testing.T) {
|
||||
testFS := Adapt(nil)
|
||||
_, ok := testFS.(experimentalsys.UnimplementedFS)
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
func TestAdapt_MkDir(t *testing.T) {
|
||||
testFS := Adapt(os.DirFS(t.TempDir()))
|
||||
func TestAdaptFS_MkDir(t *testing.T) {
|
||||
testFS := &AdaptFS{FS: os.DirFS(t.TempDir())}
|
||||
|
||||
err := testFS.Mkdir("mkdir", fs.ModeDir)
|
||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||
}
|
||||
|
||||
func TestAdapt_Chmod(t *testing.T) {
|
||||
testFS := Adapt(os.DirFS(t.TempDir()))
|
||||
func TestAdaptFS_Chmod(t *testing.T) {
|
||||
testFS := &AdaptFS{FS: os.DirFS(t.TempDir())}
|
||||
|
||||
err := testFS.Chmod("chmod", fs.ModeDir)
|
||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||
}
|
||||
|
||||
func TestAdapt_Rename(t *testing.T) {
|
||||
func TestAdaptFS_Rename(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := Adapt(os.DirFS(tmpDir))
|
||||
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||
|
||||
file1 := "file1"
|
||||
file1Path := joinPath(tmpDir, file1)
|
||||
@@ -57,9 +51,9 @@ func TestAdapt_Rename(t *testing.T) {
|
||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||
}
|
||||
|
||||
func TestAdapt_Rmdir(t *testing.T) {
|
||||
func TestAdaptFS_Rmdir(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := Adapt(os.DirFS(tmpDir))
|
||||
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||
|
||||
path := "rmdir"
|
||||
realPath := joinPath(tmpDir, path)
|
||||
@@ -69,9 +63,9 @@ func TestAdapt_Rmdir(t *testing.T) {
|
||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||
}
|
||||
|
||||
func TestAdapt_Unlink(t *testing.T) {
|
||||
func TestAdaptFS_Unlink(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := Adapt(os.DirFS(tmpDir))
|
||||
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||
|
||||
path := "unlink"
|
||||
realPath := joinPath(tmpDir, path)
|
||||
@@ -81,9 +75,9 @@ func TestAdapt_Unlink(t *testing.T) {
|
||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||
}
|
||||
|
||||
func TestAdapt_UtimesNano(t *testing.T) {
|
||||
func TestAdaptFS_UtimesNano(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := Adapt(os.DirFS(tmpDir))
|
||||
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||
|
||||
path := "utimes"
|
||||
realPath := joinPath(tmpDir, path)
|
||||
@@ -93,13 +87,13 @@ func TestAdapt_UtimesNano(t *testing.T) {
|
||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||
}
|
||||
|
||||
func TestAdapt_Open_Read(t *testing.T) {
|
||||
// Create a subdirectory, so we can test reads outside the sys.FS root.
|
||||
func TestAdaptFS_Open_Read(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
// Create a subdirectory, so we can test reads outside the sys.FS root.
|
||||
tmpDir = joinPath(tmpDir, t.Name())
|
||||
require.NoError(t, os.Mkdir(tmpDir, 0o700))
|
||||
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
|
||||
// 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()
|
||||
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||
testFS := Adapt(os.DirFS(tmpDir))
|
||||
|
||||
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
|
||||
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()
|
||||
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||
|
||||
testFS := Adapt(os.DirFS(tmpDir))
|
||||
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
|
||||
// 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.
|
||||
func TestAdapt_HackedWrites(t *testing.T) {
|
||||
func TestAdaptFS_HackedWrites(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := Adapt(hackFS(tmpDir))
|
||||
testFS := &AdaptFS{FS: hackFS(tmpDir)}
|
||||
|
||||
testOpen_O_RDWR(t, tmpDir, testFS)
|
||||
}
|
||||
@@ -237,7 +231,7 @@ func (i *sysFileInfo) Sys() any {
|
||||
// to zero).
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// For example, on Windows, we cannot reliably read the inode for a
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func NewDirFS(dir string) experimentalsys.FS {
|
||||
func DirFS(dir string) experimentalsys.FS {
|
||||
return &dirFS{
|
||||
dir: dir,
|
||||
cleanedDir: ensureTrailingPathSeparator(dir),
|
||||
@@ -23,8 +23,11 @@ func ensureTrailingPathSeparator(dir string) string {
|
||||
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 {
|
||||
experimentalsys.UnimplementedFS
|
||||
|
||||
dir string
|
||||
// cleanedDir is for easier OS-specific concatenation, as it always has
|
||||
// a trailing path separator.
|
||||
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
)
|
||||
|
||||
func TestNewDirFS(t *testing.T) {
|
||||
testFS := NewDirFS(".")
|
||||
func TestDirFS(t *testing.T) {
|
||||
testFS := DirFS(".")
|
||||
|
||||
// Guest can look up /
|
||||
f, errno := testFS.OpenFile("/", sys.O_RDONLY, 0)
|
||||
@@ -25,14 +25,14 @@ func TestNewDirFS(t *testing.T) {
|
||||
require.EqualErrno(t, 0, f.Close())
|
||||
|
||||
t.Run("host path not found", func(t *testing.T) {
|
||||
testFS := NewDirFS("a")
|
||||
testFS := DirFS("a")
|
||||
_, errno = testFS.OpenFile(".", sys.O_RDONLY, 0)
|
||||
require.EqualErrno(t, sys.ENOENT, errno)
|
||||
})
|
||||
t.Run("host path not a directory", func(t *testing.T) {
|
||||
arg0 := os.Args[0] // should be safe in scratch tests which don't have the source mounted.
|
||||
|
||||
testFS := NewDirFS(arg0)
|
||||
testFS := DirFS(arg0)
|
||||
d, errno := testFS.OpenFile(".", sys.O_RDONLY, 0)
|
||||
require.EqualErrno(t, 0, errno)
|
||||
_, errno = d.Readdir(-1)
|
||||
@@ -41,13 +41,13 @@ func TestNewDirFS(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, "/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("/"))
|
||||
@@ -55,7 +55,7 @@ func TestDirFS_join(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirFS_String(t *testing.T) {
|
||||
testFS := NewDirFS(".")
|
||||
testFS := DirFS(".")
|
||||
|
||||
// String has the name of the path entered
|
||||
require.Equal(t, ".", testFS.(fmt.Stringer).String())
|
||||
@@ -65,7 +65,7 @@ func TestDirFS_Lstat(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
for _, path := range []string{"animals.txt", "sub", "sub-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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "mkdir"
|
||||
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) {
|
||||
t.Run("from doesn't exist", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
file1 := "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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
file1 := "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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
dir1 := "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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
dir1 := "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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
file1 := "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
|
||||
t.Run("dir to empty dir should be fine", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
dir1 := "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
|
||||
t.Run("dir to non empty dir should be EXIST", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
dir1 := "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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
file1 := "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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
dir1 := "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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
file1 := "file1"
|
||||
file1Path := path.Join(tmpDir, file1)
|
||||
@@ -369,7 +369,7 @@ func TestDirFS_Rename(t *testing.T) {
|
||||
func TestDirFS_Rmdir(t *testing.T) {
|
||||
t.Run("doesn't exist", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "rmdir"
|
||||
|
||||
@@ -379,7 +379,7 @@ func TestDirFS_Rmdir(t *testing.T) {
|
||||
|
||||
t.Run("dir not empty", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "rmdir"
|
||||
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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "rmdir"
|
||||
realPath := path.Join(tmpDir, name)
|
||||
@@ -414,7 +414,7 @@ func TestDirFS_Rmdir(t *testing.T) {
|
||||
|
||||
t.Run("dir empty", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "rmdir"
|
||||
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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "rmdir"
|
||||
realPath := path.Join(tmpDir, name)
|
||||
@@ -443,7 +443,7 @@ func TestDirFS_Rmdir(t *testing.T) {
|
||||
|
||||
t.Run("not directory", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "rmdir"
|
||||
realPath := path.Join(tmpDir, name)
|
||||
@@ -460,7 +460,7 @@ func TestDirFS_Rmdir(t *testing.T) {
|
||||
func TestDirFS_Unlink(t *testing.T) {
|
||||
t.Run("doesn't exist", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
name := "unlink"
|
||||
|
||||
err := testFS.Unlink(name)
|
||||
@@ -469,7 +469,7 @@ func TestDirFS_Unlink(t *testing.T) {
|
||||
|
||||
t.Run("target: dir", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
dir := "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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
// Create link target dir.
|
||||
subDirName := "subdir"
|
||||
@@ -502,7 +502,7 @@ func TestDirFS_Unlink(t *testing.T) {
|
||||
|
||||
t.Run("file exists", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "unlink"
|
||||
realPath := path.Join(tmpDir, name)
|
||||
@@ -518,7 +518,7 @@ func TestDirFS_Unlink(t *testing.T) {
|
||||
|
||||
func TestDirFS_Utimesns(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
file := "file"
|
||||
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) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
file := path.Join(tmpDir, "file")
|
||||
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, fstest.WriteTestFiles(tmpDir))
|
||||
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
testOpen_Read(t, testFS, statSetsIno(), true)
|
||||
|
||||
@@ -656,7 +656,7 @@ func TestDirFS_Stat(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
testStat(t, testFS)
|
||||
|
||||
// from os.TestDirFSPathsValid
|
||||
@@ -673,7 +673,7 @@ func TestDirFS_Stat(t *testing.T) {
|
||||
|
||||
func TestDirFS_Readdir(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
testFS := NewDirFS(root)
|
||||
testFS := DirFS(root)
|
||||
|
||||
const readDirTarget = "dir"
|
||||
errno := testFS.Mkdir(readDirTarget, 0o700)
|
||||
@@ -728,7 +728,7 @@ func TestDirFS_Link(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
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("sub/test.txt", "sub/test.txt"), sys.EEXIST)
|
||||
@@ -745,7 +745,7 @@ func TestDirFS_Symlink(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
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"))
|
||||
// Non-existing old name is allowed.
|
||||
@@ -767,6 +767,6 @@ func TestDirFS_Readlink(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
testReadlink(t, testFS, testFS)
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func OpenFSFile(fs fs.FS, path string, flag experimentalsys.Oflag, perm fs.FileM
|
||||
return nil, errno
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// return inodes on windows.
|
||||
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
|
||||
// results.
|
||||
if f.closed {
|
||||
@@ -286,8 +286,8 @@ func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experim
|
||||
return
|
||||
}
|
||||
|
||||
// Try with fs.ReadDirFile which is available on api.FS implementations
|
||||
// like embed:fs.
|
||||
// Try with FS.ReadDirFile which is available on api.FS implementations
|
||||
// like embed:FS.
|
||||
if rdf, ok := f.file.(fs.ReadDirFile); ok {
|
||||
entries, e := rdf.ReadDir(n)
|
||||
if errno = adjustReaddirErr(f, f.closed, e); errno != 0 {
|
||||
|
||||
@@ -832,7 +832,7 @@ func testSync_NoError(t *testing.T, sync func(experimentalsys.File) experimental
|
||||
f experimentalsys.File
|
||||
}{
|
||||
{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},
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno exp
|
||||
return v.Ino, 0
|
||||
}
|
||||
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.
|
||||
return
|
||||
}
|
||||
|
||||
+13
-43
@@ -6,51 +6,21 @@ import (
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
// NewReadFS 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
|
||||
// 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 {
|
||||
type ReadFS struct {
|
||||
experimentalsys.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) {
|
||||
// TODO: Once the real implementation is complete, move the below to
|
||||
// /RATIONALE.md. Doing this while the type is unstable creates
|
||||
// documentation drift as we expect a lot of reshaping meanwhile.
|
||||
//
|
||||
// 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.
|
||||
func (r *ReadFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
|
||||
// Mask the mutually exclusive bits as they determine write mode.
|
||||
switch flag & (experimentalsys.O_RDONLY | 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 {
|
||||
return nil, experimentalsys.EISDIR
|
||||
}
|
||||
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)
|
||||
@@ -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
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,13 @@ import (
|
||||
func TestNewReadFS(t *testing.T) {
|
||||
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
|
||||
adapted := Adapt(os.DirFS(tmpDir))
|
||||
require.NotEqual(t, adapted, NewReadFS(adapted))
|
||||
adapted := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||
require.NotEqual(t, adapted, &ReadFS{FS: adapted})
|
||||
|
||||
// Wraps a writeable file system
|
||||
writeable := NewDirFS(tmpDir)
|
||||
readFS := NewReadFS(writeable)
|
||||
writeable := DirFS(tmpDir)
|
||||
readFS := &ReadFS{FS: writeable}
|
||||
require.NotEqual(t, writeable, readFS)
|
||||
}
|
||||
|
||||
@@ -31,27 +28,27 @@ func TestReadFS_Lstat(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||
|
||||
writeable := NewDirFS(tmpDir)
|
||||
writeable := DirFS(tmpDir)
|
||||
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
|
||||
require.EqualErrno(t, 0, writeable.Symlink(path, path+"-link"))
|
||||
}
|
||||
|
||||
testFS := NewReadFS(writeable)
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
|
||||
testLstat(t, testFS)
|
||||
}
|
||||
|
||||
func TestReadFS_MkDir(t *testing.T) {
|
||||
writeable := NewDirFS(t.TempDir())
|
||||
testFS := NewReadFS(writeable)
|
||||
writeable := DirFS(t.TempDir())
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
|
||||
err := testFS.Mkdir("mkdir", fs.ModeDir)
|
||||
require.EqualErrno(t, sys.EROFS, err)
|
||||
}
|
||||
|
||||
func TestReadFS_Chmod(t *testing.T) {
|
||||
writeable := NewDirFS(t.TempDir())
|
||||
testFS := NewReadFS(writeable)
|
||||
writeable := DirFS(t.TempDir())
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
|
||||
err := testFS.Chmod("chmod", fs.ModeDir)
|
||||
require.EqualErrno(t, sys.EROFS, err)
|
||||
@@ -59,8 +56,8 @@ func TestReadFS_Chmod(t *testing.T) {
|
||||
|
||||
func TestReadFS_Rename(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeable := NewDirFS(tmpDir)
|
||||
testFS := NewReadFS(writeable)
|
||||
writeable := DirFS(tmpDir)
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
|
||||
file1 := "file1"
|
||||
file1Path := joinPath(tmpDir, file1)
|
||||
@@ -80,8 +77,8 @@ func TestReadFS_Rename(t *testing.T) {
|
||||
|
||||
func TestReadFS_Rmdir(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeable := NewDirFS(tmpDir)
|
||||
testFS := NewReadFS(writeable)
|
||||
writeable := DirFS(tmpDir)
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
|
||||
path := "rmdir"
|
||||
realPath := joinPath(tmpDir, path)
|
||||
@@ -93,8 +90,8 @@ func TestReadFS_Rmdir(t *testing.T) {
|
||||
|
||||
func TestReadFS_Unlink(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeable := NewDirFS(tmpDir)
|
||||
testFS := NewReadFS(writeable)
|
||||
writeable := DirFS(tmpDir)
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
|
||||
path := "unlink"
|
||||
realPath := joinPath(tmpDir, path)
|
||||
@@ -106,8 +103,8 @@ func TestReadFS_Unlink(t *testing.T) {
|
||||
|
||||
func TestReadFS_UtimesNano(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
writeable := NewDirFS(tmpDir)
|
||||
testFS := NewReadFS(writeable)
|
||||
writeable := DirFS(tmpDir)
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
|
||||
path := "utimes"
|
||||
realPath := joinPath(tmpDir, path)
|
||||
@@ -129,7 +126,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
||||
{
|
||||
name: "DirFS",
|
||||
fs: func(tmpDir string) sys.FS {
|
||||
return NewDirFS(tmpDir)
|
||||
return DirFS(tmpDir)
|
||||
},
|
||||
expectFileIno: true,
|
||||
expectDirIno: true,
|
||||
@@ -137,7 +134,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
||||
{
|
||||
name: "fstest.MapFS",
|
||||
fs: func(tmpDir string) sys.FS {
|
||||
return Adapt(fstest.FS)
|
||||
return &AdaptFS{FS: fstest.FS}
|
||||
},
|
||||
expectFileIno: false,
|
||||
expectDirIno: false,
|
||||
@@ -145,7 +142,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
||||
{
|
||||
name: "os.DirFS",
|
||||
fs: func(tmpDir string) sys.FS {
|
||||
return Adapt(os.DirFS(tmpDir))
|
||||
return &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||
},
|
||||
expectFileIno: statSetsIno(),
|
||||
expectDirIno: runtime.GOOS != "windows",
|
||||
@@ -153,7 +150,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
||||
{
|
||||
name: "mask(os.DirFS)",
|
||||
fs: func(tmpDir string) sys.FS {
|
||||
return Adapt(&MaskOsFS{Fs: os.DirFS(tmpDir)})
|
||||
return &AdaptFS{FS: &MaskOsFS{Fs: os.DirFS(tmpDir)}}
|
||||
},
|
||||
expectFileIno: statSetsIno(),
|
||||
expectDirIno: runtime.GOOS != "windows",
|
||||
@@ -161,7 +158,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
||||
{
|
||||
name: "mask(os.DirFS) ZeroIno",
|
||||
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,
|
||||
expectDirIno: false,
|
||||
@@ -178,7 +175,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
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()
|
||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||
|
||||
writeable := NewDirFS(tmpDir)
|
||||
testFS := NewReadFS(writeable)
|
||||
writeable := DirFS(tmpDir)
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
testStat(t, testFS)
|
||||
}
|
||||
|
||||
@@ -196,7 +193,7 @@ func TestReadFS_Readlink(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||
|
||||
writeable := NewDirFS(tmpDir)
|
||||
testFS := NewReadFS(writeable)
|
||||
writeable := DirFS(tmpDir)
|
||||
testFS := &ReadFS{FS: writeable}
|
||||
testReadlink(t, testFS, writeable)
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func testOpen_O_RDWR(t *testing.T, tmpDir string, testFS experimentalsys.FS) {
|
||||
|
||||
t.Run("O_TRUNC", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
testFS := NewDirFS(tmpDir)
|
||||
testFS := DirFS(tmpDir)
|
||||
|
||||
name := "truncate"
|
||||
realPath := path.Join(tmpDir, name)
|
||||
|
||||
@@ -112,7 +112,7 @@ func TestModuleInstance_Close(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)
|
||||
fsCtx := sysCtx.FS()
|
||||
|
||||
@@ -147,7 +147,7 @@ func TestModuleInstance_Close(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.
|
||||
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)
|
||||
fsCtx := sysCtx.FS()
|
||||
|
||||
@@ -217,7 +217,7 @@ func TestModuleInstance_CallDynamic(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)
|
||||
fsCtx := sysCtx.FS()
|
||||
|
||||
@@ -245,7 +245,7 @@ func TestModuleInstance_CallDynamic(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.
|
||||
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)
|
||||
fsCtx := sysCtx.FS()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user