github.com/adityamillind98/moby@v23.0.0-rc.4+incompatible/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 // C. SRC specifies a file and DST exists as a file. 378 // 379 // This should overwrite the file at DST with the contents of the source file. 380 func TestCopyCaseC(t *testing.T) { 381 tmpDirA, tmpDirB := getTestTempDirs(t) 382 defer removeAllPaths(tmpDirA, tmpDirB) 383 384 // Load A and B with some sample files and directories. 385 createSampleDir(t, tmpDirA) 386 createSampleDir(t, tmpDirB) 387 388 srcPath := filepath.Join(tmpDirA, "file1") 389 dstPath := filepath.Join(tmpDirB, "file2") 390 391 var err error 392 393 // Ensure they start out different. 394 if err = fileContentsEqual(t, srcPath, dstPath); err == nil { 395 t.Fatal("expected different file contents") 396 } 397 398 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 399 t.Fatalf("unexpected error %T: %s", err, err) 400 } 401 402 err = fileContentsEqual(t, srcPath, dstPath) 403 assert.NilError(t, err) 404 } 405 406 // C. Symbol link following version: SRC specifies a file and DST exists as a file. 407 // 408 // This should overwrite the file at DST with the contents of the source file. 409 func TestCopyCaseCFSym(t *testing.T) { 410 tmpDirA, tmpDirB := getTestTempDirs(t) 411 defer removeAllPaths(tmpDirA, tmpDirB) 412 413 // Load A and B with some sample files and directories. 414 createSampleDir(t, tmpDirA) 415 createSampleDir(t, tmpDirB) 416 417 symlinkPathBad := filepath.Join(tmpDirA, "symlink1") 418 symlinkPath := filepath.Join(tmpDirA, "symlink3") 419 linkTarget := filepath.Join(tmpDirA, "file1") 420 dstPath := filepath.Join(tmpDirB, "file2") 421 422 var err error 423 424 // first to test broken link 425 if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil { 426 t.Fatalf("unexpected error %T: %s", err, err) 427 } 428 429 // test symbol link -> symbol link -> target 430 // Ensure they start out different. 431 if err = fileContentsEqual(t, linkTarget, dstPath); err == nil { 432 t.Fatal("expected different file contents") 433 } 434 435 if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { 436 t.Fatalf("unexpected error %T: %s", err, err) 437 } 438 439 err = fileContentsEqual(t, linkTarget, dstPath) 440 assert.NilError(t, err) 441 } 442 443 // D. SRC specifies a file and DST exists as a directory. 444 // 445 // This should place a copy of the source file inside it using the basename from 446 // SRC. Ensure this works whether DST has a trailing path separator or not. 447 func TestCopyCaseD(t *testing.T) { 448 tmpDirA, tmpDirB := getTestTempDirs(t) 449 defer removeAllPaths(tmpDirA, tmpDirB) 450 451 // Load A and B with some sample files and directories. 452 createSampleDir(t, tmpDirA) 453 createSampleDir(t, tmpDirB) 454 455 srcPath := filepath.Join(tmpDirA, "file1") 456 dstDir := filepath.Join(tmpDirB, "dir1") 457 dstPath := filepath.Join(dstDir, "file1") 458 459 var err error 460 461 // Ensure that dstPath doesn't exist. 462 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 463 t.Fatalf("did not expect dstPath %q to exist", dstPath) 464 } 465 466 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 467 t.Fatalf("unexpected error %T: %s", err, err) 468 } 469 470 err = fileContentsEqual(t, srcPath, dstPath) 471 assert.NilError(t, err) 472 473 // Now try again but using a trailing path separator for dstDir. 474 475 if err = os.RemoveAll(dstDir); err != nil { 476 t.Fatalf("unable to remove dstDir: %s", err) 477 } 478 479 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 480 t.Fatalf("unable to make dstDir: %s", err) 481 } 482 483 dstDir = joinTrailingSep(tmpDirB, "dir1") 484 485 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 486 t.Fatalf("unexpected error %T: %s", err, err) 487 } 488 489 err = fileContentsEqual(t, srcPath, dstPath) 490 assert.NilError(t, err) 491 } 492 493 // D. Symbol link following version: SRC specifies a file and DST exists as a directory. 494 // 495 // This should place a copy of the source file inside it using the basename from 496 // SRC. Ensure this works whether DST has a trailing path separator or not. 497 func TestCopyCaseDFSym(t *testing.T) { 498 tmpDirA, tmpDirB := getTestTempDirs(t) 499 defer removeAllPaths(tmpDirA, tmpDirB) 500 501 // Load A and B with some sample files and directories. 502 createSampleDir(t, tmpDirA) 503 createSampleDir(t, tmpDirB) 504 505 srcPath := filepath.Join(tmpDirA, "symlink4") 506 linkTarget := filepath.Join(tmpDirA, "file1") 507 dstDir := filepath.Join(tmpDirB, "dir1") 508 dstPath := filepath.Join(dstDir, "symlink4") 509 510 var err error 511 512 // Ensure that dstPath doesn't exist. 513 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 514 t.Fatalf("did not expect dstPath %q to exist", dstPath) 515 } 516 517 if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { 518 t.Fatalf("unexpected error %T: %s", err, err) 519 } 520 521 err = fileContentsEqual(t, linkTarget, dstPath) 522 assert.NilError(t, err) 523 524 // Now try again but using a trailing path separator for dstDir. 525 526 if err = os.RemoveAll(dstDir); err != nil { 527 t.Fatalf("unable to remove dstDir: %s", err) 528 } 529 530 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 531 t.Fatalf("unable to make dstDir: %s", err) 532 } 533 534 dstDir = joinTrailingSep(tmpDirB, "dir1") 535 536 if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { 537 t.Fatalf("unexpected error %T: %s", err, err) 538 } 539 540 err = fileContentsEqual(t, linkTarget, dstPath) 541 assert.NilError(t, err) 542 } 543 544 // E. SRC specifies a directory and DST does not exist. 545 // 546 // This should create a directory at DST and copy the contents of the SRC directory 547 // into the DST directory. Ensure this works whether DST has a trailing path 548 // separator or not. 549 func TestCopyCaseE(t *testing.T) { 550 tmpDirA, tmpDirB := getTestTempDirs(t) 551 defer removeAllPaths(tmpDirA, tmpDirB) 552 553 // Load A with some sample files and directories. 554 createSampleDir(t, tmpDirA) 555 556 srcDir := filepath.Join(tmpDirA, "dir1") 557 dstDir := filepath.Join(tmpDirB, "testDir") 558 559 var err error 560 561 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 562 t.Fatalf("unexpected error %T: %s", err, err) 563 } 564 565 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 566 t.Log("dir contents not equal") 567 logDirContents(t, tmpDirA) 568 logDirContents(t, tmpDirB) 569 t.Fatal(err) 570 } 571 572 // Now try again but using a trailing path separator for dstDir. 573 574 if err = os.RemoveAll(dstDir); err != nil { 575 t.Fatalf("unable to remove dstDir: %s", err) 576 } 577 578 dstDir = joinTrailingSep(tmpDirB, "testDir") 579 580 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 581 t.Fatalf("unexpected error %T: %s", err, err) 582 } 583 584 err = dirContentsEqual(t, dstDir, srcDir) 585 assert.NilError(t, err) 586 } 587 588 // E. Symbol link following version: SRC specifies a directory and DST does not exist. 589 // 590 // This should create a directory at DST and copy the contents of the SRC directory 591 // into the DST directory. Ensure this works whether DST has a trailing path 592 // separator or not. 593 func TestCopyCaseEFSym(t *testing.T) { 594 tmpDirA, tmpDirB := getTestTempDirs(t) 595 defer removeAllPaths(tmpDirA, tmpDirB) 596 597 // Load A with some sample files and directories. 598 createSampleDir(t, tmpDirA) 599 600 srcDir := filepath.Join(tmpDirA, "dirSymlink") 601 linkTarget := filepath.Join(tmpDirA, "dir1") 602 dstDir := filepath.Join(tmpDirB, "testDir") 603 604 var err error 605 606 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 607 t.Fatalf("unexpected error %T: %s", err, err) 608 } 609 610 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 611 t.Log("dir contents not equal") 612 logDirContents(t, tmpDirA) 613 logDirContents(t, tmpDirB) 614 t.Fatal(err) 615 } 616 617 // Now try again but using a trailing path separator for dstDir. 618 619 if err = os.RemoveAll(dstDir); err != nil { 620 t.Fatalf("unable to remove dstDir: %s", err) 621 } 622 623 dstDir = joinTrailingSep(tmpDirB, "testDir") 624 625 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 626 t.Fatalf("unexpected error %T: %s", err, err) 627 } 628 629 err = dirContentsEqual(t, dstDir, linkTarget) 630 assert.NilError(t, err) 631 } 632 633 // F. SRC specifies a directory and DST exists as a file. 634 // 635 // This should cause an error as it is not possible to overwrite a file with a 636 // directory. 637 func TestCopyCaseF(t *testing.T) { 638 tmpDirA, tmpDirB := getTestTempDirs(t) 639 defer removeAllPaths(tmpDirA, tmpDirB) 640 641 // Load A and B with some sample files and directories. 642 createSampleDir(t, tmpDirA) 643 createSampleDir(t, tmpDirB) 644 645 srcDir := filepath.Join(tmpDirA, "dir1") 646 symSrcDir := filepath.Join(tmpDirA, "dirSymlink") 647 dstFile := filepath.Join(tmpDirB, "file1") 648 649 var err error 650 651 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 652 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 653 } 654 655 if err != ErrCannotCopyDir { 656 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 657 } 658 659 // now test with symbol link 660 if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { 661 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 662 } 663 664 if err != ErrCannotCopyDir { 665 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 666 } 667 } 668 669 // G. SRC specifies a directory and DST exists as a directory. 670 // 671 // This should copy the SRC directory and all its contents to the DST directory. 672 // Ensure this works whether DST has a trailing path separator or not. 673 func TestCopyCaseG(t *testing.T) { 674 tmpDirA, tmpDirB := getTestTempDirs(t) 675 defer removeAllPaths(tmpDirA, tmpDirB) 676 677 // Load A and B with some sample files and directories. 678 createSampleDir(t, tmpDirA) 679 createSampleDir(t, tmpDirB) 680 681 srcDir := filepath.Join(tmpDirA, "dir1") 682 dstDir := filepath.Join(tmpDirB, "dir2") 683 resultDir := filepath.Join(dstDir, "dir1") 684 685 var err error 686 687 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 688 t.Fatalf("unexpected error %T: %s", err, err) 689 } 690 691 err = dirContentsEqual(t, resultDir, srcDir) 692 assert.NilError(t, err) 693 694 // Now try again but using a trailing path separator for dstDir. 695 696 if err = os.RemoveAll(dstDir); err != nil { 697 t.Fatalf("unable to remove dstDir: %s", err) 698 } 699 700 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 701 t.Fatalf("unable to make dstDir: %s", err) 702 } 703 704 dstDir = joinTrailingSep(tmpDirB, "dir2") 705 706 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 707 t.Fatalf("unexpected error %T: %s", err, err) 708 } 709 710 err = dirContentsEqual(t, resultDir, srcDir) 711 assert.NilError(t, err) 712 } 713 714 // G. Symbol link version: SRC specifies a directory and DST exists as a directory. 715 // 716 // This should copy the SRC directory and all its contents to the DST directory. 717 // Ensure this works whether DST has a trailing path separator or not. 718 func TestCopyCaseGFSym(t *testing.T) { 719 tmpDirA, tmpDirB := getTestTempDirs(t) 720 defer removeAllPaths(tmpDirA, tmpDirB) 721 722 // Load A and B with some sample files and directories. 723 createSampleDir(t, tmpDirA) 724 createSampleDir(t, tmpDirB) 725 726 srcDir := filepath.Join(tmpDirA, "dirSymlink") 727 linkTarget := filepath.Join(tmpDirA, "dir1") 728 dstDir := filepath.Join(tmpDirB, "dir2") 729 resultDir := filepath.Join(dstDir, "dirSymlink") 730 731 var err error 732 733 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 734 t.Fatalf("unexpected error %T: %s", err, err) 735 } 736 737 err = dirContentsEqual(t, resultDir, linkTarget) 738 assert.NilError(t, err) 739 740 // Now try again but using a trailing path separator for dstDir. 741 742 if err = os.RemoveAll(dstDir); err != nil { 743 t.Fatalf("unable to remove dstDir: %s", err) 744 } 745 746 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 747 t.Fatalf("unable to make dstDir: %s", err) 748 } 749 750 dstDir = joinTrailingSep(tmpDirB, "dir2") 751 752 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 753 t.Fatalf("unexpected error %T: %s", err, err) 754 } 755 756 err = dirContentsEqual(t, resultDir, linkTarget) 757 assert.NilError(t, err) 758 } 759 760 // H. SRC specifies a directory's contents only and DST does not exist. 761 // 762 // This should create a directory at DST and copy the contents of the SRC 763 // directory (but not the directory itself) into the DST directory. Ensure 764 // this works whether DST has a trailing path separator or not. 765 func TestCopyCaseH(t *testing.T) { 766 tmpDirA, tmpDirB := getTestTempDirs(t) 767 defer removeAllPaths(tmpDirA, tmpDirB) 768 769 // Load A with some sample files and directories. 770 createSampleDir(t, tmpDirA) 771 772 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 773 dstDir := filepath.Join(tmpDirB, "testDir") 774 775 var err error 776 777 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 778 t.Fatalf("unexpected error %T: %s", err, err) 779 } 780 781 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 782 t.Log("dir contents not equal") 783 logDirContents(t, tmpDirA) 784 logDirContents(t, tmpDirB) 785 t.Fatal(err) 786 } 787 788 // Now try again but using a trailing path separator for dstDir. 789 790 if err = os.RemoveAll(dstDir); err != nil { 791 t.Fatalf("unable to remove dstDir: %s", err) 792 } 793 794 dstDir = joinTrailingSep(tmpDirB, "testDir") 795 796 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 797 t.Fatalf("unexpected error %T: %s", err, err) 798 } 799 800 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 801 t.Log("dir contents not equal") 802 logDirContents(t, tmpDirA) 803 logDirContents(t, tmpDirB) 804 t.Fatal(err) 805 } 806 } 807 808 // H. Symbol link following version: SRC specifies a directory's contents only and DST does not exist. 809 // 810 // This should create a directory at DST and copy the contents of the SRC 811 // directory (but not the directory itself) into the DST directory. Ensure 812 // this works whether DST has a trailing path separator or not. 813 func TestCopyCaseHFSym(t *testing.T) { 814 tmpDirA, tmpDirB := getTestTempDirs(t) 815 defer removeAllPaths(tmpDirA, tmpDirB) 816 817 // Load A with some sample files and directories. 818 createSampleDir(t, tmpDirA) 819 820 srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." 821 linkTarget := filepath.Join(tmpDirA, "dir1") 822 dstDir := filepath.Join(tmpDirB, "testDir") 823 824 var err error 825 826 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 827 t.Fatalf("unexpected error %T: %s", err, err) 828 } 829 830 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 831 t.Log("dir contents not equal") 832 logDirContents(t, tmpDirA) 833 logDirContents(t, tmpDirB) 834 t.Fatal(err) 835 } 836 837 // Now try again but using a trailing path separator for dstDir. 838 839 if err = os.RemoveAll(dstDir); err != nil { 840 t.Fatalf("unable to remove dstDir: %s", err) 841 } 842 843 dstDir = joinTrailingSep(tmpDirB, "testDir") 844 845 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 846 t.Fatalf("unexpected error %T: %s", err, err) 847 } 848 849 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 850 t.Log("dir contents not equal") 851 logDirContents(t, tmpDirA) 852 logDirContents(t, tmpDirB) 853 t.Fatal(err) 854 } 855 } 856 857 // I. SRC specifies a directory's contents only and DST exists as a file. 858 // 859 // This should cause an error as it is not possible to overwrite a file with a 860 // directory. 861 func TestCopyCaseI(t *testing.T) { 862 tmpDirA, tmpDirB := getTestTempDirs(t) 863 defer removeAllPaths(tmpDirA, tmpDirB) 864 865 // Load A and B with some sample files and directories. 866 createSampleDir(t, tmpDirA) 867 createSampleDir(t, tmpDirB) 868 869 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 870 symSrcDir := filepath.Join(tmpDirB, "dirSymlink") 871 dstFile := filepath.Join(tmpDirB, "file1") 872 873 var err error 874 875 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 876 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 877 } 878 879 if err != ErrCannotCopyDir { 880 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 881 } 882 883 // now try with symbol link of dir 884 if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { 885 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 886 } 887 888 if err != ErrCannotCopyDir { 889 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 890 } 891 } 892 893 // J. SRC specifies a directory's contents only and DST exists as a directory. 894 // 895 // This should copy the contents of the SRC directory (but not the directory 896 // itself) into the DST directory. Ensure this works whether DST has a 897 // trailing path separator or not. 898 func TestCopyCaseJ(t *testing.T) { 899 tmpDirA, tmpDirB := getTestTempDirs(t) 900 defer removeAllPaths(tmpDirA, tmpDirB) 901 902 // Load A and B with some sample files and directories. 903 createSampleDir(t, tmpDirA) 904 createSampleDir(t, tmpDirB) 905 906 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 907 dstDir := filepath.Join(tmpDirB, "dir5") 908 909 var err error 910 911 // first to create an empty dir 912 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 913 t.Fatalf("unable to make dstDir: %s", err) 914 } 915 916 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 917 t.Fatalf("unexpected error %T: %s", err, err) 918 } 919 920 err = dirContentsEqual(t, dstDir, srcDir) 921 assert.NilError(t, err) 922 923 // Now try again but using a trailing path separator for dstDir. 924 925 if err = os.RemoveAll(dstDir); err != nil { 926 t.Fatalf("unable to remove dstDir: %s", err) 927 } 928 929 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 930 t.Fatalf("unable to make dstDir: %s", err) 931 } 932 933 dstDir = joinTrailingSep(tmpDirB, "dir5") 934 935 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 936 t.Fatalf("unexpected error %T: %s", err, err) 937 } 938 939 err = dirContentsEqual(t, dstDir, srcDir) 940 assert.NilError(t, err) 941 } 942 943 // J. Symbol link following version: SRC specifies a directory's contents only and DST exists as a directory. 944 // 945 // This should copy the contents of the SRC directory (but not the directory 946 // itself) into the DST directory. Ensure this works whether DST has a 947 // trailing path separator or not. 948 func TestCopyCaseJFSym(t *testing.T) { 949 tmpDirA, tmpDirB := getTestTempDirs(t) 950 defer removeAllPaths(tmpDirA, tmpDirB) 951 952 // Load A and B with some sample files and directories. 953 createSampleDir(t, tmpDirA) 954 createSampleDir(t, tmpDirB) 955 956 srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." 957 linkTarget := filepath.Join(tmpDirA, "dir1") 958 dstDir := filepath.Join(tmpDirB, "dir5") 959 960 var err error 961 962 // first to create an empty dir 963 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 964 t.Fatalf("unable to make dstDir: %s", err) 965 } 966 967 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 968 t.Fatalf("unexpected error %T: %s", err, err) 969 } 970 971 err = dirContentsEqual(t, dstDir, linkTarget) 972 assert.NilError(t, err) 973 974 // Now try again but using a trailing path separator for dstDir. 975 976 if err = os.RemoveAll(dstDir); err != nil { 977 t.Fatalf("unable to remove dstDir: %s", err) 978 } 979 980 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 981 t.Fatalf("unable to make dstDir: %s", err) 982 } 983 984 dstDir = joinTrailingSep(tmpDirB, "dir5") 985 986 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 987 t.Fatalf("unexpected error %T: %s", err, err) 988 } 989 990 err = dirContentsEqual(t, dstDir, linkTarget) 991 assert.NilError(t, err) 992 }