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>
81 lines
2.0 KiB
Go
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),
|
|
})
|
|
}
|