github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/dirent_refs_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 "testing" 19 20 "golang.org/x/sys/unix" 21 "github.com/SagerNet/gvisor/pkg/context" 22 "github.com/SagerNet/gvisor/pkg/sentry/contexttest" 23 ) 24 25 func newMockDirInode(ctx context.Context, cache *DirentCache) *Inode { 26 return NewMockInode(ctx, NewMockMountSource(cache), StableAttr{Type: Directory}) 27 } 28 29 func TestWalkPositive(t *testing.T) { 30 // refs == 0 -> one reference. 31 // refs == -1 -> has been destroyed. 32 33 ctx := contexttest.Context(t) 34 root := NewDirent(ctx, newMockDirInode(ctx, nil), "root") 35 36 if got := root.ReadRefs(); got != 1 { 37 t.Fatalf("root has a ref count of %d, want %d", got, 1) 38 } 39 40 name := "d" 41 d, err := root.walk(ctx, root, name, false) 42 if err != nil { 43 t.Fatalf("root.walk(root, %q) got %v, want nil", name, err) 44 } 45 46 if got := root.ReadRefs(); got != 2 { 47 t.Fatalf("root has a ref count of %d, want %d", got, 2) 48 } 49 50 if got := d.ReadRefs(); got != 1 { 51 t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 1) 52 } 53 54 d.DecRef(ctx) 55 56 if got := root.ReadRefs(); got != 1 { 57 t.Fatalf("root has a ref count of %d, want %d", got, 1) 58 } 59 60 if got := d.ReadRefs(); got != 0 { 61 t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 0) 62 } 63 64 root.flush(ctx) 65 66 if got := len(root.children); got != 0 { 67 t.Fatalf("root has %d children, want %d", got, 0) 68 } 69 } 70 71 func TestWalkNegative(t *testing.T) { 72 // refs == 0 -> one reference. 73 // refs == -1 -> has been destroyed. 74 75 ctx := contexttest.Context(t) 76 root := NewDirent(ctx, NewEmptyDir(ctx, nil), "root") 77 mn := root.Inode.InodeOperations.(*mockInodeOperationsLookupNegative) 78 79 if got := root.ReadRefs(); got != 1 { 80 t.Fatalf("root has a ref count of %d, want %d", got, 1) 81 } 82 83 name := "d" 84 for i := 0; i < 100; i++ { 85 _, err := root.walk(ctx, root, name, false) 86 if err != unix.ENOENT { 87 t.Fatalf("root.walk(root, %q) got %v, want %v", name, err, unix.ENOENT) 88 } 89 } 90 91 if got := root.ReadRefs(); got != 1 { 92 t.Fatalf("root has a ref count of %d, want %d", got, 1) 93 } 94 95 if got := len(root.children); got != 1 { 96 t.Fatalf("root has %d children, want %d", got, 1) 97 } 98 99 w, ok := root.children[name] 100 if !ok { 101 t.Fatalf("root wants child at %q", name) 102 } 103 104 child := w.Get() 105 if child == nil { 106 t.Fatalf("root wants to resolve weak reference") 107 } 108 109 if !child.(*Dirent).IsNegative() { 110 t.Fatalf("root found positive child at %q, want negative", name) 111 } 112 113 if got := child.(*Dirent).ReadRefs(); got != 2 { 114 t.Fatalf("child has a ref count of %d, want %d", got, 2) 115 } 116 117 child.DecRef(ctx) 118 119 if got := child.(*Dirent).ReadRefs(); got != 1 { 120 t.Fatalf("child has a ref count of %d, want %d", got, 1) 121 } 122 123 if got := len(root.children); got != 1 { 124 t.Fatalf("root has %d children, want %d", got, 1) 125 } 126 127 root.DecRef(ctx) 128 129 if got := root.ReadRefs(); got != 0 { 130 t.Fatalf("root has a ref count of %d, want %d", got, 0) 131 } 132 133 AsyncBarrier() 134 135 if got := mn.releaseCalled; got != true { 136 t.Fatalf("root.Close was called %v, want true", got) 137 } 138 } 139 140 type mockInodeOperationsLookupNegative struct { 141 *MockInodeOperations 142 releaseCalled bool 143 } 144 145 func NewEmptyDir(ctx context.Context, cache *DirentCache) *Inode { 146 m := NewMockMountSource(cache) 147 return NewInode(ctx, &mockInodeOperationsLookupNegative{ 148 MockInodeOperations: NewMockInodeOperations(ctx), 149 }, m, StableAttr{Type: Directory}) 150 } 151 152 func (m *mockInodeOperationsLookupNegative) Lookup(ctx context.Context, dir *Inode, p string) (*Dirent, error) { 153 return NewNegativeDirent(p), nil 154 } 155 156 func (m *mockInodeOperationsLookupNegative) Release(context.Context) { 157 m.releaseCalled = true 158 } 159 160 func TestHashNegativeToPositive(t *testing.T) { 161 // refs == 0 -> one reference. 162 // refs == -1 -> has been destroyed. 163 164 ctx := contexttest.Context(t) 165 root := NewDirent(ctx, NewEmptyDir(ctx, nil), "root") 166 167 name := "d" 168 _, err := root.walk(ctx, root, name, false) 169 if err != unix.ENOENT { 170 t.Fatalf("root.walk(root, %q) got %v, want %v", name, err, unix.ENOENT) 171 } 172 173 if got := root.exists(ctx, root, name); got != false { 174 t.Fatalf("got %q exists, want does not exist", name) 175 } 176 177 f, err := root.Create(ctx, root, name, FileFlags{}, FilePermissions{}) 178 if err != nil { 179 t.Fatalf("root.Create(%q, _), got error %v, want nil", name, err) 180 } 181 d := f.Dirent 182 183 if d.IsNegative() { 184 t.Fatalf("got negative Dirent, want positive") 185 } 186 187 if got := d.ReadRefs(); got != 1 { 188 t.Fatalf("child %q has a ref count of %d, want %d", name, got, 1) 189 } 190 191 if got := root.ReadRefs(); got != 2 { 192 t.Fatalf("root has a ref count of %d, want %d", got, 2) 193 } 194 195 if got := len(root.children); got != 1 { 196 t.Fatalf("got %d children, want %d", got, 1) 197 } 198 199 w, ok := root.children[name] 200 if !ok { 201 t.Fatalf("failed to find weak reference to %q", name) 202 } 203 204 child := w.Get() 205 if child == nil { 206 t.Fatalf("want to resolve weak reference") 207 } 208 209 if child.(*Dirent) != d { 210 t.Fatalf("got foreign child") 211 } 212 } 213 214 func TestRevalidate(t *testing.T) { 215 // refs == 0 -> one reference. 216 // refs == -1 -> has been destroyed. 217 218 for _, test := range []struct { 219 // desc is the test's description. 220 desc string 221 222 // Whether to make negative Dirents. 223 makeNegative bool 224 }{ 225 { 226 desc: "Revalidate negative Dirent", 227 makeNegative: true, 228 }, 229 { 230 desc: "Revalidate positive Dirent", 231 makeNegative: false, 232 }, 233 } { 234 t.Run(test.desc, func(t *testing.T) { 235 ctx := contexttest.Context(t) 236 root := NewDirent(ctx, NewMockInodeRevalidate(ctx, test.makeNegative), "root") 237 238 name := "d" 239 d1, err := root.walk(ctx, root, name, false) 240 if !test.makeNegative && err != nil { 241 t.Fatalf("root.walk(root, %q) got %v, want nil", name, err) 242 } 243 d2, err := root.walk(ctx, root, name, false) 244 if !test.makeNegative && err != nil { 245 t.Fatalf("root.walk(root, %q) got %v, want nil", name, err) 246 } 247 if !test.makeNegative && d1 == d2 { 248 t.Fatalf("revalidating walk got same *Dirent, want different") 249 } 250 if got := len(root.children); got != 1 { 251 t.Errorf("revalidating walk got %d children, want %d", got, 1) 252 } 253 }) 254 } 255 } 256 257 type MockInodeOperationsRevalidate struct { 258 *MockInodeOperations 259 makeNegative bool 260 } 261 262 func NewMockInodeRevalidate(ctx context.Context, makeNegative bool) *Inode { 263 mn := NewMockInodeOperations(ctx) 264 m := NewMockMountSource(nil) 265 m.MountSourceOperations.(*MockMountSourceOps).revalidate = true 266 return NewInode(ctx, &MockInodeOperationsRevalidate{MockInodeOperations: mn, makeNegative: makeNegative}, m, StableAttr{Type: Directory}) 267 } 268 269 func (m *MockInodeOperationsRevalidate) Lookup(ctx context.Context, dir *Inode, p string) (*Dirent, error) { 270 if !m.makeNegative { 271 return m.MockInodeOperations.Lookup(ctx, dir, p) 272 } 273 return NewNegativeDirent(p), nil 274 } 275 276 func TestCreateExtraRefs(t *testing.T) { 277 // refs == 0 -> one reference. 278 // refs == -1 -> has been destroyed. 279 280 ctx := contexttest.Context(t) 281 for _, test := range []struct { 282 // desc is the test's description. 283 desc string 284 285 // root is the Dirent to create from. 286 root *Dirent 287 288 // expected references on walked Dirent. 289 refs int64 290 }{ 291 { 292 desc: "Create caching", 293 root: NewDirent(ctx, NewEmptyDir(ctx, NewDirentCache(1)), "root"), 294 refs: 2, 295 }, 296 { 297 desc: "Create not caching", 298 root: NewDirent(ctx, NewEmptyDir(ctx, nil), "root"), 299 refs: 1, 300 }, 301 } { 302 t.Run(test.desc, func(t *testing.T) { 303 name := "d" 304 f, err := test.root.Create(ctx, test.root, name, FileFlags{}, FilePermissions{}) 305 if err != nil { 306 t.Fatalf("root.Create(root, %q) failed: %v", name, err) 307 } 308 d := f.Dirent 309 310 if got := d.ReadRefs(); got != test.refs { 311 t.Errorf("dirent has a ref count of %d, want %d", got, test.refs) 312 } 313 }) 314 } 315 } 316 317 func TestRemoveExtraRefs(t *testing.T) { 318 // refs == 0 -> one reference. 319 // refs == -1 -> has been destroyed. 320 321 ctx := contexttest.Context(t) 322 for _, test := range []struct { 323 // desc is the test's description. 324 desc string 325 326 // root is the Dirent to make and remove from. 327 root *Dirent 328 }{ 329 { 330 desc: "Remove caching", 331 root: NewDirent(ctx, NewEmptyDir(ctx, NewDirentCache(1)), "root"), 332 }, 333 { 334 desc: "Remove not caching", 335 root: NewDirent(ctx, NewEmptyDir(ctx, nil), "root"), 336 }, 337 } { 338 t.Run(test.desc, func(t *testing.T) { 339 name := "d" 340 f, err := test.root.Create(ctx, test.root, name, FileFlags{}, FilePermissions{}) 341 if err != nil { 342 t.Fatalf("root.Create(%q, _) failed: %v", name, err) 343 } 344 d := f.Dirent 345 346 if err := test.root.Remove(contexttest.Context(t), test.root, name, false /* dirPath */); err != nil { 347 t.Fatalf("root.Remove(root, %q) failed: %v", name, err) 348 } 349 350 if got := d.ReadRefs(); got != 1 { 351 t.Fatalf("dirent has a ref count of %d, want %d", got, 1) 352 } 353 354 d.DecRef(ctx) 355 356 test.root.flush(ctx) 357 358 if got := len(test.root.children); got != 0 { 359 t.Errorf("root has %d children, want %d", got, 0) 360 } 361 }) 362 } 363 } 364 365 func TestRenameExtraRefs(t *testing.T) { 366 // refs == 0 -> one reference. 367 // refs == -1 -> has been destroyed. 368 369 for _, test := range []struct { 370 // desc is the test's description. 371 desc string 372 373 // cache of extra Dirent references, may be nil. 374 cache *DirentCache 375 }{ 376 { 377 desc: "Rename no caching", 378 cache: nil, 379 }, 380 { 381 desc: "Rename caching", 382 cache: NewDirentCache(5), 383 }, 384 } { 385 t.Run(test.desc, func(t *testing.T) { 386 ctx := contexttest.Context(t) 387 388 dirAttr := StableAttr{Type: Directory} 389 390 oldParent := NewDirent(ctx, NewMockInode(ctx, NewMockMountSource(test.cache), dirAttr), "old_parent") 391 newParent := NewDirent(ctx, NewMockInode(ctx, NewMockMountSource(test.cache), dirAttr), "new_parent") 392 393 renamed, err := oldParent.Walk(ctx, oldParent, "old_child") 394 if err != nil { 395 t.Fatalf("Walk(oldParent, %q) got error %v, want nil", "old_child", err) 396 } 397 replaced, err := newParent.Walk(ctx, oldParent, "new_child") 398 if err != nil { 399 t.Fatalf("Walk(newParent, %q) got error %v, want nil", "new_child", err) 400 } 401 402 if err := Rename(contexttest.RootContext(t), oldParent /*root */, oldParent, "old_child", newParent, "new_child"); err != nil { 403 t.Fatalf("Rename got error %v, want nil", err) 404 } 405 406 oldParent.flush(ctx) 407 newParent.flush(ctx) 408 409 // Expect to have only active references. 410 if got := renamed.ReadRefs(); got != 1 { 411 t.Errorf("renamed has ref count %d, want only active references %d", got, 1) 412 } 413 if got := replaced.ReadRefs(); got != 1 { 414 t.Errorf("replaced has ref count %d, want only active references %d", got, 1) 415 } 416 }) 417 } 418 }