Skip to content

Commit 2f2b6a9

Browse files
sysfs: Makes ReadFS and AdaptFS embeddable (#1607)
Signed-off-by: Adrian Cole <[email protected]>
1 parent 1f8c908 commit 2f2b6a9

21 files changed

+245
-267
lines changed

Diff for: cmd/wazero/wazero.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -419,9 +419,9 @@ func validateMounts(mounts sliceFlag, stdErr logging.Writer) (rc int, rootPath s
419419
fmt.Fprintf(stdErr, "invalid mount: path %q is not a directory\n", dir)
420420
}
421421

422-
root := sysfs.NewDirFS(dir)
422+
root := sysfs.DirFS(dir)
423423
if readOnly {
424-
root = sysfs.NewReadFS(root)
424+
root = &sysfs.ReadFS{FS: root}
425425
}
426426

427427
config = config.(sysfs.FSConfig).WithSysFSMount(root, guestPath)

Diff for: config_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/tetratelabs/wazero/api"
11+
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
1112
"github.com/tetratelabs/wazero/internal/fstest"
1213
"github.com/tetratelabs/wazero/internal/platform"
1314
internalsys "github.com/tetratelabs/wazero/internal/sys"
@@ -325,7 +326,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
325326
config := base.WithFS(testFS)
326327
return config, func(t *testing.T, sys *internalsys.Context) {
327328
rootfs := sys.FS().RootFS()
328-
require.Equal(t, sysfs.Adapt(testFS), rootfs)
329+
require.Equal(t, &sysfs.AdaptFS{FS: testFS}, rootfs)
329330
}
330331
},
331332
},
@@ -336,7 +337,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
336337
config := base.WithFS(testFS).WithFS(testFS2)
337338
return config, func(t *testing.T, sys *internalsys.Context) {
338339
rootfs := sys.FS().RootFS()
339-
require.Equal(t, sysfs.Adapt(testFS2), rootfs)
340+
require.Equal(t, &sysfs.AdaptFS{FS: testFS2}, rootfs)
340341
}
341342
},
342343
},
@@ -346,7 +347,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
346347
config := base.WithFS(nil)
347348
return config, func(t *testing.T, sys *internalsys.Context) {
348349
rootfs := sys.FS().RootFS()
349-
require.Equal(t, sysfs.Adapt(nil), rootfs)
350+
require.Equal(t, experimentalsys.UnimplementedFS{}, rootfs)
350351
}
351352
},
352353
},

Diff for: experimental/sys/unimplemented.go

