fs: adds FSConfig to replace experimental writefs (#1061)

This adds a new top-level type FSConfig, which is configured via
`ModuleConfig.WithFSConfig(fcfg)`. This implements read-only and
read-write directory mounts, something not formally supported before. It
also implements `WithFS` which adapts a normal `fs.FS`. For convenience,
we retain the old `ModuleConfig.WithFS` signature so as to not affect
existing users much. A new configuration for our emerging raw
filesystem, `FSConfig.WithSysfs()` will happen later without breaking
this API.

Here's an example:
```
moduleConfig = wazero.NewModuleConfig().
	// Make the current directory read-only accessible to the guest.
	WithReadOnlyDirMount(".", "/")
	// Make "/tmp/wasm" accessible to the guest as "/tmp".
	WithDirMount("/tmp/wasm", "/tmp")
```

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-01-25 10:09:40 -10:00
committed by GitHub
parent affcf6ca80
commit cc68f8ee12
25 changed files with 458 additions and 231 deletions

View File

@@ -15,6 +15,7 @@ import (
"github.com/tetratelabs/wazero/internal/filecache"
"github.com/tetratelabs/wazero/internal/platform"
internalsys "github.com/tetratelabs/wazero/internal/sys"
"github.com/tetratelabs/wazero/internal/sysfs"
"github.com/tetratelabs/wazero/internal/wasm"
"github.com/tetratelabs/wazero/sys"
)
@@ -413,38 +414,18 @@ type ModuleConfig interface {
// See https://linux.die.net/man/3/environ and https://en.wikipedia.org/wiki/Null-terminated_string
WithEnv(key, value string) ModuleConfig
// WithFS assigns the file system to use for any paths beginning at "/".
// Defaults return fs.ErrNotExist.
//
// This example sets a read-only, embedded file-system:
//
// //go:embed testdata/index.html
// var testdataIndex embed.FS
//
// rooted, err := fs.Sub(testdataIndex, "testdata")
// require.NoError(t, err)
//
// // "index.html" is accessible as "/index.html".
// config := wazero.NewModuleConfig().WithFS(rooted)
//
// This example sets a mutable file-system:
//
// // Files relative to "/work/appA" are accessible as "/".
// config := wazero.NewModuleConfig().WithFS(os.DirFS("/work/appA"))
//
// Isolation
//
// os.DirFS documentation includes important notes about isolation, which
// also applies to fs.Sub. As of Go 1.19, the built-in file-systems are not
// jailed (chroot). See https://github.com/golang/go/issues/42322
//
// Working Directory "."
//
// Relative path resolution, such as "./config.yml" to "/config.yml" or
// otherwise, is compiler-specific. See /RATIONALE.md for notes.
// WithFS is a convenience that calls WithFSConfig with an FSConfig of the
// input for the root ("/") guest path.
WithFS(fs.FS) ModuleConfig
// WithName configures the module name. Defaults to what was decoded from the name section.
// WithFSConfig configures the filesystem available to each guest
// instantiated with this configuration. By default, no file access is
// allowed, so functions like `path_open` result in unsupported errors
// (e.g. syscall.ENOSYS).
WithFSConfig(FSConfig) ModuleConfig
// WithName configures the module name. Defaults to what was decoded from
// the name section.
WithName(string) ModuleConfig
// WithStartFunctions configures the functions to call after the module is
@@ -593,8 +574,8 @@ type moduleConfig struct {
environ [][]byte
// environKeys allow overwriting of existing values.
environKeys map[string]int
// fs is the file system to open files with
fs fs.FS
// fsConfig is the file system configuration for ABI like WASI.
fsConfig FSConfig
}
// NewModuleConfig returns a ModuleConfig that can be used for configuring module instantiation.
@@ -648,8 +629,13 @@ func (c *moduleConfig) WithEnv(key, value string) ModuleConfig {
// WithFS implements ModuleConfig.WithFS
func (c *moduleConfig) WithFS(fs fs.FS) ModuleConfig {
return c.WithFSConfig(NewFSConfig().WithFSMount(fs, ""))
}
// WithFSConfig implements ModuleConfig.WithFSConfig
func (c *moduleConfig) WithFSConfig(config FSConfig) ModuleConfig {
ret := c.clone()
ret.fs = fs
ret.fsConfig = config
return ret
}
@@ -764,6 +750,13 @@ func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) {
environ = append(environ, result)
}
var fs sysfs.FS
if f, ok := c.fsConfig.(*fsConfig); ok {
if fs, err = f.toFS(); err != nil {
return
}
}
return internalsys.NewContext(
math.MaxUint32,
c.args,
@@ -775,6 +768,6 @@ func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) {
c.walltime, c.walltimeResolution,
c.nanotime, c.nanotimeResolution,
c.nanosleep,
c.fs,
fs,
)
}