github.com/eatbyte/docker@v1.6.0/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  }