Files
wazero/internal/sysfs/dirfs.go
Crypt Keeper fb6147ca94
Some checks failed
Release CLI / Pre-release build (push) Has been cancelled
Release CLI / Pre-release test (macos-12) (push) Has been cancelled
Release CLI / Pre-release test (ubuntu-22.04) (push) Has been cancelled
Release CLI / Pre-release test (windows-2022) (push) Has been cancelled
Release CLI / Release (push) Has been cancelled
Emulates AT_SYMLINK_NOFOLLOW instead of sometimes implementing it (#1588)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
2023-07-22 08:03:47 +08:00

136 lines
4.2 KiB
Go

package sysfs
import (
"io/fs"
"os"
"syscall"
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/fsapi"
"github.com/tetratelabs/wazero/internal/platform"
"github.com/tetratelabs/wazero/sys"
)
func NewDirFS(dir string) fsapi.FS {
return &dirFS{
dir: dir,
cleanedDir: ensureTrailingPathSeparator(dir),
}
}
func ensureTrailingPathSeparator(dir string) string {
if !os.IsPathSeparator(dir[len(dir)-1]) {
return dir + string(os.PathSeparator)
}
return dir
}
type dirFS struct {
fsapi.UnimplementedFS
dir string
// cleanedDir is for easier OS-specific concatenation, as it always has
// a trailing path separator.
cleanedDir string
}
// String implements fmt.Stringer
func (d *dirFS) String() string {
return d.dir
}
// OpenFile implements the same method as documented on fsapi.FS
func (d *dirFS) OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) {
return OpenOSFile(d.join(path), flag, perm)
}
// Lstat implements the same method as documented on fsapi.FS
func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
return lstat(d.join(path))
}
// Stat implements the same method as documented on fsapi.FS
func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
return stat(d.join(path))
}
// Mkdir implements the same method as documented on fsapi.FS
func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) {
err := os.Mkdir(d.join(path), perm)
if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR {
errno = experimentalsys.ENOENT
}
return
}
// Chmod implements the same method as documented on fsapi.FS
func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
err := os.Chmod(d.join(path), perm)
return experimentalsys.UnwrapOSError(err)
}
// Rename implements the same method as documented on fsapi.FS
func (d *dirFS) Rename(from, to string) experimentalsys.Errno {
from, to = d.join(from), d.join(to)
return rename(from, to)
}
// Readlink implements the same method as documented on fsapi.FS
func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) {
// Note: do not use syscall.Readlink as that causes race on Windows.
// In any case, syscall.Readlink does almost the same logic as os.Readlink.
dst, err := os.Readlink(d.join(path))
if err != nil {
return "", experimentalsys.UnwrapOSError(err)
}
return platform.ToPosixPath(dst), 0
}
// Link implements the same method as documented on fsapi.FS
func (d *dirFS) Link(oldName, newName string) experimentalsys.Errno {
err := os.Link(d.join(oldName), d.join(newName))
return experimentalsys.UnwrapOSError(err)
}
// Rmdir implements the same method as documented on fsapi.FS
func (d *dirFS) Rmdir(path string) experimentalsys.Errno {
return rmdir(d.join(path))
}
func rmdir(path string) experimentalsys.Errno {
err := syscall.Rmdir(path)
return experimentalsys.UnwrapOSError(err)
}
// Unlink implements the same method as documented on fsapi.FS
func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) {
return unlink(d.join(path))
}
// Symlink implements the same method as documented on fsapi.FS
func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno {
// Note: do not resolve `oldName` relative to this dirFS. The link result is always resolved
// when dereference the `link` on its usage (e.g. readlink, read, etc).
// https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409
err := os.Symlink(oldName, d.join(link))
return experimentalsys.UnwrapOSError(err)
}
// Utimens implements the same method as documented on fsapi.FS
func (d *dirFS) Utimens(path string, times *[2]syscall.Timespec) experimentalsys.Errno {
return Utimens(d.join(path), times)
}
func (d *dirFS) join(path string) string {
switch path {
case "", ".", "/":
if d.cleanedDir == "/" {
return "/"
}
// cleanedDir includes an unnecessary delimiter for the root path.
return d.cleanedDir[:len(d.cleanedDir)-1]
}
// TODO: Enforce similar to safefilepath.FromFS(path), but be careful as
// relative path inputs are allowed. e.g. dir or path == ../
return d.cleanedDir + path
}