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