summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/mount_test.go
diff options
context:
space:
mode:
authorFabricio Voznika <fvoznika@google.com>2019-05-23 04:15:18 -0700
committerShentubot <shentubot@google.com>2019-05-23 04:16:10 -0700
commit9006304dfecf3670ad03c9629f9a4ac3273c386a (patch)
tree958b4c09c1118cd173675618002a2c1f32384071 /pkg/sentry/fs/mount_test.go
parent022bd0fd1091a29a41fa4c065ac35e45e3d6c576 (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.go167
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
}