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