github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/integration-cli/docker_cli_cp_to_container_test.go (about) 1 package main 2 3 import ( 4 "os" 5 6 "github.com/docker/docker/integration-cli/checker" 7 "github.com/go-check/check" 8 ) 9 10 // Try all of the test cases from the archive package which implements the 11 // internals of `docker cp` and ensure that the behavior matches when actually 12 // copying to and from containers. 13 14 // Basic assumptions about SRC and DST: 15 // 1. SRC must exist. 16 // 2. If SRC ends with a trailing separator, it must be a directory. 17 // 3. DST parent directory must exist. 18 // 4. If DST exists as a file, it must not end with a trailing separator. 19 20 // Check that copying from a local path to a symlink in a container copies to 21 // the symlink target and does not overwrite the container symlink itself. 22 func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) { 23 // stat /tmp/test-cp-to-symlink-destination-262430901/vol3 gets permission denied for the user 24 testRequires(c, NotUserNamespace) 25 testRequires(c, DaemonIsLinux) 26 testRequires(c, SameHostDaemon) // Requires local volume mount bind. 27 28 testVol := getTestDir(c, "test-cp-to-symlink-destination-") 29 defer os.RemoveAll(testVol) 30 31 makeTestContentInDir(c, testVol) 32 33 containerID := makeTestContainer(c, testContainerOptions{ 34 volumes: defaultVolumes(testVol), // Our bind mount is at /vol2 35 }) 36 37 // First, copy a local file to a symlink to a file in the container. This 38 // should overwrite the symlink target contents with the source contents. 39 srcPath := cpPath(testVol, "file2") 40 dstPath := containerCpPath(containerID, "/vol2/symlinkToFile1") 41 42 c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil) 43 44 // The symlink should not have been modified. 45 c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToFile1"), "file1"), checker.IsNil) 46 47 // The file should have the contents of "file2" now. 48 c.Assert(fileContentEquals(c, cpPath(testVol, "file1"), "file2\n"), checker.IsNil) 49 50 // Next, copy a local file to a symlink to a directory in the container. 51 // This should copy the file into the symlink target directory. 52 dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1") 53 54 c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil) 55 56 // The symlink should not have been modified. 57 c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil) 58 59 // The file should have the contents of "file2" now. 60 c.Assert(fileContentEquals(c, cpPath(testVol, "file2"), "file2\n"), checker.IsNil) 61 62 // Next, copy a file to a symlink to a file that does not exist (a broken 63 // symlink) in the container. This should create the target file with the 64 // contents of the source file. 65 dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToFileX") 66 67 c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil) 68 69 // The symlink should not have been modified. 70 c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToFileX"), "fileX"), checker.IsNil) 71 72 // The file should have the contents of "file2" now. 73 c.Assert(fileContentEquals(c, cpPath(testVol, "fileX"), "file2\n"), checker.IsNil) 74 75 // Next, copy a local directory to a symlink to a directory in the 76 // container. This should copy the directory into the symlink target 77 // directory and not modify the symlink. 78 srcPath = cpPath(testVol, "/dir2") 79 dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1") 80 81 c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil) 82 83 // The symlink should not have been modified. 84 c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil) 85 86 // The directory should now contain a copy of "dir2". 87 c.Assert(fileContentEquals(c, cpPath(testVol, "dir1/dir2/file2-1"), "file2-1\n"), checker.IsNil) 88 89 // Next, copy a local directory to a symlink to a local directory that does 90 // not exist (a broken symlink) in the container. This should create the 91 // target as a directory with the contents of the source directory. It 92 // should not modify the symlink. 93 dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToDirX") 94 95 c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil) 96 97 // The symlink should not have been modified. 98 c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToDirX"), "dirX"), checker.IsNil) 99 100 // The "dirX" directory should now be a copy of "dir2". 101 c.Assert(fileContentEquals(c, cpPath(testVol, "dirX/file2-1"), "file2-1\n"), checker.IsNil) 102 } 103 104 // Possibilities are reduced to the remaining 10 cases: 105 // 106 // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action 107 // =================================================================================================== 108 // A | no | - | no | - | no | create file 109 // B | no | - | no | - | yes | error 110 // C | no | - | yes | no | - | overwrite file 111 // D | no | - | yes | yes | - | create file in dst dir 112 // E | yes | no | no | - | - | create dir, copy contents 113 // F | yes | no | yes | no | - | error 114 // G | yes | no | yes | yes | - | copy dir and contents 115 // H | yes | yes | no | - | - | create dir, copy contents 116 // I | yes | yes | yes | no | - | error 117 // J | yes | yes | yes | yes | - | copy dir contents 118 // 119 120 // A. SRC specifies a file and DST (no trailing path separator) doesn't 121 // exist. This should create a file with the name DST and copy the 122 // contents of the source file into it. 123 func (s *DockerSuite) TestCpToCaseA(c *check.C) { 124 containerID := makeTestContainer(c, testContainerOptions{ 125 workDir: "/root", command: makeCatFileCommand("itWorks.txt"), 126 }) 127 128 tmpDir := getTestDir(c, "test-cp-to-case-a") 129 defer os.RemoveAll(tmpDir) 130 131 makeTestContentInDir(c, tmpDir) 132 133 srcPath := cpPath(tmpDir, "file1") 134 dstPath := containerCpPath(containerID, "/root/itWorks.txt") 135 136 c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil) 137 138 c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil) 139 } 140 141 // B. SRC specifies a file and DST (with trailing path separator) doesn't 142 // exist. This should cause an error because the copy operation cannot 143 // create a directory when copying a single file. 144 func (s *DockerSuite) TestCpToCaseB(c *check.C) { 145 containerID := makeTestContainer(c, testContainerOptions{ 146 command: makeCatFileCommand("testDir/file1"), 147 }) 148 149 tmpDir := getTestDir(c, "test-cp-to-case-b") 150 defer os.RemoveAll(tmpDir) 151 152 makeTestContentInDir(c, tmpDir) 153 154 srcPath := cpPath(tmpDir, "file1") 155 dstDir := containerCpPathTrailingSep(containerID, "testDir") 156 157 err := runDockerCp(c, srcPath, dstDir, nil) 158 c.Assert(err, checker.NotNil) 159 160 c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExists error, but got %T: %s", err, err)) 161 } 162 163 // C. SRC specifies a file and DST exists as a file. This should overwrite 164 // the file at DST with the contents of the source file. 165 func (s *DockerSuite) TestCpToCaseC(c *check.C) { 166 testRequires(c, DaemonIsLinux) 167 containerID := makeTestContainer(c, testContainerOptions{ 168 addContent: true, workDir: "/root", 169 command: makeCatFileCommand("file2"), 170 }) 171 172 tmpDir := getTestDir(c, "test-cp-to-case-c") 173 defer os.RemoveAll(tmpDir) 174 175 makeTestContentInDir(c, tmpDir) 176 177 srcPath := cpPath(tmpDir, "file1") 178 dstPath := containerCpPath(containerID, "/root/file2") 179 180 // Ensure the container's file starts with the original content. 181 c.Assert(containerStartOutputEquals(c, containerID, "file2\n"), checker.IsNil) 182 183 c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil) 184 185 // Should now contain file1's contents. 186 c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil) 187 } 188 189 // D. SRC specifies a file and DST exists as a directory. This should place 190 // a copy of the source file inside it using the basename from SRC. Ensure 191 // this works whether DST has a trailing path separator or not. 192 func (s *DockerSuite) TestCpToCaseD(c *check.C) { 193 testRequires(c, DaemonIsLinux) 194 containerID := makeTestContainer(c, testContainerOptions{ 195 addContent: true, 196 command: makeCatFileCommand("/dir1/file1"), 197 }) 198 199 tmpDir := getTestDir(c, "test-cp-to-case-d") 200 defer os.RemoveAll(tmpDir) 201 202 makeTestContentInDir(c, tmpDir) 203 204 srcPath := cpPath(tmpDir, "file1") 205 dstDir := containerCpPath(containerID, "dir1") 206 207 // Ensure that dstPath doesn't exist. 208 c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil) 209 210 c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil) 211 212 // Should now contain file1's contents. 213 c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil) 214 215 // Now try again but using a trailing path separator for dstDir. 216 217 // Make new destination container. 218 containerID = makeTestContainer(c, testContainerOptions{ 219 addContent: true, 220 command: makeCatFileCommand("/dir1/file1"), 221 }) 222 223 dstDir = containerCpPathTrailingSep(containerID, "dir1") 224 225 // Ensure that dstPath doesn't exist. 226 c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil) 227 228 c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil) 229 230 // Should now contain file1's contents. 231 c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil) 232 } 233 234 // E. SRC specifies a directory and DST does not exist. This should create a 235 // directory at DST and copy the contents of the SRC directory into the DST 236 // directory. Ensure this works whether DST has a trailing path separator or 237 // not. 238 func (s *DockerSuite) TestCpToCaseE(c *check.C) { 239 containerID := makeTestContainer(c, testContainerOptions{ 240 command: makeCatFileCommand("/testDir/file1-1"), 241 }) 242 243 tmpDir := getTestDir(c, "test-cp-to-case-e") 244 defer os.RemoveAll(tmpDir) 245 246 makeTestContentInDir(c, tmpDir) 247 248 srcDir := cpPath(tmpDir, "dir1") 249 dstDir := containerCpPath(containerID, "testDir") 250 251 c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil) 252 253 // Should now contain file1-1's contents. 254 c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil) 255 256 // Now try again but using a trailing path separator for dstDir. 257 258 // Make new destination container. 259 containerID = makeTestContainer(c, testContainerOptions{ 260 command: makeCatFileCommand("/testDir/file1-1"), 261 }) 262 263 dstDir = containerCpPathTrailingSep(containerID, "testDir") 264 265 c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil) 266 267 // Should now contain file1-1's contents. 268 c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil) 269 } 270 271 // F. SRC specifies a directory and DST exists as a file. This should cause an 272 // error as it is not possible to overwrite a file with a directory. 273 func (s *DockerSuite) TestCpToCaseF(c *check.C) { 274 testRequires(c, DaemonIsLinux) 275 containerID := makeTestContainer(c, testContainerOptions{ 276 addContent: true, workDir: "/root", 277 }) 278 279 tmpDir := getTestDir(c, "test-cp-to-case-f") 280 defer os.RemoveAll(tmpDir) 281 282 makeTestContentInDir(c, tmpDir) 283 284 srcDir := cpPath(tmpDir, "dir1") 285 dstFile := containerCpPath(containerID, "/root/file1") 286 287 err := runDockerCp(c, srcDir, dstFile, nil) 288 c.Assert(err, checker.NotNil) 289 290 c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err)) 291 } 292 293 // G. SRC specifies a directory and DST exists as a directory. This should copy 294 // the SRC directory and all its contents to the DST directory. Ensure this 295 // works whether DST has a trailing path separator or not. 296 func (s *DockerSuite) TestCpToCaseG(c *check.C) { 297 testRequires(c, DaemonIsLinux) 298 containerID := makeTestContainer(c, testContainerOptions{ 299 addContent: true, workDir: "/root", 300 command: makeCatFileCommand("dir2/dir1/file1-1"), 301 }) 302 303 tmpDir := getTestDir(c, "test-cp-to-case-g") 304 defer os.RemoveAll(tmpDir) 305 306 makeTestContentInDir(c, tmpDir) 307 308 srcDir := cpPath(tmpDir, "dir1") 309 dstDir := containerCpPath(containerID, "/root/dir2") 310 311 // Ensure that dstPath doesn't exist. 312 c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil) 313 314 c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil) 315 316 // Should now contain file1-1's contents. 317 c.Assert(containerStartOutputEquals(c, containerID, "file1-1\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("/dir2/dir1/file1-1"), 325 }) 326 327 dstDir = containerCpPathTrailingSep(containerID, "/dir2") 328 329 // Ensure that dstPath doesn't exist. 330 c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil) 331 332 c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil) 333 334 // Should now contain file1-1's contents. 335 c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil) 336 } 337 338 // H. SRC specifies a directory's contents only and DST does not exist. This 339 // should create a directory at DST and copy the contents of the SRC 340 // directory (but not the directory itself) into the DST directory. Ensure 341 // this works whether DST has a trailing path separator or not. 342 func (s *DockerSuite) TestCpToCaseH(c *check.C) { 343 containerID := makeTestContainer(c, testContainerOptions{ 344 command: makeCatFileCommand("/testDir/file1-1"), 345 }) 346 347 tmpDir := getTestDir(c, "test-cp-to-case-h") 348 defer os.RemoveAll(tmpDir) 349 350 makeTestContentInDir(c, tmpDir) 351 352 srcDir := cpPathTrailingSep(tmpDir, "dir1") + "." 353 dstDir := containerCpPath(containerID, "testDir") 354 355 c.Assert(runDockerCp(c, srcDir, dstDir, nil), 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, nil), 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 // I. SRC specifies a directory's contents only and DST exists as a file. This 376 // should cause an error as it is not possible to overwrite a file with a 377 // directory. 378 func (s *DockerSuite) TestCpToCaseI(c *check.C) { 379 testRequires(c, DaemonIsLinux) 380 containerID := makeTestContainer(c, testContainerOptions{ 381 addContent: true, workDir: "/root", 382 }) 383 384 tmpDir := getTestDir(c, "test-cp-to-case-i") 385 defer os.RemoveAll(tmpDir) 386 387 makeTestContentInDir(c, tmpDir) 388 389 srcDir := cpPathTrailingSep(tmpDir, "dir1") + "." 390 dstFile := containerCpPath(containerID, "/root/file1") 391 392 err := runDockerCp(c, srcDir, dstFile, nil) 393 c.Assert(err, checker.NotNil) 394 395 c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err)) 396 } 397 398 // J. SRC specifies a directory's contents only and DST exists as a directory. 399 // This should copy the contents of the SRC directory (but not the directory 400 // itself) into the DST directory. Ensure this works whether DST has a 401 // trailing path separator or not. 402 func (s *DockerSuite) TestCpToCaseJ(c *check.C) { 403 testRequires(c, DaemonIsLinux) 404 containerID := makeTestContainer(c, testContainerOptions{ 405 addContent: true, workDir: "/root", 406 command: makeCatFileCommand("/dir2/file1-1"), 407 }) 408 409 tmpDir := getTestDir(c, "test-cp-to-case-j") 410 defer os.RemoveAll(tmpDir) 411 412 makeTestContentInDir(c, tmpDir) 413 414 srcDir := cpPathTrailingSep(tmpDir, "dir1") + "." 415 dstDir := containerCpPath(containerID, "/dir2") 416 417 // Ensure that dstPath doesn't exist. 418 c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil) 419 420 c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil) 421 422 // Should now contain file1-1's contents. 423 c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil) 424 425 // Now try again but using a trailing path separator for dstDir. 426 427 // Make new destination container. 428 containerID = makeTestContainer(c, testContainerOptions{ 429 command: makeCatFileCommand("/dir2/file1-1"), 430 }) 431 432 dstDir = containerCpPathTrailingSep(containerID, "/dir2") 433 434 // Ensure that dstPath doesn't exist. 435 c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil) 436 437 c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil) 438 439 // Should now contain file1-1's contents. 440 c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil) 441 } 442 443 // The `docker cp` command should also ensure that you cannot 444 // write to a container rootfs that is marked as read-only. 445 func (s *DockerSuite) TestCpToErrReadOnlyRootfs(c *check.C) { 446 // --read-only + userns has remount issues 447 testRequires(c, DaemonIsLinux, NotUserNamespace) 448 tmpDir := getTestDir(c, "test-cp-to-err-read-only-rootfs") 449 defer os.RemoveAll(tmpDir) 450 451 makeTestContentInDir(c, tmpDir) 452 453 containerID := makeTestContainer(c, testContainerOptions{ 454 readOnly: true, workDir: "/root", 455 command: makeCatFileCommand("shouldNotExist"), 456 }) 457 458 srcPath := cpPath(tmpDir, "file1") 459 dstPath := containerCpPath(containerID, "/root/shouldNotExist") 460 461 err := runDockerCp(c, srcPath, dstPath, nil) 462 c.Assert(err, checker.NotNil) 463 464 c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrContainerRootfsReadonly error, but got %T: %s", err, err)) 465 466 // Ensure that dstPath doesn't exist. 467 c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil) 468 } 469 470 // The `docker cp` command should also ensure that you 471 // cannot write to a volume that is mounted as read-only. 472 func (s *DockerSuite) TestCpToErrReadOnlyVolume(c *check.C) { 473 // --read-only + userns has remount issues 474 testRequires(c, DaemonIsLinux, NotUserNamespace) 475 tmpDir := getTestDir(c, "test-cp-to-err-read-only-volume") 476 defer os.RemoveAll(tmpDir) 477 478 makeTestContentInDir(c, tmpDir) 479 480 containerID := makeTestContainer(c, testContainerOptions{ 481 volumes: defaultVolumes(tmpDir), workDir: "/root", 482 command: makeCatFileCommand("/vol_ro/shouldNotExist"), 483 }) 484 485 srcPath := cpPath(tmpDir, "file1") 486 dstPath := containerCpPath(containerID, "/vol_ro/shouldNotExist") 487 488 err := runDockerCp(c, srcPath, dstPath, nil) 489 c.Assert(err, checker.NotNil) 490 491 c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrVolumeReadonly error, but got %T: %s", err, err)) 492 493 // Ensure that dstPath doesn't exist. 494 c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil) 495 }