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