summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/gofer
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fs/gofer')
-rw-r--r--pkg/sentry/fs/gofer/BUILD67
-rw-r--r--pkg/sentry/fs/gofer/gofer_state_autogen.go255
-rw-r--r--pkg/sentry/fs/gofer/gofer_test.go310
3 files changed, 255 insertions, 377 deletions
diff --git a/pkg/sentry/fs/gofer/BUILD b/pkg/sentry/fs/gofer/BUILD
deleted file mode 100644
index fea135eea..000000000
--- a/pkg/sentry/fs/gofer/BUILD
+++ /dev/null
@@ -1,67 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "gofer",
- srcs = [
- "attr.go",
- "cache_policy.go",
- "context_file.go",
- "device.go",
- "fifo.go",
- "file.go",
- "file_state.go",
- "fs.go",
- "handles.go",
- "inode.go",
- "inode_state.go",
- "path.go",
- "session.go",
- "session_state.go",
- "socket.go",
- "util.go",
- ],
- visibility = ["//pkg/sentry:internal"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/context",
- "//pkg/fd",
- "//pkg/log",
- "//pkg/metric",
- "//pkg/p9",
- "//pkg/refs",
- "//pkg/safemem",
- "//pkg/secio",
- "//pkg/sentry/device",
- "//pkg/sentry/fs",
- "//pkg/sentry/fs/fdpipe",
- "//pkg/sentry/fs/fsutil",
- "//pkg/sentry/fs/host",
- "//pkg/sentry/kernel/auth",
- "//pkg/sentry/kernel/pipe",
- "//pkg/sentry/kernel/time",
- "//pkg/sentry/memmap",
- "//pkg/sentry/socket/unix/transport",
- "//pkg/sync",
- "//pkg/syserr",
- "//pkg/syserror",
- "//pkg/unet",
- "//pkg/usermem",
- "//pkg/waiter",
- ],
-)
-
-go_test(
- name = "gofer_test",
- size = "small",
- srcs = ["gofer_test.go"],
- library = ":gofer",
- deps = [
- "//pkg/context",
- "//pkg/p9",
- "//pkg/p9/p9test",
- "//pkg/sentry/contexttest",
- "//pkg/sentry/fs",
- ],
-)
diff --git a/pkg/sentry/fs/gofer/gofer_state_autogen.go b/pkg/sentry/fs/gofer/gofer_state_autogen.go
new file mode 100644
index 000000000..d2eb67d58
--- /dev/null
+++ b/pkg/sentry/fs/gofer/gofer_state_autogen.go
@@ -0,0 +1,255 @@
+// automatically generated by stateify.
+
+package gofer
+
+import (
+ "gvisor.dev/gvisor/pkg/state"
+)
+
+func (i *fifo) StateTypeName() string {
+ return "pkg/sentry/fs/gofer.fifo"
+}
+
+func (i *fifo) StateFields() []string {
+ return []string{
+ "InodeOperations",
+ "fileIops",
+ }
+}
+
+func (i *fifo) beforeSave() {}
+
+func (i *fifo) StateSave(stateSinkObject state.Sink) {
+ i.beforeSave()
+ stateSinkObject.Save(0, &i.InodeOperations)
+ stateSinkObject.Save(1, &i.fileIops)
+}
+
+func (i *fifo) afterLoad() {}
+
+func (i *fifo) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &i.InodeOperations)
+ stateSourceObject.Load(1, &i.fileIops)
+}
+
+func (f *fileOperations) StateTypeName() string {
+ return "pkg/sentry/fs/gofer.fileOperations"
+}
+
+func (f *fileOperations) StateFields() []string {
+ return []string{
+ "inodeOperations",
+ "dirCursor",
+ "flags",
+ }
+}
+
+func (f *fileOperations) beforeSave() {}
+
+func (f *fileOperations) StateSave(stateSinkObject state.Sink) {
+ f.beforeSave()
+ stateSinkObject.Save(0, &f.inodeOperations)
+ stateSinkObject.Save(1, &f.dirCursor)
+ stateSinkObject.Save(2, &f.flags)
+}
+
+func (f *fileOperations) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.LoadWait(0, &f.inodeOperations)
+ stateSourceObject.Load(1, &f.dirCursor)
+ stateSourceObject.LoadWait(2, &f.flags)
+ stateSourceObject.AfterLoad(f.afterLoad)
+}
+
+func (f *filesystem) StateTypeName() string {
+ return "pkg/sentry/fs/gofer.filesystem"
+}
+
+func (f *filesystem) StateFields() []string {
+ return []string{}
+}
+
+func (f *filesystem) beforeSave() {}
+
+func (f *filesystem) StateSave(stateSinkObject state.Sink) {
+ f.beforeSave()
+}
+
+func (f *filesystem) afterLoad() {}
+
+func (f *filesystem) StateLoad(stateSourceObject state.Source) {
+}
+
+func (i *inodeOperations) StateTypeName() string {
+ return "pkg/sentry/fs/gofer.inodeOperations"
+}
+
+func (i *inodeOperations) StateFields() []string {
+ return []string{
+ "fileState",
+ "cachingInodeOps",
+ }
+}
+
+func (i *inodeOperations) beforeSave() {}
+
+func (i *inodeOperations) StateSave(stateSinkObject state.Sink) {
+ i.beforeSave()
+ stateSinkObject.Save(0, &i.fileState)
+ stateSinkObject.Save(1, &i.cachingInodeOps)
+}
+
+func (i *inodeOperations) afterLoad() {}
+
+func (i *inodeOperations) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.LoadWait(0, &i.fileState)
+ stateSourceObject.Load(1, &i.cachingInodeOps)
+}
+
+func (i *inodeFileState) StateTypeName() string {
+ return "pkg/sentry/fs/gofer.inodeFileState"
+}
+
+func (i *inodeFileState) StateFields() []string {
+ return []string{
+ "s",
+ "sattr",
+ "loading",
+ "savedUAttr",
+ "hostMappable",
+ }
+}
+
+func (i *inodeFileState) StateSave(stateSinkObject state.Sink) {
+ i.beforeSave()
+ var loadingValue struct{} = i.saveLoading()
+ stateSinkObject.SaveValue(2, loadingValue)
+ stateSinkObject.Save(0, &i.s)
+ stateSinkObject.Save(1, &i.sattr)
+ stateSinkObject.Save(3, &i.savedUAttr)
+ stateSinkObject.Save(4, &i.hostMappable)
+}
+
+func (i *inodeFileState) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.LoadWait(0, &i.s)
+ stateSourceObject.LoadWait(1, &i.sattr)
+ stateSourceObject.Load(3, &i.savedUAttr)
+ stateSourceObject.Load(4, &i.hostMappable)
+ stateSourceObject.LoadValue(2, new(struct{}), func(y interface{}) { i.loadLoading(y.(struct{})) })
+ stateSourceObject.AfterLoad(i.afterLoad)
+}
+
+func (l *overrideInfo) StateTypeName() string {
+ return "pkg/sentry/fs/gofer.overrideInfo"
+}
+
+func (l *overrideInfo) StateFields() []string {
+ return []string{
+ "dirent",
+ "endpoint",
+ "inode",
+ }
+}
+
+func (l *overrideInfo) beforeSave() {}
+
+func (l *overrideInfo) StateSave(stateSinkObject state.Sink) {
+ l.beforeSave()
+ stateSinkObject.Save(0, &l.dirent)
+ stateSinkObject.Save(1, &l.endpoint)
+ stateSinkObject.Save(2, &l.inode)
+}
+
+func (l *overrideInfo) afterLoad() {}
+
+func (l *overrideInfo) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &l.dirent)
+ stateSourceObject.Load(1, &l.endpoint)
+ stateSourceObject.Load(2, &l.inode)
+}
+
+func (e *overrideMaps) StateTypeName() string {
+ return "pkg/sentry/fs/gofer.overrideMaps"
+}
+
+func (e *overrideMaps) StateFields() []string {
+ return []string{
+ "pathMap",
+ }
+}
+
+func (e *overrideMaps) beforeSave() {}
+
+func (e *overrideMaps) StateSave(stateSinkObject state.Sink) {
+ e.beforeSave()
+ stateSinkObject.Save(0, &e.pathMap)
+}
+
+func (e *overrideMaps) afterLoad() {}
+
+func (e *overrideMaps) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &e.pathMap)
+}
+
+func (s *session) StateTypeName() string {
+ return "pkg/sentry/fs/gofer.session"
+}
+
+func (s *session) StateFields() []string {
+ return []string{
+ "AtomicRefCount",
+ "msize",
+ "version",
+ "cachePolicy",
+ "aname",
+ "superBlockFlags",
+ "limitHostFDTranslation",
+ "overlayfsStaleRead",
+ "connID",
+ "inodeMappings",
+ "mounter",
+ "overrides",
+ }
+}
+
+func (s *session) StateSave(stateSinkObject state.Sink) {
+ s.beforeSave()
+ stateSinkObject.Save(0, &s.AtomicRefCount)
+ stateSinkObject.Save(1, &s.msize)
+ stateSinkObject.Save(2, &s.version)
+ stateSinkObject.Save(3, &s.cachePolicy)
+ stateSinkObject.Save(4, &s.aname)
+ stateSinkObject.Save(5, &s.superBlockFlags)
+ stateSinkObject.Save(6, &s.limitHostFDTranslation)
+ stateSinkObject.Save(7, &s.overlayfsStaleRead)
+ stateSinkObject.Save(8, &s.connID)
+ stateSinkObject.Save(9, &s.inodeMappings)
+ stateSinkObject.Save(10, &s.mounter)
+ stateSinkObject.Save(11, &s.overrides)
+}
+
+func (s *session) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &s.AtomicRefCount)
+ stateSourceObject.LoadWait(1, &s.msize)
+ stateSourceObject.LoadWait(2, &s.version)
+ stateSourceObject.LoadWait(3, &s.cachePolicy)
+ stateSourceObject.LoadWait(4, &s.aname)
+ stateSourceObject.LoadWait(5, &s.superBlockFlags)
+ stateSourceObject.Load(6, &s.limitHostFDTranslation)
+ stateSourceObject.Load(7, &s.overlayfsStaleRead)
+ stateSourceObject.LoadWait(8, &s.connID)
+ stateSourceObject.LoadWait(9, &s.inodeMappings)
+ stateSourceObject.LoadWait(10, &s.mounter)
+ stateSourceObject.LoadWait(11, &s.overrides)
+ stateSourceObject.AfterLoad(s.afterLoad)
+}
+
+func init() {
+ state.Register((*fifo)(nil))
+ state.Register((*fileOperations)(nil))
+ state.Register((*filesystem)(nil))
+ state.Register((*inodeOperations)(nil))
+ state.Register((*inodeFileState)(nil))
+ state.Register((*overrideInfo)(nil))
+ state.Register((*overrideMaps)(nil))
+ state.Register((*session)(nil))
+}
diff --git a/pkg/sentry/fs/gofer/gofer_test.go b/pkg/sentry/fs/gofer/gofer_test.go
deleted file mode 100644
index 326fed954..000000000
--- a/pkg/sentry/fs/gofer/gofer_test.go
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 gofer
-
-import (
- "fmt"
- "syscall"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/p9"
- "gvisor.dev/gvisor/pkg/p9/p9test"
- "gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/sentry/fs"
-)
-
-// rootTest runs a test with a p9 mock and an fs.InodeOperations created from
-// the attached root directory. The root file will be closed and client
-// disconnected, but additional files must be closed manually.
-func rootTest(t *testing.T, name string, cp cachePolicy, fn func(context.Context, *p9test.Harness, *p9test.Mock, *fs.Inode)) {
- t.Run(name, func(t *testing.T) {
- h, c := p9test.NewHarness(t)
- defer h.Finish()
-
- // Create a new root. Note that we pass an empty, but non-nil
- // map here. This allows tests to extend the root children
- // dynamically.
- root := h.NewDirectory(map[string]p9test.Generator{})(nil)
-
- // Return this as the root.
- h.Attacher.EXPECT().Attach().Return(root, nil).Times(1)
-
- // ... and open via the client.
- rootFile, err := c.Attach("/")
- if err != nil {
- t.Fatalf("unable to attach: %v", err)
- }
- defer rootFile.Close()
-
- // Wrap an a session.
- s := &session{
- mounter: fs.RootOwner,
- cachePolicy: cp,
- client: c,
- }
-
- // ... and an INode, with only the mode being explicitly valid for now.
- ctx := contexttest.Context(t)
- sattr, rootInodeOperations := newInodeOperations(ctx, s, contextFile{
- file: rootFile,
- }, root.QID, p9.AttrMaskAll(), root.Attr)
- m := fs.NewMountSource(ctx, s, &filesystem{}, fs.MountSourceFlags{})
- rootInode := fs.NewInode(ctx, rootInodeOperations, m, sattr)
-
- // Ensure that the cache is fully invalidated, so that any
- // close actions actually take place before the full harness is
- // torn down.
- defer func() {
- m.FlushDirentRefs()
-
- // Wait for all resources to be released, otherwise the
- // operations may fail after we close the rootFile.
- fs.AsyncBarrier()
- }()
-
- // Execute the test.
- fn(ctx, h, root, rootInode)
- })
-}
-
-func TestLookup(t *testing.T) {
- type lookupTest struct {
- // Name of the test.
- name string
-
- // Expected return value.
- want error
- }
-
- tests := []lookupTest{
- {
- name: "mock Walk passes (function succeeds)",
- want: nil,
- },
- {
- name: "mock Walk fails (function fails)",
- want: syscall.ENOENT,
- },
- }
-
- const file = "file" // The walked target file.
-
- for _, test := range tests {
- rootTest(t, test.name, cacheNone, func(ctx context.Context, h *p9test.Harness, rootFile *p9test.Mock, rootInode *fs.Inode) {
- // Setup the appropriate result.
- rootFile.WalkCallback = func() error {
- return test.want
- }
- if test.want == nil {
- // Set the contents of the root. We expect a
- // normal file generator for ppp above. This is
- // overriden by setting WalkErr in the mock.
- rootFile.AddChild(file, h.NewFile())
- }
-
- // Call function.
- dirent, err := rootInode.Lookup(ctx, file)
-
- // Unwrap the InodeOperations.
- var newInodeOperations fs.InodeOperations
- if dirent != nil {
- if dirent.IsNegative() {
- err = syscall.ENOENT
- } else {
- newInodeOperations = dirent.Inode.InodeOperations
- }
- }
-
- // Check return values.
- if err != test.want {
- t.Errorf("Lookup got err %v, want %v", err, test.want)
- }
- if err == nil && newInodeOperations == nil {
- t.Errorf("Lookup got non-nil err and non-nil node, wanted at least one non-nil")
- }
- })
- }
-}
-
-func TestRevalidation(t *testing.T) {
- type revalidationTest struct {
- cachePolicy cachePolicy
-
- // Whether dirent should be reloaded before any modifications.
- preModificationWantReload bool
-
- // Whether dirent should be reloaded after updating an unstable
- // attribute on the remote fs.
- postModificationWantReload bool
-
- // Whether dirent unstable attributes should be updated after
- // updating an attribute on the remote fs.
- postModificationWantUpdatedAttrs bool
-
- // Whether dirent should be reloaded after the remote has
- // removed the file.
- postRemovalWantReload bool
- }
-
- tests := []revalidationTest{
- {
- // Policy cacheNone causes Revalidate to always return
- // true.
- cachePolicy: cacheNone,
- preModificationWantReload: true,
- postModificationWantReload: true,
- postModificationWantUpdatedAttrs: true,
- postRemovalWantReload: true,
- },
- {
- // Policy cacheAll causes Revalidate to always return
- // false.
- cachePolicy: cacheAll,
- preModificationWantReload: false,
- postModificationWantReload: false,
- postModificationWantUpdatedAttrs: false,
- postRemovalWantReload: false,
- },
- {
- // Policy cacheAllWritethrough causes Revalidate to
- // always return false.
- cachePolicy: cacheAllWritethrough,
- preModificationWantReload: false,
- postModificationWantReload: false,
- postModificationWantUpdatedAttrs: false,
- postRemovalWantReload: false,
- },
- {
- // Policy cacheRemoteRevalidating causes Revalidate to
- // return update cached unstable attrs, and returns
- // true only when the remote inode itself has been
- // removed or replaced.
- cachePolicy: cacheRemoteRevalidating,
- preModificationWantReload: false,
- postModificationWantReload: false,
- postModificationWantUpdatedAttrs: true,
- postRemovalWantReload: true,
- },
- }
-
- const file = "file" // The file walked below.
-
- for _, test := range tests {
- name := fmt.Sprintf("cachepolicy=%s", test.cachePolicy)
- rootTest(t, name, test.cachePolicy, func(ctx context.Context, h *p9test.Harness, rootFile *p9test.Mock, rootInode *fs.Inode) {
- // Wrap in a dirent object.
- rootDir := fs.NewDirent(ctx, rootInode, "root")
-
- // Create a mock file a child of the root. We save when
- // this is generated, so that when the time changed, we
- // can update the original entry.
- var origMocks []*p9test.Mock
- rootFile.AddChild(file, func(parent *p9test.Mock) *p9test.Mock {
- // Regular a regular file that has a consistent
- // path number. This might be used by
- // validation so we don't change it.
- m := h.NewMock(parent, 0, p9.Attr{
- Mode: p9.ModeRegular,
- })
- origMocks = append(origMocks, m)
- return m
- })
-
- // Do the walk.
- dirent, err := rootDir.Walk(ctx, rootDir, file)
- if err != nil {
- t.Fatalf("Lookup failed: %v", err)
- }
-
- // We must release the dirent, of the test will fail
- // with a reference leak. This is tracked by p9test.
- defer dirent.DecRef(ctx)
-
- // Walk again. Depending on the cache policy, we may
- // get a new dirent.
- newDirent, err := rootDir.Walk(ctx, rootDir, file)
- if err != nil {
- t.Fatalf("Lookup failed: %v", err)
- }
- if test.preModificationWantReload && dirent == newDirent {
- t.Errorf("Lookup with cachePolicy=%s got old dirent %+v, wanted a new dirent", test.cachePolicy, dirent)
- }
- if !test.preModificationWantReload && dirent != newDirent {
- t.Errorf("Lookup with cachePolicy=%s got new dirent %+v, wanted old dirent %+v", test.cachePolicy, newDirent, dirent)
- }
- newDirent.DecRef(ctx) // See above.
-
- // Modify the underlying mocked file's modification
- // time for the next walk that occurs.
- nowSeconds := time.Now().Unix()
- rootFile.AddChild(file, func(parent *p9test.Mock) *p9test.Mock {
- // Ensure that the path is the same as above,
- // but we change only the modification time of
- // the file.
- return h.NewMock(parent, 0, p9.Attr{
- Mode: p9.ModeRegular,
- MTimeSeconds: uint64(nowSeconds),
- })
- })
-
- // We also modify the original time, so that GetAttr
- // behaves as expected for the caching case.
- for _, m := range origMocks {
- m.Attr.MTimeSeconds = uint64(nowSeconds)
- }
-
- // Walk again. Depending on the cache policy, we may
- // get a new dirent.
- newDirent, err = rootDir.Walk(ctx, rootDir, file)
- if err != nil {
- t.Fatalf("Lookup failed: %v", err)
- }
- if test.postModificationWantReload && dirent == newDirent {
- t.Errorf("Lookup with cachePolicy=%s got old dirent, wanted a new dirent", test.cachePolicy)
- }
- if !test.postModificationWantReload && dirent != newDirent {
- t.Errorf("Lookup with cachePolicy=%s got new dirent, wanted old dirent", test.cachePolicy)
- }
- uattrs, err := newDirent.Inode.UnstableAttr(ctx)
- if err != nil {
- t.Fatalf("Error getting unstable attrs: %v", err)
- }
- gotModTimeSeconds := uattrs.ModificationTime.Seconds()
- if test.postModificationWantUpdatedAttrs && gotModTimeSeconds != nowSeconds {
- t.Fatalf("Lookup with cachePolicy=%s got new modification time %v, wanted %v", test.cachePolicy, gotModTimeSeconds, nowSeconds)
- }
- newDirent.DecRef(ctx) // See above.
-
- // Remove the file from the remote fs, subsequent walks
- // should now fail to find anything.
- rootFile.RemoveChild(file)
-
- // Walk again. Depending on the cache policy, we may
- // get ENOENT.
- newDirent, err = rootDir.Walk(ctx, rootDir, file)
- if test.postRemovalWantReload && err == nil {
- t.Errorf("Lookup with cachePolicy=%s got nil error, wanted ENOENT", test.cachePolicy)
- }
- if !test.postRemovalWantReload && (err != nil || dirent != newDirent) {
- t.Errorf("Lookup with cachePolicy=%s got new dirent and error %v, wanted old dirent and nil error", test.cachePolicy, err)
- }
- if err == nil {
- newDirent.DecRef(ctx) // See above.
- }
- })
- }
-}