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)
|
fmt.Fprintf(stdErr, "invalid mount: path %q is not a directory\n", dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
root := sysfs.NewDirFS(dir)
|
root := sysfs.DirFS(dir)
|
||||||
if readOnly {
|
if readOnly {
|
||||||
root = sysfs.NewReadFS(root)
|
root = &sysfs.ReadFS{FS: root}
|
||||||
}
|
}
|
||||||
|
|
||||||
config = config.(sysfs.FSConfig).WithSysFSMount(root, guestPath)
|
config = config.(sysfs.FSConfig).WithSysFSMount(root, guestPath)
|
||||||
|
|||||||
+4
-3
@@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
|
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||||
"github.com/tetratelabs/wazero/internal/fstest"
|
"github.com/tetratelabs/wazero/internal/fstest"
|
||||||
"github.com/tetratelabs/wazero/internal/platform"
|
"github.com/tetratelabs/wazero/internal/platform"
|
||||||
internalsys "github.com/tetratelabs/wazero/internal/sys"
|
internalsys "github.com/tetratelabs/wazero/internal/sys"
|
||||||
@@ -325,7 +326,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
|
|||||||
config := base.WithFS(testFS)
|
config := base.WithFS(testFS)
|
||||||
return config, func(t *testing.T, sys *internalsys.Context) {
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
rootfs := sys.FS().RootFS()
|
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)
|
config := base.WithFS(testFS).WithFS(testFS2)
|
||||||
return config, func(t *testing.T, sys *internalsys.Context) {
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
rootfs := sys.FS().RootFS()
|
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)
|
config := base.WithFS(nil)
|
||||||
return config, func(t *testing.T, sys *internalsys.Context) {
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
rootfs := sys.FS().RootFS()
|
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.
|
// This should be embedded to have forward compatible implementations.
|
||||||
type UnimplementedFS struct{}
|
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
|
// OpenFile implements FS.OpenFile
|
||||||
func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) {
|
func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) {
|
||||||
return nil, ENOSYS
|
return nil, ENOSYS
|
||||||
@@ -80,11 +70,6 @@ func (UnimplementedFS) Utimens(path string, atim, mtim int64) Errno {
|
|||||||
return ENOSYS
|
return ENOSYS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate implements FS.Truncate
|
|
||||||
func (UnimplementedFS) Truncate(string, int64) Errno {
|
|
||||||
return ENOSYS
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedFile is a File that returns ENOSYS for all functions,
|
// UnimplementedFile is a File that returns ENOSYS for all functions,
|
||||||
// except where no-op are otherwise documented.
|
// except where no-op are otherwise documented.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -10,31 +10,31 @@ import (
|
|||||||
|
|
||||||
var moduleConfig wazero.ModuleConfig
|
var moduleConfig wazero.ModuleConfig
|
||||||
|
|
||||||
// This example shows how to configure a sysfs.NewDirFS
|
// This example shows how to adapt a fs.FS to a sys.FS
|
||||||
func ExampleNewDirFS() {
|
func ExampleAdaptFS() {
|
||||||
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() {
|
|
||||||
m := fstest.MapFS{
|
m := fstest.MapFS{
|
||||||
"a/b.txt": &fstest.MapFile{Mode: 0o666},
|
"a/b.txt": &fstest.MapFile{Mode: 0o666},
|
||||||
".": &fstest.MapFile{Mode: 0o777 | fs.ModeDir},
|
".": &fstest.MapFile{Mode: 0o777 | fs.ModeDir},
|
||||||
}
|
}
|
||||||
root := sysfs.Adapt(m)
|
root := &sysfs.AdaptFS{FS: m}
|
||||||
|
|
||||||
moduleConfig = wazero.NewModuleConfig().
|
moduleConfig = wazero.NewModuleConfig().
|
||||||
WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(root, "/"))
|
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
|
package sysfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
|
||||||
|
|
||||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||||
"github.com/tetratelabs/wazero/internal/sysfs"
|
"github.com/tetratelabs/wazero/internal/sysfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Adapt adapts the input to sys.FS unless it is already one. Use NewDirFS
|
// AdaptFS adapts the input to sys.FS. Use DirFS instead of adapting an
|
||||||
// instead of os.DirFS as it handles interop issues such as windows support.
|
// os.DirFS as it handles interop issues such as windows support.
|
||||||
//
|
//
|
||||||
// Note: This performs no flag verification on OpenFile. fs.FS cannot read
|
// Note: This performs no flag verification on OpenFile. sys.FS cannot read
|
||||||
// flags as there is no parameter to pass them through with. Moreover, fs.FS
|
// 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
|
// documentation does not require the file to be present. In summary, we can't
|
||||||
// enforce flag behavior.
|
// enforce flag behavior.
|
||||||
func Adapt(fs fs.FS) experimentalsys.FS {
|
type AdaptFS = sysfs.AdaptFS
|
||||||
return sysfs.Adapt(fs)
|
|
||||||
|
// 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
|
// 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
|
// doesn't want the guest wasm to. For example, Python libraries shouldn't be
|
||||||
// written to at runtime by the python wasm file.
|
// written to at runtime by the python wasm file.
|
||||||
func NewReadFS(fs experimentalsys.FS) experimentalsys.FS {
|
//
|
||||||
return sysfs.NewReadFS(fs)
|
// Note: This implements read-only by returning sys.EROFS or sys.EBADF,
|
||||||
}
|
// depending on the operation that require write access.
|
||||||
|
type ReadFS = sysfs.ReadFS
|
||||||
// NewDirFS is like os.DirFS except it returns sys.FS, which has more features.
|
|
||||||
func NewDirFS(dir string) experimentalsys.FS {
|
|
||||||
return sysfs.NewDirFS(dir)
|
|
||||||
}
|
|
||||||
|
|||||||
+8
-4
@@ -165,17 +165,21 @@ func (c *fsConfig) clone() *fsConfig {
|
|||||||
|
|
||||||
// WithDirMount implements FSConfig.WithDirMount
|
// WithDirMount implements FSConfig.WithDirMount
|
||||||
func (c *fsConfig) WithDirMount(dir, guestPath string) FSConfig {
|
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
|
// WithReadOnlyDirMount implements FSConfig.WithReadOnlyDirMount
|
||||||
func (c *fsConfig) WithReadOnlyDirMount(dir, guestPath string) FSConfig {
|
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
|
// WithFSMount implements FSConfig.WithFSMount
|
||||||
func (c *fsConfig) WithFSMount(fs fs.FS, guestPath string) FSConfig {
|
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
|
// 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 {
|
if i, ok := ret.guestPathToFS[cleaned]; ok {
|
||||||
ret.fs[i] = fs
|
ret.fs[i] = fs
|
||||||
ret.guestPaths[i] = guestPath
|
ret.guestPaths[i] = guestPath
|
||||||
} else {
|
} else if fs != nil {
|
||||||
ret.guestPathToFS[cleaned] = len(ret.fs)
|
ret.guestPathToFS[cleaned] = len(ret.fs)
|
||||||
ret.fs = append(ret.fs, fs)
|
ret.fs = append(ret.fs, fs)
|
||||||
ret.guestPaths = append(ret.guestPaths, guestPath)
|
ret.guestPaths = append(ret.guestPaths, guestPath)
|
||||||
|
|||||||
+4
-4
@@ -29,13 +29,13 @@ func TestFSConfig(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "WithFSMount",
|
name: "WithFSMount",
|
||||||
input: base.WithFSMount(testFS, "/"),
|
input: base.WithFSMount(testFS, "/"),
|
||||||
expectedFS: []sys.FS{sysfs.Adapt(testFS)},
|
expectedFS: []sys.FS{&sysfs.AdaptFS{FS: testFS}},
|
||||||
expectedGuestPaths: []string{"/"},
|
expectedGuestPaths: []string{"/"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithFSMount overwrites",
|
name: "WithFSMount overwrites",
|
||||||
input: base.WithFSMount(testFS, "/").WithFSMount(testFS2, "/"),
|
input: base.WithFSMount(testFS, "/").WithFSMount(testFS2, "/"),
|
||||||
expectedFS: []sys.FS{sysfs.Adapt(testFS2)},
|
expectedFS: []sys.FS{&sysfs.AdaptFS{FS: testFS2}},
|
||||||
expectedGuestPaths: []string{"/"},
|
expectedGuestPaths: []string{"/"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -45,13 +45,13 @@ func TestFSConfig(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "WithDirMount overwrites",
|
name: "WithDirMount overwrites",
|
||||||
input: base.WithFSMount(testFS, "/").WithDirMount(".", "/"),
|
input: base.WithFSMount(testFS, "/").WithDirMount(".", "/"),
|
||||||
expectedFS: []sys.FS{sysfs.NewDirFS(".")},
|
expectedFS: []sys.FS{sysfs.DirFS(".")},
|
||||||
expectedGuestPaths: []string{"/"},
|
expectedGuestPaths: []string{"/"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "multiple",
|
name: "multiple",
|
||||||
input: base.WithReadOnlyDirMount(".", "/").WithDirMount("/tmp", "/tmp"),
|
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"},
|
expectedGuestPaths: []string{"/", "/tmp"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,9 +242,9 @@ func Test_fdFdstatGet(t *testing.T) {
|
|||||||
// replace stdin with a fake TTY file.
|
// replace stdin with a fake TTY file.
|
||||||
// TODO: Make this easier once we have in-memory sys.File
|
// TODO: Make this easier once we have in-memory sys.File
|
||||||
stdin, _ := fsc.LookupFile(sys.FdStdin)
|
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,
|
Mode: fs.ModeDevice | fs.ModeCharDevice | 0o600,
|
||||||
}}).OpenFile("stdin", 0, 0)
|
}}}).OpenFile("stdin", 0, 0)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
stdin.File = stdinFile
|
stdin.File = stdinFile
|
||||||
@@ -3648,8 +3648,8 @@ func Test_pathLink(t *testing.T) {
|
|||||||
|
|
||||||
func Test_pathOpen(t *testing.T) {
|
func Test_pathOpen(t *testing.T) {
|
||||||
dir := t.TempDir() // open before loop to ensure no locking problems.
|
dir := t.TempDir() // open before loop to ensure no locking problems.
|
||||||
writeFS := sysfs.NewDirFS(dir)
|
writeFS := sysfs.DirFS(dir)
|
||||||
readFS := sysfs.NewReadFS(writeFS)
|
readFS := &sysfs.ReadFS{FS: writeFS}
|
||||||
|
|
||||||
fileName := "file"
|
fileName := "file"
|
||||||
fileContents := []byte("012")
|
fileContents := []byte("012")
|
||||||
|
|||||||
+12
-12
@@ -24,7 +24,7 @@ func TestNewFSContext(t *testing.T) {
|
|||||||
embedFS, err := fs.Sub(testdata, "testdata")
|
embedFS, err := fs.Sub(testdata, "testdata")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dirfs := sysfs.NewDirFS(".")
|
dirfs := sysfs.DirFS(".")
|
||||||
|
|
||||||
// Test various usual configuration for the file system.
|
// Test various usual configuration for the file system.
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -33,21 +33,21 @@ func TestNewFSContext(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "embed.FS",
|
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
|
// Don't use "testdata" because it may not be present in
|
||||||
// cross-architecture (a.k.a. scratch) build containers.
|
// cross-architecture (a.k.a. scratch) build containers.
|
||||||
fs: dirfs,
|
fs: dirfs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "NewReadFS",
|
name: "ReadFS",
|
||||||
fs: sysfs.NewReadFS(dirfs),
|
fs: &sysfs.ReadFS{FS: dirfs},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fstest.MapFS",
|
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) {
|
func TestFSContext_CloseFile(t *testing.T) {
|
||||||
embedFS, err := fs.Sub(testdata, "testdata")
|
embedFS, err := fs.Sub(testdata, "testdata")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testFS := sysfs.Adapt(embedFS)
|
testFS := &sysfs.AdaptFS{FS: embedFS}
|
||||||
|
|
||||||
c := Context{}
|
c := Context{}
|
||||||
err = c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
|
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) {
|
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{}
|
c := Context{}
|
||||||
err := c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
|
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) {
|
func TestContext_Close_Error(t *testing.T) {
|
||||||
file := &testfs.File{CloseErr: errors.New("error closing")}
|
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{}
|
c := Context{}
|
||||||
err := c.InitFSContext(nil, nil, nil, []sys.FS{testFS}, []string{"/"}, nil)
|
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) {
|
func TestFSContext_Renumber(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
dirFS := sysfs.NewDirFS(tmpDir)
|
dirFS := sysfs.DirFS(tmpDir)
|
||||||
|
|
||||||
const dirName = "dir"
|
const dirName = "dir"
|
||||||
errno := dirFS.Mkdir(dirName, 0o700)
|
errno := dirFS.Mkdir(dirName, 0o700)
|
||||||
@@ -252,7 +252,7 @@ func TestFSContext_Renumber(t *testing.T) {
|
|||||||
|
|
||||||
func TestDirentCache_Read(t *testing.T) {
|
func TestDirentCache_Read(t *testing.T) {
|
||||||
c := Context{}
|
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)
|
require.NoError(t, err)
|
||||||
fsc := c.fsc
|
fsc := c.fsc
|
||||||
defer fsc.Close()
|
defer fsc.Close()
|
||||||
@@ -430,7 +430,7 @@ func TestDirentCache_ReadNewFile(t *testing.T) {
|
|||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
c := Context{}
|
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)
|
require.NoError(t, err)
|
||||||
fsc := c.fsc
|
fsc := c.fsc
|
||||||
defer fsc.Close()
|
defer fsc.Close()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func TestContext_WalltimeNanos(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultSysContext(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)
|
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)
|
require.NoError(t, err)
|
||||||
|
|||||||
+63
-36
@@ -9,40 +9,35 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/sys"
|
"github.com/tetratelabs/wazero/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Adapt adapts the input to sys.FS unless it is already one. Use NewDirFS instead
|
type AdaptFS struct {
|
||||||
// of os.DirFS as it handles interop issues such as windows support.
|
FS 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 {
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements fmt.Stringer
|
// String implements fmt.Stringer
|
||||||
func (a *adapter) String() string {
|
func (a *AdaptFS) String() string {
|
||||||
return fmt.Sprintf("%v", a.fs)
|
return fmt.Sprintf("%v", a.FS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenFile implements the same method as documented on sys.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) {
|
func (a *AdaptFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
|
||||||
return OpenFSFile(a.fs, cleanPath(path), flag, perm)
|
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
|
// 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)
|
f, errno := a.OpenFile(path, experimentalsys.O_RDONLY, 0)
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return sys.Stat_t{}, errno
|
return sys.Stat_t{}, errno
|
||||||
@@ -51,17 +46,49 @@ func (a *adapter) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
|||||||
return f.Stat()
|
return f.Stat()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lstat implements the same method as documented on sys.FS
|
// Readlink implements the same method as documented on sys.FS
|
||||||
func (a *adapter) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
func (a *AdaptFS) Readlink(string) (string, experimentalsys.Errno) {
|
||||||
// At this time, we make the assumption that sys.FS instances do not support
|
return "", experimentalsys.ENOSYS
|
||||||
// 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
|
// Mkdir implements the same method as documented on sys.FS
|
||||||
// later on.
|
func (a *AdaptFS) Mkdir(string, fs.FileMode) experimentalsys.Errno {
|
||||||
//
|
return experimentalsys.ENOSYS
|
||||||
// For further discussions on the topic, see:
|
}
|
||||||
// https://github.com/golang/go/issues/49580
|
|
||||||
return a.Stat(path)
|
// 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 {
|
func cleanPath(name string) string {
|
||||||
|
|||||||
@@ -17,29 +17,23 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/sys"
|
"github.com/tetratelabs/wazero/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAdapt_nil(t *testing.T) {
|
func TestAdaptFS_MkDir(t *testing.T) {
|
||||||
testFS := Adapt(nil)
|
testFS := &AdaptFS{FS: os.DirFS(t.TempDir())}
|
||||||
_, ok := testFS.(experimentalsys.UnimplementedFS)
|
|
||||||
require.True(t, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAdapt_MkDir(t *testing.T) {
|
|
||||||
testFS := Adapt(os.DirFS(t.TempDir()))
|
|
||||||
|
|
||||||
err := testFS.Mkdir("mkdir", fs.ModeDir)
|
err := testFS.Mkdir("mkdir", fs.ModeDir)
|
||||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdapt_Chmod(t *testing.T) {
|
func TestAdaptFS_Chmod(t *testing.T) {
|
||||||
testFS := Adapt(os.DirFS(t.TempDir()))
|
testFS := &AdaptFS{FS: os.DirFS(t.TempDir())}
|
||||||
|
|
||||||
err := testFS.Chmod("chmod", fs.ModeDir)
|
err := testFS.Chmod("chmod", fs.ModeDir)
|
||||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdapt_Rename(t *testing.T) {
|
func TestAdaptFS_Rename(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := Adapt(os.DirFS(tmpDir))
|
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||||
|
|
||||||
file1 := "file1"
|
file1 := "file1"
|
||||||
file1Path := joinPath(tmpDir, file1)
|
file1Path := joinPath(tmpDir, file1)
|
||||||
@@ -57,9 +51,9 @@ func TestAdapt_Rename(t *testing.T) {
|
|||||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdapt_Rmdir(t *testing.T) {
|
func TestAdaptFS_Rmdir(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := Adapt(os.DirFS(tmpDir))
|
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||||
|
|
||||||
path := "rmdir"
|
path := "rmdir"
|
||||||
realPath := joinPath(tmpDir, path)
|
realPath := joinPath(tmpDir, path)
|
||||||
@@ -69,9 +63,9 @@ func TestAdapt_Rmdir(t *testing.T) {
|
|||||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdapt_Unlink(t *testing.T) {
|
func TestAdaptFS_Unlink(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := Adapt(os.DirFS(tmpDir))
|
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||||
|
|
||||||
path := "unlink"
|
path := "unlink"
|
||||||
realPath := joinPath(tmpDir, path)
|
realPath := joinPath(tmpDir, path)
|
||||||
@@ -81,9 +75,9 @@ func TestAdapt_Unlink(t *testing.T) {
|
|||||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdapt_UtimesNano(t *testing.T) {
|
func TestAdaptFS_UtimesNano(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := Adapt(os.DirFS(tmpDir))
|
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||||
|
|
||||||
path := "utimes"
|
path := "utimes"
|
||||||
realPath := joinPath(tmpDir, path)
|
realPath := joinPath(tmpDir, path)
|
||||||
@@ -93,13 +87,13 @@ func TestAdapt_UtimesNano(t *testing.T) {
|
|||||||
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
require.EqualErrno(t, experimentalsys.ENOSYS, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdapt_Open_Read(t *testing.T) {
|
func TestAdaptFS_Open_Read(t *testing.T) {
|
||||||
// Create a subdirectory, so we can test reads outside the sys.FS root.
|
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
|
// Create a subdirectory, so we can test reads outside the sys.FS root.
|
||||||
tmpDir = joinPath(tmpDir, t.Name())
|
tmpDir = joinPath(tmpDir, t.Name())
|
||||||
require.NoError(t, os.Mkdir(tmpDir, 0o700))
|
require.NoError(t, os.Mkdir(tmpDir, 0o700))
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
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
|
// We can't correct operating system portability issues with os.DirFS on
|
||||||
// windows. Use syscall.DirFS instead!
|
// 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()
|
tmpDir := t.TempDir()
|
||||||
|
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
testFS := Adapt(os.DirFS(tmpDir))
|
|
||||||
|
|
||||||
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
|
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
|
||||||
fullPath := joinPath(tmpDir, path)
|
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()
|
tmpDir := t.TempDir()
|
||||||
|
testFS := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
|
|
||||||
testFS := Adapt(os.DirFS(tmpDir))
|
|
||||||
testStat(t, testFS)
|
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
|
// 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.
|
// 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.
|
// api.FS contract.
|
||||||
func TestAdapt_HackedWrites(t *testing.T) {
|
func TestAdaptFS_HackedWrites(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := Adapt(hackFS(tmpDir))
|
testFS := &AdaptFS{FS: hackFS(tmpDir)}
|
||||||
|
|
||||||
testOpen_O_RDWR(t, tmpDir, testFS)
|
testOpen_O_RDWR(t, tmpDir, testFS)
|
||||||
}
|
}
|
||||||
@@ -237,7 +231,7 @@ func (i *sysFileInfo) Sys() any {
|
|||||||
// to zero).
|
// to zero).
|
||||||
//
|
//
|
||||||
// A fs.File implementing this should be functionally equivalent to an os.File,
|
// 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.
|
// Windows.
|
||||||
//
|
//
|
||||||
// For example, on Windows, we cannot reliably read the inode for a
|
// For example, on Windows, we cannot reliably read the inode for a
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/sys"
|
"github.com/tetratelabs/wazero/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDirFS(dir string) experimentalsys.FS {
|
func DirFS(dir string) experimentalsys.FS {
|
||||||
return &dirFS{
|
return &dirFS{
|
||||||
dir: dir,
|
dir: dir,
|
||||||
cleanedDir: ensureTrailingPathSeparator(dir),
|
cleanedDir: ensureTrailingPathSeparator(dir),
|
||||||
@@ -23,8 +23,11 @@ func ensureTrailingPathSeparator(dir string) string {
|
|||||||
return dir
|
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 {
|
type dirFS struct {
|
||||||
experimentalsys.UnimplementedFS
|
experimentalsys.UnimplementedFS
|
||||||
|
|
||||||
dir string
|
dir string
|
||||||
// cleanedDir is for easier OS-specific concatenation, as it always has
|
// cleanedDir is for easier OS-specific concatenation, as it always has
|
||||||
// a trailing path separator.
|
// a trailing path separator.
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewDirFS(t *testing.T) {
|
func TestDirFS(t *testing.T) {
|
||||||
testFS := NewDirFS(".")
|
testFS := DirFS(".")
|
||||||
|
|
||||||
// Guest can look up /
|
// Guest can look up /
|
||||||
f, errno := testFS.OpenFile("/", sys.O_RDONLY, 0)
|
f, errno := testFS.OpenFile("/", sys.O_RDONLY, 0)
|
||||||
@@ -25,14 +25,14 @@ func TestNewDirFS(t *testing.T) {
|
|||||||
require.EqualErrno(t, 0, f.Close())
|
require.EqualErrno(t, 0, f.Close())
|
||||||
|
|
||||||
t.Run("host path not found", func(t *testing.T) {
|
t.Run("host path not found", func(t *testing.T) {
|
||||||
testFS := NewDirFS("a")
|
testFS := DirFS("a")
|
||||||
_, errno = testFS.OpenFile(".", sys.O_RDONLY, 0)
|
_, errno = testFS.OpenFile(".", sys.O_RDONLY, 0)
|
||||||
require.EqualErrno(t, sys.ENOENT, errno)
|
require.EqualErrno(t, sys.ENOENT, errno)
|
||||||
})
|
})
|
||||||
t.Run("host path not a directory", func(t *testing.T) {
|
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.
|
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)
|
d, errno := testFS.OpenFile(".", sys.O_RDONLY, 0)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
_, errno = d.Readdir(-1)
|
_, errno = d.Readdir(-1)
|
||||||
@@ -41,13 +41,13 @@ func TestNewDirFS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDirFS_join(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, "/", testFS.join("."))
|
||||||
require.Equal(t, "/", testFS.join("/"))
|
require.Equal(t, "/", testFS.join("/"))
|
||||||
require.Equal(t, "/tmp", testFS.join("tmp"))
|
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("."))
|
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) {
|
func TestDirFS_String(t *testing.T) {
|
||||||
testFS := NewDirFS(".")
|
testFS := DirFS(".")
|
||||||
|
|
||||||
// String has the name of the path entered
|
// String has the name of the path entered
|
||||||
require.Equal(t, ".", testFS.(fmt.Stringer).String())
|
require.Equal(t, ".", testFS.(fmt.Stringer).String())
|
||||||
@@ -65,7 +65,7 @@ func TestDirFS_Lstat(t *testing.T) {
|
|||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
|
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
|
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
|
||||||
require.EqualErrno(t, 0, testFS.Symlink(path, path+"-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) {
|
func TestDirFS_MkDir(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "mkdir"
|
name := "mkdir"
|
||||||
realPath := path.Join(tmpDir, name)
|
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) {
|
func TestDirFS_Rename(t *testing.T) {
|
||||||
t.Run("from doesn't exist", func(t *testing.T) {
|
t.Run("from doesn't exist", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
file1 := "file1"
|
file1 := "file1"
|
||||||
file1Path := path.Join(tmpDir, 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) {
|
t.Run("file to non-exist", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
file1 := "file1"
|
file1 := "file1"
|
||||||
file1Path := path.Join(tmpDir, 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) {
|
t.Run("dir to non-exist", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
dir1 := "dir1"
|
dir1 := "dir1"
|
||||||
dir1Path := path.Join(tmpDir, 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) {
|
t.Run("dir to file", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
dir1 := "dir1"
|
dir1 := "dir1"
|
||||||
dir1Path := path.Join(tmpDir, 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) {
|
t.Run("file to dir", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
file1 := "file1"
|
file1 := "file1"
|
||||||
file1Path := path.Join(tmpDir, 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
|
// 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) {
|
t.Run("dir to empty dir should be fine", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
dir1 := "dir1"
|
dir1 := "dir1"
|
||||||
dir1Path := path.Join(tmpDir, 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
|
// 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) {
|
t.Run("dir to non empty dir should be EXIST", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
dir1 := "dir1"
|
dir1 := "dir1"
|
||||||
dir1Path := path.Join(tmpDir, 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) {
|
t.Run("file to file", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
file1 := "file1"
|
file1 := "file1"
|
||||||
file1Path := path.Join(tmpDir, 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) {
|
t.Run("dir to itself", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
dir1 := "dir1"
|
dir1 := "dir1"
|
||||||
dir1Path := path.Join(tmpDir, 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) {
|
t.Run("file to itself", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
file1 := "file1"
|
file1 := "file1"
|
||||||
file1Path := path.Join(tmpDir, file1)
|
file1Path := path.Join(tmpDir, file1)
|
||||||
@@ -369,7 +369,7 @@ func TestDirFS_Rename(t *testing.T) {
|
|||||||
func TestDirFS_Rmdir(t *testing.T) {
|
func TestDirFS_Rmdir(t *testing.T) {
|
||||||
t.Run("doesn't exist", func(t *testing.T) {
|
t.Run("doesn't exist", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "rmdir"
|
name := "rmdir"
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ func TestDirFS_Rmdir(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("dir not empty", func(t *testing.T) {
|
t.Run("dir not empty", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "rmdir"
|
name := "rmdir"
|
||||||
realPath := path.Join(tmpDir, name)
|
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) {
|
t.Run("dir previously not empty", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "rmdir"
|
name := "rmdir"
|
||||||
realPath := path.Join(tmpDir, name)
|
realPath := path.Join(tmpDir, name)
|
||||||
@@ -414,7 +414,7 @@ func TestDirFS_Rmdir(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("dir empty", func(t *testing.T) {
|
t.Run("dir empty", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "rmdir"
|
name := "rmdir"
|
||||||
realPath := path.Join(tmpDir, name)
|
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) {
|
t.Run("dir empty while opening", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "rmdir"
|
name := "rmdir"
|
||||||
realPath := path.Join(tmpDir, name)
|
realPath := path.Join(tmpDir, name)
|
||||||
@@ -443,7 +443,7 @@ func TestDirFS_Rmdir(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("not directory", func(t *testing.T) {
|
t.Run("not directory", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "rmdir"
|
name := "rmdir"
|
||||||
realPath := path.Join(tmpDir, name)
|
realPath := path.Join(tmpDir, name)
|
||||||
@@ -460,7 +460,7 @@ func TestDirFS_Rmdir(t *testing.T) {
|
|||||||
func TestDirFS_Unlink(t *testing.T) {
|
func TestDirFS_Unlink(t *testing.T) {
|
||||||
t.Run("doesn't exist", func(t *testing.T) {
|
t.Run("doesn't exist", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
name := "unlink"
|
name := "unlink"
|
||||||
|
|
||||||
err := testFS.Unlink(name)
|
err := testFS.Unlink(name)
|
||||||
@@ -469,7 +469,7 @@ func TestDirFS_Unlink(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("target: dir", func(t *testing.T) {
|
t.Run("target: dir", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
dir := "dir"
|
dir := "dir"
|
||||||
realPath := path.Join(tmpDir, 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) {
|
t.Run("target: symlink to dir", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
// Create link target dir.
|
// Create link target dir.
|
||||||
subDirName := "subdir"
|
subDirName := "subdir"
|
||||||
@@ -502,7 +502,7 @@ func TestDirFS_Unlink(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("file exists", func(t *testing.T) {
|
t.Run("file exists", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "unlink"
|
name := "unlink"
|
||||||
realPath := path.Join(tmpDir, name)
|
realPath := path.Join(tmpDir, name)
|
||||||
@@ -518,7 +518,7 @@ func TestDirFS_Unlink(t *testing.T) {
|
|||||||
|
|
||||||
func TestDirFS_Utimesns(t *testing.T) {
|
func TestDirFS_Utimesns(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
file := "file"
|
file := "file"
|
||||||
err := os.WriteFile(path.Join(tmpDir, file), []byte{}, 0o700)
|
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) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
file := path.Join(tmpDir, "file")
|
file := path.Join(tmpDir, "file")
|
||||||
errno := os.WriteFile(file, []byte{}, 0o700)
|
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, os.Mkdir(tmpDir, 0o700))
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
|
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
testOpen_Read(t, testFS, statSetsIno(), true)
|
testOpen_Read(t, testFS, statSetsIno(), true)
|
||||||
|
|
||||||
@@ -656,7 +656,7 @@ func TestDirFS_Stat(t *testing.T) {
|
|||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
|
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
testStat(t, testFS)
|
testStat(t, testFS)
|
||||||
|
|
||||||
// from os.TestDirFSPathsValid
|
// from os.TestDirFSPathsValid
|
||||||
@@ -673,7 +673,7 @@ func TestDirFS_Stat(t *testing.T) {
|
|||||||
|
|
||||||
func TestDirFS_Readdir(t *testing.T) {
|
func TestDirFS_Readdir(t *testing.T) {
|
||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
testFS := NewDirFS(root)
|
testFS := DirFS(root)
|
||||||
|
|
||||||
const readDirTarget = "dir"
|
const readDirTarget = "dir"
|
||||||
errno := testFS.Mkdir(readDirTarget, 0o700)
|
errno := testFS.Mkdir(readDirTarget, 0o700)
|
||||||
@@ -728,7 +728,7 @@ func TestDirFS_Link(t *testing.T) {
|
|||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
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("cat", ""), sys.ENOENT)
|
||||||
require.EqualErrno(t, testFS.Link("sub/test.txt", "sub/test.txt"), sys.EEXIST)
|
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()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
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"))
|
require.EqualErrno(t, sys.EEXIST, testFS.Symlink("sub/test.txt", "sub/test.txt"))
|
||||||
// Non-existing old name is allowed.
|
// Non-existing old name is allowed.
|
||||||
@@ -767,6 +767,6 @@ func TestDirFS_Readlink(t *testing.T) {
|
|||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
|
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
testReadlink(t, testFS, testFS)
|
testReadlink(t, testFS, testFS)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func OpenFSFile(fs fs.FS, path string, flag experimentalsys.Oflag, perm fs.FileM
|
|||||||
return nil, errno
|
return nil, errno
|
||||||
}
|
}
|
||||||
// Don't return an os.File because the path is not absolute. osFile needs
|
// 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
|
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
|
// Notably, this uses readdirFile or fs.ReadDirFile if available. This does not
|
||||||
// return inodes on windows.
|
// return inodes on windows.
|
||||||
func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
|
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
|
// close in a meaningful way. read our closed field to return consistent
|
||||||
// results.
|
// results.
|
||||||
if f.closed {
|
if f.closed {
|
||||||
@@ -286,8 +286,8 @@ func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experim
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try with fs.ReadDirFile which is available on api.FS implementations
|
// Try with FS.ReadDirFile which is available on api.FS implementations
|
||||||
// like embed:fs.
|
// like embed:FS.
|
||||||
if rdf, ok := f.file.(fs.ReadDirFile); ok {
|
if rdf, ok := f.file.(fs.ReadDirFile); ok {
|
||||||
entries, e := rdf.ReadDir(n)
|
entries, e := rdf.ReadDir(n)
|
||||||
if errno = adjustReaddirErr(f, f.closed, e); errno != 0 {
|
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
|
f experimentalsys.File
|
||||||
}{
|
}{
|
||||||
{name: "UnimplementedFile", f: experimentalsys.UnimplementedFile{}},
|
{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},
|
{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
|
return v.Ino, 0
|
||||||
}
|
}
|
||||||
if dirPath == "" {
|
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.
|
// the original file path.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-43
@@ -6,51 +6,21 @@ import (
|
|||||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewReadFS is used to mask an existing sys.FS for reads. Notably, this allows
|
type ReadFS struct {
|
||||||
// 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 {
|
|
||||||
experimentalsys.FS
|
experimentalsys.FS
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenFile implements the same method as documented on sys.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) {
|
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
|
// Mask the mutually exclusive bits as they determine write mode.
|
||||||
// /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.
|
|
||||||
switch flag & (experimentalsys.O_RDONLY | experimentalsys.O_WRONLY | experimentalsys.O_RDWR) {
|
switch flag & (experimentalsys.O_RDONLY | experimentalsys.O_WRONLY | experimentalsys.O_RDWR) {
|
||||||
case 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 {
|
if flag&experimentalsys.O_DIRECTORY != 0 {
|
||||||
return nil, experimentalsys.EISDIR
|
return nil, experimentalsys.EISDIR
|
||||||
}
|
}
|
||||||
return nil, experimentalsys.ENOSYS
|
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)
|
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
|
// 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
|
return experimentalsys.EROFS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chmod implements the same method as documented on sys.FS
|
// 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
|
return experimentalsys.EROFS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename implements the same method as documented on sys.FS
|
// 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
|
return experimentalsys.EROFS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rmdir implements the same method as documented on sys.FS
|
// 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
|
return experimentalsys.EROFS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link implements the same method as documented on sys.FS
|
// 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
|
return experimentalsys.EROFS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Symlink implements the same method as documented on sys.FS
|
// 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
|
return experimentalsys.EROFS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlink implements the same method as documented on sys.FS
|
// 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
|
return experimentalsys.EROFS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utimens implements the same method as documented on sys.FS
|
// 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
|
return experimentalsys.EROFS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,16 +14,13 @@ import (
|
|||||||
func TestNewReadFS(t *testing.T) {
|
func TestNewReadFS(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
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
|
// Wraps a sys.FS because it allows access to Write
|
||||||
adapted := Adapt(os.DirFS(tmpDir))
|
adapted := &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||||
require.NotEqual(t, adapted, NewReadFS(adapted))
|
require.NotEqual(t, adapted, &ReadFS{FS: adapted})
|
||||||
|
|
||||||
// Wraps a writeable file system
|
// Wraps a writeable file system
|
||||||
writeable := NewDirFS(tmpDir)
|
writeable := DirFS(tmpDir)
|
||||||
readFS := NewReadFS(writeable)
|
readFS := &ReadFS{FS: writeable}
|
||||||
require.NotEqual(t, writeable, readFS)
|
require.NotEqual(t, writeable, readFS)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,27 +28,27 @@ func TestReadFS_Lstat(t *testing.T) {
|
|||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
|
|
||||||
writeable := NewDirFS(tmpDir)
|
writeable := DirFS(tmpDir)
|
||||||
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
|
for _, path := range []string{"animals.txt", "sub", "sub-link"} {
|
||||||
require.EqualErrno(t, 0, writeable.Symlink(path, path+"-link"))
|
require.EqualErrno(t, 0, writeable.Symlink(path, path+"-link"))
|
||||||
}
|
}
|
||||||
|
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
|
|
||||||
testLstat(t, testFS)
|
testLstat(t, testFS)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadFS_MkDir(t *testing.T) {
|
func TestReadFS_MkDir(t *testing.T) {
|
||||||
writeable := NewDirFS(t.TempDir())
|
writeable := DirFS(t.TempDir())
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
|
|
||||||
err := testFS.Mkdir("mkdir", fs.ModeDir)
|
err := testFS.Mkdir("mkdir", fs.ModeDir)
|
||||||
require.EqualErrno(t, sys.EROFS, err)
|
require.EqualErrno(t, sys.EROFS, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadFS_Chmod(t *testing.T) {
|
func TestReadFS_Chmod(t *testing.T) {
|
||||||
writeable := NewDirFS(t.TempDir())
|
writeable := DirFS(t.TempDir())
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
|
|
||||||
err := testFS.Chmod("chmod", fs.ModeDir)
|
err := testFS.Chmod("chmod", fs.ModeDir)
|
||||||
require.EqualErrno(t, sys.EROFS, err)
|
require.EqualErrno(t, sys.EROFS, err)
|
||||||
@@ -59,8 +56,8 @@ func TestReadFS_Chmod(t *testing.T) {
|
|||||||
|
|
||||||
func TestReadFS_Rename(t *testing.T) {
|
func TestReadFS_Rename(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
writeable := NewDirFS(tmpDir)
|
writeable := DirFS(tmpDir)
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
|
|
||||||
file1 := "file1"
|
file1 := "file1"
|
||||||
file1Path := joinPath(tmpDir, file1)
|
file1Path := joinPath(tmpDir, file1)
|
||||||
@@ -80,8 +77,8 @@ func TestReadFS_Rename(t *testing.T) {
|
|||||||
|
|
||||||
func TestReadFS_Rmdir(t *testing.T) {
|
func TestReadFS_Rmdir(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
writeable := NewDirFS(tmpDir)
|
writeable := DirFS(tmpDir)
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
|
|
||||||
path := "rmdir"
|
path := "rmdir"
|
||||||
realPath := joinPath(tmpDir, path)
|
realPath := joinPath(tmpDir, path)
|
||||||
@@ -93,8 +90,8 @@ func TestReadFS_Rmdir(t *testing.T) {
|
|||||||
|
|
||||||
func TestReadFS_Unlink(t *testing.T) {
|
func TestReadFS_Unlink(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
writeable := NewDirFS(tmpDir)
|
writeable := DirFS(tmpDir)
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
|
|
||||||
path := "unlink"
|
path := "unlink"
|
||||||
realPath := joinPath(tmpDir, path)
|
realPath := joinPath(tmpDir, path)
|
||||||
@@ -106,8 +103,8 @@ func TestReadFS_Unlink(t *testing.T) {
|
|||||||
|
|
||||||
func TestReadFS_UtimesNano(t *testing.T) {
|
func TestReadFS_UtimesNano(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
writeable := NewDirFS(tmpDir)
|
writeable := DirFS(tmpDir)
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
|
|
||||||
path := "utimes"
|
path := "utimes"
|
||||||
realPath := joinPath(tmpDir, path)
|
realPath := joinPath(tmpDir, path)
|
||||||
@@ -129,7 +126,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "DirFS",
|
name: "DirFS",
|
||||||
fs: func(tmpDir string) sys.FS {
|
fs: func(tmpDir string) sys.FS {
|
||||||
return NewDirFS(tmpDir)
|
return DirFS(tmpDir)
|
||||||
},
|
},
|
||||||
expectFileIno: true,
|
expectFileIno: true,
|
||||||
expectDirIno: true,
|
expectDirIno: true,
|
||||||
@@ -137,7 +134,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "fstest.MapFS",
|
name: "fstest.MapFS",
|
||||||
fs: func(tmpDir string) sys.FS {
|
fs: func(tmpDir string) sys.FS {
|
||||||
return Adapt(fstest.FS)
|
return &AdaptFS{FS: fstest.FS}
|
||||||
},
|
},
|
||||||
expectFileIno: false,
|
expectFileIno: false,
|
||||||
expectDirIno: false,
|
expectDirIno: false,
|
||||||
@@ -145,7 +142,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "os.DirFS",
|
name: "os.DirFS",
|
||||||
fs: func(tmpDir string) sys.FS {
|
fs: func(tmpDir string) sys.FS {
|
||||||
return Adapt(os.DirFS(tmpDir))
|
return &AdaptFS{FS: os.DirFS(tmpDir)}
|
||||||
},
|
},
|
||||||
expectFileIno: statSetsIno(),
|
expectFileIno: statSetsIno(),
|
||||||
expectDirIno: runtime.GOOS != "windows",
|
expectDirIno: runtime.GOOS != "windows",
|
||||||
@@ -153,7 +150,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "mask(os.DirFS)",
|
name: "mask(os.DirFS)",
|
||||||
fs: func(tmpDir string) sys.FS {
|
fs: func(tmpDir string) sys.FS {
|
||||||
return Adapt(&MaskOsFS{Fs: os.DirFS(tmpDir)})
|
return &AdaptFS{FS: &MaskOsFS{Fs: os.DirFS(tmpDir)}}
|
||||||
},
|
},
|
||||||
expectFileIno: statSetsIno(),
|
expectFileIno: statSetsIno(),
|
||||||
expectDirIno: runtime.GOOS != "windows",
|
expectDirIno: runtime.GOOS != "windows",
|
||||||
@@ -161,7 +158,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "mask(os.DirFS) ZeroIno",
|
name: "mask(os.DirFS) ZeroIno",
|
||||||
fs: func(tmpDir string) sys.FS {
|
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,
|
expectFileIno: false,
|
||||||
expectDirIno: false,
|
expectDirIno: false,
|
||||||
@@ -178,7 +175,7 @@ func TestReadFS_Open_Read(t *testing.T) {
|
|||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
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()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
|
|
||||||
writeable := NewDirFS(tmpDir)
|
writeable := DirFS(tmpDir)
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
testStat(t, testFS)
|
testStat(t, testFS)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +193,7 @@ func TestReadFS_Readlink(t *testing.T) {
|
|||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
||||||
|
|
||||||
writeable := NewDirFS(tmpDir)
|
writeable := DirFS(tmpDir)
|
||||||
testFS := NewReadFS(writeable)
|
testFS := &ReadFS{FS: writeable}
|
||||||
testReadlink(t, testFS, 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) {
|
t.Run("O_TRUNC", func(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
testFS := NewDirFS(tmpDir)
|
testFS := DirFS(tmpDir)
|
||||||
|
|
||||||
name := "truncate"
|
name := "truncate"
|
||||||
realPath := path.Join(tmpDir, name)
|
realPath := path.Join(tmpDir, name)
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func TestModuleInstance_Close(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("calls Context.Close()", func(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)
|
sysCtx := internalsys.DefaultContext(testFS)
|
||||||
fsCtx := sysCtx.FS()
|
fsCtx := sysCtx.FS()
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ func TestModuleInstance_Close(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("error closing", func(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.
|
// 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)
|
sysCtx := internalsys.DefaultContext(testFS)
|
||||||
fsCtx := sysCtx.FS()
|
fsCtx := sysCtx.FS()
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ func TestModuleInstance_CallDynamic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("calls Context.Close()", func(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)
|
sysCtx := internalsys.DefaultContext(testFS)
|
||||||
fsCtx := sysCtx.FS()
|
fsCtx := sysCtx.FS()
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ func TestModuleInstance_CallDynamic(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("error closing", func(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.
|
// 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)
|
sysCtx := internalsys.DefaultContext(testFS)
|
||||||
fsCtx := sysCtx.FS()
|
fsCtx := sysCtx.FS()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user