diff options
author | Fabricio Voznika <fvoznika@google.com> | 2019-05-23 04:15:18 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2019-05-23 04:16:10 -0700 |
commit | 9006304dfecf3670ad03c9629f9a4ac3273c386a (patch) | |
tree | 958b4c09c1118cd173675618002a2c1f32384071 /pkg/sentry/fs/mount_test.go | |
parent | 022bd0fd1091a29a41fa4c065ac35e45e3d6c576 (diff) |
Initial support for bind mounts
Separate MountSource from Mount. This is needed to allow
mounts to be shared by multiple containers within the same
pod.
PiperOrigin-RevId: 249617810
Change-Id: Id2944feb7e4194951f355cbe6d4944ae3c02e468
Diffstat (limited to 'pkg/sentry/fs/mount_test.go')
-rw-r--r-- | pkg/sentry/fs/mount_test.go | 167 |
1 files changed, 108 insertions, 59 deletions
diff --git a/pkg/sentry/fs/mount_test.go b/pkg/sentry/fs/mount_test.go index 9f7fbeff2..2e2716643 100644 --- a/pkg/sentry/fs/mount_test.go +++ b/pkg/sentry/fs/mount_test.go @@ -32,6 +32,27 @@ func cacheReallyContains(cache *DirentCache, d *Dirent) bool { return false } +func mountPathsAre(root *Dirent, got []*Mount, want ...string) error { + gotPaths := make(map[string]struct{}, len(got)) + gotStr := make([]string, len(got)) + for i, g := range got { + groot := g.Root() + name, _ := groot.FullName(root) + groot.DecRef() + gotStr[i] = name + gotPaths[name] = struct{}{} + } + if len(got) != len(want) { + return fmt.Errorf("mount paths are different, got: %q, want: %q", gotStr, want) + } + for _, w := range want { + if _, ok := gotPaths[w]; !ok { + return fmt.Errorf("no mount with path %q found", w) + } + } + return nil +} + // TestMountSourceOnlyCachedOnce tests that a Dirent that is mounted over only ends // up in a single Dirent Cache. NOTE(b/63848693): Having a dirent in multiple // caches causes major consistency issues. @@ -91,8 +112,7 @@ func TestMountSourceOnlyCachedOnce(t *testing.T) { } } -// Test that mounts have proper parent/child relationships. -func TestMountSourceParentChildRelationship(t *testing.T) { +func TestAllMountsUnder(t *testing.T) { ctx := contexttest.Context(t) rootCache := NewDirentCache(100) @@ -122,101 +142,130 @@ func TestMountSourceParentChildRelationship(t *testing.T) { if err != nil { t.Fatalf("could not find path %q in mount manager: %v", p, err) } + submountInode := NewMockInode(ctx, NewMockMountSource(nil), StableAttr{ Type: Directory, }) if err := mm.Mount(ctx, d, submountInode); err != nil { t.Fatalf("could not mount at %q: %v", p, err) } + d.DecRef() } - // mm root should contain all submounts (and does not include the root - // mount). - allMountSources := rootDirent.Inode.MountSource.Submounts() - if err := mountPathsAre(rootDirent, allMountSources, paths...); err != nil { + // mm root should contain all submounts (and does not include the root mount). + rootMnt := mm.FindMount(rootDirent) + submounts := mm.AllMountsUnder(rootMnt) + allPaths := append(paths, "/") + if err := mountPathsAre(rootDirent, submounts, allPaths...); err != nil { t.Error(err) } // Each mount should have a unique ID. foundIDs := make(map[uint64]struct{}) - for _, m := range allMountSources { - id := m.ID() - if _, ok := foundIDs[id]; ok { - t.Errorf("got multiple mounts with id %d", id) + for _, m := range submounts { + if _, ok := foundIDs[m.ID]; ok { + t.Errorf("got multiple mounts with id %d", m.ID) } - foundIDs[id] = struct{}{} + foundIDs[m.ID] = struct{}{} } // Root mount should have no parent. - rootMountSource := mm.root.Inode.MountSource - if p := rootMountSource.Parent(); p != nil { + if p := rootMnt.ParentID; p != invalidMountID { t.Errorf("root.Parent got %v wanted nil", p) } - // Root mount should have 2 children: foo and waldo. - rootChildren := rootMountSource.Children() - if err := mountPathsAre(rootDirent, rootChildren, "/foo", "/waldo"); err != nil { - t.Error(err) - } - // All root mount children should have root as parent. - for _, c := range rootChildren { - if p := c.Parent(); p != rootMountSource { - t.Errorf("root mount child got parent %+v, wanted root mount", p) - } - } - - // "foo" mount should have two children: /foo/bar, and /foo/qux. + // Check that "foo" mount has 3 children. maxTraversals = 0 d, err := mm.FindLink(ctx, rootDirent, nil, "/foo", &maxTraversals) if err != nil { t.Fatalf("could not find path %q in mount manager: %v", "/foo", err) } - fooMountSource := d.Inode.MountSource - fooMountSourceChildren := fooMountSource.Children() - if err := mountPathsAre(rootDirent, fooMountSourceChildren, "/foo/bar", "/foo/qux"); err != nil { - t.Error(err) - } - // Each child should have fooMountSource as parent. - for _, c := range fooMountSourceChildren { - if p := c.Parent(); p != fooMountSource { - t.Errorf("foo mount child got parent %+v, wanted foo mount", p) - } - } - // Submounts of foo are /foo/bar, /foo/qux, and /foo/bar/baz. - if err := mountPathsAre(rootDirent, fooMountSource.Submounts(), "/foo/bar", "/foo/qux", "/foo/bar/baz"); err != nil { + defer d.DecRef() + submounts = mm.AllMountsUnder(mm.FindMount(d)) + if err := mountPathsAre(rootDirent, submounts, "/foo", "/foo/bar", "/foo/qux", "/foo/bar/baz"); err != nil { t.Error(err) } - // "waldo" mount should have no submounts or children. + // "waldo" mount should have no children. maxTraversals = 0 waldo, err := mm.FindLink(ctx, rootDirent, nil, "/waldo", &maxTraversals) if err != nil { t.Fatalf("could not find path %q in mount manager: %v", "/waldo", err) } - waldoMountSource := waldo.Inode.MountSource - if got := len(waldoMountSource.Children()); got != 0 { - t.Errorf("waldo got %d children, wanted 0", got) - } - if got := len(waldoMountSource.Submounts()); got != 0 { - t.Errorf("waldo got %d children, wanted 0", got) + defer waldo.DecRef() + submounts = mm.AllMountsUnder(mm.FindMount(waldo)) + if err := mountPathsAre(rootDirent, submounts, "/waldo"); err != nil { + t.Error(err) } } -func mountPathsAre(root *Dirent, got []*MountSource, want ...string) error { - if len(got) != len(want) { - return fmt.Errorf("mount paths have different lengths: got %d want %d", len(got), len(want)) +func TestUnmount(t *testing.T) { + ctx := contexttest.Context(t) + + rootCache := NewDirentCache(100) + rootInode := NewMockInode(ctx, NewMockMountSource(rootCache), StableAttr{ + Type: Directory, + }) + mm, err := NewMountNamespace(ctx, rootInode) + if err != nil { + t.Fatalf("NewMountNamespace failed: %v", err) } - gotPaths := make(map[string]struct{}, len(got)) - for _, g := range got { - groot := g.Root() - n, _ := groot.FullName(root) - groot.DecRef() - gotPaths[n] = struct{}{} + rootDirent := mm.Root() + defer rootDirent.DecRef() + + // Add mounts at the following paths: + paths := []string{ + "/foo", + "/foo/bar", + "/foo/bar/goo", + "/foo/bar/goo/abc", + "/foo/abc", + "/foo/def", + "/waldo", + "/wally", } - for _, w := range want { - if _, ok := gotPaths[w]; !ok { - return fmt.Errorf("no mount with path %q found", w) + + var maxTraversals uint + for _, p := range paths { + maxTraversals = 0 + d, err := mm.FindLink(ctx, rootDirent, nil, p, &maxTraversals) + if err != nil { + t.Fatalf("could not find path %q in mount manager: %v", p, err) + } + + submountInode := NewMockInode(ctx, NewMockMountSource(nil), StableAttr{ + Type: Directory, + }) + if err := mm.Mount(ctx, d, submountInode); err != nil { + t.Fatalf("could not mount at %q: %v", p, err) + } + d.DecRef() + } + + allPaths := make([]string, len(paths)+1) + allPaths[0] = "/" + copy(allPaths[1:], paths) + + rootMnt := mm.FindMount(rootDirent) + for i := len(paths) - 1; i >= 0; i-- { + maxTraversals = 0 + p := paths[i] + d, err := mm.FindLink(ctx, rootDirent, nil, p, &maxTraversals) + if err != nil { + t.Fatalf("could not find path %q in mount manager: %v", p, err) + } + + if err := mm.Unmount(ctx, d, false); err != nil { + t.Fatalf("could not unmount at %q: %v", p, err) + } + d.DecRef() + + // Remove the path that has been unmounted and the check that the remaining + // mounts are still there. + allPaths = allPaths[:len(allPaths)-1] + submounts := mm.AllMountsUnder(rootMnt) + if err := mountPathsAre(rootDirent, submounts, allPaths...); err != nil { + t.Error(err) } } - return nil } |