gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/p9/p9test/client_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 p9test 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "math/rand" 22 "os" 23 "slices" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/golang/mock/gomock" 29 "golang.org/x/sys/unix" 30 "gvisor.dev/gvisor/pkg/fd" 31 "gvisor.dev/gvisor/pkg/p9" 32 "gvisor.dev/gvisor/pkg/sync" 33 ) 34 35 func TestPanic(t *testing.T) { 36 h, c := NewHarness(t) 37 defer h.Finish() 38 39 // Create a new root. 40 d := h.NewDirectory(nil)(nil) 41 defer d.Close() // Needed manually. 42 h.Attacher.EXPECT().Attach().Return(d, nil).Do(func() { 43 // Panic here, and ensure that we get back EFAULT. 44 panic("handler") 45 }) 46 47 // Attach to the client. 48 if _, err := c.Attach("/"); err != unix.EREMOTEIO { 49 t.Fatalf("got attach err %v, want EREMOTEIO", err) 50 } 51 } 52 53 func TestAttachNoLeak(t *testing.T) { 54 h, c := NewHarness(t) 55 defer h.Finish() 56 57 // Create a new root. 58 d := h.NewDirectory(nil)(nil) 59 h.Attacher.EXPECT().Attach().Return(d, nil).Times(1) 60 61 // Attach to the client. 62 f, err := c.Attach("/") 63 if err != nil { 64 t.Fatalf("got attach err %v, want nil", err) 65 } 66 67 // Don't close the file. This should be closed automatically when the 68 // client disconnects. The mock asserts that everything is closed 69 // exactly once. This statement just removes the unused variable error. 70 _ = f 71 } 72 73 func TestBadAttach(t *testing.T) { 74 h, c := NewHarness(t) 75 defer h.Finish() 76 77 // Return an error on attach. 78 h.Attacher.EXPECT().Attach().Return(nil, unix.EINVAL).Times(1) 79 80 // Attach to the client. 81 if _, err := c.Attach("/"); err != unix.EINVAL { 82 t.Fatalf("got attach err %v, want unix.EINVAL", err) 83 } 84 } 85 86 func TestWalkAttach(t *testing.T) { 87 h, c := NewHarness(t) 88 defer h.Finish() 89 90 // Create a new root. 91 d := h.NewDirectory(map[string]Generator{ 92 "a": h.NewDirectory(map[string]Generator{ 93 "b": h.NewFile(), 94 }), 95 })(nil) 96 h.Attacher.EXPECT().Attach().Return(d, nil).Times(1) 97 98 // Attach to the client as a non-root, and ensure that the walk above 99 // occurs as expected. We should get back b, and all references should 100 // be dropped when the file is closed. 101 f, err := c.Attach("/a/b") 102 if err != nil { 103 t.Fatalf("got attach err %v, want nil", err) 104 } 105 defer f.Close() 106 107 // Check that's a regular file. 108 if _, _, attr, err := f.GetAttr(p9.AttrMaskAll()); err != nil { 109 t.Errorf("got err %v, want nil", err) 110 } else if !attr.Mode.IsRegular() { 111 t.Errorf("got mode %v, want regular file", err) 112 } 113 } 114 115 // newTypeMap returns a new type map dictionary. 116 func newTypeMap(h *Harness) map[string]Generator { 117 return map[string]Generator{ 118 "directory": h.NewDirectory(map[string]Generator{}), 119 "file": h.NewFile(), 120 "symlink": h.NewSymlink(), 121 "block-device": h.NewBlockDevice(), 122 "character-device": h.NewCharacterDevice(), 123 "named-pipe": h.NewNamedPipe(), 124 "socket": h.NewSocket(), 125 } 126 } 127 128 // newRoot returns a new root filesystem. 129 // 130 // This is set up in a deterministic way for testing most operations. 131 // 132 // The represented file system looks like: 133 // - file 134 // - symlink 135 // - directory 136 // 137 // ... 138 // + one 139 // - file 140 // - symlink 141 // - directory 142 // ... 143 // - two 144 // - file 145 // - symlink 146 // - directory 147 // ... 148 // 149 // + three 150 // - file 151 // - symlink 152 // - directory 153 // ... 154 func newRoot(h *Harness, c *p9.Client) (*Mock, p9.File) { 155 root := newTypeMap(h) 156 one := newTypeMap(h) 157 two := newTypeMap(h) 158 three := newTypeMap(h) 159 one["two"] = h.NewDirectory(two) // Will be nested in one. 160 root["one"] = h.NewDirectory(one) // Top level. 161 root["three"] = h.NewDirectory(three) // Alternate top-level. 162 163 // Create a new root. 164 rootBackend := h.NewDirectory(root)(nil) 165 h.Attacher.EXPECT().Attach().Return(rootBackend, nil) 166 167 // Attach to the client. 168 r, err := c.Attach("/") 169 if err != nil { 170 h.t.Fatalf("got attach err %v, want nil", err) 171 } 172 173 return rootBackend, r 174 } 175 176 func allInvalidNames(from string) []string { 177 return []string{ 178 from + "/other", 179 from + "/..", 180 from + "/.", 181 from + "/", 182 "other/" + from, 183 "/" + from, 184 "./" + from, 185 "../" + from, 186 ".", 187 "..", 188 "/", 189 "", 190 } 191 } 192 193 func TestWalkInvalid(t *testing.T) { 194 h, c := NewHarness(t) 195 defer h.Finish() 196 197 _, root := newRoot(h, c) 198 defer root.Close() 199 200 // Run relevant tests. 201 for name := range newTypeMap(h) { 202 // These are all the various ways that one might attempt to 203 // construct compound paths. They should all be rejected, as 204 // any compound that contains a / is not allowed, as well as 205 // the singular paths of '.' and '..'. 206 if _, _, err := root.Walk([]string{".", name}); err != unix.EINVAL { 207 t.Errorf("Walk through . %s wanted EINVAL, got %v", name, err) 208 } 209 if _, _, err := root.Walk([]string{"..", name}); err != unix.EINVAL { 210 t.Errorf("Walk through . %s wanted EINVAL, got %v", name, err) 211 } 212 if _, _, err := root.Walk([]string{name, "."}); err != unix.EINVAL { 213 t.Errorf("Walk through %s . wanted EINVAL, got %v", name, err) 214 } 215 if _, _, err := root.Walk([]string{name, ".."}); err != unix.EINVAL { 216 t.Errorf("Walk through %s .. wanted EINVAL, got %v", name, err) 217 } 218 for _, invalidName := range allInvalidNames(name) { 219 if _, _, err := root.Walk([]string{invalidName}); err != unix.EINVAL { 220 t.Errorf("Walk through %s wanted EINVAL, got %v", invalidName, err) 221 } 222 } 223 wantErr := unix.EINVAL 224 if name == "directory" { 225 // We can attempt a walk through a directory. However, 226 // we should never see a file named "other", so we 227 // expect this to return ENOENT. 228 wantErr = unix.ENOENT 229 } 230 if _, _, err := root.Walk([]string{name, "other"}); err != wantErr { 231 t.Errorf("Walk through %s/other wanted %v, got %v", name, wantErr, err) 232 } 233 234 // Do a successful walk. 235 _, f, err := root.Walk([]string{name}) 236 if err != nil { 237 t.Errorf("Walk to %s wanted nil, got %v", name, err) 238 } 239 defer f.Close() 240 local := h.Pop(f) 241 242 // Check that the file matches. 243 _, localMask, localAttr, localErr := local.GetAttr(p9.AttrMaskAll()) 244 if _, mask, attr, err := f.GetAttr(p9.AttrMaskAll()); mask != localMask || attr != localAttr || err != localErr { 245 t.Errorf("GetAttr got (%v, %v, %v), wanted (%v, %v, %v)", 246 mask, attr, err, localMask, localAttr, localErr) 247 } 248 249 // Ensure we can't walk backwards. 250 if _, _, err := f.Walk([]string{"."}); err != unix.EINVAL { 251 t.Errorf("Walk through %s/. wanted EINVAL, got %v", name, err) 252 } 253 if _, _, err := f.Walk([]string{".."}); err != unix.EINVAL { 254 t.Errorf("Walk through %s/.. wanted EINVAL, got %v", name, err) 255 } 256 } 257 } 258 259 // fileGenerator is a function to generate files via walk or create. 260 // 261 // Examples are: 262 // - walkHelper 263 // - walkAndOpenHelper 264 // - createHelper 265 type fileGenerator func(*Harness, string, p9.File) (*Mock, *Mock, p9.File) 266 267 // walkHelper walks to the given file. 268 // 269 // The backends of the parent and walked file are returned, as well as the 270 // walked client file. 271 func walkHelper(h *Harness, name string, dir p9.File) (parentBackend *Mock, walkedBackend *Mock, walked p9.File) { 272 _, parent, err := dir.Walk(nil) 273 if err != nil { 274 h.t.Fatalf("Walk(nil) got err %v, want nil", err) 275 } 276 defer parent.Close() 277 parentBackend = h.Pop(parent) 278 279 _, walked, err = parent.Walk([]string{name}) 280 if err != nil { 281 h.t.Fatalf("Walk(%s) got err %v, want nil", name, err) 282 } 283 walkedBackend = h.Pop(walked) 284 285 return parentBackend, walkedBackend, walked 286 } 287 288 // walkAndOpenHelper additionally opens the walked file, if possible. 289 func walkAndOpenHelper(h *Harness, name string, dir p9.File) (*Mock, *Mock, p9.File) { 290 parentBackend, walkedBackend, walked := walkHelper(h, name, dir) 291 if p9.CanOpen(walkedBackend.Attr.Mode) { 292 // Open for all file types that we can. We stick to a read-only 293 // open here because directories may not be opened otherwise. 294 walkedBackend.EXPECT().Open(p9.ReadOnly).Times(1) 295 if _, _, _, err := walked.Open(p9.ReadOnly); err != nil { 296 h.t.Errorf("got open err %v, want nil", err) 297 } 298 } else { 299 // ... or assert an error for others. 300 if _, _, _, err := walked.Open(p9.ReadOnly); err != unix.EINVAL { 301 h.t.Errorf("got open err %v, want EINVAL", err) 302 } 303 } 304 return parentBackend, walkedBackend, walked 305 } 306 307 // createHelper creates the given file and returns the parent directory, 308 // created file and client file, which must be closed when done. 309 func createHelper(h *Harness, name string, dir p9.File) (*Mock, *Mock, p9.File) { 310 // Clone the directory first, since Create replaces the existing file. 311 // We change the type after calling create. 312 _, dirThenFile, err := dir.Walk(nil) 313 if err != nil { 314 h.t.Fatalf("got walk err %v, want nil", err) 315 } 316 317 // Create a new server-side file. On the server-side, the a new file is 318 // returned from a create call. The client will reuse the same file, 319 // but we still expect the normal chain of closes. This complicates 320 // things a bit because the "parent" will always chain to the cloned 321 // dir above. 322 dirBackend := h.Pop(dirThenFile) // New backend directory. 323 newFile := h.NewFile()(dirBackend) // New file with backend parent. 324 dirBackend.EXPECT().Create(name, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, newFile, newFile.QID, uint32(0), nil) 325 326 // Create via the client. 327 _, dirThenFile, _, _, err = dirThenFile.Create(name, p9.ReadOnly, 0, 0, 0) 328 if err != nil { 329 h.t.Fatalf("got create err %v, want nil", err) 330 } 331 332 // Ensure subsequent walks succeed. 333 dirBackend.AddChild(name, h.NewFile()) 334 return dirBackend, newFile, dirThenFile 335 } 336 337 // deprecatedRemover allows us to access the deprecated Remove operation within 338 // the p9.File client object. 339 type deprecatedRemover interface { 340 Remove() error 341 } 342 343 // checkDeleted asserts that relevant methods fail for an unlinked file. 344 // 345 // This function will close the file at the end. 346 func checkDeleted(h *Harness, file p9.File) { 347 defer file.Close() // See doc. 348 349 if _, _, _, err := file.Open(p9.ReadOnly); err != unix.EINVAL { 350 h.t.Errorf("open while deleted, got %v, want EINVAL", err) 351 } 352 if _, _, _, _, err := file.Create("created", p9.ReadOnly, 0, 0, 0); err != unix.EINVAL { 353 h.t.Errorf("create while deleted, got %v, want EINVAL", err) 354 } 355 if _, err := file.Symlink("old", "new", 0, 0); err != unix.EINVAL { 356 h.t.Errorf("symlink while deleted, got %v, want EINVAL", err) 357 } 358 // N.B. This link is technically invalid, but if a call to link is 359 // actually made in the backend then the mock will panic. 360 if err := file.Link(file, "new"); err != unix.EINVAL { 361 h.t.Errorf("link while deleted, got %v, want EINVAL", err) 362 } 363 if err := file.RenameAt("src", file, "dst"); err != unix.EINVAL { 364 h.t.Errorf("renameAt while deleted, got %v, want EINVAL", err) 365 } 366 if err := file.UnlinkAt("file", 0); err != unix.EINVAL { 367 h.t.Errorf("unlinkAt while deleted, got %v, want EINVAL", err) 368 } 369 if err := file.Rename(file, "dst"); err != unix.EINVAL { 370 h.t.Errorf("rename while deleted, got %v, want EINVAL", err) 371 } 372 if _, err := file.Readlink(); err != unix.EINVAL { 373 h.t.Errorf("readlink while deleted, got %v, want EINVAL", err) 374 } 375 if _, err := file.Mkdir("dir", p9.ModeDirectory, 0, 0); err != unix.EINVAL { 376 h.t.Errorf("mkdir while deleted, got %v, want EINVAL", err) 377 } 378 if _, err := file.Mknod("dir", p9.ModeDirectory, 0, 0, 0, 0); err != unix.EINVAL { 379 h.t.Errorf("mknod while deleted, got %v, want EINVAL", err) 380 } 381 if _, err := file.Readdir(0, 1); err != unix.EINVAL { 382 h.t.Errorf("readdir while deleted, got %v, want EINVAL", err) 383 } 384 if _, err := file.Connect(p9.SocketType(0)); err != unix.EINVAL { 385 h.t.Errorf("connect while deleted, got %v, want EINVAL", err) 386 } 387 388 // The remove method is technically deprecated, but we want to ensure 389 // that it still checks for deleted appropriately. We must first clone 390 // the file because remove is equivalent to close. 391 _, newFile, err := file.Walk(nil) 392 if err == unix.EBUSY { 393 // We can't walk from here because this reference is open 394 // already. Okay, we will also have unopened cases through 395 // TestUnlink, just skip the remove operation for now. 396 return 397 } else if err != nil { 398 h.t.Fatalf("clone failed, got %v, want nil", err) 399 } 400 if err := newFile.(deprecatedRemover).Remove(); err != unix.EINVAL { 401 h.t.Errorf("remove while deleted, got %v, want EINVAL", err) 402 } 403 } 404 405 // deleter is a function to remove a file. 406 type deleter func(parent p9.File, name string) error 407 408 // unlinkAt is a deleter. 409 func unlinkAt(parent p9.File, name string) error { 410 // Call unlink. Note that a filesystem may normally impose additional 411 // constraints on unlinkat success, such as ensuring that a directory is 412 // empty, requiring AT_REMOVEDIR in flags to remove a directory, etc. 413 // None of that is required internally (entire trees can be marked 414 // deleted when this operation succeeds), so the mock will succeed. 415 return parent.UnlinkAt(name, 0) 416 } 417 418 // remove is a deleter. 419 func remove(parent p9.File, name string) error { 420 // See notes above re: remove. 421 _, newFile, err := parent.Walk([]string{name}) 422 if err != nil { 423 // Should not be expected. 424 return err 425 } 426 427 // Do the actual remove. 428 if err := newFile.(deprecatedRemover).Remove(); err != nil { 429 return err 430 } 431 432 // Ensure that the remove closed the file. 433 if err := newFile.(deprecatedRemover).Remove(); err != unix.EBADF { 434 return unix.EBADF // Propagate this code. 435 } 436 437 return nil 438 } 439 440 // unlinkHelper unlinks the noted path, and ensures that all relevant 441 // operations on that path, acquired from multiple paths, start failing. 442 func unlinkHelper(h *Harness, root p9.File, targetNames []string, targetGen fileGenerator, deleteFn deleter) { 443 // name is the file to be unlinked. 444 name := targetNames[len(targetNames)-1] 445 446 // Walk to the directory containing the target. 447 _, parent, err := root.Walk(targetNames[:len(targetNames)-1]) 448 if err != nil { 449 h.t.Fatalf("got walk err %v, want nil", err) 450 } 451 defer parent.Close() 452 parentBackend := h.Pop(parent) 453 454 // Walk to or generate the target file. 455 _, _, target := targetGen(h, name, parent) 456 defer checkDeleted(h, target) 457 458 // Walk to a second reference. 459 _, second, err := parent.Walk([]string{name}) 460 if err != nil { 461 h.t.Fatalf("got walk err %v, want nil", err) 462 } 463 defer checkDeleted(h, second) 464 465 // Walk to a third reference, from the start. 466 _, third, err := root.Walk(targetNames) 467 if err != nil { 468 h.t.Fatalf("got walk err %v, want nil", err) 469 } 470 defer checkDeleted(h, third) 471 472 // This will be translated in the backend to an unlinkat. 473 parentBackend.EXPECT().UnlinkAt(name, uint32(0)).Return(nil) 474 475 // Actually perform the deletion. 476 if err := deleteFn(parent, name); err != nil { 477 h.t.Fatalf("got delete err %v, want nil", err) 478 } 479 } 480 481 func unlinkTest(t *testing.T, targetNames []string, targetGen fileGenerator) { 482 t.Run(fmt.Sprintf("unlinkAt(%s)", strings.Join(targetNames, "/")), func(t *testing.T) { 483 h, c := NewHarness(t) 484 defer h.Finish() 485 486 _, root := newRoot(h, c) 487 defer root.Close() 488 489 unlinkHelper(h, root, targetNames, targetGen, unlinkAt) 490 }) 491 t.Run(fmt.Sprintf("remove(%s)", strings.Join(targetNames, "/")), func(t *testing.T) { 492 h, c := NewHarness(t) 493 defer h.Finish() 494 495 _, root := newRoot(h, c) 496 defer root.Close() 497 498 unlinkHelper(h, root, targetNames, targetGen, remove) 499 }) 500 } 501 502 func TestUnlink(t *testing.T) { 503 // Unlink all files. 504 for name := range newTypeMap(nil) { 505 unlinkTest(t, []string{name}, walkHelper) 506 unlinkTest(t, []string{name}, walkAndOpenHelper) 507 unlinkTest(t, []string{"one", name}, walkHelper) 508 unlinkTest(t, []string{"one", name}, walkAndOpenHelper) 509 unlinkTest(t, []string{"one", "two", name}, walkHelper) 510 unlinkTest(t, []string{"one", "two", name}, walkAndOpenHelper) 511 } 512 513 // Unlink a directory. 514 unlinkTest(t, []string{"one"}, walkHelper) 515 unlinkTest(t, []string{"one"}, walkAndOpenHelper) 516 unlinkTest(t, []string{"one", "two"}, walkHelper) 517 unlinkTest(t, []string{"one", "two"}, walkAndOpenHelper) 518 519 // Unlink created files. 520 unlinkTest(t, []string{"created"}, createHelper) 521 unlinkTest(t, []string{"one", "created"}, createHelper) 522 unlinkTest(t, []string{"one", "two", "created"}, createHelper) 523 } 524 525 func TestUnlinkAtInvalid(t *testing.T) { 526 h, c := NewHarness(t) 527 defer h.Finish() 528 529 _, root := newRoot(h, c) 530 defer root.Close() 531 532 for name := range newTypeMap(nil) { 533 for _, invalidName := range allInvalidNames(name) { 534 if err := root.UnlinkAt(invalidName, 0); err != unix.EINVAL { 535 t.Errorf("got %v for name %q, want EINVAL", err, invalidName) 536 } 537 } 538 } 539 } 540 541 // expectRenamed asserts an ordered sequence of rename calls, based on all the 542 // elements in elements being the source, and the first element therein 543 // changing to dstName, parented at dstParent. 544 func expectRenamed(file *Mock, elements []string, dstParent *Mock, dstName string) *gomock.Call { 545 if len(elements) > 0 { 546 // Recurse to the parent, if necessary. 547 call := expectRenamed(file.parent, elements[:len(elements)-1], dstParent, dstName) 548 549 // Recursive case: this element is unchanged, but should have 550 // it's hook called after the parent. 551 return file.EXPECT().Renamed(file.parent, elements[len(elements)-1]).Do(func(p p9.File, _ string) { 552 file.parent = p.(*Mock) 553 }).After(call) 554 } 555 556 // Base case: this is the changed element. 557 return file.EXPECT().Renamed(dstParent, dstName).Do(func(p p9.File, name string) { 558 file.parent = p.(*Mock) 559 }) 560 } 561 562 // renamer is a rename function. 563 type renamer func(h *Harness, srcParent, dstParent p9.File, origName, newName string, selfRename bool) error 564 565 // renameAt is a renamer. 566 func renameAt(_ *Harness, srcParent, dstParent p9.File, srcName, dstName string, selfRename bool) error { 567 return srcParent.RenameAt(srcName, dstParent, dstName) 568 } 569 570 // rename is a renamer. 571 func rename(h *Harness, srcParent, dstParent p9.File, srcName, dstName string, selfRename bool) error { 572 _, f, err := srcParent.Walk([]string{srcName}) 573 if err != nil { 574 return err 575 } 576 defer f.Close() 577 if !selfRename { 578 backend := h.Pop(f) 579 backend.EXPECT().Renamed(gomock.Any(), dstName).Do(func(p p9.File, name string) { 580 backend.parent = p.(*Mock) // Required for close ordering. 581 }) 582 } 583 return f.Rename(dstParent, dstName) 584 } 585 586 // renameHelper executes a rename, and asserts that all relevant elements 587 // receive expected notifications. If overwriting a file, this includes 588 // ensuring that the target has been appropriately marked as unlinked. 589 func renameHelper(h *Harness, root p9.File, srcNames []string, dstNames []string, target fileGenerator, renameFn renamer) { 590 // Walk to the directory containing the target. 591 srcQID, targetParent, err := root.Walk(srcNames[:len(srcNames)-1]) 592 if err != nil { 593 h.t.Fatalf("got walk err %v, want nil", err) 594 } 595 defer targetParent.Close() 596 targetParentBackend := h.Pop(targetParent) 597 598 // Walk to or generate the target file. 599 _, targetBackend, src := target(h, srcNames[len(srcNames)-1], targetParent) 600 defer src.Close() 601 602 // Walk to a second reference. 603 _, second, err := targetParent.Walk([]string{srcNames[len(srcNames)-1]}) 604 if err != nil { 605 h.t.Fatalf("got walk err %v, want nil", err) 606 } 607 defer second.Close() 608 secondBackend := h.Pop(second) 609 610 // Walk to a third reference, from the start. 611 _, third, err := root.Walk(srcNames) 612 if err != nil { 613 h.t.Fatalf("got walk err %v, want nil", err) 614 } 615 defer third.Close() 616 thirdBackend := h.Pop(third) 617 618 // Find the common suffix to identify the rename parent. 619 var ( 620 renameDestPath []string 621 renameSrcPath []string 622 selfRename bool 623 ) 624 for i := 1; i <= len(srcNames) && i <= len(dstNames); i++ { 625 if srcNames[len(srcNames)-i] != dstNames[len(dstNames)-i] { 626 // Take the full prefix of dstNames up until this 627 // point, including the first mismatched name. The 628 // first mismatch must be the renamed entry. 629 renameDestPath = dstNames[:len(dstNames)-i+1] 630 renameSrcPath = srcNames[:len(srcNames)-i+1] 631 632 // Does the renameDestPath fully contain the 633 // renameSrcPath here? If yes, then this is a mismatch. 634 // We can't rename the src to some subpath of itself. 635 if len(renameDestPath) > len(renameSrcPath) && 636 slices.Equal(renameDestPath[:len(renameSrcPath)], renameSrcPath) { 637 renameDestPath = nil 638 renameSrcPath = nil 639 continue 640 } 641 break 642 } 643 } 644 if len(renameSrcPath) == 0 || len(renameDestPath) == 0 { 645 // This must be a rename to self, or a tricky look-alike. This 646 // happens iff we fail to find a suitable divergence in the two 647 // paths. It's a true self move if the path length is the same. 648 renameDestPath = dstNames 649 renameSrcPath = srcNames 650 selfRename = len(srcNames) == len(dstNames) 651 } 652 653 // Walk to the source parent. 654 _, srcParent, err := root.Walk(renameSrcPath[:len(renameSrcPath)-1]) 655 if err != nil { 656 h.t.Fatalf("got walk err %v, want nil", err) 657 } 658 defer srcParent.Close() 659 srcParentBackend := h.Pop(srcParent) 660 661 // Walk to the destination parent. 662 _, dstParent, err := root.Walk(renameDestPath[:len(renameDestPath)-1]) 663 if err != nil { 664 h.t.Fatalf("got walk err %v, want nil", err) 665 } 666 defer dstParent.Close() 667 dstParentBackend := h.Pop(dstParent) 668 669 // expectedErr is the result of the rename operation. 670 var expectedErr error 671 672 // Walk to the target file, if one exists. 673 dstQID, dst, err := root.Walk(renameDestPath) 674 if err == nil { 675 if !selfRename && srcQID[0].Type == dstQID[0].Type { 676 // If there is a destination file, and is it of the 677 // same type as the source file, then we expect the 678 // rename to succeed. We expect the destination file to 679 // be deleted, so we run a deletion test on it in this 680 // case. 681 defer checkDeleted(h, dst) 682 } else { 683 // If the type is different than the destination, then 684 // we expect the rename to fail. We expect that this 685 // is returned. 686 // 687 // If the file being renamed to itself, this is 688 // technically allowed and a no-op, but all the 689 // triggers will fire. 690 if !selfRename { 691 expectedErr = unix.EINVAL 692 } 693 dst.Close() 694 } 695 } 696 dstName := renameDestPath[len(renameDestPath)-1] // Renamed element. 697 srcName := renameSrcPath[len(renameSrcPath)-1] // Renamed element. 698 if expectedErr == nil && !selfRename { 699 // Expect all to be renamed appropriately. Note that if this is 700 // a final file being renamed, then we expect the file to be 701 // called with the new parent. If not, then we expect the 702 // rename hook to be called, but the parent will remain 703 // unchanged. 704 elements := srcNames[len(renameSrcPath):] 705 expectRenamed(targetBackend, elements, dstParentBackend, dstName) 706 expectRenamed(secondBackend, elements, dstParentBackend, dstName) 707 expectRenamed(thirdBackend, elements, dstParentBackend, dstName) 708 709 // The target parent has also been opened, and may be moved 710 // directly or indirectly. 711 if len(elements) > 1 { 712 expectRenamed(targetParentBackend, elements[:len(elements)-1], dstParentBackend, dstName) 713 } 714 } 715 716 // Expect the rename if it's not the same file. Note that like unlink, 717 // renames are always translated to the at variant in the backend. 718 if !selfRename { 719 srcParentBackend.EXPECT().RenameAt(srcName, dstParentBackend, dstName).Return(expectedErr) 720 } 721 722 // Perform the actual rename; everything has been lined up. 723 if err := renameFn(h, srcParent, dstParent, srcName, dstName, selfRename); err != expectedErr { 724 h.t.Fatalf("got rename err %v, want %v", err, expectedErr) 725 } 726 } 727 728 func renameTest(t *testing.T, srcNames []string, dstNames []string, target fileGenerator) { 729 t.Run(fmt.Sprintf("renameAt(%s->%s)", strings.Join(srcNames, "/"), strings.Join(dstNames, "/")), func(t *testing.T) { 730 h, c := NewHarness(t) 731 defer h.Finish() 732 733 _, root := newRoot(h, c) 734 defer root.Close() 735 736 renameHelper(h, root, srcNames, dstNames, target, renameAt) 737 }) 738 t.Run(fmt.Sprintf("rename(%s->%s)", strings.Join(srcNames, "/"), strings.Join(dstNames, "/")), func(t *testing.T) { 739 h, c := NewHarness(t) 740 defer h.Finish() 741 742 _, root := newRoot(h, c) 743 defer root.Close() 744 745 renameHelper(h, root, srcNames, dstNames, target, rename) 746 }) 747 } 748 749 func TestRename(t *testing.T) { 750 // In-directory rename, simple case. 751 for name := range newTypeMap(nil) { 752 // Within the root. 753 renameTest(t, []string{name}, []string{"renamed"}, walkHelper) 754 renameTest(t, []string{name}, []string{"renamed"}, walkAndOpenHelper) 755 756 // Within a subdirectory. 757 renameTest(t, []string{"one", name}, []string{"one", "renamed"}, walkHelper) 758 renameTest(t, []string{"one", name}, []string{"one", "renamed"}, walkAndOpenHelper) 759 } 760 761 // ... with created files. 762 renameTest(t, []string{"created"}, []string{"renamed"}, createHelper) 763 renameTest(t, []string{"one", "created"}, []string{"one", "renamed"}, createHelper) 764 765 // Across directories. 766 for name := range newTypeMap(nil) { 767 // Down one level. 768 renameTest(t, []string{"one", name}, []string{"one", "two", "renamed"}, walkHelper) 769 renameTest(t, []string{"one", name}, []string{"one", "two", "renamed"}, walkAndOpenHelper) 770 771 // Up one level. 772 renameTest(t, []string{"one", "two", name}, []string{"one", "renamed"}, walkHelper) 773 renameTest(t, []string{"one", "two", name}, []string{"one", "renamed"}, walkAndOpenHelper) 774 775 // Across at the same level. 776 renameTest(t, []string{"one", name}, []string{"three", "renamed"}, walkHelper) 777 renameTest(t, []string{"one", name}, []string{"three", "renamed"}, walkAndOpenHelper) 778 } 779 780 // ... with created files. 781 renameTest(t, []string{"one", "created"}, []string{"one", "two", "renamed"}, createHelper) 782 renameTest(t, []string{"one", "two", "created"}, []string{"one", "renamed"}, createHelper) 783 renameTest(t, []string{"one", "created"}, []string{"three", "renamed"}, createHelper) 784 785 // Renaming parents. 786 for name := range newTypeMap(nil) { 787 // Rename a parent. 788 renameTest(t, []string{"one", name}, []string{"renamed", name}, walkHelper) 789 renameTest(t, []string{"one", name}, []string{"renamed", name}, walkAndOpenHelper) 790 791 // Rename a super parent. 792 renameTest(t, []string{"one", "two", name}, []string{"renamed", name}, walkHelper) 793 renameTest(t, []string{"one", "two", name}, []string{"renamed", name}, walkAndOpenHelper) 794 } 795 796 // ... with created files. 797 renameTest(t, []string{"one", "created"}, []string{"renamed", "created"}, createHelper) 798 renameTest(t, []string{"one", "two", "created"}, []string{"renamed", "created"}, createHelper) 799 800 // Over existing files, including itself. 801 for name := range newTypeMap(nil) { 802 for other := range newTypeMap(nil) { 803 // Overwrite the noted file (may be itself). 804 renameTest(t, []string{"one", name}, []string{"one", other}, walkHelper) 805 renameTest(t, []string{"one", name}, []string{"one", other}, walkAndOpenHelper) 806 807 // Overwrite other files in another directory. 808 renameTest(t, []string{"one", name}, []string{"one", "two", other}, walkHelper) 809 renameTest(t, []string{"one", name}, []string{"one", "two", other}, walkAndOpenHelper) 810 } 811 812 // Overwrite by moving the parent. 813 renameTest(t, []string{"three", name}, []string{"one", name}, walkHelper) 814 renameTest(t, []string{"three", name}, []string{"one", name}, walkAndOpenHelper) 815 816 // Create over the types. 817 renameTest(t, []string{"one", "created"}, []string{"one", name}, createHelper) 818 renameTest(t, []string{"one", "created"}, []string{"one", "two", name}, createHelper) 819 renameTest(t, []string{"three", "created"}, []string{"one", name}, createHelper) 820 } 821 } 822 823 func TestRenameInvalid(t *testing.T) { 824 h, c := NewHarness(t) 825 defer h.Finish() 826 827 _, root := newRoot(h, c) 828 defer root.Close() 829 830 for name := range newTypeMap(nil) { 831 for _, invalidName := range allInvalidNames(name) { 832 if err := root.Rename(root, invalidName); err != unix.EINVAL { 833 t.Errorf("got %v for name %q, want EINVAL", err, invalidName) 834 } 835 } 836 } 837 } 838 839 func TestRenameAtInvalid(t *testing.T) { 840 h, c := NewHarness(t) 841 defer h.Finish() 842 843 _, root := newRoot(h, c) 844 defer root.Close() 845 846 for name := range newTypeMap(nil) { 847 for _, invalidName := range allInvalidNames(name) { 848 if err := root.RenameAt(invalidName, root, "okay"); err != unix.EINVAL { 849 t.Errorf("got %v for name %q, want EINVAL", err, invalidName) 850 } 851 if err := root.RenameAt("okay", root, invalidName); err != unix.EINVAL { 852 t.Errorf("got %v for name %q, want EINVAL", err, invalidName) 853 } 854 } 855 } 856 } 857 858 // TestRenameSecondOrder tests that indirect rename targets continue to receive 859 // Renamed calls after a rename of its renamed parent. i.e., 860 // 861 // 1. Create /one/file 862 // 2. Create /directory 863 // 3. Rename /one -> /directory/one 864 // 4. Rename /directory -> /three/foo 865 // 5. file from (1) should still receive Renamed. 866 // 867 // This is a regression test for b/135219260. 868 func TestRenameSecondOrder(t *testing.T) { 869 h, c := NewHarness(t) 870 defer h.Finish() 871 872 rootBackend, root := newRoot(h, c) 873 defer root.Close() 874 875 // Walk to /one. 876 _, oneBackend, oneFile := walkHelper(h, "one", root) 877 defer oneFile.Close() 878 879 // Walk to and generate /one/file. 880 // 881 // walkHelper re-walks to oneFile, so we need the second backend, 882 // which will also receive Renamed calls. 883 oneSecondBackend, fileBackend, fileFile := walkHelper(h, "file", oneFile) 884 defer fileFile.Close() 885 886 // Walk to and generate /directory. 887 _, directoryBackend, directoryFile := walkHelper(h, "directory", root) 888 defer directoryFile.Close() 889 890 // Rename /one to /directory/one. 891 rootBackend.EXPECT().RenameAt("one", directoryBackend, "one").Return(nil) 892 expectRenamed(oneBackend, []string{}, directoryBackend, "one") 893 expectRenamed(oneSecondBackend, []string{}, directoryBackend, "one") 894 expectRenamed(fileBackend, []string{}, oneBackend, "file") 895 if err := renameAt(h, root, directoryFile, "one", "one", false); err != nil { 896 h.t.Fatalf("got rename err %v, want nil", err) 897 } 898 899 // Walk to /three. 900 _, threeBackend, threeFile := walkHelper(h, "three", root) 901 defer threeFile.Close() 902 903 // Rename /directory to /three/foo. 904 rootBackend.EXPECT().RenameAt("directory", threeBackend, "foo").Return(nil) 905 expectRenamed(directoryBackend, []string{}, threeBackend, "foo") 906 expectRenamed(oneBackend, []string{}, directoryBackend, "one") 907 expectRenamed(oneSecondBackend, []string{}, directoryBackend, "one") 908 expectRenamed(fileBackend, []string{}, oneBackend, "file") 909 if err := renameAt(h, root, threeFile, "directory", "foo", false); err != nil { 910 h.t.Fatalf("got rename err %v, want nil", err) 911 } 912 } 913 914 func TestReadlink(t *testing.T) { 915 for name := range newTypeMap(nil) { 916 t.Run(name, func(t *testing.T) { 917 h, c := NewHarness(t) 918 defer h.Finish() 919 920 _, root := newRoot(h, c) 921 defer root.Close() 922 923 // Walk to the file normally. 924 _, f, err := root.Walk([]string{name}) 925 if err != nil { 926 t.Fatalf("walk failed: got %v, wanted nil", err) 927 } 928 defer f.Close() 929 backend := h.Pop(f) 930 931 const symlinkTarget = "symlink-target" 932 933 if backend.Attr.Mode.IsSymlink() { 934 // This should only go through on symlinks. 935 backend.EXPECT().Readlink().Return(symlinkTarget, nil) 936 } 937 938 // Attempt a Readlink operation. 939 target, err := f.Readlink() 940 if err != nil && err != unix.EINVAL { 941 t.Errorf("readlink got %v, wanted EINVAL", err) 942 } else if err == nil && target != symlinkTarget { 943 t.Errorf("readlink got %v, wanted %v", target, symlinkTarget) 944 } 945 }) 946 } 947 } 948 949 // fdTest is a wrapper around operations that may send file descriptors. This 950 // asserts that the file descriptors are working as intended. 951 func fdTest(t *testing.T, sendFn func(*fd.FD) *fd.FD) { 952 // Create a pipe that we can read from. 953 r, w, err := os.Pipe() 954 if err != nil { 955 t.Fatalf("unable to create pipe: %v", err) 956 } 957 defer r.Close() 958 defer w.Close() 959 960 // Attempt to send the write end. 961 wFD, err := fd.NewFromFile(w) 962 if err != nil { 963 t.Fatalf("unable to convert file: %v", err) 964 } 965 defer wFD.Close() // This is a copy. 966 967 // Send wFD and receive newFD. 968 newFD := sendFn(wFD) 969 defer newFD.Close() 970 971 // Attempt to write. 972 const message = "hello" 973 if _, err := newFD.Write([]byte(message)); err != nil { 974 t.Fatalf("write got %v, wanted nil", err) 975 } 976 977 // Should see the message on our end. 978 buffer := []byte(message) 979 if _, err := io.ReadFull(r, buffer); err != nil { 980 t.Fatalf("read got %v, wanted nil", err) 981 } 982 if string(buffer) != message { 983 t.Errorf("got message %v, wanted %v", string(buffer), message) 984 } 985 } 986 987 func TestConnect(t *testing.T) { 988 for name := range newTypeMap(nil) { 989 t.Run(name, func(t *testing.T) { 990 h, c := NewHarness(t) 991 defer h.Finish() 992 993 _, root := newRoot(h, c) 994 defer root.Close() 995 996 // Walk to the file normally. 997 _, backend, f := walkHelper(h, name, root) 998 defer f.Close() 999 1000 // Catch all the non-socket cases. 1001 if !backend.Attr.Mode.IsSocket() { 1002 // This has been set up to fail if Connect is called. 1003 if _, err := f.Connect(p9.SocketType(0)); err != unix.EINVAL { 1004 t.Errorf("connect got %v, wanted EINVAL", err) 1005 } 1006 return 1007 } 1008 1009 // Ensure the fd exchange works. 1010 fdTest(t, func(send *fd.FD) *fd.FD { 1011 backend.EXPECT().Connect(p9.SocketType(0)).Return(send, nil) 1012 recv, err := backend.Connect(p9.SocketType(0)) 1013 if err != nil { 1014 t.Fatalf("connect got %v, wanted nil", err) 1015 } 1016 return recv 1017 }) 1018 }) 1019 } 1020 } 1021 1022 func TestReaddir(t *testing.T) { 1023 for name := range newTypeMap(nil) { 1024 t.Run(name, func(t *testing.T) { 1025 h, c := NewHarness(t) 1026 defer h.Finish() 1027 1028 _, root := newRoot(h, c) 1029 defer root.Close() 1030 1031 // Walk to the file normally. 1032 _, backend, f := walkHelper(h, name, root) 1033 defer f.Close() 1034 1035 // Catch all the non-directory cases. 1036 if !backend.Attr.Mode.IsDir() { 1037 // This has also been set up to fail if Readdir is called. 1038 if _, err := f.Readdir(0, 1); err != unix.EINVAL { 1039 t.Errorf("readdir got %v, wanted EINVAL", err) 1040 } 1041 return 1042 } 1043 1044 // Ensure that readdir works for directories. 1045 if _, err := f.Readdir(0, 1); err != unix.EINVAL { 1046 t.Errorf("readdir got %v, wanted EINVAL", err) 1047 } 1048 if _, _, _, err := f.Open(p9.ReadWrite); err != unix.EISDIR { 1049 t.Errorf("readdir got %v, wanted EISDIR", err) 1050 } 1051 if _, _, _, err := f.Open(p9.WriteOnly); err != unix.EISDIR { 1052 t.Errorf("readdir got %v, wanted EISDIR", err) 1053 } 1054 backend.EXPECT().Open(p9.ReadOnly).Times(1) 1055 if _, _, _, err := f.Open(p9.ReadOnly); err != nil { 1056 t.Errorf("readdir got %v, wanted nil", err) 1057 } 1058 backend.EXPECT().Readdir(uint64(0), uint32(1)).Times(1) 1059 if _, err := f.Readdir(0, 1); err != nil { 1060 t.Errorf("readdir got %v, wanted nil", err) 1061 } 1062 }) 1063 } 1064 } 1065 1066 func TestOpen(t *testing.T) { 1067 type openTest struct { 1068 name string 1069 flags p9.OpenFlags 1070 err error 1071 match func(p9.FileMode) bool 1072 } 1073 1074 cases := []openTest{ 1075 { 1076 name: "not-openable-read-only", 1077 flags: p9.ReadOnly, 1078 err: unix.EINVAL, 1079 match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) }, 1080 }, 1081 { 1082 name: "not-openable-write-only", 1083 flags: p9.WriteOnly, 1084 err: unix.EINVAL, 1085 match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) }, 1086 }, 1087 { 1088 name: "not-openable-read-write", 1089 flags: p9.ReadWrite, 1090 err: unix.EINVAL, 1091 match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) }, 1092 }, 1093 { 1094 name: "directory-read-only", 1095 flags: p9.ReadOnly, 1096 err: nil, 1097 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 1098 }, 1099 { 1100 name: "directory-read-write", 1101 flags: p9.ReadWrite, 1102 err: unix.EISDIR, 1103 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 1104 }, 1105 { 1106 name: "directory-write-only", 1107 flags: p9.WriteOnly, 1108 err: unix.EISDIR, 1109 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 1110 }, 1111 { 1112 name: "read-only", 1113 flags: p9.ReadOnly, 1114 err: nil, 1115 match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) }, 1116 }, 1117 { 1118 name: "write-only", 1119 flags: p9.WriteOnly, 1120 err: nil, 1121 match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() }, 1122 }, 1123 { 1124 name: "read-write", 1125 flags: p9.ReadWrite, 1126 err: nil, 1127 match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() }, 1128 }, 1129 { 1130 name: "directory-read-only-truncate", 1131 flags: p9.ReadOnly | p9.OpenTruncate, 1132 err: unix.EISDIR, 1133 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 1134 }, 1135 { 1136 name: "read-only-truncate", 1137 flags: p9.ReadOnly | p9.OpenTruncate, 1138 err: nil, 1139 match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() }, 1140 }, 1141 { 1142 name: "write-only-truncate", 1143 flags: p9.WriteOnly | p9.OpenTruncate, 1144 err: nil, 1145 match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() }, 1146 }, 1147 { 1148 name: "read-write-truncate", 1149 flags: p9.ReadWrite | p9.OpenTruncate, 1150 err: nil, 1151 match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() }, 1152 }, 1153 } 1154 1155 // Open(flags OpenFlags) (*fd.FD, QID, uint32, error) 1156 // - only works on Regular, NamedPipe, BLockDevice, CharacterDevice 1157 // - returning a file works as expected 1158 for name := range newTypeMap(nil) { 1159 for _, tc := range cases { 1160 t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) { 1161 h, c := NewHarness(t) 1162 defer h.Finish() 1163 1164 _, root := newRoot(h, c) 1165 defer root.Close() 1166 1167 // Walk to the file normally. 1168 _, backend, f := walkHelper(h, name, root) 1169 defer f.Close() 1170 1171 // Does this match the case? 1172 if !tc.match(backend.Attr.Mode) { 1173 t.SkipNow() 1174 } 1175 1176 // Ensure open-required operations fail. 1177 if _, err := f.ReadAt([]byte("hello"), 0); err != unix.EINVAL { 1178 t.Errorf("readAt got %v, wanted EINVAL", err) 1179 } 1180 if _, err := f.WriteAt(make([]byte, 6), 0); err != unix.EINVAL { 1181 t.Errorf("writeAt got %v, wanted EINVAL", err) 1182 } 1183 if err := f.FSync(); err != unix.EINVAL { 1184 t.Errorf("fsync got %v, wanted EINVAL", err) 1185 } 1186 if _, err := f.Readdir(0, 1); err != unix.EINVAL { 1187 t.Errorf("readdir got %v, wanted EINVAL", err) 1188 } 1189 1190 // Attempt the given open. 1191 if tc.err != nil { 1192 // We expect an error, just test and return. 1193 if _, _, _, err := f.Open(tc.flags); err != tc.err { 1194 t.Fatalf("open with flags %v got %v, want %v", tc.flags, err, tc.err) 1195 } 1196 return 1197 } 1198 1199 // Run an FD test, since we expect success. 1200 fdTest(t, func(send *fd.FD) *fd.FD { 1201 backend.EXPECT().Open(tc.flags).Return(send, p9.QID{}, uint32(0), nil).Times(1) 1202 recv, _, _, err := f.Open(tc.flags) 1203 if err != tc.err { 1204 t.Fatalf("open with flags %v got %v, want %v", tc.flags, err, tc.err) 1205 } 1206 return recv 1207 }) 1208 1209 // If the open was successful, attempt another one. 1210 if _, _, _, err := f.Open(tc.flags); err != unix.EINVAL { 1211 t.Errorf("second open with flags %v got %v, want EINVAL", tc.flags, err) 1212 } 1213 1214 // Ensure that all illegal operations fail. 1215 if _, _, err := f.Walk(nil); err != unix.EINVAL && err != unix.EBUSY { 1216 t.Errorf("walk got %v, wanted EINVAL or EBUSY", err) 1217 } 1218 if _, _, _, _, err := f.WalkGetAttr(nil); err != unix.EINVAL && err != unix.EBUSY { 1219 t.Errorf("walkgetattr got %v, wanted EINVAL or EBUSY", err) 1220 } 1221 }) 1222 } 1223 } 1224 } 1225 1226 func TestClose(t *testing.T) { 1227 type closeTest struct { 1228 name string 1229 closeFn func(backend *Mock, f p9.File) error 1230 } 1231 1232 cases := []closeTest{ 1233 { 1234 name: "close", 1235 closeFn: func(_ *Mock, f p9.File) error { 1236 return f.Close() 1237 }, 1238 }, 1239 { 1240 name: "remove", 1241 closeFn: func(backend *Mock, f p9.File) error { 1242 // Allow the rename call in the parent, automatically translated. 1243 backend.parent.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Times(1) 1244 return f.(deprecatedRemover).Remove() 1245 }, 1246 }, 1247 { 1248 name: "setAttrClose", 1249 closeFn: func(backend *Mock, f p9.File) error { 1250 valid := p9.SetAttrMask{ATime: true} 1251 attr := p9.SetAttr{ATimeSeconds: 1, ATimeNanoSeconds: 2} 1252 backend.EXPECT().SetAttr(valid, attr).Times(1) 1253 return f.SetAttrClose(valid, attr) 1254 }, 1255 }, 1256 } 1257 1258 for name := range newTypeMap(nil) { 1259 for _, tc := range cases { 1260 t.Run(fmt.Sprintf("%s(%s)", tc.name, name), func(t *testing.T) { 1261 h, c := NewHarness(t) 1262 defer h.Finish() 1263 1264 _, root := newRoot(h, c) 1265 defer root.Close() 1266 1267 // Walk to the file normally. 1268 _, backend, f := walkHelper(h, name, root) 1269 1270 // Close via the prescribed method. 1271 if err := tc.closeFn(backend, f); err != nil { 1272 t.Fatalf("closeFn failed: %v", err) 1273 } 1274 1275 // Everything should fail with EBADF. 1276 if _, _, err := f.Walk(nil); err != unix.EBADF { 1277 t.Errorf("walk got %v, wanted EBADF", err) 1278 } 1279 if _, err := f.StatFS(); err != unix.EBADF { 1280 t.Errorf("statfs got %v, wanted EBADF", err) 1281 } 1282 if _, _, _, err := f.GetAttr(p9.AttrMaskAll()); err != unix.EBADF { 1283 t.Errorf("getattr got %v, wanted EBADF", err) 1284 } 1285 if err := f.SetAttr(p9.SetAttrMask{}, p9.SetAttr{}); err != unix.EBADF { 1286 t.Errorf("setattrk got %v, wanted EBADF", err) 1287 } 1288 if err := f.Rename(root, "new-name"); err != unix.EBADF { 1289 t.Errorf("rename got %v, wanted EBADF", err) 1290 } 1291 if err := f.Close(); err != unix.EBADF { 1292 t.Errorf("close got %v, wanted EBADF", err) 1293 } 1294 if _, _, _, err := f.Open(p9.ReadOnly); err != unix.EBADF { 1295 t.Errorf("open got %v, wanted EBADF", err) 1296 } 1297 if _, err := f.ReadAt([]byte("hello"), 0); err != unix.EBADF { 1298 t.Errorf("readAt got %v, wanted EBADF", err) 1299 } 1300 if _, err := f.WriteAt(make([]byte, 6), 0); err != unix.EBADF { 1301 t.Errorf("writeAt got %v, wanted EBADF", err) 1302 } 1303 if err := f.FSync(); err != unix.EBADF { 1304 t.Errorf("fsync got %v, wanted EBADF", err) 1305 } 1306 if _, _, _, _, err := f.Create("new-file", p9.ReadWrite, 0, 0, 0); err != unix.EBADF { 1307 t.Errorf("create got %v, wanted EBADF", err) 1308 } 1309 if _, err := f.Mkdir("new-directory", 0, 0, 0); err != unix.EBADF { 1310 t.Errorf("mkdir got %v, wanted EBADF", err) 1311 } 1312 if _, err := f.Symlink("old-name", "new-name", 0, 0); err != unix.EBADF { 1313 t.Errorf("symlink got %v, wanted EBADF", err) 1314 } 1315 if err := f.Link(root, "new-name"); err != unix.EBADF { 1316 t.Errorf("link got %v, wanted EBADF", err) 1317 } 1318 if _, err := f.Mknod("new-block-device", 0, 0, 0, 0, 0); err != unix.EBADF { 1319 t.Errorf("mknod got %v, wanted EBADF", err) 1320 } 1321 if err := f.RenameAt("old-name", root, "new-name"); err != unix.EBADF { 1322 t.Errorf("renameAt got %v, wanted EBADF", err) 1323 } 1324 if err := f.UnlinkAt("name", 0); err != unix.EBADF { 1325 t.Errorf("unlinkAt got %v, wanted EBADF", err) 1326 } 1327 if _, err := f.Readdir(0, 1); err != unix.EBADF { 1328 t.Errorf("readdir got %v, wanted EBADF", err) 1329 } 1330 if _, err := f.Readlink(); err != unix.EBADF { 1331 t.Errorf("readlink got %v, wanted EBADF", err) 1332 } 1333 if err := f.Flush(); err != unix.EBADF { 1334 t.Errorf("flush got %v, wanted EBADF", err) 1335 } 1336 if _, _, _, _, err := f.WalkGetAttr(nil); err != unix.EBADF { 1337 t.Errorf("walkgetattr got %v, wanted EBADF", err) 1338 } 1339 if _, err := f.Connect(p9.SocketType(0)); err != unix.EBADF { 1340 t.Errorf("connect got %v, wanted EBADF", err) 1341 } 1342 }) 1343 } 1344 } 1345 } 1346 1347 // onlyWorksOnOpenThings is a helper test method for operations that should 1348 // only work on files that have been explicitly opened. 1349 func onlyWorksOnOpenThings(h *Harness, t *testing.T, name string, root p9.File, mode p9.OpenFlags, expectedErr error, fn func(backend *Mock, f p9.File, shouldSucceed bool) error) { 1350 // Walk to the file normally. 1351 _, backend, f := walkHelper(h, name, root) 1352 defer f.Close() 1353 1354 // Does it work before opening? 1355 if err := fn(backend, f, false); err != unix.EINVAL { 1356 t.Errorf("operation got %v, wanted EINVAL", err) 1357 } 1358 1359 // Is this openable? 1360 if !p9.CanOpen(backend.Attr.Mode) { 1361 return // Nothing to do. 1362 } 1363 1364 // If this is a directory, we can't handle writing. 1365 if backend.Attr.Mode.IsDir() && (mode == p9.ReadWrite || mode == p9.WriteOnly) { 1366 return // Skip. 1367 } 1368 1369 // Open the file. 1370 backend.EXPECT().Open(mode) 1371 if _, _, _, err := f.Open(mode); err != nil { 1372 t.Fatalf("open got %v, wanted nil", err) 1373 } 1374 1375 // Attempt the operation. 1376 if err := fn(backend, f, expectedErr == nil); err != expectedErr { 1377 t.Fatalf("operation got %v, wanted %v", err, expectedErr) 1378 } 1379 } 1380 1381 func TestRead(t *testing.T) { 1382 type readTest struct { 1383 name string 1384 mode p9.OpenFlags 1385 err error 1386 } 1387 1388 cases := []readTest{ 1389 { 1390 name: "read-only", 1391 mode: p9.ReadOnly, 1392 err: nil, 1393 }, 1394 { 1395 name: "read-write", 1396 mode: p9.ReadWrite, 1397 err: nil, 1398 }, 1399 { 1400 name: "write-only", 1401 mode: p9.WriteOnly, 1402 err: unix.EPERM, 1403 }, 1404 } 1405 1406 for name := range newTypeMap(nil) { 1407 for _, tc := range cases { 1408 t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) { 1409 h, c := NewHarness(t) 1410 defer h.Finish() 1411 1412 _, root := newRoot(h, c) 1413 defer root.Close() 1414 1415 const message = "hello" 1416 1417 onlyWorksOnOpenThings(h, t, name, root, tc.mode, tc.err, func(backend *Mock, f p9.File, shouldSucceed bool) error { 1418 if !shouldSucceed { 1419 _, err := f.ReadAt([]byte(message), 0) 1420 return err 1421 } 1422 1423 // Prepare for the call to readAt in the backend. 1424 backend.EXPECT().ReadAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) { 1425 copy(p, message) 1426 }).Return(len(message), nil) 1427 1428 // Make the client call. 1429 p := make([]byte, 2*len(message)) // Double size. 1430 n, err := f.ReadAt(p, 0) 1431 1432 // Sanity check result. 1433 if err != nil { 1434 return err 1435 } 1436 if n != len(message) { 1437 t.Fatalf("message length incorrect, got %d, want %d", n, len(message)) 1438 } 1439 if !bytes.Equal(p[:n], []byte(message)) { 1440 t.Fatalf("message incorrect, got %v, want %v", p, []byte(message)) 1441 } 1442 return nil // Success. 1443 }) 1444 }) 1445 } 1446 } 1447 } 1448 1449 func TestWrite(t *testing.T) { 1450 type writeTest struct { 1451 name string 1452 mode p9.OpenFlags 1453 err error 1454 } 1455 1456 cases := []writeTest{ 1457 { 1458 name: "read-only", 1459 mode: p9.ReadOnly, 1460 err: unix.EPERM, 1461 }, 1462 { 1463 name: "read-write", 1464 mode: p9.ReadWrite, 1465 err: nil, 1466 }, 1467 { 1468 name: "write-only", 1469 mode: p9.WriteOnly, 1470 err: nil, 1471 }, 1472 } 1473 1474 for name := range newTypeMap(nil) { 1475 for _, tc := range cases { 1476 t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) { 1477 h, c := NewHarness(t) 1478 defer h.Finish() 1479 1480 _, root := newRoot(h, c) 1481 defer root.Close() 1482 1483 const message = "hello" 1484 1485 onlyWorksOnOpenThings(h, t, name, root, tc.mode, tc.err, func(backend *Mock, f p9.File, shouldSucceed bool) error { 1486 if !shouldSucceed { 1487 _, err := f.WriteAt([]byte(message), 0) 1488 return err 1489 } 1490 1491 // Prepare for the call to readAt in the backend. 1492 var output []byte // Saved by Do below. 1493 backend.EXPECT().WriteAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) { 1494 output = p 1495 }).Return(len(message), nil) 1496 1497 // Make the client call. 1498 n, err := f.WriteAt([]byte(message), 0) 1499 1500 // Sanity check result. 1501 if err != nil { 1502 return err 1503 } 1504 if n != len(message) { 1505 t.Fatalf("message length incorrect, got %d, want %d", n, len(message)) 1506 } 1507 if !bytes.Equal(output, []byte(message)) { 1508 t.Fatalf("message incorrect, got %v, want %v", output, []byte(message)) 1509 } 1510 return nil // Success. 1511 }) 1512 }) 1513 } 1514 } 1515 } 1516 1517 func TestFSync(t *testing.T) { 1518 for name := range newTypeMap(nil) { 1519 for _, mode := range []p9.OpenFlags{p9.ReadOnly, p9.WriteOnly, p9.ReadWrite} { 1520 t.Run(fmt.Sprintf("%s-%s", mode, name), func(t *testing.T) { 1521 h, c := NewHarness(t) 1522 defer h.Finish() 1523 1524 _, root := newRoot(h, c) 1525 defer root.Close() 1526 1527 onlyWorksOnOpenThings(h, t, name, root, mode, nil, func(backend *Mock, f p9.File, shouldSucceed bool) error { 1528 if shouldSucceed { 1529 backend.EXPECT().FSync().Times(1) 1530 } 1531 return f.FSync() 1532 }) 1533 }) 1534 } 1535 } 1536 } 1537 1538 func TestFlush(t *testing.T) { 1539 for name := range newTypeMap(nil) { 1540 t.Run(name, func(t *testing.T) { 1541 h, c := NewHarness(t) 1542 defer h.Finish() 1543 1544 _, root := newRoot(h, c) 1545 defer root.Close() 1546 1547 _, backend, f := walkHelper(h, name, root) 1548 defer f.Close() 1549 1550 backend.EXPECT().Flush() 1551 f.Flush() 1552 }) 1553 } 1554 } 1555 1556 // onlyWorksOnDirectories is a helper test method for operations that should 1557 // only work on unopened directories, such as create, mkdir and symlink. 1558 func onlyWorksOnDirectories(h *Harness, t *testing.T, name string, root p9.File, fn func(backend *Mock, f p9.File, shouldSucceed bool) error) { 1559 // Walk to the file normally. 1560 _, backend, f := walkHelper(h, name, root) 1561 defer f.Close() 1562 1563 // Only directories support mknod. 1564 if !backend.Attr.Mode.IsDir() { 1565 if err := fn(backend, f, false); err != unix.EINVAL { 1566 t.Errorf("operation got %v, wanted EINVAL", err) 1567 } 1568 return // Nothing else to do. 1569 } 1570 1571 // Should succeed. 1572 if err := fn(backend, f, true); err != nil { 1573 t.Fatalf("operation got %v, wanted nil", err) 1574 } 1575 1576 // Open the directory. 1577 backend.EXPECT().Open(p9.ReadOnly).Times(1) 1578 if _, _, _, err := f.Open(p9.ReadOnly); err != nil { 1579 t.Fatalf("open got %v, wanted nil", err) 1580 } 1581 1582 // Should not work again. 1583 if err := fn(backend, f, false); err != unix.EINVAL { 1584 t.Fatalf("operation got %v, wanted EINVAL", err) 1585 } 1586 } 1587 1588 func TestCreate(t *testing.T) { 1589 for name := range newTypeMap(nil) { 1590 t.Run(name, func(t *testing.T) { 1591 h, c := NewHarness(t) 1592 defer h.Finish() 1593 1594 _, root := newRoot(h, c) 1595 defer root.Close() 1596 1597 onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { 1598 if !shouldSucceed { 1599 _, _, _, _, err := f.Create("new-file", p9.ReadWrite, 0, 1, 2) 1600 return err 1601 } 1602 1603 // If the create is going to succeed, then we 1604 // need to create a new backend file, and we 1605 // clone to ensure that we don't close the 1606 // original. 1607 _, newF, err := f.Walk(nil) 1608 if err != nil { 1609 t.Fatalf("clone got %v, wanted nil", err) 1610 } 1611 defer newF.Close() 1612 newBackend := h.Pop(newF) 1613 1614 // Run a regular FD test to validate that path. 1615 fdTest(t, func(send *fd.FD) *fd.FD { 1616 // Return the send FD on success. 1617 newFile := h.NewFile()(backend) // New file with the parent backend. 1618 newBackend.EXPECT().Create("new-file", p9.ReadWrite, p9.FileMode(0), p9.UID(1), p9.GID(2)).Return(send, newFile, p9.QID{}, uint32(0), nil) 1619 1620 // Receive the fd back. 1621 recv, _, _, _, err := newF.Create("new-file", p9.ReadWrite, 0, 1, 2) 1622 if err != nil { 1623 t.Fatalf("create got %v, wanted nil", err) 1624 } 1625 return recv 1626 }) 1627 1628 // The above will fail via normal test flow, so 1629 // we can assume that it passed. 1630 return nil 1631 }) 1632 }) 1633 } 1634 } 1635 1636 func TestCreateInvalid(t *testing.T) { 1637 h, c := NewHarness(t) 1638 defer h.Finish() 1639 1640 _, root := newRoot(h, c) 1641 defer root.Close() 1642 1643 for name := range newTypeMap(nil) { 1644 for _, invalidName := range allInvalidNames(name) { 1645 if _, _, _, _, err := root.Create(invalidName, p9.ReadWrite, 0, 0, 0); err != unix.EINVAL { 1646 t.Errorf("got %v for name %q, want EINVAL", err, invalidName) 1647 } 1648 } 1649 } 1650 } 1651 1652 func TestMkdir(t *testing.T) { 1653 for name := range newTypeMap(nil) { 1654 t.Run(name, func(t *testing.T) { 1655 h, c := NewHarness(t) 1656 defer h.Finish() 1657 1658 _, root := newRoot(h, c) 1659 defer root.Close() 1660 1661 onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { 1662 if shouldSucceed { 1663 backend.EXPECT().Mkdir("new-directory", p9.FileMode(0), p9.UID(1), p9.GID(2)) 1664 } 1665 _, err := f.Mkdir("new-directory", 0, 1, 2) 1666 return err 1667 }) 1668 }) 1669 } 1670 } 1671 1672 func TestMkdirInvalid(t *testing.T) { 1673 h, c := NewHarness(t) 1674 defer h.Finish() 1675 1676 _, root := newRoot(h, c) 1677 defer root.Close() 1678 1679 for name := range newTypeMap(nil) { 1680 for _, invalidName := range allInvalidNames(name) { 1681 if _, err := root.Mkdir(invalidName, 0, 0, 0); err != unix.EINVAL { 1682 t.Errorf("got %v for name %q, want EINVAL", err, invalidName) 1683 } 1684 } 1685 } 1686 } 1687 1688 func TestSymlink(t *testing.T) { 1689 for name := range newTypeMap(nil) { 1690 t.Run(name, func(t *testing.T) { 1691 h, c := NewHarness(t) 1692 defer h.Finish() 1693 1694 _, root := newRoot(h, c) 1695 defer root.Close() 1696 1697 onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { 1698 if shouldSucceed { 1699 backend.EXPECT().Symlink("old-name", "new-name", p9.UID(1), p9.GID(2)) 1700 } 1701 _, err := f.Symlink("old-name", "new-name", 1, 2) 1702 return err 1703 }) 1704 }) 1705 } 1706 } 1707 1708 func TestSyminkInvalid(t *testing.T) { 1709 h, c := NewHarness(t) 1710 defer h.Finish() 1711 1712 _, root := newRoot(h, c) 1713 defer root.Close() 1714 1715 for name := range newTypeMap(nil) { 1716 for _, invalidName := range allInvalidNames(name) { 1717 // We need only test for invalid names in the new name, 1718 // the target can be an arbitrary string and we don't 1719 // need to sanity check it. 1720 if _, err := root.Symlink("old-name", invalidName, 0, 0); err != unix.EINVAL { 1721 t.Errorf("got %v for name %q, want EINVAL", err, invalidName) 1722 } 1723 } 1724 } 1725 } 1726 1727 func TestLink(t *testing.T) { 1728 for name := range newTypeMap(nil) { 1729 t.Run(name, func(t *testing.T) { 1730 h, c := NewHarness(t) 1731 defer h.Finish() 1732 1733 _, root := newRoot(h, c) 1734 defer root.Close() 1735 1736 onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { 1737 if shouldSucceed { 1738 backend.EXPECT().Link(gomock.Any(), "new-link") 1739 } 1740 return f.Link(f, "new-link") 1741 }) 1742 }) 1743 } 1744 } 1745 1746 func TestLinkInvalid(t *testing.T) { 1747 h, c := NewHarness(t) 1748 defer h.Finish() 1749 1750 _, root := newRoot(h, c) 1751 defer root.Close() 1752 1753 for name := range newTypeMap(nil) { 1754 for _, invalidName := range allInvalidNames(name) { 1755 if err := root.Link(root, invalidName); err != unix.EINVAL { 1756 t.Errorf("got %v for name %q, want EINVAL", err, invalidName) 1757 } 1758 } 1759 } 1760 } 1761 1762 func TestMknod(t *testing.T) { 1763 for name := range newTypeMap(nil) { 1764 t.Run(name, func(t *testing.T) { 1765 h, c := NewHarness(t) 1766 defer h.Finish() 1767 1768 _, root := newRoot(h, c) 1769 defer root.Close() 1770 1771 onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error { 1772 if shouldSucceed { 1773 backend.EXPECT().Mknod("new-block-device", p9.FileMode(0), uint32(1), uint32(2), p9.UID(3), p9.GID(4)).Times(1) 1774 } 1775 _, err := f.Mknod("new-block-device", 0, 1, 2, 3, 4) 1776 return err 1777 }) 1778 }) 1779 } 1780 } 1781 1782 // concurrentFn is a specification of a concurrent operation. This is used to 1783 // drive the concurrency tests below. 1784 type concurrentFn struct { 1785 name string 1786 match func(p9.FileMode) bool 1787 op func(h *Harness, backend *Mock, f p9.File, callback func()) 1788 } 1789 1790 func concurrentTest(t *testing.T, name string, fn1, fn2 concurrentFn, sameDir, expectedOkay bool) { 1791 var ( 1792 names1 []string 1793 names2 []string 1794 ) 1795 if sameDir { 1796 // Use the same file one directory up. 1797 names1, names2 = []string{"one", name}, []string{"one", name} 1798 } else { 1799 // For different directories, just use siblings. 1800 names1, names2 = []string{"one", name}, []string{"three", name} 1801 } 1802 1803 t.Run(fmt.Sprintf("%s(%v)+%s(%v)", fn1.name, names1, fn2.name, names2), func(t *testing.T) { 1804 h, c := NewHarness(t) 1805 defer h.Finish() 1806 1807 _, root := newRoot(h, c) 1808 defer root.Close() 1809 1810 // Walk to both files as given. 1811 _, f1, err := root.Walk(names1) 1812 if err != nil { 1813 t.Fatalf("error walking, got %v, want nil", err) 1814 } 1815 defer f1.Close() 1816 b1 := h.Pop(f1) 1817 _, f2, err := root.Walk(names2) 1818 if err != nil { 1819 t.Fatalf("error walking, got %v, want nil", err) 1820 } 1821 defer f2.Close() 1822 b2 := h.Pop(f2) 1823 1824 // Are these a good match for the current test case? 1825 if !fn1.match(b1.Attr.Mode) { 1826 t.SkipNow() 1827 } 1828 if !fn2.match(b2.Attr.Mode) { 1829 t.SkipNow() 1830 } 1831 1832 // Construct our "concurrency creator". 1833 in1 := make(chan struct{}, 1) 1834 in2 := make(chan struct{}, 1) 1835 var top sync.WaitGroup 1836 var fns sync.WaitGroup 1837 defer top.Wait() 1838 top.Add(2) // Accounting for below. 1839 defer fns.Done() 1840 fns.Add(1) // See line above; released before top.Wait. 1841 go func() { 1842 defer top.Done() 1843 fn1.op(h, b1, f1, func() { 1844 in1 <- struct{}{} 1845 fns.Wait() 1846 }) 1847 }() 1848 go func() { 1849 defer top.Done() 1850 fn2.op(h, b2, f2, func() { 1851 in2 <- struct{}{} 1852 fns.Wait() 1853 }) 1854 }() 1855 1856 // Compute a reasonable timeout. If we expect the operation to hang, 1857 // give it 10 milliseconds before we assert that it's fine. After all, 1858 // there will be a lot of these tests. If we don't expect it to hang, 1859 // give it a full minute, since the machine could be slow. 1860 timeout := 10 * time.Millisecond 1861 if expectedOkay { 1862 timeout = 1 * time.Minute 1863 } 1864 1865 // Read the first channel. 1866 var second chan struct{} 1867 select { 1868 case <-in1: 1869 second = in2 1870 case <-in2: 1871 second = in1 1872 } 1873 1874 // Catch concurrency. 1875 select { 1876 case <-second: 1877 // We finished successful. Is this good? Depends on the 1878 // expected result. 1879 if !expectedOkay { 1880 t.Errorf("%q and %q proceeded concurrently!", fn1.name, fn2.name) 1881 } 1882 case <-time.After(timeout): 1883 // Great, things did not proceed concurrently. Is that what we 1884 // expected? 1885 if expectedOkay { 1886 t.Errorf("%q and %q hung concurrently!", fn1.name, fn2.name) 1887 } 1888 } 1889 }) 1890 } 1891 1892 func randomFileName() string { 1893 return fmt.Sprintf("%x", rand.Int63()) 1894 } 1895 1896 func TestConcurrency(t *testing.T) { 1897 readExclusive := []concurrentFn{ 1898 { 1899 // N.B. We can't explicitly check WalkGetAttr behavior, 1900 // but we rely on the fact that the internal code paths 1901 // are the same. 1902 name: "walk", 1903 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 1904 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 1905 // See the documentation of WalkCallback. 1906 // Because walk is actually implemented by the 1907 // mock, we need a special place for this 1908 // callback. 1909 // 1910 // Note that a clone actually locks the parent 1911 // node. So we walk from this node to test 1912 // concurrent operations appropriately. 1913 backend.WalkCallback = func() error { 1914 callback() 1915 return nil 1916 } 1917 f.Walk([]string{randomFileName()}) // Won't exist. 1918 }, 1919 }, 1920 { 1921 name: "fsync", 1922 match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) }, 1923 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 1924 backend.EXPECT().Open(gomock.Any()) 1925 backend.EXPECT().FSync().Do(func() { 1926 callback() 1927 }) 1928 f.Open(p9.ReadOnly) // Required. 1929 f.FSync() 1930 }, 1931 }, 1932 { 1933 name: "readdir", 1934 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 1935 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 1936 backend.EXPECT().Open(gomock.Any()) 1937 backend.EXPECT().Readdir(gomock.Any(), gomock.Any()).Do(func(uint64, uint32) { 1938 callback() 1939 }) 1940 f.Open(p9.ReadOnly) // Required. 1941 f.Readdir(0, 1) 1942 }, 1943 }, 1944 { 1945 name: "readlink", 1946 match: func(mode p9.FileMode) bool { return mode.IsSymlink() }, 1947 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 1948 backend.EXPECT().Readlink().Do(func() { 1949 callback() 1950 }) 1951 f.Readlink() 1952 }, 1953 }, 1954 { 1955 name: "connect", 1956 match: func(mode p9.FileMode) bool { return mode.IsSocket() }, 1957 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 1958 backend.EXPECT().Connect(gomock.Any()).Do(func(p9.SocketType) { 1959 callback() 1960 }) 1961 f.Connect(0) 1962 }, 1963 }, 1964 { 1965 name: "open", 1966 match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) }, 1967 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 1968 backend.EXPECT().Open(gomock.Any()).Do(func(p9.OpenFlags) { 1969 callback() 1970 }) 1971 f.Open(p9.ReadOnly) 1972 }, 1973 }, 1974 { 1975 name: "flush", 1976 match: func(mode p9.FileMode) bool { return true }, 1977 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 1978 backend.EXPECT().Flush().Do(func() { 1979 callback() 1980 }) 1981 f.Flush() 1982 }, 1983 }, 1984 } 1985 writeExclusive := []concurrentFn{ 1986 { 1987 // N.B. We can't really check getattr. But this is an 1988 // extremely low-risk function, it seems likely that 1989 // this check is paranoid anyways. 1990 name: "setattr", 1991 match: func(mode p9.FileMode) bool { return true }, 1992 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 1993 backend.EXPECT().SetAttr(gomock.Any(), gomock.Any()).Do(func(p9.SetAttrMask, p9.SetAttr) { 1994 callback() 1995 }) 1996 f.SetAttr(p9.SetAttrMask{}, p9.SetAttr{}) 1997 }, 1998 }, 1999 { 2000 name: "unlinkAt", 2001 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2002 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2003 backend.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Do(func(string, uint32) { 2004 callback() 2005 }) 2006 f.UnlinkAt(randomFileName(), 0) 2007 }, 2008 }, 2009 { 2010 name: "mknod", 2011 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2012 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2013 backend.EXPECT().Mknod(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.FileMode, uint32, uint32, p9.UID, p9.GID) { 2014 callback() 2015 }) 2016 f.Mknod(randomFileName(), 0, 0, 0, 0, 0) 2017 }, 2018 }, 2019 { 2020 name: "link", 2021 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2022 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2023 backend.EXPECT().Link(gomock.Any(), gomock.Any()).Do(func(p9.File, string) { 2024 callback() 2025 }) 2026 f.Link(f, randomFileName()) 2027 }, 2028 }, 2029 { 2030 name: "symlink", 2031 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2032 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2033 backend.EXPECT().Symlink(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, string, p9.UID, p9.GID) { 2034 callback() 2035 }) 2036 f.Symlink(randomFileName(), randomFileName(), 0, 0) 2037 }, 2038 }, 2039 { 2040 name: "mkdir", 2041 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2042 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2043 backend.EXPECT().Mkdir(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.FileMode, p9.UID, p9.GID) { 2044 callback() 2045 }) 2046 f.Mkdir(randomFileName(), 0, 0, 0) 2047 }, 2048 }, 2049 { 2050 name: "create", 2051 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2052 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2053 // Return an error for the creation operation, as this is the simplest. 2054 backend.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, p9.QID{}, uint32(0), unix.EINVAL).Do(func(string, p9.OpenFlags, p9.FileMode, p9.UID, p9.GID) { 2055 callback() 2056 }) 2057 f.Create(randomFileName(), p9.ReadOnly, 0, 0, 0) 2058 }, 2059 }, 2060 } 2061 globalExclusive := []concurrentFn{ 2062 { 2063 name: "remove", 2064 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2065 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2066 // Remove operates on a locked parent. So we 2067 // add a child, walk to it and call remove. 2068 // Note that because this operation can operate 2069 // concurrently with itself, we need to 2070 // generate a random file name. 2071 randomFile := randomFileName() 2072 backend.AddChild(randomFile, h.NewFile()) 2073 defer backend.RemoveChild(randomFile) 2074 _, file, err := f.Walk([]string{randomFile}) 2075 if err != nil { 2076 h.t.Fatalf("walk got %v, want nil", err) 2077 } 2078 2079 // Remove is automatically translated to the parent. 2080 backend.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Do(func(string, uint32) { 2081 callback() 2082 }) 2083 2084 // Remove is also a close. 2085 file.(deprecatedRemover).Remove() 2086 }, 2087 }, 2088 { 2089 name: "rename", 2090 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2091 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2092 // Similarly to remove, because we need to 2093 // operate on a child, we allow a walk. 2094 randomFile := randomFileName() 2095 backend.AddChild(randomFile, h.NewFile()) 2096 defer backend.RemoveChild(randomFile) 2097 _, file, err := f.Walk([]string{randomFile}) 2098 if err != nil { 2099 h.t.Fatalf("walk got %v, want nil", err) 2100 } 2101 defer file.Close() 2102 fileBackend := h.Pop(file) 2103 2104 // Rename is automatically translated to the parent. 2105 backend.EXPECT().RenameAt(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.File, string) { 2106 callback() 2107 }) 2108 2109 // Attempt the rename. 2110 fileBackend.EXPECT().Renamed(gomock.Any(), gomock.Any()) 2111 file.Rename(f, randomFileName()) 2112 }, 2113 }, 2114 { 2115 name: "renameAt", 2116 match: func(mode p9.FileMode) bool { return mode.IsDir() }, 2117 op: func(h *Harness, backend *Mock, f p9.File, callback func()) { 2118 backend.EXPECT().RenameAt(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.File, string) { 2119 callback() 2120 }) 2121 2122 // Attempt the rename. There are no active fids 2123 // with this name, so we don't need to expect 2124 // Renamed hooks on anything. 2125 f.RenameAt(randomFileName(), f, randomFileName()) 2126 }, 2127 }, 2128 } 2129 2130 for _, fn1 := range readExclusive { 2131 for _, fn2 := range readExclusive { 2132 for name := range newTypeMap(nil) { 2133 // Everything should be able to proceed in parallel. 2134 concurrentTest(t, name, fn1, fn2, true, true) 2135 concurrentTest(t, name, fn1, fn2, false, true) 2136 } 2137 } 2138 } 2139 2140 for _, fn1 := range append(readExclusive, writeExclusive...) { 2141 for _, fn2 := range writeExclusive { 2142 for name := range newTypeMap(nil) { 2143 // Only cross-directory functions should proceed in parallel. 2144 concurrentTest(t, name, fn1, fn2, true, false) 2145 concurrentTest(t, name, fn1, fn2, false, true) 2146 } 2147 } 2148 } 2149 2150 for _, fn1 := range append(append(readExclusive, writeExclusive...), globalExclusive...) { 2151 for _, fn2 := range globalExclusive { 2152 for name := range newTypeMap(nil) { 2153 // Nothing should be able to run in parallel. 2154 concurrentTest(t, name, fn1, fn2, true, false) 2155 concurrentTest(t, name, fn1, fn2, false, false) 2156 } 2157 } 2158 } 2159 } 2160 2161 func TestReadWriteConcurrent(t *testing.T) { 2162 h, c := NewHarness(t) 2163 defer h.Finish() 2164 2165 _, root := newRoot(h, c) 2166 defer root.Close() 2167 2168 const ( 2169 instances = 10 2170 iterations = 10000 2171 dataSize = 1024 2172 ) 2173 var ( 2174 dataSets [instances][dataSize]byte 2175 backends [instances]*Mock 2176 files [instances]p9.File 2177 ) 2178 2179 // Walk to the file normally. 2180 for i := 0; i < instances; i++ { 2181 _, backends[i], files[i] = walkHelper(h, "file", root) 2182 defer files[i].Close() 2183 } 2184 2185 // Open the files. 2186 for i := 0; i < instances; i++ { 2187 backends[i].EXPECT().Open(p9.ReadWrite) 2188 if _, _, _, err := files[i].Open(p9.ReadWrite); err != nil { 2189 t.Fatalf("open got %v, wanted nil", err) 2190 } 2191 } 2192 2193 // Initialize random data for each instance. 2194 for i := 0; i < instances; i++ { 2195 if _, err := rand.Read(dataSets[i][:]); err != nil { 2196 t.Fatalf("error initializing dataSet#%d, got %v", i, err) 2197 } 2198 } 2199 2200 // Define our random read/write mechanism. 2201 randRead := func(h *Harness, backend *Mock, f p9.File, data, test []byte) { 2202 // Prepare the backend. 2203 backend.EXPECT().ReadAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) { 2204 if n := copy(p, data); n != len(data) { 2205 // Note that we have to assert the result here, as the Return statement 2206 // below cannot be dynamic: it will be bound before this call is made. 2207 h.t.Errorf("wanted length %d, got %d", len(data), n) 2208 } 2209 }).Return(len(data), nil) 2210 2211 // Execute the read. 2212 if n, err := f.ReadAt(test, 0); n != len(test) || err != nil { 2213 t.Errorf("failed read: wanted (%d, nil), got (%d, %v)", len(test), n, err) 2214 return // No sense doing check below. 2215 } 2216 if !bytes.Equal(test, data) { 2217 t.Errorf("data integrity failed during read") // Not as expected. 2218 } 2219 } 2220 randWrite := func(h *Harness, backend *Mock, f p9.File, data []byte) { 2221 // Prepare the backend. 2222 backend.EXPECT().WriteAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) { 2223 if !bytes.Equal(p, data) { 2224 h.t.Errorf("data integrity failed during write") // Not as expected. 2225 } 2226 }).Return(len(data), nil) 2227 2228 // Execute the write. 2229 if n, err := f.WriteAt(data, 0); n != len(data) || err != nil { 2230 t.Errorf("failed read: wanted (%d, nil), got (%d, %v)", len(data), n, err) 2231 } 2232 } 2233 randReadWrite := func(n int, h *Harness, backend *Mock, f p9.File, data []byte) { 2234 test := make([]byte, len(data)) 2235 for i := 0; i < n; i++ { 2236 if rand.Intn(2) == 0 { 2237 randRead(h, backend, f, data, test) 2238 } else { 2239 randWrite(h, backend, f, data) 2240 } 2241 } 2242 } 2243 2244 // Start reading and writing. 2245 var wg sync.WaitGroup 2246 for i := 0; i < instances; i++ { 2247 wg.Add(1) 2248 go func(i int) { 2249 defer wg.Done() 2250 randReadWrite(iterations, h, backends[i], files[i], dataSets[i][:]) 2251 }(i) 2252 } 2253 wg.Wait() 2254 }