diff --git a/internal/sandbox/dirfs_unix.go b/internal/sandbox/dirfs_unix.go index b573cd00..5a9c1e58 100644 --- a/internal/sandbox/dirfs_unix.go +++ b/internal/sandbox/dirfs_unix.go @@ -13,7 +13,7 @@ type dirFS struct { root string } -func (fsys *dirFS) Open(name string, flags int, mode fs.FileMode) (File, error) { +func (fsys *dirFS) Open(name string, flags OpenFlags, mode fs.FileMode) (File, error) { f, err := fsys.openRoot() if err != nil { return nil, err @@ -26,7 +26,7 @@ func (fsys *dirFS) Open(name string, flags int, mode fs.FileMode) (File, error) } func (fsys *dirFS) openRoot() (File, error) { - dirfd, err := openat(unix.AT_FDCWD, fsys.root, O_DIRECTORY, 0) + dirfd, err := openat(unix.AT_FDCWD, fsys.root, O_DIRECTORY.sysFlags(), 0) if err != nil { return nil, err } @@ -100,15 +100,15 @@ func (f *dirFile) openRoot() (File, error) { return f.openSelf() } -func (f *dirFile) openFile(name string, flags int, mode fs.FileMode) (File, error) { - fd, err := openat(f.fd, name, flags|O_NOFOLLOW, uint32(mode.Perm())) +func (f *dirFile) openFile(name string, flags OpenFlags, mode fs.FileMode) (File, error) { + fd, err := openat(f.fd, name, (flags | O_NOFOLLOW).sysFlags(), uint32(mode.Perm())) if err != nil { return nil, err } return newFile(f, fd), nil } -func (f *dirFile) open(name string, flags int, mode fs.FileMode) (File, error) { +func (f *dirFile) open(name string, flags OpenFlags, mode fs.FileMode) (File, error) { switch name { case ".": return f.openSelf() @@ -119,11 +119,14 @@ func (f *dirFile) open(name string, flags int, mode fs.FileMode) (File, error) { } } -func (f *dirFile) Open(name string, flags int, mode fs.FileMode) (File, error) { +func (f *dirFile) Open(name string, flags OpenFlags, mode fs.FileMode) (File, error) { if fspath.IsRoot(name) { return f.openRoot() } - return ResolvePath(f, name, flags, func(d *dirFile, name string) (File, error) { + if fspath.HasTrailingSlash(name) { + flags |= O_DIRECTORY + } + return ResolvePath(f, name, flags.LookupFlags(), func(d *dirFile, name string) (File, error) { return d.open(name, flags, mode) }) } @@ -164,12 +167,13 @@ func (f *dirFile) Datasync() error { return fdatasync(f.fd) } -func (f *dirFile) Flags() (int, error) { - return unix.FcntlInt(uintptr(f.fd), unix.F_GETFL, 0) +func (f *dirFile) Flags() (OpenFlags, error) { + flags, err := unix.FcntlInt(uintptr(f.fd), unix.F_GETFL, 0) + return makeOpenFlags(flags), err } -func (f *dirFile) SetFlags(flags int) error { - _, err := unix.FcntlInt(uintptr(f.fd), unix.F_SETFL, flags) +func (f *dirFile) SetFlags(flags OpenFlags) error { + _, err := unix.FcntlInt(uintptr(f.fd), unix.F_SETFL, flags.sysFlags()) return err } @@ -177,15 +181,15 @@ func (f *dirFile) ReadDirent(buf []byte) (int, error) { return ignoreEINTR2(func() (int, error) { return unix.ReadDirent(f.fd, buf) }) } -func (f *dirFile) Stat(name string, flags int) (FileInfo, error) { - return ResolvePath(f, name, openFlags(flags), func(d *dirFile, name string) (FileInfo, error) { +func (f *dirFile) Stat(name string, flags LookupFlags) (FileInfo, error) { + return ResolvePath(f, name, flags, func(d *dirFile, name string) (FileInfo, error) { var stat unix.Stat_t var err error if name == "" { err = fstat(d.fd, &stat) } else { - err = fstatat(d.fd, name, &stat, AT_SYMLINK_NOFOLLOW) + err = fstatat(d.fd, name, &stat, AT_SYMLINK_NOFOLLOW.sysFlags()) } if err != nil { return FileInfo{}, err @@ -231,7 +235,7 @@ func (f *dirFile) Stat(name string, flags int) (FileInfo, error) { } func (f *dirFile) Readlink(name string, buf []byte) (int, error) { - return ResolvePath(f, name, O_NOFOLLOW, func(d *dirFile, name string) (int, error) { + return ResolvePath(f, name, AT_SYMLINK_NOFOLLOW, func(d *dirFile, name string) (int, error) { if name == "" { return freadlink(d.fd, buf) } else { @@ -240,24 +244,24 @@ func (f *dirFile) Readlink(name string, buf []byte) (int, error) { }) } -func (f *dirFile) Chtimes(name string, times [2]Timespec, flags int) error { - return resolvePath1(f, name, openFlags(flags), func(d *dirFile, name string) error { +func (f *dirFile) Chtimes(name string, times [2]Timespec, flags LookupFlags) error { + return resolvePath1(f, name, flags, func(d *dirFile, name string) error { if name == "" { return futimens(d.fd, ×) } else { - return utimensat(d.fd, name, ×, AT_SYMLINK_NOFOLLOW) + return utimensat(d.fd, name, ×, AT_SYMLINK_NOFOLLOW.sysFlags()) } }) } func (f *dirFile) Mkdir(name string, mode fs.FileMode) error { - return resolvePath1(f, name, O_NOFOLLOW, func(d *dirFile, name string) error { + return resolvePath1(f, name, AT_SYMLINK_NOFOLLOW, func(d *dirFile, name string) error { return mkdirat(d.fd, name, uint32(mode.Perm())) }) } func (f *dirFile) Rmdir(name string) error { - return resolvePath1(f, name, O_NOFOLLOW, func(d *dirFile, name string) error { + return resolvePath1(f, name, AT_SYMLINK_NOFOLLOW, func(d *dirFile, name string) error { return unlinkat(d.fd, name, unix.AT_REMOVEDIR) }) } @@ -267,46 +271,38 @@ func (f1 *dirFile) Rename(oldName string, newDir File, newName string) error { if !ok { return EXDEV } - return resolvePath1(f1, oldName, O_NOFOLLOW, func(d1 *dirFile, name1 string) error { - return resolvePath1(f2, newName, O_NOFOLLOW, func(d2 *dirFile, name2 string) error { + return resolvePath1(f1, oldName, AT_SYMLINK_NOFOLLOW, func(d1 *dirFile, name1 string) error { + return resolvePath1(f2, newName, AT_SYMLINK_NOFOLLOW, func(d2 *dirFile, name2 string) error { return renameat(d1.fd, name1, d2.fd, name2) }) }) } -func (f1 *dirFile) Link(oldName string, newDir File, newName string, flags int) error { +func (f1 *dirFile) Link(oldName string, newDir File, newName string, flags LookupFlags) error { f2, ok := newDir.(*dirFile) if !ok { return EXDEV } - oflags := openFlags(flags) - return resolvePath1(f1, oldName, oflags, func(d1 *dirFile, name1 string) error { - return resolvePath1(f2, newName, oflags, func(d2 *dirFile, name2 string) error { + return resolvePath1(f1, oldName, flags, func(d1 *dirFile, name1 string) error { + return resolvePath1(f2, newName, flags, func(d2 *dirFile, name2 string) error { return linkat(d1.fd, name1, d2.fd, name2, 0) }) }) } func (f *dirFile) Symlink(oldName string, newName string) error { - return resolvePath1(f, newName, O_NOFOLLOW, func(d *dirFile, name string) error { + return resolvePath1(f, newName, AT_SYMLINK_NOFOLLOW, func(d *dirFile, name string) error { return symlinkat(oldName, d.fd, name) }) } func (f *dirFile) Unlink(name string) error { - return resolvePath1(f, name, O_NOFOLLOW, func(d *dirFile, name string) error { + return resolvePath1(f, name, AT_SYMLINK_NOFOLLOW, func(d *dirFile, name string) error { return unlinkat(d.fd, name, 0) }) } -func openFlags(flags int) int { - if (flags & AT_SYMLINK_NOFOLLOW) != 0 { - return O_NOFOLLOW - } - return 0 -} - -func resolvePath1(d *dirFile, name string, flags int, do func(*dirFile, string) error) error { +func resolvePath1(d *dirFile, name string, flags LookupFlags, do func(*dirFile, string) error) error { _, err := ResolvePath(d, name, flags, func(d *dirFile, name string) (_ struct{}, err error) { err = do(d, name) return diff --git a/internal/sandbox/flags.go b/internal/sandbox/flags.go new file mode 100644 index 00000000..5f033149 --- /dev/null +++ b/internal/sandbox/flags.go @@ -0,0 +1,86 @@ +package sandbox + +import ( + "sort" + "strings" +) + +// OpenFlags is a bitset of flags that can be passed to the Open method of +// File and FileSystem values. +type OpenFlags int + +func makeOpenFlags(sysFlags int) OpenFlags { + return OpenFlags(sysFlags) +} + +func (openFlags OpenFlags) String() string { + var names []string + + switch openFlags & (O_RDWR | O_WRONLY | O_RDONLY) { + case O_RDWR: + names = append(names, "O_RDWR") + case O_WRONLY: + names = append(names, "O_WRONLY") + } + + for _, f := range [...]struct { + flag OpenFlags + name string + }{ + {O_APPEND, "O_APPEND"}, + {O_CREAT, "O_CREAT"}, + {O_EXCL, "O_EXCL"}, + {O_SYNC, "O_SYNC"}, + {O_TRUNC, "O_TRUNC"}, + {O_DIRECTORY, "O_DIRECTORY"}, + {O_NOFOLLOW, "O_NOFOLLOW"}, + {O_NONBLOCK, "O_NONBLOCK"}, + } { + if (openFlags & f.flag) != 0 { + names = append(names, f.name) + } + } + + if len(names) == 0 { + names = append(names, "O_RDONLY") + } + + sort.Strings(names) + return strings.Join(names, "|") +} + +func (openFlags OpenFlags) LookupFlags() LookupFlags { + if (openFlags & O_NOFOLLOW) != 0 { + return AT_SYMLINK_NOFOLLOW + } else { + return 0 + } +} + +func (openFlags OpenFlags) sysFlags() int { + return int(openFlags) +} + +// LookupFlags is a bitset of flags that can be passed to methods of File and +// FileSystem values to customize the behavior of file name lookups. +type LookupFlags int + +func (lookupFlags LookupFlags) String() string { + if (lookupFlags & AT_SYMLINK_NOFOLLOW) != 0 { + return "AT_SYMLINK_NOFOLLOW" + } else { + return "AT_SYMLINK_FOLLOW" + } +} + +func (lookupFlags LookupFlags) OpenFlags() OpenFlags { + if (lookupFlags & AT_SYMLINK_NOFOLLOW) != 0 { + return O_NOFOLLOW + } else { + return 0 + } +} + +func (lookupFlags LookupFlags) sysFlags() int { + return int(lookupFlags) +} diff --git a/internal/sandbox/fs.go b/internal/sandbox/fs.go index 27d3eb1f..49ee6206 100644 --- a/internal/sandbox/fs.go +++ b/internal/sandbox/fs.go @@ -30,7 +30,7 @@ const ( // root directory and use the methods of the returned File instance to access // the rest of the directory tree. type FileSystem interface { - Open(name string, flags int, mode fs.FileMode) (File, error) + Open(name string, flags OpenFlags, mode fs.FileMode) (File, error) } // Create creates and opens a file on a file system. The name is the location @@ -102,8 +102,8 @@ func Stat(fsys FileSystem, name string) (FileInfo, error) { // the location where the file is recorded on the file system. The flags are // passed to configure how the file is opened (e.g. passing O_NOFOLLOW will // fail if a symbolic link exists at that location). -func ReadFile(fsys FileSystem, name string, flags int) ([]byte, error) { - f, err := fsys.Open(name, flags|O_RDONLY, 0) +func ReadFile(fsys FileSystem, name string, flags LookupFlags) ([]byte, error) { + f, err := fsys.Open(name, flags.OpenFlags()|O_RDONLY, 0) if err != nil { return nil, &fs.PathError{Op: "read", Path: name, Err: err} } @@ -310,7 +310,7 @@ type File interface { // file system. // // The file must point to a directory or the method errors with ENOTDIR. - Open(name string, flags int, mode fs.FileMode) (File, error) + Open(name string, flags OpenFlags, mode fs.FileMode) (File, error) // Readv reads data from the current seek offset of the file into the list // of vectors passed as arguments. @@ -378,13 +378,13 @@ type File interface { // combination of O_* flags such as those that can be passed to Open. // // The set of flags supported by the file depends on the underlying type. - Flags() (int, error) + Flags() (OpenFlags, error) // Changes the bitset of flags set on the file. The flags are a combination // of O_* flags such as those that can be passed to Open. // // The set of flags supported by the file depends on the underlying type. - SetFlags(flags int) error + SetFlags(flags OpenFlags) error // Read directory entries into the given buffer. The caller must be aware of // the way directory entries are laid out by the underlying file system to @@ -402,7 +402,7 @@ type File interface { // // If the name is empty, flags are ignored and the method returns metdata // for the receiver. - Stat(name string, flags int) (FileInfo, error) + Stat(name string, flags LookupFlags) (FileInfo, error) // Reads the target of a symbolic link into buf. // @@ -425,7 +425,7 @@ type File interface { // // If the name is empty, flags are ignored and the method changes times of // the receiver. - Chtimes(name string, times [2]Timespec, flags int) error + Chtimes(name string, times [2]Timespec, flags LookupFlags) error // Creates a directory at the named location. // @@ -462,7 +462,7 @@ type File interface { // // The flags may be AT_SYMLINK_NOFOLLOW to create a link to a symbolic link // instead of its target. - Link(oldName string, newDir File, newName string, flags int) error + Link(oldName string, newDir File, newName string, flags LookupFlags) error // Creates a symbolic link to a named location. // @@ -734,13 +734,10 @@ func (dirent *fsDirEntry) Info() (fs.FileInfo, error) { // was encountered and must be followed, in which case ResolvePath continues // walking the path at the link target. Any other value or error returned by the // do function will be returned immediately. -func ResolvePath[F File, R any](dir F, name string, flags int, do func(F, string) (R, error)) (ret R, err error) { +func ResolvePath[F File, R any](dir F, name string, flags LookupFlags, do func(F, string) (R, error)) (ret R, err error) { if name == "" { return do(dir, "") } - if fspath.HasTrailingSlash(name) { - flags |= O_DIRECTORY - } var lastOpenDir File defer func() { closeFileIfNotNil(lastOpenDir) }() @@ -795,7 +792,7 @@ func ResolvePath[F File, R any](dir F, name string, flags int, do func(F, string doFile: ret, err = do(dir, elem) if err != nil { - if !errors.Is(err, ELOOP) || ((flags & O_NOFOLLOW) != 0) { + if !errors.Is(err, ELOOP) || ((flags & AT_SYMLINK_NOFOLLOW) != 0) { return ret, err } switch err := followSymlink(elem, ""); { @@ -816,7 +813,6 @@ func ResolvePath[F File, R any](dir F, name string, flags int, do func(F, string continue } - openPath: d, err := dir.Open(elem, openPathFlags, 0) if err != nil { if !errors.Is(err, ENOTDIR) { @@ -826,7 +822,7 @@ func ResolvePath[F File, R any](dir F, name string, flags int, do func(F, string case errors.Is(err, nil): continue case errors.Is(err, EINVAL): - goto openPath + return ret, ENOTDIR default: return ret, err } diff --git a/internal/sandbox/ocifs/file.go b/internal/sandbox/ocifs/file.go index a643054e..7f94afba 100644 --- a/internal/sandbox/ocifs/file.go +++ b/internal/sandbox/ocifs/file.go @@ -87,7 +87,7 @@ func (f *file) openRoot() (sandbox.File, error) { return f.fsys.newFile(l), nil } -func (f *file) openFile(name string, flags int, mode fs.FileMode) (sandbox.File, error) { +func (f *file) openFile(name string, flags sandbox.OpenFlags, mode fs.FileMode) (sandbox.File, error) { l := f.ref() if l == nil { return nil, sandbox.EBADF @@ -193,7 +193,7 @@ func hasWhiteout(file sandbox.File, whiteout string) (has bool, err error) { return false, nil } -func (f *file) open(name string, flags int, mode fs.FileMode) (sandbox.File, error) { +func (f *file) open(name string, flags sandbox.OpenFlags, mode fs.FileMode) (sandbox.File, error) { switch name { case ".": return f.openSelf() @@ -204,7 +204,7 @@ func (f *file) open(name string, flags int, mode fs.FileMode) (sandbox.File, err } } -func (f *file) Open(name string, flags int, mode fs.FileMode) (sandbox.File, error) { +func (f *file) Open(name string, flags sandbox.OpenFlags, mode fs.FileMode) (sandbox.File, error) { const unsupportedFlags = sandbox.O_CREAT | sandbox.O_APPEND | sandbox.O_RDWR | @@ -213,29 +213,25 @@ func (f *file) Open(name string, flags int, mode fs.FileMode) (sandbox.File, err if ((flags & unsupportedFlags) != 0) || mode != 0 || name == "" { return nil, sandbox.EINVAL } - if fspath.IsRoot(name) { return f.openRoot() } - - return sandbox.ResolvePath(f, name, flags, func(at *file, name string) (sandbox.File, error) { + if fspath.HasTrailingSlash(name) { + flags |= sandbox.O_DIRECTORY + } + return sandbox.ResolvePath(f, name, flags.LookupFlags(), func(at *file, name string) (sandbox.File, error) { return at.open(name, flags, mode) }) } -func (f *file) Stat(name string, flags int) (sandbox.FileInfo, error) { +func (f *file) Stat(name string, flags sandbox.LookupFlags) (sandbox.FileInfo, error) { l := f.ref() if l == nil { return sandbox.FileInfo{}, sandbox.EBADF } defer unref(l) - openFlags := 0 - if (flags & sandbox.AT_SYMLINK_NOFOLLOW) != 0 { - openFlags |= sandbox.O_NOFOLLOW - } - - return sandbox.ResolvePath(f, name, openFlags, func(at *file, name string) (sandbox.FileInfo, error) { + return sandbox.ResolvePath(f, name, flags, func(at *file, name string) (sandbox.FileInfo, error) { l := at.ref() defer unref(l) @@ -272,7 +268,7 @@ func (f *file) Readlink(name string, buf []byte) (int, error) { } defer unref(l) - return sandbox.ResolvePath(f, name, sandbox.O_NOFOLLOW, func(at *file, name string) (int, error) { + return sandbox.ResolvePath(f, name, sandbox.AT_SYMLINK_NOFOLLOW, func(at *file, name string) (int, error) { l := at.ref() defer unref(l) @@ -368,11 +364,11 @@ func (f *file) Datasync() error { return nil } -func (f *file) Flags() (int, error) { +func (f *file) Flags() (sandbox.OpenFlags, error) { return 0, nil } -func (f *file) SetFlags(int) error { +func (f *file) SetFlags(sandbox.OpenFlags) error { return sandbox.EINVAL } @@ -398,7 +394,7 @@ func (f *file) ReadDirent(buf []byte) (int, error) { return d.readDirent(buf, l.files) } -func (f *file) Chtimes(string, [2]sandbox.Timespec, int) error { +func (f *file) Chtimes(string, [2]sandbox.Timespec, sandbox.LookupFlags) error { return sandbox.EPERM } @@ -414,7 +410,7 @@ func (f *file) Rename(string, sandbox.File, string) error { return f.expectDirectory() } -func (f *file) Link(string, sandbox.File, string, int) error { +func (f *file) Link(string, sandbox.File, string, sandbox.LookupFlags) error { return f.expectDirectory() } diff --git a/internal/sandbox/ocifs/ocifs.go b/internal/sandbox/ocifs/ocifs.go index a0007d13..123676b8 100644 --- a/internal/sandbox/ocifs/ocifs.go +++ b/internal/sandbox/ocifs/ocifs.go @@ -36,7 +36,7 @@ func New(layers ...sandbox.FileSystem) *FileSystem { } // Open satisfies the sandbox.FileSystem interface. -func (fsys *FileSystem) Open(name string, flags int, mode fs.FileMode) (sandbox.File, error) { +func (fsys *FileSystem) Open(name string, flags sandbox.OpenFlags, mode fs.FileMode) (sandbox.File, error) { f, err := fsys.openRoot() if err != nil { return nil, err diff --git a/internal/sandbox/sandboxtest/fs_stat.go b/internal/sandbox/sandboxtest/fs_stat.go index 9e6b9710..fdce8ceb 100644 --- a/internal/sandbox/sandboxtest/fs_stat.go +++ b/internal/sandbox/sandboxtest/fs_stat.go @@ -38,4 +38,19 @@ var fsTestStat = fsTestSuite{ assert.Equal(t, s.Mode.Type(), 0) assert.Equal(t, s.Size, 5) }, + + "stat of a non-existing directory returns ENOENT": func(t *testing.T, fsys sandbox.FileSystem) { + assert.OK(t, sandbox.Mkdir(fsys, "test", 0700)) + + _, err := sandbox.Stat(fsys, "test/a/b") + assert.Error(t, err, sandbox.ENOENT) // "a" does not exist + }, + + "stat of a path which contains a file instead of a directory returns ENOTDIR": func(t *testing.T, fsys sandbox.FileSystem) { + assert.OK(t, sandbox.Mkdir(fsys, "test", 0700)) + assert.OK(t, sandbox.WriteFile(fsys, "test/a", []byte("123"), 0600)) + + _, err := sandbox.Stat(fsys, "test/a/b") + assert.Error(t, err, sandbox.ENOTDIR) // "a" is a file, not a directory + }, } diff --git a/internal/sandbox/sandboxtest/rootfs.go b/internal/sandbox/sandboxtest/rootfs.go index 1f2890a0..89d68786 100644 --- a/internal/sandbox/sandboxtest/rootfs.go +++ b/internal/sandbox/sandboxtest/rootfs.go @@ -16,7 +16,7 @@ func TestRootFS(t *testing.T, makeRootFS func(*testing.T, string) sandbox.FileSy tests := []struct { scenario string path string - flags int + flags sandbox.LookupFlags want string err error }{ @@ -81,9 +81,9 @@ func TestRootFS(t *testing.T, makeRootFS func(*testing.T, string) sandbox.FileSy }, { - scenario: "does not follow symlinks when O_NOFOLLOW is set", + scenario: "does not follow symlinks when AT_SYMLINK_NOFOLLOW is set", path: "symlink-to-answer", - flags: sandbox.O_NOFOLLOW, + flags: sandbox.AT_SYMLINK_NOFOLLOW, err: sandbox.ELOOP, }, diff --git a/internal/sandbox/syscall_unix.go b/internal/sandbox/syscall_unix.go index 6780ea95..85b8c2de 100644 --- a/internal/sandbox/syscall_unix.go +++ b/internal/sandbox/syscall_unix.go @@ -49,21 +49,21 @@ const ( ) const ( - O_RDONLY = unix.O_RDONLY - O_WRONLY = unix.O_WRONLY - O_RDWR = unix.O_RDWR - O_APPEND = unix.O_APPEND - O_CREAT = unix.O_CREAT - O_EXCL = unix.O_EXCL - O_SYNC = unix.O_SYNC - O_TRUNC = unix.O_TRUNC - O_DIRECTORY = unix.O_DIRECTORY - O_NOFOLLOW = unix.O_NOFOLLOW - O_NONBLOCK = unix.O_NONBLOCK + O_RDONLY OpenFlags = unix.O_RDONLY + O_WRONLY OpenFlags = unix.O_WRONLY + O_RDWR OpenFlags = unix.O_RDWR + O_APPEND OpenFlags = unix.O_APPEND + O_CREAT OpenFlags = unix.O_CREAT + O_EXCL OpenFlags = unix.O_EXCL + O_SYNC OpenFlags = unix.O_SYNC + O_TRUNC OpenFlags = unix.O_TRUNC + O_DIRECTORY OpenFlags = unix.O_DIRECTORY + O_NOFOLLOW OpenFlags = unix.O_NOFOLLOW + O_NONBLOCK OpenFlags = unix.O_NONBLOCK ) const ( - AT_SYMLINK_NOFOLLOW = unix.AT_SYMLINK_NOFOLLOW + AT_SYMLINK_NOFOLLOW LookupFlags = unix.AT_SYMLINK_NOFOLLOW ) const ( diff --git a/internal/sandbox/tarfs/dir.go b/internal/sandbox/tarfs/dir.go index 0002a0a0..5ddbbf4a 100644 --- a/internal/sandbox/tarfs/dir.go +++ b/internal/sandbox/tarfs/dir.go @@ -101,7 +101,7 @@ func (d *dir) find(name string) fileEntry { return d.ents[i].file } -func resolve[R any](fsys *FileSystem, cwd *dir, name string, flags int, do func(fileEntry) (R, error)) (R, error) { +func resolve[R any](fsys *FileSystem, cwd *dir, name string, flags sandbox.OpenFlags, do func(fileEntry) (R, error)) (R, error) { var zero R for loop := 0; loop < sandbox.MaxFollowSymlink; loop++ { @@ -171,7 +171,7 @@ func (d *openDir) Close() error { return nil } -func (d *openDir) Open(name string, flags int, mode fs.FileMode) (sandbox.File, error) { +func (d *openDir) Open(name string, flags sandbox.OpenFlags, mode fs.FileMode) (sandbox.File, error) { const unsupportedFlags = sandbox.O_CREAT | sandbox.O_APPEND | sandbox.O_RDWR | @@ -198,16 +198,12 @@ func (d *openDir) Open(name string, flags int, mode fs.FileMode) (sandbox.File, }) } -func (d *openDir) Stat(name string, flags int) (sandbox.FileInfo, error) { +func (d *openDir) Stat(name string, flags sandbox.LookupFlags) (sandbox.FileInfo, error) { dir := d.dir.Load() if dir == nil { return sandbox.FileInfo{}, sandbox.EBADF } - openFlags := 0 - if (flags & sandbox.AT_SYMLINK_NOFOLLOW) != 0 { - openFlags |= sandbox.O_NOFOLLOW - } - return resolve(d.fsys, dir, name, openFlags, func(f fileEntry) (sandbox.FileInfo, error) { + return resolve(d.fsys, dir, name, flags.OpenFlags(), func(f fileEntry) (sandbox.FileInfo, error) { return f.stat(), nil }) } @@ -287,7 +283,7 @@ func (*openDir) Rmdir(string) error { return sandbox.EROFS } func (*openDir) Rename(string, sandbox.File, string) error { return sandbox.EROFS } -func (*openDir) Link(string, sandbox.File, string, int) error { return sandbox.EROFS } +func (*openDir) Link(string, sandbox.File, string, sandbox.LookupFlags) error { return sandbox.EROFS } func (*openDir) Symlink(string, string) error { return sandbox.EROFS } diff --git a/internal/sandbox/tarfs/file.go b/internal/sandbox/tarfs/file.go index 3b012527..576bd4a8 100644 --- a/internal/sandbox/tarfs/file.go +++ b/internal/sandbox/tarfs/file.go @@ -81,7 +81,7 @@ func (f *openFile) Close() error { return nil } -func (f *openFile) Stat(name string, flags int) (sandbox.FileInfo, error) { +func (f *openFile) Stat(name string, flags sandbox.LookupFlags) (sandbox.FileInfo, error) { file := f.file.Load() switch { case file == nil: diff --git a/internal/sandbox/tarfs/tarfs.go b/internal/sandbox/tarfs/tarfs.go index caae2003..d8b1366a 100644 --- a/internal/sandbox/tarfs/tarfs.go +++ b/internal/sandbox/tarfs/tarfs.go @@ -25,7 +25,7 @@ type FileSystem struct { } // Open satisfies sandbox.FileSystem. -func (fsys *FileSystem) Open(name string, flags int, mode fs.FileMode) (sandbox.File, error) { +func (fsys *FileSystem) Open(name string, flags sandbox.OpenFlags, mode fs.FileMode) (sandbox.File, error) { f, err := fsys.root.open(fsys) if err != nil { return nil, err @@ -229,15 +229,19 @@ func (readOnlyFile) Sync() error { return nil } func (readOnlyFile) Datasync() error { return nil } -func (readOnlyFile) Flags() (int, error) { return 0, nil } +func (readOnlyFile) Flags() (sandbox.OpenFlags, error) { return 0, nil } -func (readOnlyFile) SetFlags(int) error { return sandbox.EINVAL } +func (readOnlyFile) SetFlags(sandbox.OpenFlags) error { return sandbox.EINVAL } -func (readOnlyFile) Chtimes(string, [2]sandbox.Timespec, int) error { return sandbox.EPERM } +func (readOnlyFile) Chtimes(string, [2]sandbox.Timespec, sandbox.LookupFlags) error { + return sandbox.EPERM +} type leafFile struct{ readOnlyFile } -func (leafFile) Open(string, int, fs.FileMode) (sandbox.File, error) { return nil, sandbox.ENOTDIR } +func (leafFile) Open(string, sandbox.OpenFlags, fs.FileMode) (sandbox.File, error) { + return nil, sandbox.ENOTDIR +} func (leafFile) ReadDirent([]byte) (int, error) { return 0, sandbox.ENOTDIR } @@ -247,7 +251,7 @@ func (leafFile) Rmdir(string) error { return sandbox.ENOTDIR } func (leafFile) Rename(string, sandbox.File, string) error { return sandbox.ENOTDIR } -func (leafFile) Link(string, sandbox.File, string, int) error { return sandbox.ENOTDIR } +func (leafFile) Link(string, sandbox.File, string, sandbox.LookupFlags) error { return sandbox.ENOTDIR } func (leafFile) Symlink(string, string) error { return sandbox.ENOTDIR } diff --git a/internal/sandbox/wasi.go b/internal/sandbox/wasi.go index 062c73be..020b5e66 100644 --- a/internal/sandbox/wasi.go +++ b/internal/sandbox/wasi.go @@ -305,7 +305,7 @@ func (f *wasiFile) PathLink(ctx context.Context, lookupFlags wasi.LookupFlags, o } func (f *wasiFile) PathOpen(ctx context.Context, lookupFlags wasi.LookupFlags, path string, openFlags wasi.OpenFlags, rightsBase, rightsInheriting wasi.Rights, fdFlags wasi.FDFlags) (anyFile, wasi.Errno) { - flags := 0 + flags := OpenFlags(0) if openFlags.Has(wasi.OpenDirectory) { flags |= O_DIRECTORY @@ -399,7 +399,7 @@ func makeTimespec(t wasi.Timestamp, set, now bool) Timespec { return nsecToTimespec(int64(t)) } -func makeLookupFlags(lookupFlags wasi.LookupFlags) (flags int) { +func makeLookupFlags(lookupFlags wasi.LookupFlags) (flags LookupFlags) { if !lookupFlags.Has(wasi.SymlinkFollow) { flags |= AT_SYMLINK_NOFOLLOW }