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