github.com/sevki/docker@v1.7.1/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 13 "github.com/go-check/check" 14 ) 15 16 const ( 17 cpTestPathParent = "/some" 18 cpTestPath = "/some/path" 19 cpTestName = "test" 20 cpFullPath = "/some/path/test" 21 22 cpContainerContents = "holla, i am the container" 23 cpHostContents = "hello, i am the host" 24 ) 25 26 // Test for #5656 27 // Check that garbage paths don't escape the container's rootfs 28 func (s *DockerSuite) TestCpGarbagePath(c *check.C) { 29 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath) 30 if exitCode != 0 { 31 c.Fatal("failed to create a container", out) 32 } 33 34 cleanedContainerID := strings.TrimSpace(out) 35 36 out, _ = dockerCmd(c, "wait", cleanedContainerID) 37 if strings.TrimSpace(out) != "0" { 38 c.Fatal("failed to set up container", out) 39 } 40 41 if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil { 42 c.Fatal(err) 43 } 44 45 hostFile, err := os.Create(cpFullPath) 46 if err != nil { 47 c.Fatal(err) 48 } 49 defer hostFile.Close() 50 defer os.RemoveAll(cpTestPathParent) 51 52 fmt.Fprintf(hostFile, "%s", cpHostContents) 53 54 tmpdir, err := ioutil.TempDir("", "docker-integration") 55 if err != nil { 56 c.Fatal(err) 57 } 58 59 tmpname := filepath.Join(tmpdir, cpTestName) 60 defer os.RemoveAll(tmpdir) 61 62 path := path.Join("../../../../../../../../../../../../", cpFullPath) 63 64 _, _ = dockerCmd(c, "cp", cleanedContainerID+":"+path, tmpdir) 65 66 file, _ := os.Open(tmpname) 67 defer file.Close() 68 69 test, err := ioutil.ReadAll(file) 70 if err != nil { 71 c.Fatal(err) 72 } 73 74 if string(test) == cpHostContents { 75 c.Errorf("output matched host file -- garbage path can escape container rootfs") 76 } 77 78 if string(test) != cpContainerContents { 79 c.Errorf("output doesn't match the input for garbage path") 80 } 81 82 } 83 84 // Check that relative paths are relative to the container's rootfs 85 func (s *DockerSuite) TestCpRelativePath(c *check.C) { 86 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath) 87 if exitCode != 0 { 88 c.Fatal("failed to create a container", out) 89 } 90 91 cleanedContainerID := strings.TrimSpace(out) 92 93 out, _ = dockerCmd(c, "wait", cleanedContainerID) 94 if strings.TrimSpace(out) != "0" { 95 c.Fatal("failed to set up container", out) 96 } 97 98 if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil { 99 c.Fatal(err) 100 } 101 102 hostFile, err := os.Create(cpFullPath) 103 if err != nil { 104 c.Fatal(err) 105 } 106 defer hostFile.Close() 107 defer os.RemoveAll(cpTestPathParent) 108 109 fmt.Fprintf(hostFile, "%s", cpHostContents) 110 111 tmpdir, err := ioutil.TempDir("", "docker-integration") 112 113 if err != nil { 114 c.Fatal(err) 115 } 116 117 tmpname := filepath.Join(tmpdir, cpTestName) 118 defer os.RemoveAll(tmpdir) 119 120 var relPath string 121 if path.IsAbs(cpFullPath) { 122 // normally this is `filepath.Rel("/", cpFullPath)` but we cannot 123 // get this unix-path manipulation on windows with filepath. 124 relPath = cpFullPath[1:] 125 } else { 126 c.Fatalf("path %s was assumed to be an absolute path", cpFullPath) 127 } 128 129 _, _ = dockerCmd(c, "cp", cleanedContainerID+":"+relPath, tmpdir) 130 131 file, _ := os.Open(tmpname) 132 defer file.Close() 133 134 test, err := ioutil.ReadAll(file) 135 if err != nil { 136 c.Fatal(err) 137 } 138 139 if string(test) == cpHostContents { 140 c.Errorf("output matched host file -- relative path can escape container rootfs") 141 } 142 143 if string(test) != cpContainerContents { 144 c.Errorf("output doesn't match the input for relative path") 145 } 146 147 } 148 149 // Check that absolute paths are relative to the container's rootfs 150 func (s *DockerSuite) TestCpAbsolutePath(c *check.C) { 151 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath) 152 if exitCode != 0 { 153 c.Fatal("failed to create a container", out) 154 } 155 156 cleanedContainerID := strings.TrimSpace(out) 157 158 out, _ = dockerCmd(c, "wait", cleanedContainerID) 159 if strings.TrimSpace(out) != "0" { 160 c.Fatal("failed to set up container", out) 161 } 162 163 if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil { 164 c.Fatal(err) 165 } 166 167 hostFile, err := os.Create(cpFullPath) 168 if err != nil { 169 c.Fatal(err) 170 } 171 defer hostFile.Close() 172 defer os.RemoveAll(cpTestPathParent) 173 174 fmt.Fprintf(hostFile, "%s", cpHostContents) 175 176 tmpdir, err := ioutil.TempDir("", "docker-integration") 177 178 if err != nil { 179 c.Fatal(err) 180 } 181 182 tmpname := filepath.Join(tmpdir, cpTestName) 183 defer os.RemoveAll(tmpdir) 184 185 path := cpFullPath 186 187 _, _ = dockerCmd(c, "cp", cleanedContainerID+":"+path, tmpdir) 188 189 file, _ := os.Open(tmpname) 190 defer file.Close() 191 192 test, err := ioutil.ReadAll(file) 193 if err != nil { 194 c.Fatal(err) 195 } 196 197 if string(test) == cpHostContents { 198 c.Errorf("output matched host file -- absolute path can escape container rootfs") 199 } 200 201 if string(test) != cpContainerContents { 202 c.Errorf("output doesn't match the input for absolute path") 203 } 204 205 } 206 207 // Test for #5619 208 // Check that absolute symlinks are still relative to the container's rootfs 209 func (s *DockerSuite) TestCpAbsoluteSymlink(c *check.C) { 210 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpFullPath+" container_path") 211 if exitCode != 0 { 212 c.Fatal("failed to create a container", out) 213 } 214 215 cleanedContainerID := strings.TrimSpace(out) 216 217 out, _ = dockerCmd(c, "wait", cleanedContainerID) 218 if strings.TrimSpace(out) != "0" { 219 c.Fatal("failed to set up container", out) 220 } 221 222 if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil { 223 c.Fatal(err) 224 } 225 226 hostFile, err := os.Create(cpFullPath) 227 if err != nil { 228 c.Fatal(err) 229 } 230 defer hostFile.Close() 231 defer os.RemoveAll(cpTestPathParent) 232 233 fmt.Fprintf(hostFile, "%s", cpHostContents) 234 235 tmpdir, err := ioutil.TempDir("", "docker-integration") 236 237 if err != nil { 238 c.Fatal(err) 239 } 240 241 tmpname := filepath.Join(tmpdir, cpTestName) 242 defer os.RemoveAll(tmpdir) 243 244 path := path.Join("/", "container_path") 245 246 _, _ = dockerCmd(c, "cp", cleanedContainerID+":"+path, tmpdir) 247 248 file, _ := os.Open(tmpname) 249 defer file.Close() 250 251 test, err := ioutil.ReadAll(file) 252 if err != nil { 253 c.Fatal(err) 254 } 255 256 if string(test) == cpHostContents { 257 c.Errorf("output matched host file -- absolute symlink can escape container rootfs") 258 } 259 260 if string(test) != cpContainerContents { 261 c.Errorf("output doesn't match the input for absolute symlink") 262 } 263 264 } 265 266 // Test for #5619 267 // Check that symlinks which are part of the resource path are still relative to the container's rootfs 268 func (s *DockerSuite) TestCpSymlinkComponent(c *check.C) { 269 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpTestPath+" container_path") 270 if exitCode != 0 { 271 c.Fatal("failed to create a container", out) 272 } 273 274 cleanedContainerID := strings.TrimSpace(out) 275 276 out, _ = dockerCmd(c, "wait", cleanedContainerID) 277 if strings.TrimSpace(out) != "0" { 278 c.Fatal("failed to set up container", out) 279 } 280 281 if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil { 282 c.Fatal(err) 283 } 284 285 hostFile, err := os.Create(cpFullPath) 286 if err != nil { 287 c.Fatal(err) 288 } 289 defer hostFile.Close() 290 defer os.RemoveAll(cpTestPathParent) 291 292 fmt.Fprintf(hostFile, "%s", cpHostContents) 293 294 tmpdir, err := ioutil.TempDir("", "docker-integration") 295 296 if err != nil { 297 c.Fatal(err) 298 } 299 300 tmpname := filepath.Join(tmpdir, cpTestName) 301 defer os.RemoveAll(tmpdir) 302 303 path := path.Join("/", "container_path", cpTestName) 304 305 _, _ = dockerCmd(c, "cp", cleanedContainerID+":"+path, tmpdir) 306 307 file, _ := os.Open(tmpname) 308 defer file.Close() 309 310 test, err := ioutil.ReadAll(file) 311 if err != nil { 312 c.Fatal(err) 313 } 314 315 if string(test) == cpHostContents { 316 c.Errorf("output matched host file -- symlink path component can escape container rootfs") 317 } 318 319 if string(test) != cpContainerContents { 320 c.Errorf("output doesn't match the input for symlink path component") 321 } 322 323 } 324 325 // Check that cp with unprivileged user doesn't return any error 326 func (s *DockerSuite) TestCpUnprivilegedUser(c *check.C) { 327 testRequires(c, UnixCli) // uses chmod/su: not available on windows 328 329 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "touch "+cpTestName) 330 if exitCode != 0 { 331 c.Fatal("failed to create a container", out) 332 } 333 334 cleanedContainerID := strings.TrimSpace(out) 335 336 out, _ = dockerCmd(c, "wait", cleanedContainerID) 337 if strings.TrimSpace(out) != "0" { 338 c.Fatal("failed to set up container", out) 339 } 340 341 tmpdir, err := ioutil.TempDir("", "docker-integration") 342 if err != nil { 343 c.Fatal(err) 344 } 345 346 defer os.RemoveAll(tmpdir) 347 348 if err = os.Chmod(tmpdir, 0777); err != nil { 349 c.Fatal(err) 350 } 351 352 path := cpTestName 353 354 _, _, err = runCommandWithOutput(exec.Command("su", "unprivilegeduser", "-c", dockerBinary+" cp "+cleanedContainerID+":"+path+" "+tmpdir)) 355 if err != nil { 356 c.Fatalf("couldn't copy with unprivileged user: %s:%s %s", cleanedContainerID, path, err) 357 } 358 359 } 360 361 func (s *DockerSuite) TestCpSpecialFiles(c *check.C) { 362 testRequires(c, SameHostDaemon) 363 364 outDir, err := ioutil.TempDir("", "cp-test-special-files") 365 if err != nil { 366 c.Fatal(err) 367 } 368 defer os.RemoveAll(outDir) 369 370 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "touch /foo") 371 if exitCode != 0 { 372 c.Fatal("failed to create a container", out) 373 } 374 375 cleanedContainerID := strings.TrimSpace(out) 376 377 out, _ = dockerCmd(c, "wait", cleanedContainerID) 378 if strings.TrimSpace(out) != "0" { 379 c.Fatal("failed to set up container", out) 380 } 381 382 // Copy actual /etc/resolv.conf 383 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/etc/resolv.conf", outDir) 384 385 expected, err := ioutil.ReadFile("/var/lib/docker/containers/" + cleanedContainerID + "/resolv.conf") 386 actual, err := ioutil.ReadFile(outDir + "/resolv.conf") 387 388 if !bytes.Equal(actual, expected) { 389 c.Fatalf("Expected copied file to be duplicate of the container resolvconf") 390 } 391 392 // Copy actual /etc/hosts 393 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/etc/hosts", outDir) 394 395 expected, err = ioutil.ReadFile("/var/lib/docker/containers/" + cleanedContainerID + "/hosts") 396 actual, err = ioutil.ReadFile(outDir + "/hosts") 397 398 if !bytes.Equal(actual, expected) { 399 c.Fatalf("Expected copied file to be duplicate of the container hosts") 400 } 401 402 // Copy actual /etc/resolv.conf 403 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/etc/hostname", outDir) 404 405 expected, err = ioutil.ReadFile("/var/lib/docker/containers/" + cleanedContainerID + "/hostname") 406 actual, err = ioutil.ReadFile(outDir + "/hostname") 407 408 if !bytes.Equal(actual, expected) { 409 c.Fatalf("Expected copied file to be duplicate of the container resolvconf") 410 } 411 412 } 413 414 func (s *DockerSuite) TestCpVolumePath(c *check.C) { 415 testRequires(c, SameHostDaemon) 416 417 tmpDir, err := ioutil.TempDir("", "cp-test-volumepath") 418 if err != nil { 419 c.Fatal(err) 420 } 421 defer os.RemoveAll(tmpDir) 422 outDir, err := ioutil.TempDir("", "cp-test-volumepath-out") 423 if err != nil { 424 c.Fatal(err) 425 } 426 defer os.RemoveAll(outDir) 427 _, err = os.Create(tmpDir + "/test") 428 if err != nil { 429 c.Fatal(err) 430 } 431 432 out, exitCode := dockerCmd(c, "run", "-d", "-v", "/foo", "-v", tmpDir+"/test:/test", "-v", tmpDir+":/baz", "busybox", "/bin/sh", "-c", "touch /foo/bar") 433 if exitCode != 0 { 434 c.Fatal("failed to create a container", out) 435 } 436 437 cleanedContainerID := strings.TrimSpace(out) 438 439 out, _ = dockerCmd(c, "wait", cleanedContainerID) 440 if strings.TrimSpace(out) != "0" { 441 c.Fatal("failed to set up container", out) 442 } 443 444 // Copy actual volume path 445 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/foo", outDir) 446 447 stat, err := os.Stat(outDir + "/foo") 448 if err != nil { 449 c.Fatal(err) 450 } 451 if !stat.IsDir() { 452 c.Fatal("expected copied content to be dir") 453 } 454 stat, err = os.Stat(outDir + "/foo/bar") 455 if err != nil { 456 c.Fatal(err) 457 } 458 if stat.IsDir() { 459 c.Fatal("Expected file `bar` to be a file") 460 } 461 462 // Copy file nested in volume 463 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/foo/bar", outDir) 464 465 stat, err = os.Stat(outDir + "/bar") 466 if err != nil { 467 c.Fatal(err) 468 } 469 if stat.IsDir() { 470 c.Fatal("Expected file `bar` to be a file") 471 } 472 473 // Copy Bind-mounted dir 474 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/baz", outDir) 475 stat, err = os.Stat(outDir + "/baz") 476 if err != nil { 477 c.Fatal(err) 478 } 479 if !stat.IsDir() { 480 c.Fatal("Expected `baz` to be a dir") 481 } 482 483 // Copy file nested in bind-mounted dir 484 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/baz/test", outDir) 485 fb, err := ioutil.ReadFile(outDir + "/baz/test") 486 if err != nil { 487 c.Fatal(err) 488 } 489 fb2, err := ioutil.ReadFile(tmpDir + "/test") 490 if err != nil { 491 c.Fatal(err) 492 } 493 if !bytes.Equal(fb, fb2) { 494 c.Fatalf("Expected copied file to be duplicate of bind-mounted file") 495 } 496 497 // Copy bind-mounted file 498 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/test", outDir) 499 fb, err = ioutil.ReadFile(outDir + "/test") 500 if err != nil { 501 c.Fatal(err) 502 } 503 fb2, err = ioutil.ReadFile(tmpDir + "/test") 504 if err != nil { 505 c.Fatal(err) 506 } 507 if !bytes.Equal(fb, fb2) { 508 c.Fatalf("Expected copied file to be duplicate of bind-mounted file") 509 } 510 511 } 512 513 func (s *DockerSuite) TestCpToDot(c *check.C) { 514 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test") 515 if exitCode != 0 { 516 c.Fatal("failed to create a container", out) 517 } 518 519 cleanedContainerID := strings.TrimSpace(out) 520 521 out, _ = dockerCmd(c, "wait", cleanedContainerID) 522 if strings.TrimSpace(out) != "0" { 523 c.Fatal("failed to set up container", out) 524 } 525 526 tmpdir, err := ioutil.TempDir("", "docker-integration") 527 if err != nil { 528 c.Fatal(err) 529 } 530 defer os.RemoveAll(tmpdir) 531 cwd, err := os.Getwd() 532 if err != nil { 533 c.Fatal(err) 534 } 535 defer os.Chdir(cwd) 536 if err := os.Chdir(tmpdir); err != nil { 537 c.Fatal(err) 538 } 539 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/test", ".") 540 content, err := ioutil.ReadFile("./test") 541 if string(content) != "lololol\n" { 542 c.Fatalf("Wrong content in copied file %q, should be %q", content, "lololol\n") 543 } 544 } 545 546 func (s *DockerSuite) TestCpToStdout(c *check.C) { 547 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test") 548 if exitCode != 0 { 549 c.Fatalf("failed to create a container:%s\n", out) 550 } 551 552 cID := strings.TrimSpace(out) 553 554 out, _ = dockerCmd(c, "wait", cID) 555 if strings.TrimSpace(out) != "0" { 556 c.Fatalf("failed to set up container:%s\n", out) 557 } 558 559 out, _, err := runCommandPipelineWithOutput( 560 exec.Command(dockerBinary, "cp", cID+":/test", "-"), 561 exec.Command("tar", "-vtf", "-")) 562 563 if err != nil { 564 c.Fatalf("Failed to run commands: %s", err) 565 } 566 567 if !strings.Contains(out, "test") || !strings.Contains(out, "-rw") { 568 c.Fatalf("Missing file from tar TOC:\n%s", out) 569 } 570 } 571 572 func (s *DockerSuite) TestCpNameHasColon(c *check.C) { 573 testRequires(c, SameHostDaemon) 574 575 out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /te:s:t") 576 if exitCode != 0 { 577 c.Fatal("failed to create a container", out) 578 } 579 580 cleanedContainerID := strings.TrimSpace(out) 581 582 out, _ = dockerCmd(c, "wait", cleanedContainerID) 583 if strings.TrimSpace(out) != "0" { 584 c.Fatal("failed to set up container", out) 585 } 586 587 tmpdir, err := ioutil.TempDir("", "docker-integration") 588 if err != nil { 589 c.Fatal(err) 590 } 591 defer os.RemoveAll(tmpdir) 592 _, _ = dockerCmd(c, "cp", cleanedContainerID+":/te:s:t", tmpdir) 593 content, err := ioutil.ReadFile(tmpdir + "/te:s:t") 594 if string(content) != "lololol\n" { 595 c.Fatalf("Wrong content in copied file %q, should be %q", content, "lololol\n") 596 } 597 } 598 599 func (s *DockerSuite) TestCopyAndRestart(c *check.C) { 600 expectedMsg := "hello" 601 out, err := exec.Command(dockerBinary, "run", "-d", "busybox", "echo", expectedMsg).CombinedOutput() 602 if err != nil { 603 c.Fatal(string(out), err) 604 } 605 id := strings.TrimSpace(string(out)) 606 607 if out, err = exec.Command(dockerBinary, "wait", id).CombinedOutput(); err != nil { 608 c.Fatalf("unable to wait for container: %s", err) 609 } 610 611 status := strings.TrimSpace(string(out)) 612 if status != "0" { 613 c.Fatalf("container exited with status %s", status) 614 } 615 616 tmpDir, err := ioutil.TempDir("", "test-docker-restart-after-copy-") 617 if err != nil { 618 c.Fatalf("unable to make temporary directory: %s", err) 619 } 620 defer os.RemoveAll(tmpDir) 621 622 if _, err = exec.Command(dockerBinary, "cp", fmt.Sprintf("%s:/etc/issue", id), tmpDir).CombinedOutput(); err != nil { 623 c.Fatalf("unable to copy from busybox container: %s", err) 624 } 625 626 if out, err = exec.Command(dockerBinary, "start", "-a", id).CombinedOutput(); err != nil { 627 c.Fatalf("unable to start busybox container after copy: %s - %s", err, out) 628 } 629 630 msg := strings.TrimSpace(string(out)) 631 if msg != expectedMsg { 632 c.Fatalf("expected %q but got %q", expectedMsg, msg) 633 } 634 } 635 636 func (s *DockerSuite) TestCopyCreatedContainer(c *check.C) { 637 out, err := exec.Command(dockerBinary, "create", "--name", "test_cp", "-v", "/test", "busybox").CombinedOutput() 638 if err != nil { 639 c.Fatalf(string(out), err) 640 } 641 642 tmpDir, err := ioutil.TempDir("", "test") 643 if err != nil { 644 c.Fatalf("unable to make temporary directory: %s", err) 645 } 646 defer os.RemoveAll(tmpDir) 647 out, err = exec.Command(dockerBinary, "cp", "test_cp:/bin/sh", tmpDir).CombinedOutput() 648 if err != nil { 649 c.Fatalf(string(out), err) 650 } 651 }