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