github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/pkg/archive/copy_unix_test.go (about) 1 //go:build !windows 2 // +build !windows 3 4 // TODO Windows: Some of these tests may be salvageable and portable to Windows. 5 6 package archive // import "github.com/docker/docker/pkg/archive" 7 8 import ( 9 "bytes" 10 "crypto/sha256" 11 "encoding/hex" 12 "fmt" 13 "io" 14 "os" 15 "path/filepath" 16 "strings" 17 "testing" 18 19 "gotest.tools/v3/assert" 20 ) 21 22 func removeAllPaths(paths ...string) { 23 for _, path := range paths { 24 os.RemoveAll(path) 25 } 26 } 27 28 func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) { 29 var err error 30 31 tmpDirA, err = os.MkdirTemp("", "archive-copy-test") 32 assert.NilError(t, err) 33 34 tmpDirB, err = os.MkdirTemp("", "archive-copy-test") 35 assert.NilError(t, err) 36 37 return 38 } 39 40 func isNotDir(err error) bool { 41 return strings.Contains(err.Error(), "not a directory") 42 } 43 44 func joinTrailingSep(pathElements ...string) string { 45 joined := filepath.Join(pathElements...) 46 47 return fmt.Sprintf("%s%c", joined, filepath.Separator) 48 } 49 50 func fileContentsEqual(t *testing.T, filenameA, filenameB string) (err error) { 51 t.Logf("checking for equal file contents: %q and %q\n", filenameA, filenameB) 52 53 fileA, err := os.Open(filenameA) 54 if err != nil { 55 return 56 } 57 defer fileA.Close() 58 59 fileB, err := os.Open(filenameB) 60 if err != nil { 61 return 62 } 63 defer fileB.Close() 64 65 hasher := sha256.New() 66 67 if _, err = io.Copy(hasher, fileA); err != nil { 68 return 69 } 70 71 hashA := hasher.Sum(nil) 72 hasher.Reset() 73 74 if _, err = io.Copy(hasher, fileB); err != nil { 75 return 76 } 77 78 hashB := hasher.Sum(nil) 79 80 if !bytes.Equal(hashA, hashB) { 81 err = fmt.Errorf("file content hashes not equal - expected %s, got %s", hex.EncodeToString(hashA), hex.EncodeToString(hashB)) 82 } 83 84 return 85 } 86 87 func dirContentsEqual(t *testing.T, newDir, oldDir string) (err error) { 88 t.Logf("checking for equal directory contents: %q and %q\n", newDir, oldDir) 89 90 var changes []Change 91 92 if changes, err = ChangesDirs(newDir, oldDir); err != nil { 93 return 94 } 95 96 if len(changes) != 0 { 97 err = fmt.Errorf("expected no changes between directories, but got: %v", changes) 98 } 99 100 return 101 } 102 103 func logDirContents(t *testing.T, dirPath string) { 104 logWalkedPaths := filepath.WalkFunc(func(path string, info os.FileInfo, err error) error { 105 if err != nil { 106 t.Errorf("stat error for path %q: %s", path, err) 107 return nil 108 } 109 110 if info.IsDir() { 111 path = joinTrailingSep(path) 112 } 113 114 t.Logf("\t%s", path) 115 116 return nil 117 }) 118 119 t.Logf("logging directory contents: %q", dirPath) 120 121 err := filepath.Walk(dirPath, logWalkedPaths) 122 assert.NilError(t, err) 123 } 124 125 func testCopyHelper(t *testing.T, srcPath, dstPath string) (err error) { 126 t.Logf("copying from %q to %q (not follow symbol link)", srcPath, dstPath) 127 128 return CopyResource(srcPath, dstPath, false) 129 } 130 131 func testCopyHelperFSym(t *testing.T, srcPath, dstPath string) (err error) { 132 t.Logf("copying from %q to %q (follow symbol link)", srcPath, dstPath) 133 134 return CopyResource(srcPath, dstPath, true) 135 } 136 137 // Basic assumptions about SRC and DST: 138 // 1. SRC must exist. 139 // 2. If SRC ends with a trailing separator, it must be a directory. 140 // 3. DST parent directory must exist. 141 // 4. If DST exists as a file, it must not end with a trailing separator. 142 143 // First get these easy error cases out of the way. 144 145 // Test for error when SRC does not exist. 146 func TestCopyErrSrcNotExists(t *testing.T) { 147 tmpDirA, tmpDirB := getTestTempDirs(t) 148 defer removeAllPaths(tmpDirA, tmpDirB) 149 150 if _, err := CopyInfoSourcePath(filepath.Join(tmpDirA, "file1"), false); !os.IsNotExist(err) { 151 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 152 } 153 } 154 155 // Test for error when SRC ends in a trailing 156 // path separator but it exists as a file. 157 func TestCopyErrSrcNotDir(t *testing.T) { 158 tmpDirA, tmpDirB := getTestTempDirs(t) 159 defer removeAllPaths(tmpDirA, tmpDirB) 160 161 // Load A with some sample files and directories. 162 createSampleDir(t, tmpDirA) 163 164 if _, err := CopyInfoSourcePath(joinTrailingSep(tmpDirA, "file1"), false); !isNotDir(err) { 165 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 166 } 167 } 168 169 // Test for error when SRC is a valid file or directory, 170 // but the DST parent directory does not exist. 171 func TestCopyErrDstParentNotExists(t *testing.T) { 172 tmpDirA, tmpDirB := getTestTempDirs(t) 173 defer removeAllPaths(tmpDirA, tmpDirB) 174 175 // Load A with some sample files and directories. 176 createSampleDir(t, tmpDirA) 177 178 srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} 179 180 // Try with a file source. 181 content, err := TarResource(srcInfo) 182 if err != nil { 183 t.Fatalf("unexpected error %T: %s", err, err) 184 } 185 defer content.Close() 186 187 // Copy to a file whose parent does not exist. 188 if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil { 189 t.Fatal("expected IsNotExist error, but got nil instead") 190 } 191 192 if !os.IsNotExist(err) { 193 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 194 } 195 196 // Try with a directory source. 197 srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true} 198 199 content, err = TarResource(srcInfo) 200 if err != nil { 201 t.Fatalf("unexpected error %T: %s", err, err) 202 } 203 defer content.Close() 204 205 // Copy to a directory whose parent does not exist. 206 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil { 207 t.Fatal("expected IsNotExist error, but got nil instead") 208 } 209 210 if !os.IsNotExist(err) { 211 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 212 } 213 } 214 215 // Test for error when DST ends in a trailing 216 // path separator but exists as a file. 217 func TestCopyErrDstNotDir(t *testing.T) { 218 tmpDirA, tmpDirB := getTestTempDirs(t) 219 defer removeAllPaths(tmpDirA, tmpDirB) 220 221 // Load A and B with some sample files and directories. 222 createSampleDir(t, tmpDirA) 223 createSampleDir(t, tmpDirB) 224 225 // Try with a file source. 226 srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} 227 228 content, err := TarResource(srcInfo) 229 if err != nil { 230 t.Fatalf("unexpected error %T: %s", err, err) 231 } 232 defer content.Close() 233 234 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil { 235 t.Fatal("expected IsNotDir error, but got nil instead") 236 } 237 238 if !isNotDir(err) { 239 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 240 } 241 242 // Try with a directory source. 243 srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true} 244 245 content, err = TarResource(srcInfo) 246 if err != nil { 247 t.Fatalf("unexpected error %T: %s", err, err) 248 } 249 defer content.Close() 250 251 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil { 252 t.Fatal("expected IsNotDir error, but got nil instead") 253 } 254 255 if !isNotDir(err) { 256 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 257 } 258 } 259 260 // Test to check if CopyTo works with a long (>100 characters) destination file name. 261 // This is a regression (see https://github.com/docker/for-linux/issues/484). 262 func TestCopyLongDstFilename(t *testing.T) { 263 const longName = "a_very_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_long_filename_that_is_101_characters" 264 tmpDirA, tmpDirB := getTestTempDirs(t) 265 defer removeAllPaths(tmpDirA, tmpDirB) 266 267 // Load A with some sample files and directories. 268 createSampleDir(t, tmpDirA) 269 270 srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} 271 272 content, err := TarResource(srcInfo) 273 if err != nil { 274 t.Fatalf("unexpected error %T: %s", err, err) 275 } 276 defer content.Close() 277 278 err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, longName)) 279 if err != nil { 280 t.Fatalf("unexpected error %T: %s", err, err) 281 } 282 } 283 284 // Possibilities are reduced to the remaining 10 cases: 285 // 286 // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action 287 // =================================================================================================== 288 // A | no | - | no | - | no | create file 289 // B | no | - | no | - | yes | error 290 // C | no | - | yes | no | - | overwrite file 291 // D | no | - | yes | yes | - | create file in dst dir 292 // E | yes | no | no | - | - | create dir, copy contents 293 // F | yes | no | yes | no | - | error 294 // G | yes | no | yes | yes | - | copy dir and contents 295 // H | yes | yes | no | - | - | create dir, copy contents 296 // I | yes | yes | yes | no | - | error 297 // J | yes | yes | yes | yes | - | copy dir contents 298 // 299 300 // A. SRC specifies a file and DST (no trailing path separator) doesn't exist. 301 // 302 // This should create a file with the name DST and copy the contents of the source 303 // file into it. 304 func TestCopyCaseA(t *testing.T) { 305 tmpDirA, tmpDirB := getTestTempDirs(t) 306 defer removeAllPaths(tmpDirA, tmpDirB) 307 308 // Load A with some sample files and directories. 309 createSampleDir(t, tmpDirA) 310 311 srcPath := filepath.Join(tmpDirA, "file1") 312 dstPath := filepath.Join(tmpDirB, "itWorks.txt") 313 314 var err error 315 316 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 317 t.Fatalf("unexpected error %T: %s", err, err) 318 } 319 320 err = fileContentsEqual(t, srcPath, dstPath) 321 assert.NilError(t, err) 322 os.Remove(dstPath) 323 324 symlinkPath := filepath.Join(tmpDirA, "symlink3") 325 symlinkPath1 := filepath.Join(tmpDirA, "symlink4") 326 linkTarget := filepath.Join(tmpDirA, "file1") 327 328 if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { 329 t.Fatalf("unexpected error %T: %s", err, err) 330 } 331 332 err = fileContentsEqual(t, linkTarget, dstPath) 333 assert.NilError(t, err) 334 os.Remove(dstPath) 335 if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil { 336 t.Fatalf("unexpected error %T: %s", err, err) 337 } 338 339 err = fileContentsEqual(t, linkTarget, dstPath) 340 assert.NilError(t, err) 341 } 342 343 // B. SRC specifies a file and DST (with trailing path separator) doesn't exist. 344 // 345 // This should cause an error because the copy operation cannot create a directory 346 // when copying a single file. 347 func TestCopyCaseB(t *testing.T) { 348 tmpDirA, tmpDirB := getTestTempDirs(t) 349 defer removeAllPaths(tmpDirA, tmpDirB) 350 351 // Load A with some sample files and directories. 352 createSampleDir(t, tmpDirA) 353 354 srcPath := filepath.Join(tmpDirA, "file1") 355 dstDir := joinTrailingSep(tmpDirB, "testDir") 356 357 var err error 358 359 if err = testCopyHelper(t, srcPath, dstDir); err == nil { 360 t.Fatal("expected ErrDirNotExists error, but got nil instead") 361 } 362 363 if err != ErrDirNotExists { 364 t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) 365 } 366 367 symlinkPath := filepath.Join(tmpDirA, "symlink3") 368 369 if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil { 370 t.Fatal("expected ErrDirNotExists error, but got nil instead") 371 } 372 if err != ErrDirNotExists { 373 t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) 374 } 375 376 } 377 378 // C. SRC specifies a file and DST exists as a file. 379 // 380 // This should overwrite the file at DST with the contents of the source file. 381 func TestCopyCaseC(t *testing.T) { 382 tmpDirA, tmpDirB := getTestTempDirs(t) 383 defer removeAllPaths(tmpDirA, tmpDirB) 384 385 // Load A and B with some sample files and directories. 386 createSampleDir(t, tmpDirA) 387 createSampleDir(t, tmpDirB) 388 389 srcPath := filepath.Join(tmpDirA, "file1") 390 dstPath := filepath.Join(tmpDirB, "file2") 391 392 var err error 393 394 // Ensure they start out different. 395 if err = fileContentsEqual(t, srcPath, dstPath); err == nil { 396 t.Fatal("expected different file contents") 397 } 398 399 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 400 t.Fatalf("unexpected error %T: %s", err, err) 401 } 402 403 err = fileContentsEqual(t, srcPath, dstPath) 404 assert.NilError(t, err) 405 } 406 407 // C. Symbol link following version: SRC specifies a file and DST exists as a file. 408 // 409 // This should overwrite the file at DST with the contents of the source file. 410 func TestCopyCaseCFSym(t *testing.T) { 411 tmpDirA, tmpDirB := getTestTempDirs(t) 412 defer removeAllPaths(tmpDirA, tmpDirB) 413 414 // Load A and B with some sample files and directories. 415 createSampleDir(t, tmpDirA) 416 createSampleDir(t, tmpDirB) 417 418 symlinkPathBad := filepath.Join(tmpDirA, "symlink1") 419 symlinkPath := filepath.Join(tmpDirA, "symlink3") 420 linkTarget := filepath.Join(tmpDirA, "file1") 421 dstPath := filepath.Join(tmpDirB, "file2") 422 423 var err error 424 425 // first to test broken link 426 if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil { 427 t.Fatalf("unexpected error %T: %s", err, err) 428 } 429 430 // test symbol link -> symbol link -> target 431 // Ensure they start out different. 432 if err = fileContentsEqual(t, linkTarget, dstPath); err == nil { 433 t.Fatal("expected different file contents") 434 } 435 436 if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { 437 t.Fatalf("unexpected error %T: %s", err, err) 438 } 439 440 err = fileContentsEqual(t, linkTarget, dstPath) 441 assert.NilError(t, err) 442 } 443 444 // D. SRC specifies a file and DST exists as a directory. 445 // 446 // This should place a copy of the source file inside it using the basename from 447 // SRC. Ensure this works whether DST has a trailing path separator or not. 448 func TestCopyCaseD(t *testing.T) { 449 tmpDirA, tmpDirB := getTestTempDirs(t) 450 defer removeAllPaths(tmpDirA, tmpDirB) 451 452 // Load A and B with some sample files and directories. 453 createSampleDir(t, tmpDirA) 454 createSampleDir(t, tmpDirB) 455 456 srcPath := filepath.Join(tmpDirA, "file1") 457 dstDir := filepath.Join(tmpDirB, "dir1") 458 dstPath := filepath.Join(dstDir, "file1") 459 460 var err error 461 462 // Ensure that dstPath doesn't exist. 463 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 464 t.Fatalf("did not expect dstPath %q to exist", dstPath) 465 } 466 467 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 468 t.Fatalf("unexpected error %T: %s", err, err) 469 } 470 471 err = fileContentsEqual(t, srcPath, dstPath) 472 assert.NilError(t, err) 473 474 // Now try again but using a trailing path separator for dstDir. 475 476 if err = os.RemoveAll(dstDir); err != nil { 477 t.Fatalf("unable to remove dstDir: %s", err) 478 } 479 480 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 481 t.Fatalf("unable to make dstDir: %s", err) 482 } 483 484 dstDir = joinTrailingSep(tmpDirB, "dir1") 485 486 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 487 t.Fatalf("unexpected error %T: %s", err, err) 488 } 489 490 err = fileContentsEqual(t, srcPath, dstPath) 491 assert.NilError(t, err) 492 } 493 494 // D. Symbol link following version: SRC specifies a file and DST exists as a directory. 495 // 496 // This should place a copy of the source file inside it using the basename from 497 // SRC. Ensure this works whether DST has a trailing path separator or not. 498 func TestCopyCaseDFSym(t *testing.T) { 499 tmpDirA, tmpDirB := getTestTempDirs(t) 500 defer removeAllPaths(tmpDirA, tmpDirB) 501 502 // Load A and B with some sample files and directories. 503 createSampleDir(t, tmpDirA) 504 createSampleDir(t, tmpDirB) 505 506 srcPath := filepath.Join(tmpDirA, "symlink4") 507 linkTarget := filepath.Join(tmpDirA, "file1") 508 dstDir := filepath.Join(tmpDirB, "dir1") 509 dstPath := filepath.Join(dstDir, "symlink4") 510 511 var err error 512 513 // Ensure that dstPath doesn't exist. 514 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 515 t.Fatalf("did not expect dstPath %q to exist", dstPath) 516 } 517 518 if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { 519 t.Fatalf("unexpected error %T: %s", err, err) 520 } 521 522 err = fileContentsEqual(t, linkTarget, dstPath) 523 assert.NilError(t, err) 524 525 // Now try again but using a trailing path separator for dstDir. 526 527 if err = os.RemoveAll(dstDir); err != nil { 528 t.Fatalf("unable to remove dstDir: %s", err) 529 } 530 531 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 532 t.Fatalf("unable to make dstDir: %s", err) 533 } 534 535 dstDir = joinTrailingSep(tmpDirB, "dir1") 536 537 if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { 538 t.Fatalf("unexpected error %T: %s", err, err) 539 } 540 541 err = fileContentsEqual(t, linkTarget, dstPath) 542 assert.NilError(t, err) 543 } 544 545 // E. SRC specifies a directory and DST does not exist. 546 // 547 // This should create a directory at DST and copy the contents of the SRC directory 548 // into the DST directory. Ensure this works whether DST has a trailing path 549 // separator or not. 550 func TestCopyCaseE(t *testing.T) { 551 tmpDirA, tmpDirB := getTestTempDirs(t) 552 defer removeAllPaths(tmpDirA, tmpDirB) 553 554 // Load A with some sample files and directories. 555 createSampleDir(t, tmpDirA) 556 557 srcDir := filepath.Join(tmpDirA, "dir1") 558 dstDir := filepath.Join(tmpDirB, "testDir") 559 560 var err error 561 562 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 563 t.Fatalf("unexpected error %T: %s", err, err) 564 } 565 566 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 567 t.Log("dir contents not equal") 568 logDirContents(t, tmpDirA) 569 logDirContents(t, tmpDirB) 570 t.Fatal(err) 571 } 572 573 // Now try again but using a trailing path separator for dstDir. 574 575 if err = os.RemoveAll(dstDir); err != nil { 576 t.Fatalf("unable to remove dstDir: %s", err) 577 } 578 579 dstDir = joinTrailingSep(tmpDirB, "testDir") 580 581 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 582 t.Fatalf("unexpected error %T: %s", err, err) 583 } 584 585 err = dirContentsEqual(t, dstDir, srcDir) 586 assert.NilError(t, err) 587 } 588 589 // E. Symbol link following version: SRC specifies a directory and DST does not exist. 590 // 591 // This should create a directory at DST and copy the contents of the SRC directory 592 // into the DST directory. Ensure this works whether DST has a trailing path 593 // separator or not. 594 func TestCopyCaseEFSym(t *testing.T) { 595 tmpDirA, tmpDirB := getTestTempDirs(t) 596 defer removeAllPaths(tmpDirA, tmpDirB) 597 598 // Load A with some sample files and directories. 599 createSampleDir(t, tmpDirA) 600 601 srcDir := filepath.Join(tmpDirA, "dirSymlink") 602 linkTarget := filepath.Join(tmpDirA, "dir1") 603 dstDir := filepath.Join(tmpDirB, "testDir") 604 605 var err error 606 607 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 608 t.Fatalf("unexpected error %T: %s", err, err) 609 } 610 611 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 612 t.Log("dir contents not equal") 613 logDirContents(t, tmpDirA) 614 logDirContents(t, tmpDirB) 615 t.Fatal(err) 616 } 617 618 // Now try again but using a trailing path separator for dstDir. 619 620 if err = os.RemoveAll(dstDir); err != nil { 621 t.Fatalf("unable to remove dstDir: %s", err) 622 } 623 624 dstDir = joinTrailingSep(tmpDirB, "testDir") 625 626 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 627 t.Fatalf("unexpected error %T: %s", err, err) 628 } 629 630 err = dirContentsEqual(t, dstDir, linkTarget) 631 assert.NilError(t, err) 632 } 633 634 // F. SRC specifies a directory and DST exists as a file. 635 // 636 // This should cause an error as it is not possible to overwrite a file with a 637 // directory. 638 func TestCopyCaseF(t *testing.T) { 639 tmpDirA, tmpDirB := getTestTempDirs(t) 640 defer removeAllPaths(tmpDirA, tmpDirB) 641 642 // Load A and B with some sample files and directories. 643 createSampleDir(t, tmpDirA) 644 createSampleDir(t, tmpDirB) 645 646 srcDir := filepath.Join(tmpDirA, "dir1") 647 symSrcDir := filepath.Join(tmpDirA, "dirSymlink") 648 dstFile := filepath.Join(tmpDirB, "file1") 649 650 var err error 651 652 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 653 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 654 } 655 656 if err != ErrCannotCopyDir { 657 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 658 } 659 660 // now test with symbol link 661 if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { 662 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 663 } 664 665 if err != ErrCannotCopyDir { 666 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 667 } 668 } 669 670 // G. SRC specifies a directory and DST exists as a directory. 671 // 672 // This should copy the SRC directory and all its contents to the DST directory. 673 // Ensure this works whether DST has a trailing path separator or not. 674 func TestCopyCaseG(t *testing.T) { 675 tmpDirA, tmpDirB := getTestTempDirs(t) 676 defer removeAllPaths(tmpDirA, tmpDirB) 677 678 // Load A and B with some sample files and directories. 679 createSampleDir(t, tmpDirA) 680 createSampleDir(t, tmpDirB) 681 682 srcDir := filepath.Join(tmpDirA, "dir1") 683 dstDir := filepath.Join(tmpDirB, "dir2") 684 resultDir := filepath.Join(dstDir, "dir1") 685 686 var err error 687 688 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 689 t.Fatalf("unexpected error %T: %s", err, err) 690 } 691 692 err = dirContentsEqual(t, resultDir, srcDir) 693 assert.NilError(t, err) 694 695 // Now try again but using a trailing path separator for dstDir. 696 697 if err = os.RemoveAll(dstDir); err != nil { 698 t.Fatalf("unable to remove dstDir: %s", err) 699 } 700 701 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 702 t.Fatalf("unable to make dstDir: %s", err) 703 } 704 705 dstDir = joinTrailingSep(tmpDirB, "dir2") 706 707 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 708 t.Fatalf("unexpected error %T: %s", err, err) 709 } 710 711 err = dirContentsEqual(t, resultDir, srcDir) 712 assert.NilError(t, err) 713 } 714 715 // G. Symbol link version: SRC specifies a directory and DST exists as a directory. 716 // 717 // This should copy the SRC directory and all its contents to the DST directory. 718 // Ensure this works whether DST has a trailing path separator or not. 719 func TestCopyCaseGFSym(t *testing.T) { 720 tmpDirA, tmpDirB := getTestTempDirs(t) 721 defer removeAllPaths(tmpDirA, tmpDirB) 722 723 // Load A and B with some sample files and directories. 724 createSampleDir(t, tmpDirA) 725 createSampleDir(t, tmpDirB) 726 727 srcDir := filepath.Join(tmpDirA, "dirSymlink") 728 linkTarget := filepath.Join(tmpDirA, "dir1") 729 dstDir := filepath.Join(tmpDirB, "dir2") 730 resultDir := filepath.Join(dstDir, "dirSymlink") 731 732 var err error 733 734 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 735 t.Fatalf("unexpected error %T: %s", err, err) 736 } 737 738 err = dirContentsEqual(t, resultDir, linkTarget) 739 assert.NilError(t, err) 740 741 // Now try again but using a trailing path separator for dstDir. 742 743 if err = os.RemoveAll(dstDir); err != nil { 744 t.Fatalf("unable to remove dstDir: %s", err) 745 } 746 747 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 748 t.Fatalf("unable to make dstDir: %s", err) 749 } 750 751 dstDir = joinTrailingSep(tmpDirB, "dir2") 752 753 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 754 t.Fatalf("unexpected error %T: %s", err, err) 755 } 756 757 err = dirContentsEqual(t, resultDir, linkTarget) 758 assert.NilError(t, err) 759 } 760 761 // H. SRC specifies a directory's contents only and DST does not exist. 762 // 763 // This should create a directory at DST and copy the contents of the SRC 764 // directory (but not the directory itself) into the DST directory. Ensure 765 // this works whether DST has a trailing path separator or not. 766 func TestCopyCaseH(t *testing.T) { 767 tmpDirA, tmpDirB := getTestTempDirs(t) 768 defer removeAllPaths(tmpDirA, tmpDirB) 769 770 // Load A with some sample files and directories. 771 createSampleDir(t, tmpDirA) 772 773 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 774 dstDir := filepath.Join(tmpDirB, "testDir") 775 776 var err error 777 778 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 779 t.Fatalf("unexpected error %T: %s", err, err) 780 } 781 782 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 783 t.Log("dir contents not equal") 784 logDirContents(t, tmpDirA) 785 logDirContents(t, tmpDirB) 786 t.Fatal(err) 787 } 788 789 // Now try again but using a trailing path separator for dstDir. 790 791 if err = os.RemoveAll(dstDir); err != nil { 792 t.Fatalf("unable to remove dstDir: %s", err) 793 } 794 795 dstDir = joinTrailingSep(tmpDirB, "testDir") 796 797 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 798 t.Fatalf("unexpected error %T: %s", err, err) 799 } 800 801 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 802 t.Log("dir contents not equal") 803 logDirContents(t, tmpDirA) 804 logDirContents(t, tmpDirB) 805 t.Fatal(err) 806 } 807 } 808 809 // H. Symbol link following version: SRC specifies a directory's contents only and DST does not exist. 810 // 811 // This should create a directory at DST and copy the contents of the SRC 812 // directory (but not the directory itself) into the DST directory. Ensure 813 // this works whether DST has a trailing path separator or not. 814 func TestCopyCaseHFSym(t *testing.T) { 815 tmpDirA, tmpDirB := getTestTempDirs(t) 816 defer removeAllPaths(tmpDirA, tmpDirB) 817 818 // Load A with some sample files and directories. 819 createSampleDir(t, tmpDirA) 820 821 srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." 822 linkTarget := filepath.Join(tmpDirA, "dir1") 823 dstDir := filepath.Join(tmpDirB, "testDir") 824 825 var err error 826 827 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 828 t.Fatalf("unexpected error %T: %s", err, err) 829 } 830 831 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 832 t.Log("dir contents not equal") 833 logDirContents(t, tmpDirA) 834 logDirContents(t, tmpDirB) 835 t.Fatal(err) 836 } 837 838 // Now try again but using a trailing path separator for dstDir. 839 840 if err = os.RemoveAll(dstDir); err != nil { 841 t.Fatalf("unable to remove dstDir: %s", err) 842 } 843 844 dstDir = joinTrailingSep(tmpDirB, "testDir") 845 846 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 847 t.Fatalf("unexpected error %T: %s", err, err) 848 } 849 850 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 851 t.Log("dir contents not equal") 852 logDirContents(t, tmpDirA) 853 logDirContents(t, tmpDirB) 854 t.Fatal(err) 855 } 856 } 857 858 // I. SRC specifies a directory's contents only and DST exists as a file. 859 // 860 // This should cause an error as it is not possible to overwrite a file with a 861 // directory. 862 func TestCopyCaseI(t *testing.T) { 863 tmpDirA, tmpDirB := getTestTempDirs(t) 864 defer removeAllPaths(tmpDirA, tmpDirB) 865 866 // Load A and B with some sample files and directories. 867 createSampleDir(t, tmpDirA) 868 createSampleDir(t, tmpDirB) 869 870 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 871 symSrcDir := filepath.Join(tmpDirB, "dirSymlink") 872 dstFile := filepath.Join(tmpDirB, "file1") 873 874 var err error 875 876 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 877 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 878 } 879 880 if err != ErrCannotCopyDir { 881 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 882 } 883 884 // now try with symbol link of dir 885 if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { 886 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 887 } 888 889 if err != ErrCannotCopyDir { 890 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 891 } 892 } 893 894 // J. SRC specifies a directory's contents only and DST exists as a directory. 895 // 896 // This should copy the contents of the SRC directory (but not the directory 897 // itself) into the DST directory. Ensure this works whether DST has a 898 // trailing path separator or not. 899 func TestCopyCaseJ(t *testing.T) { 900 tmpDirA, tmpDirB := getTestTempDirs(t) 901 defer removeAllPaths(tmpDirA, tmpDirB) 902 903 // Load A and B with some sample files and directories. 904 createSampleDir(t, tmpDirA) 905 createSampleDir(t, tmpDirB) 906 907 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 908 dstDir := filepath.Join(tmpDirB, "dir5") 909 910 var err error 911 912 // first to create an empty dir 913 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 914 t.Fatalf("unable to make dstDir: %s", err) 915 } 916 917 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 918 t.Fatalf("unexpected error %T: %s", err, err) 919 } 920 921 err = dirContentsEqual(t, dstDir, srcDir) 922 assert.NilError(t, err) 923 924 // Now try again but using a trailing path separator for dstDir. 925 926 if err = os.RemoveAll(dstDir); err != nil { 927 t.Fatalf("unable to remove dstDir: %s", err) 928 } 929 930 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 931 t.Fatalf("unable to make dstDir: %s", err) 932 } 933 934 dstDir = joinTrailingSep(tmpDirB, "dir5") 935 936 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 937 t.Fatalf("unexpected error %T: %s", err, err) 938 } 939 940 err = dirContentsEqual(t, dstDir, srcDir) 941 assert.NilError(t, err) 942 } 943 944 // J. Symbol link following version: SRC specifies a directory's contents only and DST exists as a directory. 945 // 946 // This should copy the contents of the SRC directory (but not the directory 947 // itself) into the DST directory. Ensure this works whether DST has a 948 // trailing path separator or not. 949 func TestCopyCaseJFSym(t *testing.T) { 950 tmpDirA, tmpDirB := getTestTempDirs(t) 951 defer removeAllPaths(tmpDirA, tmpDirB) 952 953 // Load A and B with some sample files and directories. 954 createSampleDir(t, tmpDirA) 955 createSampleDir(t, tmpDirB) 956 957 srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." 958 linkTarget := filepath.Join(tmpDirA, "dir1") 959 dstDir := filepath.Join(tmpDirB, "dir5") 960 961 var err error 962 963 // first to create an empty dir 964 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 965 t.Fatalf("unable to make dstDir: %s", err) 966 } 967 968 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 969 t.Fatalf("unexpected error %T: %s", err, err) 970 } 971 972 err = dirContentsEqual(t, dstDir, linkTarget) 973 assert.NilError(t, err) 974 975 // Now try again but using a trailing path separator for dstDir. 976 977 if err = os.RemoveAll(dstDir); err != nil { 978 t.Fatalf("unable to remove dstDir: %s", err) 979 } 980 981 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 982 t.Fatalf("unable to make dstDir: %s", err) 983 } 984 985 dstDir = joinTrailingSep(tmpDirB, "dir5") 986 987 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 988 t.Fatalf("unexpected error %T: %s", err, err) 989 } 990 991 err = dirContentsEqual(t, dstDir, linkTarget) 992 assert.NilError(t, err) 993 }