Files
wazero/internal/syscallfs/dirfs.go
Crypt Keeper bedde6dc7a Clarifies at semantics and preopen semantics in WASI (#1009)
This adds FS.Path which holds the pre-open path currently only used in
WASI. It also fixes a TODO where we didn't know for sure if the FD
parameter for `path_` functions must always be a pre-open. The TL;DR; is
that usually it is, but it may not be (e.g. in our zig-cc example we can
see any directory FD, not just pre-opens).

Finally, this fixes a bug in our path resolution where we mistook paths
like "foo/foo" for "foo" because we only considered basenames instead of
the full path from the pre-open root.

This also makes pre-open directory lookup lazy because I noticed in
Trivy specifically, this is unnecessary for us to do eagerly, as they
change the FS at runtime per-call. In other words, any value from init
time is invalid later.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2023-01-05 18:59:55 +08:00

81 lines
2.0 KiB
Go

package syscallfs
import (
"fmt"
"io/fs"
"os"
"path"
"syscall"
)
func NewDirFS(dir string) (FS, error) {
if stat, err := os.Stat(dir); err != nil {
return nil, syscall.ENOENT
} else if !stat.IsDir() {
return nil, syscall.ENOTDIR
}
return dirFS(dir), nil
}
// dirFS currently validates each path, which means that input paths cannot
// escape the directory, except via symlink. We may want to relax this in the
// future, especially as we decoupled from fs.FS which has this requirement.
type dirFS string
// Open implements the same method as documented on fs.FS
func (dir dirFS) Open(name string) (fs.File, error) {
panic(fmt.Errorf("unexpected to call fs.FS.Open(%s)", name))
}
// Path implements FS.Path
func (dir dirFS) Path() string {
return "/"
}
// OpenFile implements FS.OpenFile
func (dir dirFS) OpenFile(name string, flag int, perm fs.FileMode) (fs.File, error) {
f, err := os.OpenFile(path.Join(string(dir), name), flag, perm)
if err != nil {
return nil, err
}
if flag == 0 || flag == os.O_RDONLY {
return maskForReads(f), nil
}
return f, nil
}
// Mkdir implements FS.Mkdir
func (dir dirFS) Mkdir(name string, perm fs.FileMode) error {
err := os.Mkdir(path.Join(string(dir), name), perm)
return adjustMkdirError(err)
}
// Rename implements FS.Rename
func (dir dirFS) Rename(from, to string) error {
if from == to {
return nil
}
return rename(path.Join(string(dir), from), path.Join(string(dir), to))
}
// Rmdir implements FS.Rmdir
func (dir dirFS) Rmdir(name string) error {
err := syscall.Rmdir(path.Join(string(dir), name))
return adjustRmdirError(err)
}
// Unlink implements FS.Unlink
func (dir dirFS) Unlink(name string) error {
err := syscall.Unlink(path.Join(string(dir), name))
return adjustUnlinkError(err)
}
// Utimes implements FS.Utimes
func (dir dirFS) Utimes(name string, atimeNsec, mtimeNsec int64) error {
return syscall.UtimesNano(path.Join(string(dir), name), []syscall.Timespec{
syscall.NsecToTimespec(atimeNsec),
syscall.NsecToTimespec(mtimeNsec),
})
}