summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/fsutil
diff options
context:
space:
mode:
authorNicolas Lacasse <nlacasse@google.com>2019-01-14 20:33:29 -0800
committerShentubot <shentubot@google.com>2019-01-14 20:34:28 -0800
commitdc8450b5676d4c4ac9bcfa23cabd862e0060527d (patch)
treea4ef1ad59764f46f674b7003221ba8ae399b9e65 /pkg/sentry/fs/fsutil
parent343ebe9789087b099ea7feae19879f5c24e59bf1 (diff)
Remove fs.Handle, ramfs.Entry, and all the DeprecatedFileOperations.
More helper structs have been added to the fsutil package to make it easier to implement fs.InodeOperations and fs.FileOperations. PiperOrigin-RevId: 229305982 Change-Id: Ib6f8d3862f4216745116857913dbfa351530223b
Diffstat (limited to 'pkg/sentry/fs/fsutil')
-rw-r--r--pkg/sentry/fs/fsutil/BUILD15
-rw-r--r--pkg/sentry/fs/fsutil/file.go226
-rw-r--r--pkg/sentry/fs/fsutil/fsutil.go2
-rw-r--r--pkg/sentry/fs/fsutil/handle.go128
-rw-r--r--pkg/sentry/fs/fsutil/handle_test.go227
-rw-r--r--pkg/sentry/fs/fsutil/inode.go409
-rw-r--r--pkg/sentry/fs/fsutil/inode_cached_test.go14
7 files changed, 401 insertions, 620 deletions
diff --git a/pkg/sentry/fs/fsutil/BUILD b/pkg/sentry/fs/fsutil/BUILD
index 6834e1272..4965e1a5f 100644
--- a/pkg/sentry/fs/fsutil/BUILD
+++ b/pkg/sentry/fs/fsutil/BUILD
@@ -67,7 +67,6 @@ go_library(
"frame_ref_set.go",
"frame_ref_set_impl.go",
"fsutil.go",
- "handle.go",
"host_file_mapper.go",
"host_file_mapper_state.go",
"host_file_mapper_unsafe.go",
@@ -97,20 +96,6 @@ go_library(
)
go_test(
- name = "fsutil_x_test",
- size = "small",
- srcs = ["handle_test.go"],
- deps = [
- ":fsutil",
- "//pkg/sentry/context",
- "//pkg/sentry/context/contexttest",
- "//pkg/sentry/fs",
- "//pkg/sentry/fs/ramfs/test",
- "//pkg/sentry/usermem",
- ],
-)
-
-go_test(
name = "fsutil_test",
size = "small",
srcs = [
diff --git a/pkg/sentry/fs/fsutil/file.go b/pkg/sentry/fs/fsutil/file.go
index 46db2e51c..0970f782b 100644
--- a/pkg/sentry/fs/fsutil/file.go
+++ b/pkg/sentry/fs/fsutil/file.go
@@ -24,12 +24,12 @@ import (
"gvisor.googlesource.com/gvisor/pkg/waiter"
)
-// NoopRelease implements FileOperations.Release for files that have no
+// FileNoopRelease implements fs.FileOperations.Release for files that have no
// resources to release.
-type NoopRelease struct{}
+type FileNoopRelease struct{}
// Release is a no-op.
-func (NoopRelease) Release() {}
+func (FileNoopRelease) Release() {}
// SeekWithDirCursor is used to implement fs.FileOperations.Seek. If dirCursor
// is not nil and the seek was on a directory, the cursor will be updated.
@@ -127,71 +127,81 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence,
return current, syserror.EINVAL
}
-// GenericSeek implements FileOperations.Seek for files that use a generic
-// seek implementation.
-type GenericSeek struct{}
+// FileGenericSeek implements fs.FileOperations.Seek for files that use a
+// generic seek implementation.
+type FileGenericSeek struct{}
// Seek implements fs.FileOperations.Seek.
-func (GenericSeek) Seek(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64) (int64, error) {
+func (FileGenericSeek) Seek(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64) (int64, error) {
return SeekWithDirCursor(ctx, file, whence, offset, nil)
}
-// ZeroSeek implements FileOperations.Seek for files that maintain a constant
-// zero-value offset and require a no-op Seek.
-type ZeroSeek struct{}
+// FileZeroSeek implements fs.FileOperations.Seek for files that maintain a
+// constant zero-value offset and require a no-op Seek.
+type FileZeroSeek struct{}
-// Seek implements FileOperations.Seek.
-func (ZeroSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
+// Seek implements fs.FileOperations.Seek.
+func (FileZeroSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
return 0, nil
}
-// PipeSeek implements FileOperations.Seek and can be used for files that behave
-// like pipes (seeking is not supported).
-type PipeSeek struct{}
+// FileNoSeek implements fs.FileOperations.Seek to return EINVAL.
+type FileNoSeek struct{}
+
+// Seek implements fs.FileOperations.Seek.
+func (FileNoSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
+ return 0, syserror.EINVAL
+}
-// Seek implements FileOperations.Seek.
-func (PipeSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
+// FilePipeSeek implements fs.FileOperations.Seek and can be used for files
+// that behave like pipes (seeking is not supported).
+type FilePipeSeek struct{}
+
+// Seek implements fs.FileOperations.Seek.
+func (FilePipeSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
return 0, syserror.ESPIPE
}
-// NotDirReaddir implements FileOperations.Readdir for non-directories.
-type NotDirReaddir struct{}
+// FileNotDirReaddir implements fs.FileOperations.Readdir for non-directories.
+type FileNotDirReaddir struct{}
-// Readdir implements FileOperations.NotDirReaddir.
-func (NotDirReaddir) Readdir(context.Context, *fs.File, fs.DentrySerializer) (int64, error) {
+// Readdir implements fs.FileOperations.FileNotDirReaddir.
+func (FileNotDirReaddir) Readdir(context.Context, *fs.File, fs.DentrySerializer) (int64, error) {
return 0, syserror.ENOTDIR
}
-// NoFsync implements FileOperations.Fsync for files that don't support syncing.
-type NoFsync struct{}
+// FileNoFsync implements fs.FileOperations.Fsync for files that don't support
+// syncing.
+type FileNoFsync struct{}
-// Fsync implements FileOperations.Fsync.
-func (NoFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error {
+// Fsync implements fs.FileOperations.Fsync.
+func (FileNoFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error {
return syserror.EINVAL
}
-// NoopFsync implements FileOperations.Fsync for files that don't need to synced.
-type NoopFsync struct{}
+// FileNoopFsync implements fs.FileOperations.Fsync for files that don't need
+// to synced.
+type FileNoopFsync struct{}
-// Fsync implements FileOperations.Fsync.
-func (NoopFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error {
+// Fsync implements fs.FileOperations.Fsync.
+func (FileNoopFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error {
return nil
}
-// NoopFlush implements FileOperations.Flush as a no-op.
-type NoopFlush struct{}
+// FileNoopFlush implements fs.FileOperations.Flush as a no-op.
+type FileNoopFlush struct{}
-// Flush implements FileOperations.Flush.
-func (NoopFlush) Flush(context.Context, *fs.File) error {
+// Flush implements fs.FileOperations.Flush.
+func (FileNoopFlush) Flush(context.Context, *fs.File) error {
return nil
}
-// NoMMap implements fs.FileOperations.Mappable for files that cannot
+// FileNoMMap implements fs.FileOperations.Mappable for files that cannot
// be memory mapped.
-type NoMMap struct{}
+type FileNoMMap struct{}
// ConfigureMMap implements fs.FileOperations.ConfigureMMap.
-func (NoMMap) ConfigureMMap(context.Context, *fs.File, *memmap.MMapOpts) error {
+func (FileNoMMap) ConfigureMMap(context.Context, *fs.File, *memmap.MMapOpts) error {
return syserror.ENODEV
}
@@ -204,26 +214,43 @@ func GenericConfigureMMap(file *fs.File, m memmap.Mappable, opts *memmap.MMapOpt
return nil
}
-// NoIoctl implements fs.FileOperations.Ioctl for files that don't implement
-// the ioctl syscall.
-type NoIoctl struct{}
+// FileNoIoctl implements fs.FileOperations.Ioctl for files that don't
+// implement the ioctl syscall.
+type FileNoIoctl struct{}
// Ioctl implements fs.FileOperations.Ioctl.
-func (NoIoctl) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
+func (FileNoIoctl) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
return 0, syserror.ENOTTY
}
-// DirFileOperations implements FileOperations for directories.
+// DirFileOperations implements most of fs.FileOperations for directories,
+// except for Readdir which the embedding type must implement.
+type DirFileOperations struct {
+ waiter.AlwaysReady
+ FileGenericSeek
+ FileNoFsync
+ FileNoIoctl
+ FileNoMMap
+ FileNoopFlush
+ FileNoopRelease
+}
+
+// Read implements fs.FileOperations.Read
+func (*DirFileOperations) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
+ return 0, syserror.EISDIR
+}
+
+// Write implements fs.FileOperations.Write.
+func (*DirFileOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
+ return 0, syserror.EISDIR
+}
+
+// StaticDirFileOperations implements fs.FileOperations for directories with
+// static children.
//
// +stateify savable
-type DirFileOperations struct {
- waiter.AlwaysReady `state:"nosave"`
- NoopRelease `state:"nosave"`
- GenericSeek `state:"nosave"`
- NoFsync `state:"nosave"`
- NoopFlush `state:"nosave"`
- NoMMap `state:"nosave"`
- NoIoctl `state:"nosave"`
+type StaticDirFileOperations struct {
+ DirFileOperations
// dentryMap is a SortedDentryMap used to implement Readdir.
dentryMap *fs.SortedDentryMap
@@ -233,37 +260,106 @@ type DirFileOperations struct {
dirCursor string
}
-// NewDirFileOperations returns a new DirFileOperations that will iterate the
-// given denty map.
-func NewDirFileOperations(dentries *fs.SortedDentryMap) *DirFileOperations {
- return &DirFileOperations{
+// NewStaticDirFileOperations returns a new StaticDirFileOperations that will
+// iterate the given denty map.
+func NewStaticDirFileOperations(dentries *fs.SortedDentryMap) *StaticDirFileOperations {
+ return &StaticDirFileOperations{
dentryMap: dentries,
}
}
// IterateDir implements DirIterator.IterateDir.
-func (dfo *DirFileOperations) IterateDir(ctx context.Context, dirCtx *fs.DirCtx, offset int) (int, error) {
- n, err := fs.GenericReaddir(dirCtx, dfo.dentryMap)
+func (sdfo *StaticDirFileOperations) IterateDir(ctx context.Context, dirCtx *fs.DirCtx, offset int) (int, error) {
+ n, err := fs.GenericReaddir(dirCtx, sdfo.dentryMap)
return offset + n, err
}
-// Readdir implements FileOperations.Readdir.
-func (dfo *DirFileOperations) Readdir(ctx context.Context, file *fs.File, serializer fs.DentrySerializer) (int64, error) {
+// Readdir implements fs.FileOperations.Readdir.
+func (sdfo *StaticDirFileOperations) Readdir(ctx context.Context, file *fs.File, serializer fs.DentrySerializer) (int64, error) {
root := fs.RootFromContext(ctx)
defer root.DecRef()
dirCtx := &fs.DirCtx{
Serializer: serializer,
- DirCursor: &dfo.dirCursor,
+ DirCursor: &sdfo.dirCursor,
}
- return fs.DirentReaddir(ctx, file.Dirent, dfo, root, dirCtx, file.Offset())
+ return fs.DirentReaddir(ctx, file.Dirent, sdfo, root, dirCtx, file.Offset())
}
-// Read implements FileOperations.Read
-func (*DirFileOperations) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
- return 0, syserror.EISDIR
+// NoReadWriteFile is a file that does not support reading or writing.
+//
+// +stateify savable
+type NoReadWriteFile struct {
+ waiter.AlwaysReady `state:"nosave"`
+ FileGenericSeek `state:"nosave"`
+ FileNoIoctl `state:"nosave"`
+ FileNoMMap `state:"nosave"`
+ FileNoopFsync `state:"nosave"`
+ FileNoopFlush `state:"nosave"`
+ FileNoopRelease `state:"nosave"`
+ FileNoRead `state:"nosave"`
+ FileNoWrite `state:"nosave"`
+ FileNotDirReaddir `state:"nosave"`
}
-// Write implements FileOperations.Write.
-func (*DirFileOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
- return 0, syserror.EISDIR
+var _ fs.FileOperations = (*NoReadWriteFile)(nil)
+
+// FileStaticContentReader is a helper to implement fs.FileOperations.Read with
+// static content.
+//
+// +stateify savable
+type FileStaticContentReader struct {
+ // content is immutable.
+ content []byte
+}
+
+// NewFileStaticContentReader initializes a FileStaticContentReader with the
+// given content.
+func NewFileStaticContentReader(b []byte) FileStaticContentReader {
+ return FileStaticContentReader{
+ content: b,
+ }
+}
+
+// Read implements fs.FileOperations.Read.
+func (scr *FileStaticContentReader) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
+ if offset < 0 {
+ return 0, syserror.EINVAL
+ }
+ if offset >= int64(len(scr.content)) {
+ return 0, nil
+ }
+ n, err := dst.CopyOut(ctx, scr.content[offset:])
+ return int64(n), err
+}
+
+// FileNoopWrite implements fs.FileOperations.Write as a noop.
+type FileNoopWrite struct{}
+
+// Write implements fs.FileOperations.Write.
+func (FileNoopWrite) Write(_ context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) {
+ return src.NumBytes(), nil
+}
+
+// FileNoRead implements fs.FileOperations.Read to return EINVAL.
+type FileNoRead struct{}
+
+// Read implements fs.FileOperations.Read.
+func (FileNoRead) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
+ return 0, syserror.EINVAL
+}
+
+// FileNoWrite implements fs.FileOperations.Write to return EINVAL.
+type FileNoWrite struct{}
+
+// Write implements fs.FileOperations.Write.
+func (FileNoWrite) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
+ return 0, syserror.EINVAL
+}
+
+// FileNoopRead implement fs.FileOperations.Read as a noop.
+type FileNoopRead struct{}
+
+// Read implements fs.FileOperations.Read.
+func (FileNoopRead) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
+ return 0, nil
}
diff --git a/pkg/sentry/fs/fsutil/fsutil.go b/pkg/sentry/fs/fsutil/fsutil.go
index 3d7f3732d..319c4841b 100644
--- a/pkg/sentry/fs/fsutil/fsutil.go
+++ b/pkg/sentry/fs/fsutil/fsutil.go
@@ -20,7 +20,5 @@
// - For fs.Inodes that require a page cache to be memory mapped, see
// inode_cache.go.
//
-// - For fs.Files that implement fs.HandleOps, see handle.go.
-//
// - For anon fs.Inodes, see anon.go.
package fsutil
diff --git a/pkg/sentry/fs/fsutil/handle.go b/pkg/sentry/fs/fsutil/handle.go
deleted file mode 100644
index 8920b72ee..000000000
--- a/pkg/sentry/fs/fsutil/handle.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2018 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package fsutil
-
-import (
- "gvisor.googlesource.com/gvisor/pkg/sentry/context"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs"
- "gvisor.googlesource.com/gvisor/pkg/sentry/memmap"
- "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
- "gvisor.googlesource.com/gvisor/pkg/syserror"
- "gvisor.googlesource.com/gvisor/pkg/waiter"
-)
-
-// Handle implements FileOperations.
-//
-// FIXME: Remove Handle entirely in favor of individual fs.File
-// implementations using simple generic utilities.
-//
-// +stateify savable
-type Handle struct {
- NoopRelease `state:"nosave"`
- NoIoctl `state:"nosave"`
- HandleOperations fs.HandleOperations
-
- // dirCursor is the directory cursor.
- dirCursor string
-}
-
-// NewHandle returns a File backed by the Dirent and FileFlags.
-func NewHandle(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags, hops fs.HandleOperations) *fs.File {
- if !fs.IsPipe(dirent.Inode.StableAttr) && !fs.IsSocket(dirent.Inode.StableAttr) {
- // Allow reading/writing at an arbitrary offset for non-pipes
- // and non-sockets.
- flags.Pread = true
- flags.Pwrite = true
- }
-
- return fs.NewFile(ctx, dirent, flags, &Handle{HandleOperations: hops})
-}
-
-// Readiness implements waiter.Waitable.Readiness.
-func (h *Handle) Readiness(mask waiter.EventMask) waiter.EventMask {
- return h.HandleOperations.Readiness(mask)
-}
-
-// EventRegister implements waiter.Waitable.EventRegister.
-func (h *Handle) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
- h.HandleOperations.EventRegister(e, mask)
-}
-
-// EventUnregister implements waiter.Waitable.EventUnregister.
-func (h *Handle) EventUnregister(e *waiter.Entry) {
- h.HandleOperations.EventUnregister(e)
-}
-
-// Readdir implements FileOperations.Readdir.
-func (h *Handle) Readdir(ctx context.Context, file *fs.File, serializer fs.DentrySerializer) (int64, error) {
- root := fs.RootFromContext(ctx)
- defer root.DecRef()
- dirCtx := &fs.DirCtx{
- Serializer: serializer,
- DirCursor: &h.dirCursor,
- }
- n, err := fs.DirentReaddir(ctx, file.Dirent, h, root, dirCtx, file.Offset())
- return n, err
-}
-
-// Seek implements FileOperations.Seek.
-func (h *Handle) Seek(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64) (int64, error) {
- return SeekWithDirCursor(ctx, file, whence, offset, &h.dirCursor)
-}
-
-// IterateDir implements DirIterator.IterateDir.
-func (h *Handle) IterateDir(ctx context.Context, dirCtx *fs.DirCtx, offset int) (int, error) {
- return h.HandleOperations.DeprecatedReaddir(ctx, dirCtx, offset)
-}
-
-// Read implements FileOperations.Read.
-func (h *Handle) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
- return h.HandleOperations.DeprecatedPreadv(ctx, dst, offset)
-}
-
-// Write implements FileOperations.Write.
-func (h *Handle) Write(ctx context.Context, file *fs.File, src usermem.IOSequence, offset int64) (int64, error) {
- return h.HandleOperations.DeprecatedPwritev(ctx, src, offset)
-}
-
-// Fsync implements FileOperations.Fsync.
-func (h *Handle) Fsync(ctx context.Context, file *fs.File, start int64, end int64, syncType fs.SyncType) error {
- switch syncType {
- case fs.SyncAll, fs.SyncData:
- // Write out metadata.
- if err := file.Dirent.Inode.WriteOut(ctx); err != nil {
- return err
- }
- fallthrough
- case fs.SyncBackingStorage:
- // Use DeprecatedFsync to sync disks.
- return h.HandleOperations.DeprecatedFsync()
- }
- panic("invalid sync type")
-}
-
-// Flush implements FileOperations.Flush.
-func (h *Handle) Flush(context.Context, *fs.File) error {
- return h.HandleOperations.DeprecatedFlush()
-}
-
-// ConfigureMMap implements FileOperations.ConfigureMMap.
-func (h *Handle) ConfigureMMap(ctx context.Context, file *fs.File, opts *memmap.MMapOpts) error {
- mappable := file.Dirent.Inode.Mappable()
- if mappable == nil {
- return syserror.ENODEV
- }
- return GenericConfigureMMap(file, mappable, opts)
-}
diff --git a/pkg/sentry/fs/fsutil/handle_test.go b/pkg/sentry/fs/fsutil/handle_test.go
deleted file mode 100644
index 43e1a3bdf..000000000
--- a/pkg/sentry/fs/fsutil/handle_test.go
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2018 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package fsutil_test
-
-import (
- "io"
- "syscall"
- "testing"
-
- "gvisor.googlesource.com/gvisor/pkg/sentry/context"
- "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
- ramfstest "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs/test"
- "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
-)
-
-type testInodeOperations struct {
- fs.InodeOperations
- fs.InodeType
- FileSize int64
- writes uint
- reads uint
-}
-
-func (t *testInodeOperations) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) {
- return fs.UnstableAttr{Size: t.FileSize}, nil
-}
-
-// Check implements InodeOperations.Check.
-func (t *testInodeOperations) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
- return fs.ContextCanAccessFile(ctx, inode, p)
-}
-
-func (t *testInodeOperations) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
- t.reads++
- return t.InodeOperations.DeprecatedPreadv(ctx, dst, offset)
-}
-
-func (t *testInodeOperations) DeprecatedPwritev(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
- t.writes++
- return t.InodeOperations.DeprecatedPwritev(ctx, src, offset)
-}
-
-// testHandle returns a handle for a test node.
-//
-// The size of the node is fixed at 20 bytes.
-func testHandle(t *testing.T, flags fs.FileFlags, nt fs.InodeType) (*fs.File, *testInodeOperations) {
- ctx := contexttest.Context(t)
- m := fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{})
- n := &testInodeOperations{
- InodeOperations: ramfstest.NewFile(ctx, fs.FilePermissions{User: fs.PermMask{Read: true, Write: true}}),
- FileSize: 20,
- }
- d := fs.NewDirent(fs.NewInode(n, m, fs.StableAttr{Type: nt}), "test")
- return fsutil.NewHandle(ctx, d, flags, d.Inode.HandleOps()), n
-}
-
-func TestHandleOps(t *testing.T) {
- h, n := testHandle(t, fs.FileFlags{Read: true, Write: true}, fs.RegularFile)
- defer h.DecRef()
-
- // Make sure a write request works.
- if n, err := h.Writev(contexttest.Context(t), usermem.BytesIOSequence([]byte("a"))); n != 1 || err != nil {
- t.Fatalf("Writev: got (%d, %v), wanted (1, nil)", n, err)
- }
- if n.writes != 1 {
- t.Errorf("found %d writes, expected 1", n.writes)
- }
-
- // Make sure a read request works.
- dst := make([]byte, 1)
- if n, err := h.Preadv(contexttest.Context(t), usermem.BytesIOSequence(dst), 0); n != 1 || (err != nil && err != io.EOF) {
- t.Errorf("Preadv: got (%d, %v), wanted (1, nil or EOF)", n, err)
- }
- if dst[0] != 'a' {
- t.Errorf("Preadv: read %q, wanted 'a'", dst[0])
- }
- if n.reads != 1 {
- t.Errorf("found %d reads, expected 1", n.reads)
- }
-}
-
-type seekTest struct {
- whence fs.SeekWhence
- offset int64
- result int64
- err error
-}
-
-type seekSuite struct {
- nodeType fs.InodeType
- cases []seekTest
-}
-
-// FIXME: This is currently missing fs.SeekEnd tests due to the
-// fact that NullInodeOperations returns an error on stat.
-func TestHandleSeek(t *testing.T) {
- ts := []seekSuite{
- {
- nodeType: fs.RegularFile,
- cases: []seekTest{
- {fs.SeekSet, 0, 0, nil},
- {fs.SeekSet, 10, 10, nil},
- {fs.SeekSet, -5, 10, syscall.EINVAL},
- {fs.SeekCurrent, -1, 9, nil},
- {fs.SeekCurrent, 2, 11, nil},
- {fs.SeekCurrent, -12, 11, syscall.EINVAL},
- {fs.SeekEnd, -1, 19, nil},
- {fs.SeekEnd, 0, 20, nil},
- {fs.SeekEnd, 2, 22, nil},
- },
- },
- {
- nodeType: fs.Directory,
- cases: []seekTest{
- {fs.SeekSet, 0, 0, nil},
- {fs.SeekSet, 10, 0, syscall.EINVAL},
- {fs.SeekSet, -5, 0, syscall.EINVAL},
- {fs.SeekCurrent, 0, 0, nil},
- {fs.SeekCurrent, 11, 0, syscall.EINVAL},
- {fs.SeekCurrent, -6, 0, syscall.EINVAL},
- {fs.SeekEnd, 0, 0, syscall.EINVAL},
- {fs.SeekEnd, -1, 0, syscall.EINVAL},
- {fs.SeekEnd, 2, 0, syscall.EINVAL},
- },
- },
- {
- nodeType: fs.Symlink,
- cases: []seekTest{
- {fs.SeekSet, 5, 0, syscall.EINVAL},
- {fs.SeekSet, -5, 0, syscall.EINVAL},
- {fs.SeekSet, 0, 0, syscall.EINVAL},
- {fs.SeekCurrent, 5, 0, syscall.EINVAL},
- {fs.SeekCurrent, -5, 0, syscall.EINVAL},
- {fs.SeekCurrent, 0, 0, syscall.EINVAL},
- {fs.SeekEnd, 5, 0, syscall.EINVAL},
- {fs.SeekEnd, -5, 0, syscall.EINVAL},
- {fs.SeekEnd, 0, 0, syscall.EINVAL},
- },
- },
- {
- nodeType: fs.Pipe,
- cases: []seekTest{
- {fs.SeekSet, 5, 0, syscall.ESPIPE},
- {fs.SeekSet, -5, 0, syscall.ESPIPE},
- {fs.SeekSet, 0, 0, syscall.ESPIPE},
- {fs.SeekCurrent, 5, 0, syscall.ESPIPE},
- {fs.SeekCurrent, -5, 0, syscall.ESPIPE},
- {fs.SeekCurrent, 0, 0, syscall.ESPIPE},
- {fs.SeekEnd, 5, 0, syscall.ESPIPE},
- {fs.SeekEnd, -5, 0, syscall.ESPIPE},
- {fs.SeekEnd, 0, 0, syscall.ESPIPE},
- },
- },
- {
- nodeType: fs.Socket,
- cases: []seekTest{
- {fs.SeekSet, 5, 0, syscall.ESPIPE},
- {fs.SeekSet, -5, 0, syscall.ESPIPE},
- {fs.SeekSet, 0, 0, syscall.ESPIPE},
- {fs.SeekCurrent, 5, 0, syscall.ESPIPE},
- {fs.SeekCurrent, -5, 0, syscall.ESPIPE},
- {fs.SeekCurrent, 0, 0, syscall.ESPIPE},
- {fs.SeekEnd, 5, 0, syscall.ESPIPE},
- {fs.SeekEnd, -5, 0, syscall.ESPIPE},
- {fs.SeekEnd, 0, 0, syscall.ESPIPE},
- },
- },
- {
- nodeType: fs.CharacterDevice,
- cases: []seekTest{
- {fs.SeekSet, 5, 0, nil},
- {fs.SeekSet, -5, 0, nil},
- {fs.SeekSet, 0, 0, nil},
- {fs.SeekCurrent, 5, 0, nil},
- {fs.SeekCurrent, -5, 0, nil},
- {fs.SeekCurrent, 0, 0, nil},
- {fs.SeekEnd, 5, 0, nil},
- {fs.SeekEnd, -5, 0, nil},
- {fs.SeekEnd, 0, 0, nil},
- },
- },
- {
- nodeType: fs.BlockDevice,
- cases: []seekTest{
- {fs.SeekSet, 0, 0, nil},
- {fs.SeekSet, 10, 10, nil},
- {fs.SeekSet, -5, 10, syscall.EINVAL},
- {fs.SeekCurrent, -1, 9, nil},
- {fs.SeekCurrent, 2, 11, nil},
- {fs.SeekCurrent, -12, 11, syscall.EINVAL},
- {fs.SeekEnd, -1, 19, nil},
- {fs.SeekEnd, 0, 20, nil},
- {fs.SeekEnd, 2, 22, nil},
- },
- },
- }
-
- for _, s := range ts {
- h, _ := testHandle(t, fs.FileFlags{Read: true, Write: true}, s.nodeType)
- defer h.DecRef()
-
- for _, c := range s.cases {
- // Try the given seek.
- offset, err := h.Seek(contexttest.Context(t), c.whence, c.offset)
- if err != c.err {
- t.Errorf("seek(%s, %d) on %s had unexpected error: expected %v, got %v", c.whence, c.offset, s.nodeType, c.err, err)
- }
- if err == nil && offset != c.result {
- t.Errorf("seek(%s, %d) on %s had bad result: expected %v, got %v", c.whence, c.offset, s.nodeType, c.result, offset)
- }
- }
- }
-}
diff --git a/pkg/sentry/fs/fsutil/inode.go b/pkg/sentry/fs/fsutil/inode.go
index d4db1c2de..f1f5ec1de 100644
--- a/pkg/sentry/fs/fsutil/inode.go
+++ b/pkg/sentry/fs/fsutil/inode.go
@@ -15,213 +15,270 @@
package fsutil
import (
+ "sync"
+
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
"gvisor.googlesource.com/gvisor/pkg/sentry/memmap"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport"
- "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserror"
"gvisor.googlesource.com/gvisor/pkg/waiter"
)
-// NewSimpleInodeOperations constructs fs.InodeOperations from InodeSimpleAttributes.
-func NewSimpleInodeOperations(i InodeSimpleAttributes) fs.InodeOperations {
- return &simpleInodeOperations{InodeSimpleAttributes: i}
+// SimpleFileInode is a simple implementation of InodeOperations.
+//
+// +stateify savable
+type SimpleFileInode struct {
+ InodeGenericChecker `state:"nosave"`
+ InodeNoExtendedAttributes `state:"nosave"`
+ InodeNoopRelease `state:"nosave"`
+ InodeNoopWriteOut `state:"nosave"`
+ InodeNotDirectory `state:"nosave"`
+ InodeNotMappable `state:"nosave"`
+ InodeNotOpenable `state:"nosave"`
+ InodeNotSocket `state:"nosave"`
+ InodeNotSymlink `state:"nosave"`
+ InodeNotTruncatable `state:"nosave"`
+ InodeNotVirtual `state:"nosave"`
+
+ InodeSimpleAttributes
+}
+
+// NewSimpleFileInode returns a new SimpleFileInode.
+func NewSimpleFileInode(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) *SimpleFileInode {
+ return &SimpleFileInode{
+ InodeSimpleAttributes: NewInodeSimpleAttributes(ctx, owner, perms, typ),
+ }
}
-// simpleInodeOperations is a simple implementation of Inode.
+// NoReadWriteFileInode is an implementation of InodeOperations that supports
+// opening files that are not readable or writeable.
//
// +stateify savable
-type simpleInodeOperations struct {
- DeprecatedFileOperations `state:"nosave"`
+type NoReadWriteFileInode struct {
+ InodeGenericChecker `state:"nosave"`
+ InodeNoExtendedAttributes `state:"nosave"`
+ InodeNoopRelease `state:"nosave"`
+ InodeNoopWriteOut `state:"nosave"`
InodeNotDirectory `state:"nosave"`
+ InodeNotMappable `state:"nosave"`
InodeNotSocket `state:"nosave"`
- InodeNotRenameable `state:"nosave"`
- InodeNotOpenable `state:"nosave"`
- InodeNotVirtual `state:"nosave"`
InodeNotSymlink `state:"nosave"`
- InodeNoExtendedAttributes `state:"nosave"`
- NoMappable `state:"nosave"`
- NoopWriteOut `state:"nosave"`
+ InodeNotTruncatable `state:"nosave"`
+ InodeNotVirtual `state:"nosave"`
InodeSimpleAttributes
}
-// InodeSimpleAttributes implements a subset of the Inode interface. It provides
-// read-only access to attributes.
+// NewNoReadWriteFileInode returns a new NoReadWriteFileInode.
+func NewNoReadWriteFileInode(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) *NoReadWriteFileInode {
+ return &NoReadWriteFileInode{
+ InodeSimpleAttributes: NewInodeSimpleAttributes(ctx, owner, perms, typ),
+ }
+}
+
+// GetFile implements fs.InodeOperations.GetFile.
+func (*NoReadWriteFileInode) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
+ return fs.NewFile(ctx, dirent, flags, &NoReadWriteFile{}), nil
+}
+
+// InodeSimpleAttributes implements methods for updating in-memory unstable
+// attributes.
//
// +stateify savable
type InodeSimpleAttributes struct {
- // FSType is the filesystem type reported by StatFS.
+ // FSType is the immutable filesystem type that will be returned by
+ // StatFS.
FSType uint64
- // UAttr are the unstable attributes of the Inode.
- UAttr fs.UnstableAttr
+ // mu protects unstable.
+ mu sync.RWMutex `state:"nosave"`
+ Unstable fs.UnstableAttr
}
-// Release implements fs.InodeOperations.Release.
-func (i *InodeSimpleAttributes) Release(context.Context) {}
-
-// StatFS implements fs.InodeOperations.StatFS.
-func (i *InodeSimpleAttributes) StatFS(context.Context) (fs.Info, error) {
- return fs.Info{Type: i.FSType}, nil
+// NewInodeSimpleAttributes returns a new InodeSimpleAttributes.
+func NewInodeSimpleAttributes(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) InodeSimpleAttributes {
+ return InodeSimpleAttributes{
+ FSType: typ,
+ Unstable: fs.WithCurrentTime(ctx, fs.UnstableAttr{
+ Owner: owner,
+ Perms: perms,
+ }),
+ }
}
// UnstableAttr implements fs.InodeOperations.UnstableAttr.
-func (i *InodeSimpleAttributes) UnstableAttr(context.Context, *fs.Inode) (fs.UnstableAttr, error) {
- return i.UAttr, nil
+func (i *InodeSimpleAttributes) UnstableAttr(ctx context.Context, _ *fs.Inode) (fs.UnstableAttr, error) {
+ i.mu.RLock()
+ u := i.Unstable
+ i.mu.RUnlock()
+ return u, nil
}
-// Check implements fs.InodeOperations.Check.
-func (i *InodeSimpleAttributes) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
- return fs.ContextCanAccessFile(ctx, inode, p)
+// SetPermissions implements fs.InodeOperations.SetPermissions.
+func (i *InodeSimpleAttributes) SetPermissions(ctx context.Context, _ *fs.Inode, p fs.FilePermissions) bool {
+ i.mu.Lock()
+ i.Unstable.SetPermissions(ctx, p)
+ i.mu.Unlock()
+ return true
+}
+
+// SetOwner implements fs.InodeOperations.SetOwner.
+func (i *InodeSimpleAttributes) SetOwner(ctx context.Context, _ *fs.Inode, owner fs.FileOwner) error {
+ i.mu.Lock()
+ i.Unstable.SetOwner(ctx, owner)
+ i.mu.Unlock()
+ return nil
+}
+
+// SetTimestamps implements fs.InodeOperations.SetTimestamps.
+func (i *InodeSimpleAttributes) SetTimestamps(ctx context.Context, _ *fs.Inode, ts fs.TimeSpec) error {
+ i.mu.Lock()
+ i.Unstable.SetTimestamps(ctx, ts)
+ i.mu.Unlock()
+ return nil
}
// AddLink implements fs.InodeOperations.AddLink.
-func (*InodeSimpleAttributes) AddLink() {}
+func (i *InodeSimpleAttributes) AddLink() {
+ i.mu.Lock()
+ i.Unstable.Links++
+ i.mu.Unlock()
+}
// DropLink implements fs.InodeOperations.DropLink.
-func (*InodeSimpleAttributes) DropLink() {}
-
-// NotifyStatusChange implements fs.fs.InodeOperations.
-func (i *InodeSimpleAttributes) NotifyStatusChange(ctx context.Context) {
- i.UAttr.StatusChangeTime = ktime.NowFromContext(ctx)
+func (i *InodeSimpleAttributes) DropLink() {
+ i.mu.Lock()
+ i.Unstable.Links--
+ i.mu.Unlock()
}
-// SetPermissions implements fs.InodeOperations.SetPermissions.
-func (*InodeSimpleAttributes) SetPermissions(context.Context, *fs.Inode, fs.FilePermissions) bool {
- return false
+// StatFS implements fs.InodeOperations.StatFS.
+func (i *InodeSimpleAttributes) StatFS(context.Context) (fs.Info, error) {
+ if i.FSType == 0 {
+ return fs.Info{}, syserror.ENOSYS
+ }
+ return fs.Info{Type: i.FSType}, nil
}
-// SetOwner implements fs.InodeOperations.SetOwner.
-func (*InodeSimpleAttributes) SetOwner(context.Context, *fs.Inode, fs.FileOwner) error {
- return syserror.EINVAL
+// NotifyAccess updates the access time.
+func (i *InodeSimpleAttributes) NotifyAccess(ctx context.Context) {
+ i.mu.Lock()
+ i.Unstable.AccessTime = ktime.NowFromContext(ctx)
+ i.mu.Unlock()
}
-// SetTimestamps implements fs.InodeOperations.SetTimestamps.
-func (*InodeSimpleAttributes) SetTimestamps(context.Context, *fs.Inode, fs.TimeSpec) error {
- return syserror.EINVAL
+// NotifyModification updates the modification time.
+func (i *InodeSimpleAttributes) NotifyModification(ctx context.Context) {
+ i.mu.Lock()
+ i.Unstable.ModificationTime = ktime.NowFromContext(ctx)
+ i.mu.Unlock()
}
-// Truncate implements fs.InodeOperations.Truncate.
-func (*InodeSimpleAttributes) Truncate(context.Context, *fs.Inode, int64) error {
- return syserror.EINVAL
+// NotifyStatusChange updates the status change time.
+func (i *InodeSimpleAttributes) NotifyStatusChange(ctx context.Context) {
+ i.mu.Lock()
+ i.Unstable.StatusChangeTime = ktime.NowFromContext(ctx)
+ i.mu.Unlock()
}
-// InMemoryAttributes implements utilities for updating in-memory unstable
-// attributes and extended attributes. It is not thread-safe.
-//
-// Users need not initialize Xattrs to non-nil (it will be initialized
-// when the first extended attribute is set.
+// InodeSimpleExtendedAttributes implements
+// fs.InodeOperations.{Get,Set,List}xattr.
//
// +stateify savable
-type InMemoryAttributes struct {
- Unstable fs.UnstableAttr
- Xattrs map[string][]byte
+type InodeSimpleExtendedAttributes struct {
+ // mu protects xattrs.
+ mu sync.RWMutex `state:"nosave"`
+ xattrs map[string][]byte
}
-// SetPermissions updates the permissions to p.
-func (i *InMemoryAttributes) SetPermissions(ctx context.Context, p fs.FilePermissions) bool {
- i.Unstable.Perms = p
- i.Unstable.StatusChangeTime = ktime.NowFromContext(ctx)
- return true
+// Getxattr implements fs.InodeOperations.Getxattr.
+func (i *InodeSimpleExtendedAttributes) Getxattr(_ *fs.Inode, name string) ([]byte, error) {
+ i.mu.RLock()
+ value, ok := i.xattrs[name]
+ i.mu.RUnlock()
+ if !ok {
+ return nil, syserror.ENOATTR
+ }
+ return value, nil
}
-// SetOwner updates the file owner to owner.
-func (i *InMemoryAttributes) SetOwner(ctx context.Context, owner fs.FileOwner) error {
- if owner.UID.Ok() {
- i.Unstable.Owner.UID = owner.UID
- }
- if owner.GID.Ok() {
- i.Unstable.Owner.GID = owner.GID
+// Setxattr implements fs.InodeOperations.Setxattr.
+func (i *InodeSimpleExtendedAttributes) Setxattr(_ *fs.Inode, name string, value []byte) error {
+ i.mu.Lock()
+ if i.xattrs == nil {
+ i.xattrs = make(map[string][]byte)
}
+ i.xattrs[name] = value
+ i.mu.Unlock()
return nil
}
-// SetTimestamps sets the timestamps to ts.
-func (i *InMemoryAttributes) SetTimestamps(ctx context.Context, ts fs.TimeSpec) error {
- if ts.ATimeOmit && ts.MTimeOmit {
- return nil
- }
-
- now := ktime.NowFromContext(ctx)
- if !ts.ATimeOmit {
- if ts.ATimeSetSystemTime {
- i.Unstable.AccessTime = now
- } else {
- i.Unstable.AccessTime = ts.ATime
- }
- }
- if !ts.MTimeOmit {
- if ts.MTimeSetSystemTime {
- i.Unstable.ModificationTime = now
- } else {
- i.Unstable.ModificationTime = ts.MTime
- }
+// Listxattr implements fs.InodeOperations.Listxattr.
+func (i *InodeSimpleExtendedAttributes) Listxattr(_ *fs.Inode) (map[string]struct{}, error) {
+ i.mu.RLock()
+ names := make(map[string]struct{}, len(i.xattrs))
+ for name := range i.xattrs {
+ names[name] = struct{}{}
}
- i.Unstable.StatusChangeTime = now
- return nil
+ i.mu.RUnlock()
+ return names, nil
}
-// TouchAccessTime updates access time to the current time.
-func (i *InMemoryAttributes) TouchAccessTime(ctx context.Context) {
- i.Unstable.AccessTime = ktime.NowFromContext(ctx)
-}
+// staticFile is a file with static contents. It is returned by
+// InodeStaticFileGetter.GetFile.
+//
+// +stateify savable
+type staticFile struct {
+ waiter.AlwaysReady `state:"nosave"`
+ FileGenericSeek `state:"nosave"`
+ FileNoIoctl `state:"nosave"`
+ FileNoMMap `state:"nosave"`
+ FileNoopFsync `state:"nosave"`
+ FileNoopFlush `state:"nosave"`
+ FileNoopRelease `state:"nosave"`
+ FileNoopWrite `state:"nosave"`
+ FileNotDirReaddir `state:"nosave"`
-// TouchModificationTime updates modification and status change
-// time to the current time.
-func (i *InMemoryAttributes) TouchModificationTime(ctx context.Context) {
- now := ktime.NowFromContext(ctx)
- i.Unstable.ModificationTime = now
- i.Unstable.StatusChangeTime = now
+ FileStaticContentReader
}
-// TouchStatusChangeTime updates status change time to the current time.
-func (i *InMemoryAttributes) TouchStatusChangeTime(ctx context.Context) {
- i.Unstable.StatusChangeTime = ktime.NowFromContext(ctx)
-}
+// InodeNoStatFS implement StatFS by retuning ENOSYS.
+type InodeNoStatFS struct{}
-// Getxattr returns the extended attribute at name or ENOATTR if
-// it isn't set.
-func (i *InMemoryAttributes) Getxattr(name string) ([]byte, error) {
- if value, ok := i.Xattrs[name]; ok {
- return value, nil
- }
- return nil, syserror.ENOATTR
+// StatFS implements fs.InodeOperations.StatFS.
+func (InodeNoStatFS) StatFS(context.Context) (fs.Info, error) {
+ return fs.Info{}, syserror.ENOSYS
}
-// Setxattr sets the extended attribute at name to value.
-func (i *InMemoryAttributes) Setxattr(name string, value []byte) error {
- if i.Xattrs == nil {
- i.Xattrs = make(map[string][]byte)
- }
- i.Xattrs[name] = value
- return nil
+// InodeStaticFileGetter implements GetFile for a file with static contents.
+//
+// +stateify savable
+type InodeStaticFileGetter struct {
+ Contents []byte
}
-// Listxattr returns the set of all currently set extended attributes.
-func (i *InMemoryAttributes) Listxattr() (map[string]struct{}, error) {
- names := make(map[string]struct{}, len(i.Xattrs))
- for name := range i.Xattrs {
- names[name] = struct{}{}
- }
- return names, nil
+// GetFile implements fs.InodeOperations.GetFile.
+func (i *InodeStaticFileGetter) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
+ return fs.NewFile(ctx, dirent, flags, &staticFile{
+ FileStaticContentReader: NewFileStaticContentReader(i.Contents),
+ }), nil
}
-// NoMappable returns a nil memmap.Mappable.
-type NoMappable struct{}
+// InodeNotMappable returns a nil memmap.Mappable.
+type InodeNotMappable struct{}
// Mappable implements fs.InodeOperations.Mappable.
-func (NoMappable) Mappable(*fs.Inode) memmap.Mappable {
+func (InodeNotMappable) Mappable(*fs.Inode) memmap.Mappable {
return nil
}
-// NoopWriteOut is a no-op implementation of Inode.WriteOut.
-type NoopWriteOut struct{}
+// InodeNoopWriteOut is a no-op implementation of fs.InodeOperations.WriteOut.
+type InodeNoopWriteOut struct{}
// WriteOut is a no-op.
-func (NoopWriteOut) WriteOut(context.Context, *fs.Inode) error {
+func (InodeNoopWriteOut) WriteOut(context.Context, *fs.Inode) error {
return nil
}
@@ -273,6 +330,11 @@ func (InodeNotDirectory) RemoveDirectory(context.Context, *fs.Inode, string) err
return syserror.ENOTDIR
}
+// Rename implements fs.FileOperations.Rename.
+func (InodeNotDirectory) Rename(context.Context, *fs.Inode, string, *fs.Inode, string) error {
+ return syserror.EINVAL
+}
+
// InodeNotSocket can be used by Inodes that are not sockets.
type InodeNotSocket struct{}
@@ -281,7 +343,31 @@ func (InodeNotSocket) BoundEndpoint(*fs.Inode, string) transport.BoundEndpoint {
return nil
}
-// InodeNotRenameable can be used by Inodes that cannot be renamed.
+// InodeNotTruncatable can be used by Inodes that cannot be truncated.
+type InodeNotTruncatable struct{}
+
+// Truncate implements fs.InodeOperations.Truncate.
+func (InodeNotTruncatable) Truncate(context.Context, *fs.Inode, int64) error {
+ return syserror.EINVAL
+}
+
+// InodeIsDirTruncate implements fs.InodeOperations.Truncate for directories.
+type InodeIsDirTruncate struct{}
+
+// Truncate implements fs.InodeOperations.Truncate.
+func (InodeIsDirTruncate) Truncate(context.Context, *fs.Inode, int64) error {
+ return syserror.EISDIR
+}
+
+// InodeNoopTruncate implements fs.InodeOperations.Truncate as a noop.
+type InodeNoopTruncate struct{}
+
+// Truncate implements fs.InodeOperations.Truncate.
+func (InodeNoopTruncate) Truncate(context.Context, *fs.Inode, int64) error {
+ return nil
+}
+
+// InodeNotRenameable can be used by Inodes that cannot be truncated.
type InodeNotRenameable struct{}
// Rename implements fs.InodeOperations.Rename.
@@ -305,6 +391,14 @@ func (InodeNotVirtual) IsVirtual() bool {
return false
}
+// InodeVirtual can be used by Inodes that are virtual.
+type InodeVirtual struct{}
+
+// IsVirtual implements fs.InodeOperations.IsVirtual.
+func (InodeVirtual) IsVirtual() bool {
+ return true
+}
+
// InodeNotSymlink can be used by Inodes that are not symlinks.
type InodeNotSymlink struct{}
@@ -337,50 +431,17 @@ func (InodeNoExtendedAttributes) Listxattr(*fs.Inode) (map[string]struct{}, erro
return nil, syserror.EOPNOTSUPP
}
-// DeprecatedFileOperations panics if any deprecated Inode method is called.
-type DeprecatedFileOperations struct{}
+// InodeNoopRelease implements fs.InodeOperations.Release as a noop.
+type InodeNoopRelease struct{}
-// Readiness implements fs.InodeOperations.Waitable.Readiness.
-func (DeprecatedFileOperations) Readiness(waiter.EventMask) waiter.EventMask {
- panic("not implemented")
-}
-
-// EventRegister implements fs.InodeOperations.Waitable.EventRegister.
-func (DeprecatedFileOperations) EventRegister(*waiter.Entry, waiter.EventMask) {
- panic("not implemented")
-}
-
-// EventUnregister implements fs.InodeOperations.Waitable.EventUnregister.
-func (DeprecatedFileOperations) EventUnregister(*waiter.Entry) {
- panic("not implemented")
-}
-
-// DeprecatedPreadv implements fs.InodeOperations.DeprecatedPreadv.
-func (DeprecatedFileOperations) DeprecatedPreadv(context.Context, usermem.IOSequence, int64) (int64, error) {
- panic("not implemented")
-}
-
-// DeprecatedPwritev implements fs.InodeOperations.DeprecatedPwritev.
-func (DeprecatedFileOperations) DeprecatedPwritev(context.Context, usermem.IOSequence, int64) (int64, error) {
- panic("not implemented")
-}
-
-// DeprecatedReaddir implements fs.InodeOperations.DeprecatedReaddir.
-func (DeprecatedFileOperations) DeprecatedReaddir(context.Context, *fs.DirCtx, int) (int, error) {
- panic("not implemented")
-}
-
-// DeprecatedFsync implements fs.InodeOperations.DeprecatedFsync.
-func (DeprecatedFileOperations) DeprecatedFsync() error {
- panic("not implemented")
-}
+// Release implements fs.InodeOperations.Release.
+func (InodeNoopRelease) Release(context.Context) {}
-// DeprecatedFlush implements fs.InodeOperations.DeprecatedFlush.
-func (DeprecatedFileOperations) DeprecatedFlush() error {
- panic("not implemented")
-}
+// InodeGenericChecker implements fs.InodeOperations.Check with a generic
+// implementation.
+type InodeGenericChecker struct{}
-// DeprecatedMappable implements fs.InodeOperations.DeprecatedMappable.
-func (DeprecatedFileOperations) DeprecatedMappable(context.Context, *fs.Inode) (memmap.Mappable, bool) {
- panic("not implemented")
+// Check implements fs.InodeOperations.Check.
+func (InodeGenericChecker) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
+ return fs.ContextCanAccessFile(ctx, inode, p)
}
diff --git a/pkg/sentry/fs/fsutil/inode_cached_test.go b/pkg/sentry/fs/fsutil/inode_cached_test.go
index ce5201a40..9c9391511 100644
--- a/pkg/sentry/fs/fsutil/inode_cached_test.go
+++ b/pkg/sentry/fs/fsutil/inode_cached_test.go
@@ -261,15 +261,11 @@ func (noopMappingSpace) Invalidate(ar usermem.AddrRange, opts memmap.InvalidateO
}
func anonInode(ctx context.Context) *fs.Inode {
- return fs.NewInode(NewSimpleInodeOperations(InodeSimpleAttributes{
- UAttr: fs.WithCurrentTime(ctx, fs.UnstableAttr{
- Owner: fs.FileOwnerFromContext(ctx),
- Perms: fs.FilePermissions{
- User: fs.PermMask{Read: true, Write: true},
- },
- Links: 1,
- }),
- }), fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{}), fs.StableAttr{
+ return fs.NewInode(&SimpleFileInode{
+ InodeSimpleAttributes: NewInodeSimpleAttributes(ctx, fs.FileOwnerFromContext(ctx), fs.FilePermissions{
+ User: fs.PermMask{Read: true, Write: true},
+ }, 0),
+ }, fs.NewPseudoMountSource(), fs.StableAttr{
Type: fs.Anonymous,
BlockSize: usermem.PageSize,
})