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