Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sysfs: Makes ReadFS and AdaptFS embeddable #1607

Merged
merged 2 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/wazero/wazero.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reamed to be more like os.DirFS

if readOnly {
root = sysfs.NewReadFS(root)
root = &sysfs.ReadFS{FS: root}
}

config = config.(sysfs.FSConfig).WithSysFSMount(root, guestPath)
Expand Down
7 changes: 4 additions & 3 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
},
},
Expand All @@ -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)
}
},
},
Expand All @@ -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)
}
},
},
Expand Down
15 changes: 0 additions & 15 deletions experimental/sys/unimplemented.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fuzz as we removed this from FS a while ago

return ENOSYS
}

// UnimplementedFile is a File that returns ENOSYS for all functions,
// except where no-op are otherwise documented.
//
Expand Down
32 changes: 16 additions & 16 deletions experimental/sysfs/config_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@ import (

var moduleConfig wazero.ModuleConfig

// This example shows how to configure a sysfs.NewDirFS
func ExampleNewDirFS() {
root := sysfs.NewDirFS(".")
// 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.AdaptFS{FS: m}

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)
// This example shows how to configure a sysfs.DirFS
func ExampleDirFS() {
root := sysfs.DirFS(".")

moduleConfig = wazero.NewModuleConfig().
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(readOnly, "/"))
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/"))
}

// This example shows how to adapt a fs.FS as a sys.FS
func ExampleAdapt() {
m := fstest.MapFS{
"a/b.txt": &fstest.MapFile{Mode: 0o666},
".": &fstest.MapFile{Mode: 0o777 | fs.ModeDir},
}
root := sysfs.Adapt(m)
// 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(root, "/"))
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(readOnly, "/"))
}
31 changes: 14 additions & 17 deletions experimental/sysfs/sysfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 8 additions & 4 deletions fsconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions fsconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{"/"},
},
{
Expand All @@ -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"},
},
}
Expand Down
8 changes: 4 additions & 4 deletions imports/wasi_snapshot_preview1/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
24 changes: 12 additions & 12 deletions internal/sys/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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{}},
},
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion internal/sys/sys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading