github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/integration-cli/docker_cli_cp_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path" 10 "path/filepath" 11 "strings" 12 "testing" 13 14 "gotest.tools/v3/assert" 15 is "gotest.tools/v3/assert/cmp" 16 "gotest.tools/v3/icmd" 17 ) 18 19 const ( 20 cpTestPathParent = "/some" 21 cpTestPath = "/some/path" 22 cpTestName = "test" 23 cpFullPath = "/some/path/test" 24 25 cpContainerContents = "holla, i am the container" 26 cpHostContents = "hello, i am the host" 27 ) 28 29 // Ensure that an all-local path case returns an error. 30 func (s *DockerSuite) TestCpLocalOnly(c *testing.T) { 31 err := runDockerCp(c, "foo", "bar") 32 assert.ErrorContains(c, err, "must specify at least one container source") 33 } 34 35 // Test for #5656 36 // Check that garbage paths don't escape the container's rootfs 37 func (s *DockerSuite) TestCpGarbagePath(c *testing.T) { 38 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath) 39 40 containerID := strings.TrimSpace(out) 41 42 out, _ = dockerCmd(c, "wait", containerID) 43 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 44 assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir)) 45 46 hostFile, err := os.Create(cpFullPath) 47 assert.NilError(c, err) 48 defer hostFile.Close() 49 defer os.RemoveAll(cpTestPathParent) 50 51 fmt.Fprintf(hostFile, "%s", cpHostContents) 52 53 tmpdir, err := ioutil.TempDir("", "docker-integration") 54 assert.NilError(c, err) 55 56 tmpname := filepath.Join(tmpdir, cpTestName) 57 defer os.RemoveAll(tmpdir) 58 59 path := path.Join("../../../../../../../../../../../../", cpFullPath) 60 61 dockerCmd(c, "cp", containerID+":"+path, tmpdir) 62 63 file, _ := os.Open(tmpname) 64 defer file.Close() 65 66 test, err := ioutil.ReadAll(file) 67 assert.NilError(c, err) 68 assert.Assert(c, string(test) != cpHostContents, "output matched host file -- garbage path can escape container rootfs") 69 assert.Assert(c, string(test) == cpContainerContents, "output doesn't match the input for garbage path") 70 } 71 72 // Check that relative paths are relative to the container's rootfs 73 func (s *DockerSuite) TestCpRelativePath(c *testing.T) { 74 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath) 75 76 containerID := strings.TrimSpace(out) 77 78 out, _ = dockerCmd(c, "wait", containerID) 79 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 80 assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir)) 81 82 hostFile, err := os.Create(cpFullPath) 83 assert.NilError(c, err) 84 defer hostFile.Close() 85 defer os.RemoveAll(cpTestPathParent) 86 87 fmt.Fprintf(hostFile, "%s", cpHostContents) 88 89 tmpdir, err := ioutil.TempDir("", "docker-integration") 90 assert.NilError(c, err) 91 92 tmpname := filepath.Join(tmpdir, cpTestName) 93 defer os.RemoveAll(tmpdir) 94 95 var relPath string 96 if path.IsAbs(cpFullPath) { 97 // normally this is `filepath.Rel("/", cpFullPath)` but we cannot 98 // get this unix-path manipulation on windows with filepath. 99 relPath = cpFullPath[1:] 100 } 101 assert.Assert(c, path.IsAbs(cpFullPath), "path %s was assumed to be an absolute path", cpFullPath) 102 103 dockerCmd(c, "cp", containerID+":"+relPath, tmpdir) 104 105 file, _ := os.Open(tmpname) 106 defer file.Close() 107 108 test, err := ioutil.ReadAll(file) 109 assert.NilError(c, err) 110 assert.Assert(c, string(test) != cpHostContents, "output matched host file -- relative path can escape container rootfs") 111 assert.Assert(c, string(test) == cpContainerContents, "output doesn't match the input for relative path") 112 } 113 114 // Check that absolute paths are relative to the container's rootfs 115 func (s *DockerSuite) TestCpAbsolutePath(c *testing.T) { 116 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath) 117 118 containerID := strings.TrimSpace(out) 119 120 out, _ = dockerCmd(c, "wait", containerID) 121 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 122 assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir)) 123 124 hostFile, err := os.Create(cpFullPath) 125 assert.NilError(c, err) 126 defer hostFile.Close() 127 defer os.RemoveAll(cpTestPathParent) 128 129 fmt.Fprintf(hostFile, "%s", cpHostContents) 130 131 tmpdir, err := ioutil.TempDir("", "docker-integration") 132 assert.NilError(c, err) 133 134 tmpname := filepath.Join(tmpdir, cpTestName) 135 defer os.RemoveAll(tmpdir) 136 137 path := cpFullPath 138 139 dockerCmd(c, "cp", containerID+":"+path, tmpdir) 140 141 file, _ := os.Open(tmpname) 142 defer file.Close() 143 144 test, err := ioutil.ReadAll(file) 145 assert.NilError(c, err) 146 assert.Assert(c, string(test) != cpHostContents, "output matched host file -- absolute path can escape container rootfs") 147 assert.Assert(c, string(test) == cpContainerContents, "output doesn't match the input for absolute path") 148 } 149 150 // Test for #5619 151 // Check that absolute symlinks are still relative to the container's rootfs 152 func (s *DockerSuite) TestCpAbsoluteSymlink(c *testing.T) { 153 testRequires(c, DaemonIsLinux) 154 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpFullPath+" container_path") 155 156 containerID := strings.TrimSpace(out) 157 158 out, _ = dockerCmd(c, "wait", containerID) 159 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 160 161 assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir)) 162 163 hostFile, err := os.Create(cpFullPath) 164 assert.NilError(c, err) 165 defer hostFile.Close() 166 defer os.RemoveAll(cpTestPathParent) 167 168 fmt.Fprintf(hostFile, "%s", cpHostContents) 169 170 tmpdir, err := ioutil.TempDir("", "docker-integration") 171 assert.NilError(c, err) 172 173 tmpname := filepath.Join(tmpdir, "container_path") 174 defer os.RemoveAll(tmpdir) 175 176 path := path.Join("/", "container_path") 177 178 dockerCmd(c, "cp", containerID+":"+path, tmpdir) 179 180 // We should have copied a symlink *NOT* the file itself! 181 linkTarget, err := os.Readlink(tmpname) 182 assert.NilError(c, err) 183 assert.Equal(c, linkTarget, filepath.FromSlash(cpFullPath)) 184 } 185 186 // Check that symlinks to a directory behave as expected when copying one from 187 // a container. 188 func (s *DockerSuite) TestCpFromSymlinkToDirectory(c *testing.T) { 189 testRequires(c, DaemonIsLinux) 190 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpTestPathParent+" /dir_link") 191 192 containerID := strings.TrimSpace(out) 193 194 out, _ = dockerCmd(c, "wait", containerID) 195 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 196 197 testDir, err := ioutil.TempDir("", "test-cp-from-symlink-to-dir-") 198 assert.NilError(c, err) 199 defer os.RemoveAll(testDir) 200 201 // This copy command should copy the symlink, not the target, into the 202 // temporary directory. 203 dockerCmd(c, "cp", containerID+":"+"/dir_link", testDir) 204 205 expectedPath := filepath.Join(testDir, "dir_link") 206 linkTarget, err := os.Readlink(expectedPath) 207 assert.NilError(c, err) 208 209 assert.Equal(c, linkTarget, filepath.FromSlash(cpTestPathParent)) 210 211 os.Remove(expectedPath) 212 213 // This copy command should resolve the symlink (note the trailing 214 // separator), copying the target into the temporary directory. 215 dockerCmd(c, "cp", containerID+":"+"/dir_link/", testDir) 216 217 // It *should not* have copied the directory using the target's name, but 218 // used the given name instead. 219 unexpectedPath := filepath.Join(testDir, cpTestPathParent) 220 stat, err := os.Lstat(unexpectedPath) 221 if err == nil { 222 out = fmt.Sprintf("target name was copied: %q - %q", stat.Mode(), stat.Name()) 223 } 224 assert.ErrorContains(c, err, "", out) 225 226 // It *should* have copied the directory using the asked name "dir_link". 227 stat, err = os.Lstat(expectedPath) 228 assert.NilError(c, err, "unable to stat resource at %q", expectedPath) 229 assert.Assert(c, stat.IsDir(), "should have copied a directory but got %q instead", stat.Mode()) 230 } 231 232 // Check that symlinks to a directory behave as expected when copying one to a 233 // container. 234 func (s *DockerSuite) TestCpToSymlinkToDirectory(c *testing.T) { 235 testRequires(c, DaemonIsLinux) 236 testRequires(c, testEnv.IsLocalDaemon) // Requires local volume mount bind. 237 238 testVol, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-") 239 assert.NilError(c, err) 240 defer os.RemoveAll(testVol) 241 242 // Create a test container with a local volume. We will test by copying 243 // to the volume path in the container which we can then verify locally. 244 out, _ := dockerCmd(c, "create", "-v", testVol+":/testVol", "busybox") 245 246 containerID := strings.TrimSpace(out) 247 248 // Create a temp directory to hold a test file nested in a directory. 249 testDir, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-") 250 assert.NilError(c, err) 251 defer os.RemoveAll(testDir) 252 253 // This file will be at "/testDir/some/path/test" and will be copied into 254 // the test volume later. 255 hostTestFilename := filepath.Join(testDir, cpFullPath) 256 assert.NilError(c, os.MkdirAll(filepath.Dir(hostTestFilename), os.FileMode(0700))) 257 assert.NilError(c, ioutil.WriteFile(hostTestFilename, []byte(cpHostContents), os.FileMode(0600))) 258 259 // Now create another temp directory to hold a symlink to the 260 // "/testDir/some" directory. 261 linkDir, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-") 262 assert.NilError(c, err) 263 defer os.RemoveAll(linkDir) 264 265 // Then symlink "/linkDir/dir_link" to "/testdir/some". 266 linkTarget := filepath.Join(testDir, cpTestPathParent) 267 localLink := filepath.Join(linkDir, "dir_link") 268 assert.NilError(c, os.Symlink(linkTarget, localLink)) 269 270 // Now copy that symlink into the test volume in the container. 271 dockerCmd(c, "cp", localLink, containerID+":/testVol") 272 273 // This copy command should have copied the symlink *not* the target. 274 expectedPath := filepath.Join(testVol, "dir_link") 275 actualLinkTarget, err := os.Readlink(expectedPath) 276 assert.NilError(c, err, "unable to read symlink at %q", expectedPath) 277 assert.Equal(c, actualLinkTarget, linkTarget) 278 279 // Good, now remove that copied link for the next test. 280 os.Remove(expectedPath) 281 282 // This copy command should resolve the symlink (note the trailing 283 // separator), copying the target into the test volume directory in the 284 // container. 285 dockerCmd(c, "cp", localLink+"/", containerID+":/testVol") 286 287 // It *should not* have copied the directory using the target's name, but 288 // used the given name instead. 289 unexpectedPath := filepath.Join(testVol, cpTestPathParent) 290 stat, err := os.Lstat(unexpectedPath) 291 if err == nil { 292 out = fmt.Sprintf("target name was copied: %q - %q", stat.Mode(), stat.Name()) 293 } 294 assert.ErrorContains(c, err, "", out) 295 296 // It *should* have copied the directory using the asked name "dir_link". 297 stat, err = os.Lstat(expectedPath) 298 assert.NilError(c, err, "unable to stat resource at %q", expectedPath) 299 assert.Assert(c, stat.IsDir(), "should have copied a directory but got %q instead", stat.Mode()) 300 301 // And this directory should contain the file copied from the host at the 302 // expected location: "/testVol/dir_link/path/test" 303 expectedFilepath := filepath.Join(testVol, "dir_link/path/test") 304 fileContents, err := ioutil.ReadFile(expectedFilepath) 305 assert.NilError(c, err) 306 assert.Equal(c, string(fileContents), cpHostContents) 307 } 308 309 // Test for #5619 310 // Check that symlinks which are part of the resource path are still relative to the container's rootfs 311 func (s *DockerSuite) TestCpSymlinkComponent(c *testing.T) { 312 testRequires(c, DaemonIsLinux) 313 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpTestPath+" container_path") 314 315 containerID := strings.TrimSpace(out) 316 317 out, _ = dockerCmd(c, "wait", containerID) 318 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 319 320 assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir)) 321 322 hostFile, err := os.Create(cpFullPath) 323 assert.NilError(c, err) 324 defer hostFile.Close() 325 defer os.RemoveAll(cpTestPathParent) 326 327 fmt.Fprintf(hostFile, "%s", cpHostContents) 328 329 tmpdir, err := ioutil.TempDir("", "docker-integration") 330 331 assert.NilError(c, err) 332 333 tmpname := filepath.Join(tmpdir, cpTestName) 334 defer os.RemoveAll(tmpdir) 335 336 path := path.Join("/", "container_path", cpTestName) 337 338 dockerCmd(c, "cp", containerID+":"+path, tmpdir) 339 340 file, _ := os.Open(tmpname) 341 defer file.Close() 342 343 test, err := ioutil.ReadAll(file) 344 assert.NilError(c, err) 345 assert.Assert(c, string(test) != cpHostContents, "output matched host file -- symlink path component can escape container rootfs") 346 assert.Equal(c, string(test), cpContainerContents, "output doesn't match the input for symlink path component") 347 } 348 349 // Check that cp with unprivileged user doesn't return any error 350 func (s *DockerSuite) TestCpUnprivilegedUser(c *testing.T) { 351 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 352 testRequires(c, UnixCli) // uses chmod/su: not available on windows 353 354 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "touch "+cpTestName) 355 356 containerID := strings.TrimSpace(out) 357 358 out, _ = dockerCmd(c, "wait", containerID) 359 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 360 361 tmpdir, err := ioutil.TempDir("", "docker-integration") 362 assert.NilError(c, err) 363 364 defer os.RemoveAll(tmpdir) 365 366 err = os.Chmod(tmpdir, 0777) 367 assert.NilError(c, err) 368 369 result := icmd.RunCommand("su", "unprivilegeduser", "-c", 370 fmt.Sprintf("%s cp %s:%s %s", dockerBinary, containerID, cpTestName, tmpdir)) 371 result.Assert(c, icmd.Expected{}) 372 } 373 374 func (s *DockerSuite) TestCpSpecialFiles(c *testing.T) { 375 testRequires(c, DaemonIsLinux) 376 testRequires(c, testEnv.IsLocalDaemon) 377 378 outDir, err := ioutil.TempDir("", "cp-test-special-files") 379 assert.NilError(c, err) 380 defer os.RemoveAll(outDir) 381 382 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "touch /foo") 383 384 containerID := strings.TrimSpace(out) 385 386 out, _ = dockerCmd(c, "wait", containerID) 387 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 388 389 // Copy actual /etc/resolv.conf 390 dockerCmd(c, "cp", containerID+":/etc/resolv.conf", outDir) 391 392 expected := readContainerFile(c, containerID, "resolv.conf") 393 actual, err := ioutil.ReadFile(outDir + "/resolv.conf") 394 assert.NilError(c, err) 395 assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container resolvconf") 396 397 // Copy actual /etc/hosts 398 dockerCmd(c, "cp", containerID+":/etc/hosts", outDir) 399 400 expected = readContainerFile(c, containerID, "hosts") 401 actual, err = ioutil.ReadFile(outDir + "/hosts") 402 assert.NilError(c, err) 403 assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container hosts") 404 405 // Copy actual /etc/resolv.conf 406 dockerCmd(c, "cp", containerID+":/etc/hostname", outDir) 407 408 expected = readContainerFile(c, containerID, "hostname") 409 actual, err = ioutil.ReadFile(outDir + "/hostname") 410 assert.NilError(c, err) 411 assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container hostname") 412 } 413 414 func (s *DockerSuite) TestCpVolumePath(c *testing.T) { 415 // stat /tmp/cp-test-volumepath851508420/test gets permission denied for the user 416 testRequires(c, NotUserNamespace) 417 testRequires(c, DaemonIsLinux) 418 testRequires(c, testEnv.IsLocalDaemon) 419 420 tmpDir, err := ioutil.TempDir("", "cp-test-volumepath") 421 assert.NilError(c, err) 422 defer os.RemoveAll(tmpDir) 423 outDir, err := ioutil.TempDir("", "cp-test-volumepath-out") 424 assert.NilError(c, err) 425 defer os.RemoveAll(outDir) 426 _, err = os.Create(tmpDir + "/test") 427 assert.NilError(c, err) 428 429 out, _ := dockerCmd(c, "run", "-d", "-v", "/foo", "-v", tmpDir+"/test:/test", "-v", tmpDir+":/baz", "busybox", "/bin/sh", "-c", "touch /foo/bar") 430 431 containerID := strings.TrimSpace(out) 432 433 out, _ = dockerCmd(c, "wait", containerID) 434 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 435 436 // Copy actual volume path 437 dockerCmd(c, "cp", containerID+":/foo", outDir) 438 439 stat, err := os.Stat(outDir + "/foo") 440 assert.NilError(c, err) 441 assert.Assert(c, stat.IsDir(), "Expected copied content to be dir") 442 443 stat, err = os.Stat(outDir + "/foo/bar") 444 assert.NilError(c, err) 445 assert.Assert(c, !stat.IsDir(), "Expected file `bar` to be a file") 446 447 // Copy file nested in volume 448 dockerCmd(c, "cp", containerID+":/foo/bar", outDir) 449 450 stat, err = os.Stat(outDir + "/bar") 451 assert.NilError(c, err) 452 assert.Assert(c, !stat.IsDir(), "Expected file `bar` to be a file") 453 454 // Copy Bind-mounted dir 455 dockerCmd(c, "cp", containerID+":/baz", outDir) 456 stat, err = os.Stat(outDir + "/baz") 457 assert.NilError(c, err) 458 assert.Assert(c, stat.IsDir(), "Expected `baz` to be a dir") 459 460 // Copy file nested in bind-mounted dir 461 dockerCmd(c, "cp", containerID+":/baz/test", outDir) 462 fb, err := ioutil.ReadFile(outDir + "/baz/test") 463 assert.NilError(c, err) 464 fb2, err := ioutil.ReadFile(tmpDir + "/test") 465 assert.NilError(c, err) 466 assert.Assert(c, bytes.Equal(fb, fb2), "Expected copied file to be duplicate of bind-mounted file") 467 468 // Copy bind-mounted file 469 dockerCmd(c, "cp", containerID+":/test", outDir) 470 fb, err = ioutil.ReadFile(outDir + "/test") 471 assert.NilError(c, err) 472 fb2, err = ioutil.ReadFile(tmpDir + "/test") 473 assert.NilError(c, err) 474 assert.Assert(c, bytes.Equal(fb, fb2), "Expected copied file to be duplicate of bind-mounted file") 475 } 476 477 func (s *DockerSuite) TestCpToDot(c *testing.T) { 478 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test") 479 480 containerID := strings.TrimSpace(out) 481 482 out, _ = dockerCmd(c, "wait", containerID) 483 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 484 485 tmpdir, err := ioutil.TempDir("", "docker-integration") 486 assert.NilError(c, err) 487 defer os.RemoveAll(tmpdir) 488 cwd, err := os.Getwd() 489 assert.NilError(c, err) 490 defer os.Chdir(cwd) 491 err = os.Chdir(tmpdir) 492 assert.NilError(c, err) 493 494 dockerCmd(c, "cp", containerID+":/test", ".") 495 content, err := ioutil.ReadFile("./test") 496 assert.NilError(c, err) 497 assert.Equal(c, string(content), "lololol\n") 498 } 499 500 func (s *DockerSuite) TestCpToStdout(c *testing.T) { 501 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test") 502 503 containerID := strings.TrimSpace(out) 504 505 out, _ = dockerCmd(c, "wait", containerID) 506 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 507 508 out, err := RunCommandPipelineWithOutput( 509 exec.Command(dockerBinary, "cp", containerID+":/test", "-"), 510 exec.Command("tar", "-vtf", "-")) 511 512 assert.NilError(c, err) 513 assert.Check(c, is.Contains(out, "test")) 514 assert.Check(c, is.Contains(out, "-rw")) 515 } 516 517 func (s *DockerSuite) TestCpNameHasColon(c *testing.T) { 518 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 519 520 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /te:s:t") 521 522 containerID := strings.TrimSpace(out) 523 524 out, _ = dockerCmd(c, "wait", containerID) 525 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 526 527 tmpdir, err := ioutil.TempDir("", "docker-integration") 528 assert.NilError(c, err) 529 defer os.RemoveAll(tmpdir) 530 dockerCmd(c, "cp", containerID+":/te:s:t", tmpdir) 531 content, err := ioutil.ReadFile(tmpdir + "/te:s:t") 532 assert.NilError(c, err) 533 assert.Equal(c, string(content), "lololol\n") 534 } 535 536 func (s *DockerSuite) TestCopyAndRestart(c *testing.T) { 537 testRequires(c, DaemonIsLinux) 538 expectedMsg := "hello" 539 out, _ := dockerCmd(c, "run", "-d", "busybox", "echo", expectedMsg) 540 containerID := strings.TrimSpace(out) 541 542 out, _ = dockerCmd(c, "wait", containerID) 543 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 544 545 tmpDir, err := ioutil.TempDir("", "test-docker-restart-after-copy-") 546 assert.NilError(c, err) 547 defer os.RemoveAll(tmpDir) 548 549 dockerCmd(c, "cp", fmt.Sprintf("%s:/etc/group", containerID), tmpDir) 550 551 out, _ = dockerCmd(c, "start", "-a", containerID) 552 assert.Equal(c, strings.TrimSpace(out), expectedMsg) 553 } 554 555 func (s *DockerSuite) TestCopyCreatedContainer(c *testing.T) { 556 testRequires(c, DaemonIsLinux) 557 dockerCmd(c, "create", "--name", "test_cp", "-v", "/test", "busybox") 558 559 tmpDir, err := ioutil.TempDir("", "test") 560 assert.NilError(c, err) 561 defer os.RemoveAll(tmpDir) 562 dockerCmd(c, "cp", "test_cp:/bin/sh", tmpDir) 563 } 564 565 // test copy with option `-L`: following symbol link 566 // Check that symlinks to a file behave as expected when copying one from 567 // a container to host following symbol link 568 func (s *DockerSuite) TestCpSymlinkFromConToHostFollowSymlink(c *testing.T) { 569 testRequires(c, DaemonIsLinux) 570 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpFullPath+" /dir_link") 571 assert.Equal(c, exitCode, 0, "failed to set up container: %s", out) 572 573 cleanedContainerID := strings.TrimSpace(out) 574 575 out, _ = dockerCmd(c, "wait", cleanedContainerID) 576 assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container") 577 578 testDir, err := ioutil.TempDir("", "test-cp-symlink-container-to-host-follow-symlink") 579 assert.NilError(c, err) 580 defer os.RemoveAll(testDir) 581 582 // This copy command should copy the symlink, not the target, into the 583 // temporary directory. 584 dockerCmd(c, "cp", "-L", cleanedContainerID+":"+"/dir_link", testDir) 585 586 expectedPath := filepath.Join(testDir, "dir_link") 587 588 expected := []byte(cpContainerContents) 589 actual, err := ioutil.ReadFile(expectedPath) 590 assert.NilError(c, err) 591 os.Remove(expectedPath) 592 assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container symbol link target") 593 594 // now test copy symbol link to a non-existing file in host 595 expectedPath = filepath.Join(testDir, "somefile_host") 596 // expectedPath shouldn't exist, if exists, remove it 597 if _, err := os.Lstat(expectedPath); err == nil { 598 os.Remove(expectedPath) 599 } 600 601 dockerCmd(c, "cp", "-L", cleanedContainerID+":"+"/dir_link", expectedPath) 602 603 actual, err = ioutil.ReadFile(expectedPath) 604 assert.NilError(c, err) 605 defer os.Remove(expectedPath) 606 assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container symbol link target") 607 }