github.com/skatsuta/docker@v1.8.1/integration-cli/docker_cli_cp_to_container_test.go (about) 1 package main 2 3 import ( 4 "os" 5 6 "github.com/go-check/check" 7 ) 8 9 // docker cp LOCALPATH CONTAINER:PATH 10 11 // Try all of the test cases from the archive package which implements the 12 // internals of `docker cp` and ensure that the behavior matches when actually 13 // copying to and from containers. 14 15 // Basic assumptions about SRC and DST: 16 // 1. SRC must exist. 17 // 2. If SRC ends with a trailing separator, it must be a directory. 18 // 3. DST parent directory must exist. 19 // 4. If DST exists as a file, it must not end with a trailing separator. 20 21 // First get these easy error cases out of the way. 22 23 // Test for error when SRC does not exist. 24 func (s *DockerSuite) TestCpToErrSrcNotExists(c *check.C) { 25 cID := makeTestContainer(c, testContainerOptions{}) 26 defer deleteContainer(cID) 27 28 tmpDir := getTestDir(c, "test-cp-to-err-src-not-exists") 29 defer os.RemoveAll(tmpDir) 30 31 srcPath := cpPath(tmpDir, "file1") 32 dstPath := containerCpPath(cID, "file1") 33 34 err := runDockerCp(c, srcPath, dstPath) 35 if err == nil { 36 c.Fatal("expected IsNotExist error, but got nil instead") 37 } 38 39 if !isCpNotExist(err) { 40 c.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 41 } 42 } 43 44 // Test for error when SRC ends in a trailing 45 // path separator but it exists as a file. 46 func (s *DockerSuite) TestCpToErrSrcNotDir(c *check.C) { 47 cID := makeTestContainer(c, testContainerOptions{}) 48 defer deleteContainer(cID) 49 50 tmpDir := getTestDir(c, "test-cp-to-err-src-not-dir") 51 defer os.RemoveAll(tmpDir) 52 53 makeTestContentInDir(c, tmpDir) 54 55 srcPath := cpPathTrailingSep(tmpDir, "file1") 56 dstPath := containerCpPath(cID, "testDir") 57 58 err := runDockerCp(c, srcPath, dstPath) 59 if err == nil { 60 c.Fatal("expected IsNotDir error, but got nil instead") 61 } 62 63 if !isCpNotDir(err) { 64 c.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 65 } 66 } 67 68 // Test for error when SRC is a valid file or directory, 69 // bu the DST parent directory does not exist. 70 func (s *DockerSuite) TestCpToErrDstParentNotExists(c *check.C) { 71 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 72 defer deleteContainer(cID) 73 74 tmpDir := getTestDir(c, "test-cp-to-err-dst-parent-not-exists") 75 defer os.RemoveAll(tmpDir) 76 77 makeTestContentInDir(c, tmpDir) 78 79 // Try with a file source. 80 srcPath := cpPath(tmpDir, "file1") 81 dstPath := containerCpPath(cID, "/notExists", "file1") 82 83 err := runDockerCp(c, srcPath, dstPath) 84 if err == nil { 85 c.Fatal("expected IsNotExist error, but got nil instead") 86 } 87 88 if !isCpNotExist(err) { 89 c.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 90 } 91 92 // Try with a directory source. 93 srcPath = cpPath(tmpDir, "dir1") 94 95 if err := runDockerCp(c, srcPath, dstPath); err == nil { 96 c.Fatal("expected IsNotExist error, but got nil instead") 97 } 98 99 if !isCpNotExist(err) { 100 c.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 101 } 102 } 103 104 // Test for error when DST ends in a trailing path separator but exists as a 105 // file. Also test that we cannot overwirite an existing directory with a 106 // non-directory and cannot overwrite an existing 107 func (s *DockerSuite) TestCpToErrDstNotDir(c *check.C) { 108 cID := makeTestContainer(c, testContainerOptions{addContent: true}) 109 defer deleteContainer(cID) 110 111 tmpDir := getTestDir(c, "test-cp-to-err-dst-not-dir") 112 defer os.RemoveAll(tmpDir) 113 114 makeTestContentInDir(c, tmpDir) 115 116 // Try with a file source. 117 srcPath := cpPath(tmpDir, "dir1/file1-1") 118 dstPath := containerCpPathTrailingSep(cID, "file1") 119 120 // The client should encounter an error trying to stat the destination 121 // and then be unable to copy since the destination is asserted to be a 122 // directory but does not exist. 123 err := runDockerCp(c, srcPath, dstPath) 124 if err == nil { 125 c.Fatal("expected DirNotExist error, but got nil instead") 126 } 127 128 if !isCpDirNotExist(err) { 129 c.Fatalf("expected DirNotExist error, but got %T: %s", err, err) 130 } 131 132 // Try with a directory source. 133 srcPath = cpPath(tmpDir, "dir1") 134 135 // The client should encounter an error trying to stat the destination and 136 // then decide to extract to the parent directory instead with a rebased 137 // name in the source archive, but this directory would overwrite the 138 // existing file with the same name. 139 err = runDockerCp(c, srcPath, dstPath) 140 if err == nil { 141 c.Fatal("expected CannotOverwriteNonDirWithDir error, but got nil instead") 142 } 143 144 if !isCannotOverwriteNonDirWithDir(err) { 145 c.Fatalf("expected CannotOverwriteNonDirWithDir error, but got %T: %s", err, err) 146 } 147 } 148 149 // Check that copying from a local path to a symlink in a container copies to 150 // the symlink target and does not overwrite the container symlink itself. 151 func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) { 152 testRequires(c, SameHostDaemon) // Requires local volume mount bind. 153 154 testVol := getTestDir(c, "test-cp-to-symlink-destination-") 155 defer os.RemoveAll(testVol) 156 157 makeTestContentInDir(c, testVol) 158 159 cID := makeTestContainer(c, testContainerOptions{ 160 volumes: defaultVolumes(testVol), // Our bind mount is at /vol2 161 }) 162 defer deleteContainer(cID) 163 164 // First, copy a local file to a symlink to a file in the container. This 165 // should overwrite the symlink target contents with the source contents. 166 srcPath := cpPath(testVol, "file2") 167 dstPath := containerCpPath(cID, "/vol2/symlinkToFile1") 168 169 if err := runDockerCp(c, srcPath, dstPath); err != nil { 170 c.Fatalf("unexpected error %T: %s", err, err) 171 } 172 173 // The symlink should not have been modified. 174 if err := symlinkTargetEquals(c, cpPath(testVol, "symlinkToFile1"), "file1"); err != nil { 175 c.Fatal(err) 176 } 177 178 // The file should have the contents of "file2" now. 179 if err := fileContentEquals(c, cpPath(testVol, "file1"), "file2\n"); err != nil { 180 c.Fatal(err) 181 } 182 183 // Next, copy a local file to a symlink to a directory in the container. 184 // This should copy the file into the symlink target directory. 185 dstPath = containerCpPath(cID, "/vol2/symlinkToDir1") 186 187 if err := runDockerCp(c, srcPath, dstPath); err != nil { 188 c.Fatalf("unexpected error %T: %s", err, err) 189 } 190 191 // The symlink should not have been modified. 192 if err := symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"); err != nil { 193 c.Fatal(err) 194 } 195 196 // The file should have the contents of "file2" now. 197 if err := fileContentEquals(c, cpPath(testVol, "file2"), "file2\n"); err != nil { 198 c.Fatal(err) 199 } 200 201 // Next, copy a file to a symlink to a file that does not exist (a broken 202 // symlink) in the container. This should create the target file with the 203 // contents of the source file. 204 dstPath = containerCpPath(cID, "/vol2/brokenSymlinkToFileX") 205 206 if err := runDockerCp(c, srcPath, dstPath); err != nil { 207 c.Fatalf("unexpected error %T: %s", err, err) 208 } 209 210 // The symlink should not have been modified. 211 if err := symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToFileX"), "fileX"); err != nil { 212 c.Fatal(err) 213 } 214 215 // The file should have the contents of "file2" now. 216 if err := fileContentEquals(c, cpPath(testVol, "fileX"), "file2\n"); err != nil { 217 c.Fatal(err) 218 } 219 220 // Next, copy a local directory to a symlink to a directory in the 221 // container. This should copy the directory into the symlink target 222 // directory and not modify the symlink. 223 srcPath = cpPath(testVol, "/dir2") 224 dstPath = containerCpPath(cID, "/vol2/symlinkToDir1") 225 226 if err := runDockerCp(c, srcPath, dstPath); err != nil { 227 c.Fatalf("unexpected error %T: %s", err, err) 228 } 229 230 // The symlink should not have been modified. 231 if err := symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"); err != nil { 232 c.Fatal(err) 233 } 234 235 // The directory should now contain a copy of "dir2". 236 if err := fileContentEquals(c, cpPath(testVol, "dir1/dir2/file2-1"), "file2-1\n"); err != nil { 237 c.Fatal(err) 238 } 239 240 // Next, copy a local directory to a symlink to a local directory that does 241 // not exist (a broken symlink) in the container. This should create the 242 // target as a directory with the contents of the source directory. It 243 // should not modify the symlink. 244 dstPath = containerCpPath(cID, "/vol2/brokenSymlinkToDirX") 245 246 if err := runDockerCp(c, srcPath, dstPath); err != nil { 247 c.Fatalf("unexpected error %T: %s", err, err) 248 } 249 250 // The symlink should not have been modified. 251 if err := symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToDirX"), "dirX"); err != nil { 252 c.Fatal(err) 253 } 254 255 // The "dirX" directory should now be a copy of "dir2". 256 if err := fileContentEquals(c, cpPath(testVol, "dirX/file2-1"), "file2-1\n"); err != nil { 257 c.Fatal(err) 258 } 259 } 260 261 // Possibilities are reduced to the remaining 10 cases: 262 // 263 // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action 264 // =================================================================================================== 265 // A | no | - | no | - | no | create file 266 // B | no | - | no | - | yes | error 267 // C | no | - | yes | no | - | overwrite file 268 // D | no | - | yes | yes | - | create file in dst dir 269 // E | yes | no | no | - | - | create dir, copy contents 270 // F | yes | no | yes | no | - | error 271 // G | yes | no | yes | yes | - | copy dir and contents 272 // H | yes | yes | no | - | - | create dir, copy contents 273 // I | yes | yes | yes | no | - | error 274 // J | yes | yes | yes | yes | - | copy dir contents 275 // 276 277 // A. SRC specifies a file and DST (no trailing path separator) doesn't 278 // exist. This should create a file with the name DST and copy the 279 // contents of the source file into it. 280 func (s *DockerSuite) TestCpToCaseA(c *check.C) { 281 cID := makeTestContainer(c, testContainerOptions{ 282 workDir: "/root", command: makeCatFileCommand("itWorks.txt"), 283 }) 284 defer deleteContainer(cID) 285 286 tmpDir := getTestDir(c, "test-cp-to-case-a") 287 defer os.RemoveAll(tmpDir) 288 289 makeTestContentInDir(c, tmpDir) 290 291 srcPath := cpPath(tmpDir, "file1") 292 dstPath := containerCpPath(cID, "/root/itWorks.txt") 293 294 if err := runDockerCp(c, srcPath, dstPath); err != nil { 295 c.Fatalf("unexpected error %T: %s", err, err) 296 } 297 298 if err := containerStartOutputEquals(c, cID, "file1\n"); err != nil { 299 c.Fatal(err) 300 } 301 } 302 303 // B. SRC specifies a file and DST (with trailing path separator) doesn't 304 // exist. This should cause an error because the copy operation cannot 305 // create a directory when copying a single file. 306 func (s *DockerSuite) TestCpToCaseB(c *check.C) { 307 cID := makeTestContainer(c, testContainerOptions{ 308 command: makeCatFileCommand("testDir/file1"), 309 }) 310 defer deleteContainer(cID) 311 312 tmpDir := getTestDir(c, "test-cp-to-case-b") 313 defer os.RemoveAll(tmpDir) 314 315 makeTestContentInDir(c, tmpDir) 316 317 srcPath := cpPath(tmpDir, "file1") 318 dstDir := containerCpPathTrailingSep(cID, "testDir") 319 320 err := runDockerCp(c, srcPath, dstDir) 321 if err == nil { 322 c.Fatal("expected DirNotExists error, but got nil instead") 323 } 324 325 if !isCpDirNotExist(err) { 326 c.Fatalf("expected DirNotExists error, but got %T: %s", err, err) 327 } 328 } 329 330 // C. SRC specifies a file and DST exists as a file. This should overwrite 331 // the file at DST with the contents of the source file. 332 func (s *DockerSuite) TestCpToCaseC(c *check.C) { 333 cID := makeTestContainer(c, testContainerOptions{ 334 addContent: true, workDir: "/root", 335 command: makeCatFileCommand("file2"), 336 }) 337 defer deleteContainer(cID) 338 339 tmpDir := getTestDir(c, "test-cp-to-case-c") 340 defer os.RemoveAll(tmpDir) 341 342 makeTestContentInDir(c, tmpDir) 343 344 srcPath := cpPath(tmpDir, "file1") 345 dstPath := containerCpPath(cID, "/root/file2") 346 347 // Ensure the container's file starts with the original content. 348 if err := containerStartOutputEquals(c, cID, "file2\n"); err != nil { 349 c.Fatal(err) 350 } 351 352 if err := runDockerCp(c, srcPath, dstPath); err != nil { 353 c.Fatalf("unexpected error %T: %s", err, err) 354 } 355 356 // Should now contain file1's contents. 357 if err := containerStartOutputEquals(c, cID, "file1\n"); err != nil { 358 c.Fatal(err) 359 } 360 } 361 362 // D. SRC specifies a file and DST exists as a directory. This should place 363 // a copy of the source file inside it using the basename from SRC. Ensure 364 // this works whether DST has a trailing path separator or not. 365 func (s *DockerSuite) TestCpToCaseD(c *check.C) { 366 cID := makeTestContainer(c, testContainerOptions{ 367 addContent: true, 368 command: makeCatFileCommand("/dir1/file1"), 369 }) 370 defer deleteContainer(cID) 371 372 tmpDir := getTestDir(c, "test-cp-to-case-d") 373 defer os.RemoveAll(tmpDir) 374 375 makeTestContentInDir(c, tmpDir) 376 377 srcPath := cpPath(tmpDir, "file1") 378 dstDir := containerCpPath(cID, "dir1") 379 380 // Ensure that dstPath doesn't exist. 381 if err := containerStartOutputEquals(c, cID, ""); err != nil { 382 c.Fatal(err) 383 } 384 385 if err := runDockerCp(c, srcPath, dstDir); err != nil { 386 c.Fatalf("unexpected error %T: %s", err, err) 387 } 388 389 // Should now contain file1's contents. 390 if err := containerStartOutputEquals(c, cID, "file1\n"); err != nil { 391 c.Fatal(err) 392 } 393 394 // Now try again but using a trailing path separator for dstDir. 395 396 // Make new destination container. 397 cID = makeTestContainer(c, testContainerOptions{ 398 addContent: true, 399 command: makeCatFileCommand("/dir1/file1"), 400 }) 401 defer deleteContainer(cID) 402 403 dstDir = containerCpPathTrailingSep(cID, "dir1") 404 405 // Ensure that dstPath doesn't exist. 406 if err := containerStartOutputEquals(c, cID, ""); err != nil { 407 c.Fatal(err) 408 } 409 410 if err := runDockerCp(c, srcPath, dstDir); err != nil { 411 c.Fatalf("unexpected error %T: %s", err, err) 412 } 413 414 // Should now contain file1's contents. 415 if err := containerStartOutputEquals(c, cID, "file1\n"); err != nil { 416 c.Fatal(err) 417 } 418 } 419 420 // E. SRC specifies a directory and DST does not exist. This should create a 421 // directory at DST and copy the contents of the SRC directory into the DST 422 // directory. Ensure this works whether DST has a trailing path separator or 423 // not. 424 func (s *DockerSuite) TestCpToCaseE(c *check.C) { 425 cID := makeTestContainer(c, testContainerOptions{ 426 command: makeCatFileCommand("/testDir/file1-1"), 427 }) 428 defer deleteContainer(cID) 429 430 tmpDir := getTestDir(c, "test-cp-to-case-e") 431 defer os.RemoveAll(tmpDir) 432 433 makeTestContentInDir(c, tmpDir) 434 435 srcDir := cpPath(tmpDir, "dir1") 436 dstDir := containerCpPath(cID, "testDir") 437 438 if err := runDockerCp(c, srcDir, dstDir); err != nil { 439 c.Fatalf("unexpected error %T: %s", err, err) 440 } 441 442 // Should now contain file1-1's contents. 443 if err := containerStartOutputEquals(c, cID, "file1-1\n"); err != nil { 444 c.Fatal(err) 445 } 446 447 // Now try again but using a trailing path separator for dstDir. 448 449 // Make new destination container. 450 cID = makeTestContainer(c, testContainerOptions{ 451 command: makeCatFileCommand("/testDir/file1-1"), 452 }) 453 defer deleteContainer(cID) 454 455 dstDir = containerCpPathTrailingSep(cID, "testDir") 456 457 err := runDockerCp(c, srcDir, dstDir) 458 if err != nil { 459 c.Fatalf("unexpected error %T: %s", err, err) 460 } 461 462 // Should now contain file1-1's contents. 463 if err := containerStartOutputEquals(c, cID, "file1-1\n"); err != nil { 464 c.Fatal(err) 465 } 466 } 467 468 // F. SRC specifies a directory and DST exists as a file. This should cause an 469 // error as it is not possible to overwrite a file with a directory. 470 func (s *DockerSuite) TestCpToCaseF(c *check.C) { 471 cID := makeTestContainer(c, testContainerOptions{ 472 addContent: true, workDir: "/root", 473 }) 474 defer deleteContainer(cID) 475 476 tmpDir := getTestDir(c, "test-cp-to-case-f") 477 defer os.RemoveAll(tmpDir) 478 479 makeTestContentInDir(c, tmpDir) 480 481 srcDir := cpPath(tmpDir, "dir1") 482 dstFile := containerCpPath(cID, "/root/file1") 483 484 err := runDockerCp(c, srcDir, dstFile) 485 if err == nil { 486 c.Fatal("expected ErrCannotCopyDir error, but got nil instead") 487 } 488 489 if !isCpCannotCopyDir(err) { 490 c.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 491 } 492 } 493 494 // G. SRC specifies a directory and DST exists as a directory. This should copy 495 // the SRC directory and all its contents to the DST directory. Ensure this 496 // works whether DST has a trailing path separator or not. 497 func (s *DockerSuite) TestCpToCaseG(c *check.C) { 498 cID := makeTestContainer(c, testContainerOptions{ 499 addContent: true, workDir: "/root", 500 command: makeCatFileCommand("dir2/dir1/file1-1"), 501 }) 502 defer deleteContainer(cID) 503 504 tmpDir := getTestDir(c, "test-cp-to-case-g") 505 defer os.RemoveAll(tmpDir) 506 507 makeTestContentInDir(c, tmpDir) 508 509 srcDir := cpPath(tmpDir, "dir1") 510 dstDir := containerCpPath(cID, "/root/dir2") 511 512 // Ensure that dstPath doesn't exist. 513 if err := containerStartOutputEquals(c, cID, ""); err != nil { 514 c.Fatal(err) 515 } 516 517 if err := runDockerCp(c, srcDir, dstDir); err != nil { 518 c.Fatalf("unexpected error %T: %s", err, err) 519 } 520 521 // Should now contain file1-1's contents. 522 if err := containerStartOutputEquals(c, cID, "file1-1\n"); err != nil { 523 c.Fatal(err) 524 } 525 526 // Now try again but using a trailing path separator for dstDir. 527 528 // Make new destination container. 529 cID = makeTestContainer(c, testContainerOptions{ 530 addContent: true, 531 command: makeCatFileCommand("/dir2/dir1/file1-1"), 532 }) 533 defer deleteContainer(cID) 534 535 dstDir = containerCpPathTrailingSep(cID, "/dir2") 536 537 // Ensure that dstPath doesn't exist. 538 if err := containerStartOutputEquals(c, cID, ""); err != nil { 539 c.Fatal(err) 540 } 541 542 if err := runDockerCp(c, srcDir, dstDir); err != nil { 543 c.Fatalf("unexpected error %T: %s", err, err) 544 } 545 546 // Should now contain file1-1's contents. 547 if err := containerStartOutputEquals(c, cID, "file1-1\n"); err != nil { 548 c.Fatal(err) 549 } 550 } 551 552 // H. SRC specifies a directory's contents only and DST does not exist. This 553 // should create a directory at DST and copy the contents of the SRC 554 // directory (but not the directory itself) into the DST directory. Ensure 555 // this works whether DST has a trailing path separator or not. 556 func (s *DockerSuite) TestCpToCaseH(c *check.C) { 557 cID := makeTestContainer(c, testContainerOptions{ 558 command: makeCatFileCommand("/testDir/file1-1"), 559 }) 560 defer deleteContainer(cID) 561 562 tmpDir := getTestDir(c, "test-cp-to-case-h") 563 defer os.RemoveAll(tmpDir) 564 565 makeTestContentInDir(c, tmpDir) 566 567 srcDir := cpPathTrailingSep(tmpDir, "dir1") + "." 568 dstDir := containerCpPath(cID, "testDir") 569 570 if err := runDockerCp(c, srcDir, dstDir); err != nil { 571 c.Fatalf("unexpected error %T: %s", err, err) 572 } 573 574 // Should now contain file1-1's contents. 575 if err := containerStartOutputEquals(c, cID, "file1-1\n"); err != nil { 576 c.Fatal(err) 577 } 578 579 // Now try again but using a trailing path separator for dstDir. 580 581 // Make new destination container. 582 cID = makeTestContainer(c, testContainerOptions{ 583 command: makeCatFileCommand("/testDir/file1-1"), 584 }) 585 defer deleteContainer(cID) 586 587 dstDir = containerCpPathTrailingSep(cID, "testDir") 588 589 if err := runDockerCp(c, srcDir, dstDir); err != nil { 590 c.Fatalf("unexpected error %T: %s", err, err) 591 } 592 593 // Should now contain file1-1's contents. 594 if err := containerStartOutputEquals(c, cID, "file1-1\n"); err != nil { 595 c.Fatal(err) 596 } 597 } 598 599 // I. SRC specifies a direcotry's contents only and DST exists as a file. This 600 // should cause an error as it is not possible to overwrite a file with a 601 // directory. 602 func (s *DockerSuite) TestCpToCaseI(c *check.C) { 603 cID := makeTestContainer(c, testContainerOptions{ 604 addContent: true, workDir: "/root", 605 }) 606 defer deleteContainer(cID) 607 608 tmpDir := getTestDir(c, "test-cp-to-case-i") 609 defer os.RemoveAll(tmpDir) 610 611 makeTestContentInDir(c, tmpDir) 612 613 srcDir := cpPathTrailingSep(tmpDir, "dir1") + "." 614 dstFile := containerCpPath(cID, "/root/file1") 615 616 err := runDockerCp(c, srcDir, dstFile) 617 if err == nil { 618 c.Fatal("expected ErrCannotCopyDir error, but got nil instead") 619 } 620 621 if !isCpCannotCopyDir(err) { 622 c.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 623 } 624 } 625 626 // J. SRC specifies a directory's contents only and DST exists as a directory. 627 // This should copy the contents of the SRC directory (but not the directory 628 // itself) into the DST directory. Ensure this works whether DST has a 629 // trailing path separator or not. 630 func (s *DockerSuite) TestCpToCaseJ(c *check.C) { 631 cID := makeTestContainer(c, testContainerOptions{ 632 addContent: true, workDir: "/root", 633 command: makeCatFileCommand("/dir2/file1-1"), 634 }) 635 defer deleteContainer(cID) 636 637 tmpDir := getTestDir(c, "test-cp-to-case-j") 638 defer os.RemoveAll(tmpDir) 639 640 makeTestContentInDir(c, tmpDir) 641 642 srcDir := cpPathTrailingSep(tmpDir, "dir1") + "." 643 dstDir := containerCpPath(cID, "/dir2") 644 645 // Ensure that dstPath doesn't exist. 646 if err := containerStartOutputEquals(c, cID, ""); err != nil { 647 c.Fatal(err) 648 } 649 650 if err := runDockerCp(c, srcDir, dstDir); err != nil { 651 c.Fatalf("unexpected error %T: %s", err, err) 652 } 653 654 // Should now contain file1-1's contents. 655 if err := containerStartOutputEquals(c, cID, "file1-1\n"); err != nil { 656 c.Fatal(err) 657 } 658 659 // Now try again but using a trailing path separator for dstDir. 660 661 // Make new destination container. 662 cID = makeTestContainer(c, testContainerOptions{ 663 command: makeCatFileCommand("/dir2/file1-1"), 664 }) 665 defer deleteContainer(cID) 666 667 dstDir = containerCpPathTrailingSep(cID, "/dir2") 668 669 // Ensure that dstPath doesn't exist. 670 if err := containerStartOutputEquals(c, cID, ""); err != nil { 671 c.Fatal(err) 672 } 673 674 if err := runDockerCp(c, srcDir, dstDir); err != nil { 675 c.Fatalf("unexpected error %T: %s", err, err) 676 } 677 678 // Should now contain file1-1's contents. 679 if err := containerStartOutputEquals(c, cID, "file1-1\n"); err != nil { 680 c.Fatal(err) 681 } 682 } 683 684 // The `docker cp` command should also ensure that you cannot 685 // write to a container rootfs that is marked as read-only. 686 func (s *DockerSuite) TestCpToErrReadOnlyRootfs(c *check.C) { 687 tmpDir := getTestDir(c, "test-cp-to-err-read-only-rootfs") 688 defer os.RemoveAll(tmpDir) 689 690 makeTestContentInDir(c, tmpDir) 691 692 cID := makeTestContainer(c, testContainerOptions{ 693 readOnly: true, workDir: "/root", 694 command: makeCatFileCommand("shouldNotExist"), 695 }) 696 defer deleteContainer(cID) 697 698 srcPath := cpPath(tmpDir, "file1") 699 dstPath := containerCpPath(cID, "/root/shouldNotExist") 700 701 err := runDockerCp(c, srcPath, dstPath) 702 if err == nil { 703 c.Fatal("expected ErrContainerRootfsReadonly error, but got nil instead") 704 } 705 706 if !isCpCannotCopyReadOnly(err) { 707 c.Fatalf("expected ErrContainerRootfsReadonly error, but got %T: %s", err, err) 708 } 709 710 // Ensure that dstPath doesn't exist. 711 if err := containerStartOutputEquals(c, cID, ""); err != nil { 712 c.Fatal(err) 713 } 714 } 715 716 // The `docker cp` command should also ensure that you 717 // cannot write to a volume that is mounted as read-only. 718 func (s *DockerSuite) TestCpToErrReadOnlyVolume(c *check.C) { 719 tmpDir := getTestDir(c, "test-cp-to-err-read-only-volume") 720 defer os.RemoveAll(tmpDir) 721 722 makeTestContentInDir(c, tmpDir) 723 724 cID := makeTestContainer(c, testContainerOptions{ 725 volumes: defaultVolumes(tmpDir), workDir: "/root", 726 command: makeCatFileCommand("/vol_ro/shouldNotExist"), 727 }) 728 defer deleteContainer(cID) 729 730 srcPath := cpPath(tmpDir, "file1") 731 dstPath := containerCpPath(cID, "/vol_ro/shouldNotExist") 732 733 err := runDockerCp(c, srcPath, dstPath) 734 if err == nil { 735 c.Fatal("expected ErrVolumeReadonly error, but got nil instead") 736 } 737 738 if !isCpCannotCopyReadOnly(err) { 739 c.Fatalf("expected ErrVolumeReadonly error, but got %T: %s", err, err) 740 } 741 742 // Ensure that dstPath doesn't exist. 743 if err := containerStartOutputEquals(c, cID, ""); err != nil { 744 c.Fatal(err) 745 } 746 }