fs: allows non-root only mounts (#1047)

This adds syntax needed for wasi-testsuite, which mounts a path like
below without mounting anything for "/".

```
-mount=./tests/c/testsuite/fs-tests.dir:/fs-tests.dir
```

Basically, this creates a virtual root path with fake stat.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-01-19 01:32:24 -06:00
committed by GitHub
parent 319d6cca62
commit f484f22e47
3 changed files with 63 additions and 8 deletions
+7
View File
@@ -233,6 +233,13 @@ func TestRun(t *testing.T) {
wazeroOpts: []string{fmt.Sprintf("--mount=%s:/:ro", bearDir)},
stdOut: "pooh\n",
},
{
name: "wasi non root",
wasm: wasmCat,
wazeroOpts: []string{fmt.Sprintf("--mount=%s:/animals:ro", bearDir)},
wasmArgs: []string{"/animals/bear.txt"},
stdOut: "pooh\n",
},
{
name: "wasi logging",
wasm: wasmWasiFd,
+36 -5
View File
@@ -53,10 +53,9 @@ func NewRootFS(fs ...FS) (FS, error) {
// Ensure there is always a root match to keep runtime logic simpler.
if ret.rootIndex == -1 {
// TODO: Make a fake root filesystem that can do a directory listing of
// any existing prefixes. We can't use UnimplementedFS as the pre-open
// for root must work.
return nil, fmt.Errorf("you must supply a root filesystem: %s", fsString(fs))
ret.rootIndex = len(fs)
ret.prefixes = append(ret.prefixes, "")
ret.fs = append(ret.fs, fakeRootFS{})
}
return ret, nil
}
@@ -90,7 +89,7 @@ func fsString(fs []FS) string {
func (c *CompositeFS) Unwrap() []FS {
result := make([]FS, 0, len(c.fs))
for i := len(c.fs) - 1; i >= 0; i-- {
if fs := c.fs[i]; fs != (UnimplementedFS{}) {
if fs := c.fs[i]; fs != (fakeRootFS{}) {
result = append(result, fs)
}
}
@@ -376,3 +375,35 @@ loop:
}
return
}
type fakeRootFS struct{ UnimplementedFS }
// OpenFile implements FS.OpenFile
func (fakeRootFS) OpenFile(path string, flag int, perm fs.FileMode) (fs.File, error) {
switch path {
case ".", "/", "":
return fakeRootDir{}, nil
}
return nil, syscall.ENOENT
}
type fakeRootDir struct{}
func (fakeRootDir) Close() (err error) { return }
func (fakeRootDir) Stat() (fs.FileInfo, error) { return fakeRootDirInfo{}, nil }
func (fakeRootDir) Read([]byte) (int, error) {
return 0, &fs.PathError{Op: "read", Path: "/", Err: syscall.EISDIR}
}
type fakeRootDirInfo struct{}
func (fakeRootDirInfo) Name() string { return "/" }
func (fakeRootDirInfo) Size() int64 { return 0 }
func (fakeRootDirInfo) Mode() fs.FileMode { return fs.ModeDir | 0o500 }
func (fakeRootDirInfo) ModTime() time.Time { return time.Unix(0, 0) }
func (fakeRootDirInfo) IsDir() bool { return true }
func (fakeRootDirInfo) Sys() interface{} { return nil }
func (fakeRootDir) ReadDir(int) (dirents []fs.DirEntry, err error) { return }
+20 -3
View File
@@ -34,12 +34,29 @@ func TestNewRootFS(t *testing.T) {
// Should not be a composite filesystem
require.Equal(t, testFS, rootFS)
})
t.Run("only non root unsupported", func(t *testing.T) {
t.Run("only non root", func(t *testing.T) {
testFS, err := NewDirFS(".", "/tmp")
require.NoError(t, err)
_, err = NewRootFS(testFS)
require.EqualError(t, err, "you must supply a root filesystem: .:/tmp")
rootFS, err := NewRootFS(testFS)
require.NoError(t, err)
// String doesn't include the fake name
require.Equal(t, ".:/tmp", rootFS.String())
// Guest can look up /tmp
f, err := rootFS.OpenFile("/tmp", os.O_RDONLY, 0)
require.NoError(t, err)
require.NoError(t, f.Close())
// Guest can look up / and see "/tmp" in it
f, err = rootFS.OpenFile("/", os.O_RDONLY, 0)
require.NoError(t, err)
dirents, err := f.(fs.ReadDirFile).ReadDir(-1)
require.NoError(t, err)
require.Equal(t, 1, len(dirents))
require.Equal(t, "tmp", dirents[0].Name())
require.True(t, dirents[0].IsDir())
})
t.Run("multiple roots unsupported", func(t *testing.T) {
testFS, err := NewDirFS(".", "/")