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