-15
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,6 @@ import (
1010
// This should be embedded to have forward compatible implementations.
1111
type UnimplementedFS struct{}
1212

13-
// String implements fmt.Stringer
14-
func (UnimplementedFS) String() string {
15-
return "Unimplemented:/"
16-
}
17-
18-
// Open implements the same method as documented on fs.FS
19-
func (UnimplementedFS) Open(name string) (fs.File, error) {
20-
return nil, &fs.PathError{Op: "open", Path: name, Err: ENOSYS}
21-
}
22-
2313
// OpenFile implements FS.OpenFile
2414
func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) {
2515
return nil, ENOSYS
@@ -80,11 +70,6 @@ func (UnimplementedFS) Utimens(path string, atim, mtim int64) Errno {
8070
return ENOSYS
8171
}
8272

83-
// Truncate implements FS.Truncate
84-
func (UnimplementedFS) Truncate(string, int64) Errno {
85-
return ENOSYS
86-
}
87-
8873
// UnimplementedFile is a File that returns ENOSYS for all functions,
8974
// except where no-op are otherwise documented.
9075
//

Diff for: experimental/sysfs/config_example_test.go

+16-16
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,31 @@ import (
1010

1111
var moduleConfig wazero.ModuleConfig
1212

13-
// This example shows how to configure a sysfs.NewDirFS
14-
func ExampleNewDirFS() {
15-
root := sysfs.NewDirFS(".")
13+
// This example shows how to adapt a fs.FS to a sys.FS
14+
func ExampleAdaptFS() {
15+
m := fstest.MapFS{
16+
"a/b.txt": &fstest.MapFile{Mode: 0o666},
17+
".": &fstest.MapFile{Mode: 0o777 | fs.ModeDir},
18+
}
19+
root := &sysfs.AdaptFS{FS: m}
1620

1721
moduleConfig = wazero.NewModuleConfig().
1822
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/"))
1923
}
2024

21-
// This example shows how to configure a sysfs.NewReadFS
22-
func ExampleNewReadFS() {
23-
root := sysfs.NewDirFS(".")
24-
readOnly := sysfs.NewReadFS(root)
25+
// This example shows how to configure a sysfs.DirFS
26+
func ExampleDirFS() {
27+
root := sysfs.DirFS(".")
2528

2629
moduleConfig = wazero.NewModuleConfig().
27-
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(readOnly, "/"))
30+
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/"))
2831
}
2932

30-
// This example shows how to adapt a fs.FS as a sys.FS
31-
func ExampleAdapt() {
32-
m := fstest.MapFS{
33-
"a/b.txt": &fstest.MapFile{Mode: 0o666},
34-
".": &fstest.MapFile{Mode: 0o777 | fs.ModeDir},
35-
}
36-
root := sysfs.Adapt(m)
33+
// This example shows how to configure a sysfs.ReadFS
34+
func ExampleReadFS() {
35+
root := sysfs.DirFS(".")
36+
readOnly := &sysfs.ReadFS{FS: root}
3737

3838
moduleConfig = wazero.NewModuleConfig().
39-
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/"))
39+
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(readOnly, "/"))
4040
}

Diff for: experimental/sysfs/sysfs.go

+14-17
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,29 @@
88
package sysfs
99

1010
import (
11-
"io/fs"
12-
1311
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
1412
"github.com/tetratelabs/wazero/internal/sysfs"
1513
)
1614

17-
// Adapt adapts the input to sys.FS unless it is already one. Use NewDirFS
18-
// instead of os.DirFS as it handles interop issues such as windows support.
15+
// AdaptFS adapts the input to sys.FS. Use DirFS instead of adapting an
16+
// os.DirFS as it handles interop issues such as windows support.
1917
//
20-
// Note: This performs no flag verification on OpenFile. fs.FS cannot read
21-
// flags as there is no parameter to pass them through with. Moreover, fs.FS
18+
// Note: This performs no flag verification on OpenFile. sys.FS cannot read
19+
// flags as there is no parameter to pass them through with. Moreover, sys.FS
2220
// documentation does not require the file to be present. In summary, we can't
2321
// enforce flag behavior.
24-
func Adapt(fs fs.FS) experimentalsys.FS {
25-
return sysfs.Adapt(fs)
22+
type AdaptFS = sysfs.AdaptFS
23+
24+
// DirFS is like os.DirFS except it returns sys.FS, which has more features.
25+
func DirFS(dir string) experimentalsys.FS {
26+
return sysfs.DirFS(dir)
2627
}
2728

28-
// NewReadFS is used to mask an existing sys.FS for reads. Notably, this allows
29+
// ReadFS is used to mask an existing sys.FS for reads. Notably, this allows
2930
// the CLI to do read-only mounts of directories the host user can write, but
3031
// doesn't want the guest wasm to. For example, Python libraries shouldn't be
3132
// written to at runtime by the python wasm file.
32-
func NewReadFS(fs experimentalsys.FS) experimentalsys.FS {
33-
return sysfs.NewReadFS(fs)
34-
}
35-
36-
// NewDirFS is like os.DirFS except it returns sys.FS, which has more features.
37-
func NewDirFS(dir string) experimentalsys.FS {
38-
return sysfs.NewDirFS(dir)
39-
}
33+
//
34+
// Note: This implements read-only by returning sys.EROFS or sys.EBADF,
35+
// depending on the operation that require write access.
36+
type ReadFS = sysfs.ReadFS

Diff for: fsconfig.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,21 @@ func (c *fsConfig) clone() *fsConfig {
165165

166166
// WithDirMount implements FSConfig.WithDirMount
167167
func (c *fsConfig) WithDirMount(dir, guestPath string) FSConfig {
168-
return c.WithSysFSMount(sysfs.NewDirFS(dir), guestPath)
168+
return c.WithSysFSMount(sysfs.DirFS(dir), guestPath)
169169
}
170170

171171
// WithReadOnlyDirMount implements FSConfig.WithReadOnlyDirMount
172172
func (c *fsConfig) WithReadOnlyDirMount(dir, guestPath string) FSConfig {
173-
return c.WithSysFSMount(sysfs.NewReadFS(sysfs.NewDirFS(dir)), guestPath)
173+
return c.WithSysFSMount(&sysfs.ReadFS{FS: sysfs.DirFS(dir)}, guestPath)
174174
}
175175

176176
// WithFSMount implements FSConfig.WithFSMount
177177
func (c *fsConfig) WithFSMount(fs fs.FS, guestPath string) FSConfig {
178-
return c.WithSysFSMount(sysfs.Adapt(fs), guestPath)
178+
var adapted experimentalsys.FS
179+
if fs != nil {
180+
adapted = &sysfs.AdaptFS{FS: fs}
181+
}
182+
return c.WithSysFSMount(adapted, guestPath)
179183
}
180184

181185
// WithSysFSMount implements sysfs.FSConfig
@@ -188,7 +192,7 @@ func (c *fsConfig) WithSysFSMount(fs experimentalsys.FS, guestPath string) FSCon
188192
if i, ok := ret.guestPathToFS[cleaned]; ok {
189193
ret.fs[i] = fs
190194
ret.guestPaths[i] = guestPath
191-
} else {
195+
} else if fs != nil {
192196
ret.guestPathToFS[cleaned] = len(ret.fs)
193197
ret.fs = append(ret.fs, fs)
194198
ret.guestPaths = append(ret.guestPaths, guestPath)

Diff for: fsconfig_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ func TestFSConfig(t *testing.T) {
2929
{
3030
name: "WithFSMount",
3131
input: base.WithFSMount(testFS, "/"),
32-
expectedFS: []sys.FS{sysfs.Adapt(testFS)},
32+
expectedFS: []sys.FS{&sysfs.AdaptFS{FS: testFS}},
3333
expectedGuestPaths: []string{"/"},
3434
},
3535
{
3636
name: "WithFSMount overwrites",
3737
input: base.WithFSMount(testFS, "/").WithFSMount(testFS2, "/"),
38-
expectedFS: []sys.FS{sysfs.Adapt(testFS2)},
38+
expectedFS: []sys.FS{&sysfs.AdaptFS{FS: testFS2}},
3939
expectedGuestPaths: []string{"/"},
4040
},
4141
{
@@ -45,13 +45,13 @@ func TestFSConfig(t *testing.T) {
4545
{
4646
name: "WithDirMount overwrites",
4747
input: base.WithFSMount(testFS, "/").WithDirMount(".", "/"),
48-
expectedFS: []sys.FS{sysfs.NewDirFS(".")},
48+
expectedFS: []sys.FS{sysfs.DirFS(".")},
4949
expectedGuestPaths: []string{"/"},
5050
},
5151
{
5252
name: "multiple",
5353
input: base.WithReadOnlyDirMount(".", "/").WithDirMount("/tmp", "/tmp"),
54-
expectedFS: []sys.FS{sysfs.NewReadFS(sysfs.NewDirFS(".")), sysfs.NewDirFS("/tmp")},
54+
expectedFS: []sys.FS{&sysfs.ReadFS{FS: sysfs.DirFS(".")}, sysfs.DirFS("/tmp")},
5555
expectedGuestPaths: []string{"/", "/tmp"},
5656
},
5757
}

Diff for: imports/wasi_snapshot_preview1/fs_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,9 @@ func Test_fdFdstatGet(t *testing.T) {
242242
// replace stdin with a fake TTY file.
243243
// TODO: Make this easier once we have in-memory sys.File
244244
stdin, _ := fsc.LookupFile(sys.FdStdin)
245-
stdinFile, errno := sysfs.Adapt(&gofstest.MapFS{"stdin": &gofstest.MapFile{
245+
stdinFile, errno := (&sysfs.AdaptFS{FS: &gofstest.MapFS{"stdin": &gofstest.MapFile{
246246
Mode: fs.ModeDevice | fs.ModeCharDevice | 0o600,
247-
}}).OpenFile("stdin", 0, 0)
247+
}}}).OpenFile("stdin", 0, 0)
248248
require.EqualErrno(t, 0, errno)
249249

250250
stdin.File = stdinFile
@@ -3648,8 +3648,8 @@ func Test_pathLink(t *testing.T) {
36483648

36493649
func Test_pathOpen(t *testing.T) {
36503650
dir := t.TempDir() // open before loop to ensure no locking problems.
3651-
writeFS := sysfs.NewDirFS(dir)
3652-
readFS := sysfs.NewReadFS(writeFS)
3651+
writeFS := sysfs.DirFS(dir)
3652+
readFS := &sysfs.ReadFS{FS: writeFS}
36533653

36543654
fileName := "file"
36553655
fileContents := []byte("012")

Diff for: internal/sys/fs_test.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestNewFSContext(t *testing.T) {
2424
embedFS, err := fs.Sub(testdata, "testdata")
2525
require.NoError(t, err)
2626

27-
dirfs := sysfs.NewDirFS(".")
27+
dirfs := sysfs.DirFS(".")
2828

2929
// Test various usual configuration for the file system.
3030
tests := []struct {
@@ -33,21 +33,21 @@ func TestNewFSContext(t *testing.T) {
3333
}{
3434
{
3535
name: "embed.FS",
36-
fs: sysfs.Adapt(embedFS),
36+
fs: &sysfs.AdaptFS{FS: embedFS},
3737
},
3838
{
39-
name: "NewDirFS",
39+
name: "DirFS",
4040
// Don't use "testdata" because it may not be present in
4141
// cross-architecture (a.k.a. scratch) build containers.
4242
fs: dirfs,
4343
},
4444
{
45-
name: "NewReadFS",
46-
fs: sysfs.NewReadFS(dirfs),
45+
name: "ReadFS",
46+
fs: &sysfs.ReadFS{FS: dirfs},
4747
},
4848
{
4949
name: "fstest.MapFS",
50-
fs: sysfs.Adapt(gofstest.MapFS{}),
50+
fs: &sysfs.AdaptFS{FS: gofstest.MapFS{}},
5151
},
5252
}
5353

@@ -96,7 +96,7 @@ func TestNewFSContext(t *testing.T) {
9696
func TestFSContext_CloseFile(t *testing.T) {
9797
embedFS, err := fs.Sub(testdata, "testdata")
9898
require.NoError(t, err)
99-
testFS := sysfs.Adapt(embedFS)
99+
testFS := &sysfs.AdaptFS{FS: embedFS}
100100

101101
c := Context{}
102102
err = c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
@@ -154,7 +154,7 @@ func TestFSContext_noPreopens(t *testing.T) {
154154
}
155155

156156
func TestContext_Close(t *testing.T) {
157-
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{}})
157+
testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": &testfs.File{}}}
158158

159159
c := Context{}
160160
err := c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
@@ -181,7 +181,7 @@ func TestContext_Close(t *testing.T) {
181181
func TestContext_Close_Error(t *testing.T) {
182182
file := &testfs.File{CloseErr: errors.New("error closing")}
183183

184-
testFS := sysfs.Adapt(testfs.FS{"foo": file})
184+
testFS := &sysfs.AdaptFS{FS: testfs.FS{"foo": file}}
185185

186186
c := Context{}
187187
err := c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
@@ -201,7 +201,7 @@ func TestContext_Close_Error(t *testing.T) {
201201

202202
func TestFSContext_Renumber(t *testing.T) {
203203
tmpDir := t.TempDir()
204-
dirFS := sysfs.NewDirFS(tmpDir)
204+
dirFS := sysfs.DirFS(tmpDir)
205205

206206
const dirName = "dir"
207207
errno := dirFS.Mkdir(dirName, 0o700)
@@ -252,7 +252,7 @@ func TestFSContext_Renumber(t *testing.T) {
252252

253253
func TestDirentCache_Read(t *testing.T) {
254254
c := Context{}
255-
err := c.InitFSContext(nil, nil, nil, []sys.FS{sysfs.Adapt(fstest.FS)}, []string{"/"}, nil)
255+
err := c.InitFSContext(nil, nil, nil, []sys.FS{&sysfs.AdaptFS{FS: fstest.FS}}, []string{"/"}, nil)
256256
require.NoError(t, err)
257257
fsc := c.fsc
258258
defer fsc.Close()
@@ -430,7 +430,7 @@ func TestDirentCache_ReadNewFile(t *testing.T) {
430430
tmpDir := t.TempDir()
431431

432432
c := Context{}
433-
err := c.InitFSContext(nil, nil, nil, []sys.FS{sysfs.NewDirFS(tmpDir)}, []string{"/"}, nil)
433+
err := c.InitFSContext(nil, nil, nil, []sys.FS{sysfs.DirFS(tmpDir)}, []string{"/"}, nil)
434434
require.NoError(t, err)
435435
fsc := c.fsc
436436
defer fsc.Close()

Diff for: internal/sys/sys_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestContext_WalltimeNanos(t *testing.T) {
2020
}
2121

2222
func TestDefaultSysContext(t *testing.T) {
23-
testFS := sysfs.Adapt(fstest.FS)
23+
testFS := &sysfs.AdaptFS{FS: fstest.FS}
2424

2525
sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil, nil, []experimentalsys.FS{testFS}, []string{"/"}, nil)
2626
require.NoError(t, err)

0 commit comments

Comments
 (0)