summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/mount_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fs/mount_test.go')
-rw-r--r--pkg/sentry/fs/mount_test.go216
1 files changed, 216 insertions, 0 deletions
diff --git a/pkg/sentry/fs/mount_test.go b/pkg/sentry/fs/mount_test.go
new file mode 100644
index 000000000..3a053c154
--- /dev/null
+++ b/pkg/sentry/fs/mount_test.go
@@ -0,0 +1,216 @@
+// Copyright 2018 Google Inc.
+//
+// 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 fs
+
+import (
+ "fmt"
+ "testing"
+
+ "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest"
+)
+
+// cacheReallyContains iterates through the dirent cache to determine whether
+// it contains the given dirent.
+func cacheReallyContains(cache *DirentCache, d *Dirent) bool {
+ for i := cache.list.Front(); i != nil; i = i.Next() {
+ if i == d {
+ return true
+ }
+ }
+ return false
+}
+
+// TestMountSourceOnlyCachedOnce tests that a Dirent that is mounted over only ends
+// up in a single Dirent Cache. NOTE: Having a dirent in multiple
+// caches causes major consistency issues.
+func TestMountSourceOnlyCachedOnce(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)
+ }
+ rootDirent := mm.Root()
+ defer rootDirent.DecRef()
+
+ // Get a child of the root which we will mount over. Note that the
+ // MockInodeOperations causes Walk to always succeed.
+ child, err := rootDirent.Walk(ctx, rootDirent, "child")
+ if err != nil {
+ t.Fatalf("failed to walk to child dirent: %v", err)
+ }
+ child.maybeExtendReference() // Cache.
+
+ // Ensure that the root cache contains the child.
+ if !cacheReallyContains(rootCache, child) {
+ t.Errorf("wanted rootCache to contain child dirent, but it did not")
+ }
+
+ // Create a new cache and inode, and mount it over child.
+ submountCache := NewDirentCache(100)
+ submountInode := NewMockInode(ctx, NewMockMountSource(submountCache), StableAttr{
+ Type: Directory,
+ })
+ if err := mm.Mount(ctx, child, submountInode); err != nil {
+ t.Fatalf("failed to mount over child: %v", err)
+ }
+
+ // Walk to the child again.
+ child2, err := rootDirent.Walk(ctx, rootDirent, "child")
+ if err != nil {
+ t.Fatalf("failed to walk to child dirent: %v", err)
+ }
+
+ // Should have a different Dirent than before.
+ if child == child2 {
+ t.Fatalf("expected %v not equal to %v, but they are the same", child, child2)
+ }
+
+ // Neither of the caches should no contain the child.
+ if cacheReallyContains(rootCache, child) {
+ t.Errorf("wanted rootCache not to contain child dirent, but it did")
+ }
+ if cacheReallyContains(submountCache, child) {
+ t.Errorf("wanted submountCache not to contain child dirent, but it did")
+ }
+}
+
+// Test that mounts have proper parent/child relationships.
+func TestMountSourceParentChildRelationship(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)
+ }
+ rootDirent := mm.Root()
+ defer rootDirent.DecRef()
+
+ // Add mounts at the following paths:
+ paths := []string{
+ "/foo",
+ "/foo/bar",
+ "/foo/bar/baz",
+ "/foo/qux",
+ "/waldo",
+ }
+
+ for _, p := range paths {
+ d, err := mm.FindLink(ctx, rootDirent, nil, p, 0)
+ 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)
+ }
+ }
+
+ // 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 {
+ 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)
+ }
+ foundIDs[id] = struct{}{}
+ }
+
+ // Root mount should have no parent.
+ rootMountSource := mm.root.Inode.MountSource
+ if p := rootMountSource.Parent(); p != nil {
+ 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.
+ d, err := mm.FindLink(ctx, rootDirent, nil, "/foo", 0)
+ 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 {
+ t.Error(err)
+ }
+
+ // "waldo" mount should have no submounts or children.
+ waldo, err := mm.FindLink(ctx, rootDirent, nil, "/waldo", 0)
+ 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)
+ }
+}
+
+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))
+ }
+ gotPaths := make(map[string]struct{}, len(got))
+ for _, g := range got {
+ n, _ := g.Root().FullName(root)
+ gotPaths[n] = struct{}{}
+ }
+ for _, w := range want {
+ if _, ok := gotPaths[w]; !ok {
+ return fmt.Errorf("no mount with path %q found", w)
+ }
+ }
+ return nil
+}