diff options
author | Nicolas Lacasse <nlacasse@google.com> | 2019-01-14 20:33:29 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2019-01-14 20:34:28 -0800 |
commit | dc8450b5676d4c4ac9bcfa23cabd862e0060527d (patch) | |
tree | a4ef1ad59764f46f674b7003221ba8ae399b9e65 /pkg/sentry/fs/fsutil | |
parent | 343ebe9789087b099ea7feae19879f5c24e59bf1 (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/BUILD | 15 | ||||
-rw-r--r-- | pkg/sentry/fs/fsutil/file.go | 226 | ||||
-rw-r--r-- | pkg/sentry/fs/fsutil/fsutil.go | 2 | ||||
-rw-r--r-- | pkg/sentry/fs/fsutil/handle.go | 128 | ||||
-rw-r--r-- | pkg/sentry/fs/fsutil/handle_test.go | 227 | ||||
-rw-r--r-- | pkg/sentry/fs/fsutil/inode.go | 409 | ||||
-rw-r--r-- | pkg/sentry/fs/fsutil/inode_cached_test.go | 14 |
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, }) |