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