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 }