github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/mount_test.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fs
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/context"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/contexttest"
    23  )
    24  
    25  // cacheReallyContains iterates through the dirent cache to determine whether
    26  // it contains the given dirent.
    27  func cacheReallyContains(cache *DirentCache, d *Dirent) bool {
    28  	for i := cache.list.Front(); i != nil; i = i.Next() {
    29  		if i == d {
    30  			return true
    31  		}
    32  	}
    33  	return false
    34  }
    35  
    36  func mountPathsAre(ctx context.Context, root *Dirent, got []*Mount, want ...string) error {
    37  	gotPaths := make(map[string]struct{}, len(got))
    38  	gotStr := make([]string, len(got))
    39  	for i, g := range got {
    40  		if groot := g.Root(); groot != nil {
    41  			name, _ := groot.FullName(root)
    42  			groot.DecRef(ctx)
    43  			gotStr[i] = name
    44  			gotPaths[name] = struct{}{}
    45  		}
    46  	}
    47  	if len(got) != len(want) {
    48  		return fmt.Errorf("mount paths are different, got: %q, want: %q", gotStr, want)
    49  	}
    50  	for _, w := range want {
    51  		if _, ok := gotPaths[w]; !ok {
    52  			return fmt.Errorf("no mount with path %q found", w)
    53  		}
    54  	}
    55  	return nil
    56  }
    57  
    58  // TestMountSourceOnlyCachedOnce tests that a Dirent that is mounted over only ends
    59  // up in a single Dirent Cache. NOTE(b/63848693): Having a dirent in multiple
    60  // caches causes major consistency issues.
    61  func TestMountSourceOnlyCachedOnce(t *testing.T) {
    62  	ctx := contexttest.Context(t)
    63  
    64  	rootCache := NewDirentCache(100)
    65  	rootInode := NewMockInode(ctx, NewMockMountSource(rootCache), StableAttr{
    66  		Type: Directory,
    67  	})
    68  	mm, err := NewMountNamespace(ctx, rootInode)
    69  	if err != nil {
    70  		t.Fatalf("NewMountNamespace failed: %v", err)
    71  	}
    72  	rootDirent := mm.Root()
    73  	defer rootDirent.DecRef(ctx)
    74  
    75  	// Get a child of the root which we will mount over.  Note that the
    76  	// MockInodeOperations causes Walk to always succeed.
    77  	child, err := rootDirent.Walk(ctx, rootDirent, "child")
    78  	if err != nil {
    79  		t.Fatalf("failed to walk to child dirent: %v", err)
    80  	}
    81  	child.maybeExtendReference() // Cache.
    82  
    83  	// Ensure that the root cache contains the child.
    84  	if !cacheReallyContains(rootCache, child) {
    85  		t.Errorf("wanted rootCache to contain child dirent, but it did not")
    86  	}
    87  
    88  	// Create a new cache and inode, and mount it over child.
    89  	submountCache := NewDirentCache(100)
    90  	submountInode := NewMockInode(ctx, NewMockMountSource(submountCache), StableAttr{
    91  		Type: Directory,
    92  	})
    93  	if err := mm.Mount(ctx, child, submountInode); err != nil {
    94  		t.Fatalf("failed to mount over child: %v", err)
    95  	}
    96  
    97  	// Walk to the child again.
    98  	child2, err := rootDirent.Walk(ctx, rootDirent, "child")
    99  	if err != nil {
   100  		t.Fatalf("failed to walk to child dirent: %v", err)
   101  	}
   102  
   103  	// Should have a different Dirent than before.
   104  	if child == child2 {
   105  		t.Fatalf("expected %v not equal to %v, but they are the same", child, child2)
   106  	}
   107  
   108  	// Neither of the caches should no contain the child.
   109  	if cacheReallyContains(rootCache, child) {
   110  		t.Errorf("wanted rootCache not to contain child dirent, but it did")
   111  	}
   112  	if cacheReallyContains(submountCache, child) {
   113  		t.Errorf("wanted submountCache not to contain child dirent, but it did")
   114  	}
   115  }
   116  
   117  func TestAllMountsUnder(t *testing.T) {
   118  	ctx := contexttest.Context(t)
   119  
   120  	rootCache := NewDirentCache(100)
   121  	rootInode := NewMockInode(ctx, NewMockMountSource(rootCache), StableAttr{
   122  		Type: Directory,
   123  	})
   124  	mm, err := NewMountNamespace(ctx, rootInode)
   125  	if err != nil {
   126  		t.Fatalf("NewMountNamespace failed: %v", err)
   127  	}
   128  	rootDirent := mm.Root()
   129  	defer rootDirent.DecRef(ctx)
   130  
   131  	// Add mounts at the following paths:
   132  	paths := []string{
   133  		"/foo",
   134  		"/foo/bar",
   135  		"/foo/bar/baz",
   136  		"/foo/qux",
   137  		"/waldo",
   138  	}
   139  
   140  	var maxTraversals uint
   141  	for _, p := range paths {
   142  		maxTraversals = 0
   143  		d, err := mm.FindLink(ctx, rootDirent, nil, p, &maxTraversals)
   144  		if err != nil {
   145  			t.Fatalf("could not find path %q in mount manager: %v", p, err)
   146  		}
   147  
   148  		submountInode := NewMockInode(ctx, NewMockMountSource(nil), StableAttr{
   149  			Type: Directory,
   150  		})
   151  		if err := mm.Mount(ctx, d, submountInode); err != nil {
   152  			t.Fatalf("could not mount at %q: %v", p, err)
   153  		}
   154  		d.DecRef(ctx)
   155  	}
   156  
   157  	// mm root should contain all submounts (and does not include the root mount).
   158  	rootMnt := mm.FindMount(rootDirent)
   159  	submounts := mm.AllMountsUnder(rootMnt)
   160  	allPaths := append(paths, "/")
   161  	if err := mountPathsAre(ctx, rootDirent, submounts, allPaths...); err != nil {
   162  		t.Error(err)
   163  	}
   164  
   165  	// Each mount should have a unique ID.
   166  	foundIDs := make(map[uint64]struct{})
   167  	for _, m := range submounts {
   168  		if _, ok := foundIDs[m.ID]; ok {
   169  			t.Errorf("got multiple mounts with id %d", m.ID)
   170  		}
   171  		foundIDs[m.ID] = struct{}{}
   172  	}
   173  
   174  	// Root mount should have no parent.
   175  	if p := rootMnt.ParentID; p != invalidMountID {
   176  		t.Errorf("root.Parent got %v wanted nil", p)
   177  	}
   178  
   179  	// Check that "foo" mount has 3 children.
   180  	maxTraversals = 0
   181  	d, err := mm.FindLink(ctx, rootDirent, nil, "/foo", &maxTraversals)
   182  	if err != nil {
   183  		t.Fatalf("could not find path %q in mount manager: %v", "/foo", err)
   184  	}
   185  	defer d.DecRef(ctx)
   186  	submounts = mm.AllMountsUnder(mm.FindMount(d))
   187  	if err := mountPathsAre(ctx, rootDirent, submounts, "/foo", "/foo/bar", "/foo/qux", "/foo/bar/baz"); err != nil {
   188  		t.Error(err)
   189  	}
   190  
   191  	// "waldo" mount should have no children.
   192  	maxTraversals = 0
   193  	waldo, err := mm.FindLink(ctx, rootDirent, nil, "/waldo", &maxTraversals)
   194  	if err != nil {
   195  		t.Fatalf("could not find path %q in mount manager: %v", "/waldo", err)
   196  	}
   197  	defer waldo.DecRef(ctx)
   198  	submounts = mm.AllMountsUnder(mm.FindMount(waldo))
   199  	if err := mountPathsAre(ctx, rootDirent, submounts, "/waldo"); err != nil {
   200  		t.Error(err)
   201  	}
   202  }
   203  
   204  func TestUnmount(t *testing.T) {
   205  	ctx := contexttest.Context(t)
   206  
   207  	rootCache := NewDirentCache(100)
   208  	rootInode := NewMockInode(ctx, NewMockMountSource(rootCache), StableAttr{
   209  		Type: Directory,
   210  	})
   211  	mm, err := NewMountNamespace(ctx, rootInode)
   212  	if err != nil {
   213  		t.Fatalf("NewMountNamespace failed: %v", err)
   214  	}
   215  	rootDirent := mm.Root()
   216  	defer rootDirent.DecRef(ctx)
   217  
   218  	// Add mounts at the following paths:
   219  	paths := []string{
   220  		"/foo",
   221  		"/foo/bar",
   222  		"/foo/bar/goo",
   223  		"/foo/bar/goo/abc",
   224  		"/foo/abc",
   225  		"/foo/def",
   226  		"/waldo",
   227  		"/wally",
   228  	}
   229  
   230  	var maxTraversals uint
   231  	for _, p := range paths {
   232  		maxTraversals = 0
   233  		d, err := mm.FindLink(ctx, rootDirent, nil, p, &maxTraversals)
   234  		if err != nil {
   235  			t.Fatalf("could not find path %q in mount manager: %v", p, err)
   236  		}
   237  
   238  		submountInode := NewMockInode(ctx, NewMockMountSource(nil), StableAttr{
   239  			Type: Directory,
   240  		})
   241  		if err := mm.Mount(ctx, d, submountInode); err != nil {
   242  			t.Fatalf("could not mount at %q: %v", p, err)
   243  		}
   244  		d.DecRef(ctx)
   245  	}
   246  
   247  	allPaths := make([]string, len(paths)+1)
   248  	allPaths[0] = "/"
   249  	copy(allPaths[1:], paths)
   250  
   251  	rootMnt := mm.FindMount(rootDirent)
   252  	for i := len(paths) - 1; i >= 0; i-- {
   253  		maxTraversals = 0
   254  		p := paths[i]
   255  		d, err := mm.FindLink(ctx, rootDirent, nil, p, &maxTraversals)
   256  		if err != nil {
   257  			t.Fatalf("could not find path %q in mount manager: %v", p, err)
   258  		}
   259  
   260  		if err := mm.Unmount(ctx, d, false); err != nil {
   261  			t.Fatalf("could not unmount at %q: %v", p, err)
   262  		}
   263  		d.DecRef(ctx)
   264  
   265  		// Remove the path that has been unmounted and the check that the remaining
   266  		// mounts are still there.
   267  		allPaths = allPaths[:len(allPaths)-1]
   268  		submounts := mm.AllMountsUnder(rootMnt)
   269  		if err := mountPathsAre(ctx, rootDirent, submounts, allPaths...); err != nil {
   270  			t.Error(err)
   271  		}
   272  	}
   273  }