github.com/codemac/docker@v1.2.1-0.20150518222241-6a18412d5b9c/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  	defer dockerCmd(c, "rm", "-fv", cleanedContainerID)
   439  
   440  	out, _ = dockerCmd(c, "wait", cleanedContainerID)
   441  	if strings.TrimSpace(out) != "0" {
   442  		c.Fatal("failed to set up container", out)
   443  	}
   444  
   445  	// Copy actual volume path
   446  	_, _ = dockerCmd(c, "cp", cleanedContainerID+":/foo", outDir)
   447  
   448  	stat, err := os.Stat(outDir + "/foo")
   449  	if err != nil {
   450  		c.Fatal(err)
   451  	}
   452  	if !stat.IsDir() {
   453  		c.Fatal("expected copied content to be dir")
   454  	}
   455  	stat, err = os.Stat(outDir + "/foo/bar")
   456  	if err != nil {
   457  		c.Fatal(err)
   458  	}
   459  	if stat.IsDir() {
   460  		c.Fatal("Expected file `bar` to be a file")
   461  	}
   462  
   463  	// Copy file nested in volume
   464  	_, _ = dockerCmd(c, "cp", cleanedContainerID+":/foo/bar", outDir)
   465  
   466  	stat, err = os.Stat(outDir + "/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 Bind-mounted dir
   475  	_, _ = dockerCmd(c, "cp", cleanedContainerID+":/baz", outDir)
   476  	stat, err = os.Stat(outDir + "/baz")
   477  	if err != nil {
   478  		c.Fatal(err)
   479  	}
   480  	if !stat.IsDir() {
   481  		c.Fatal("Expected `baz` to be a dir")
   482  	}
   483  
   484  	// Copy file nested in bind-mounted dir
   485  	_, _ = dockerCmd(c, "cp", cleanedContainerID+":/baz/test", outDir)
   486  	fb, err := ioutil.ReadFile(outDir + "/baz/test")
   487  	if err != nil {
   488  		c.Fatal(err)
   489  	}
   490  	fb2, err := ioutil.ReadFile(tmpDir + "/test")
   491  	if err != nil {
   492  		c.Fatal(err)
   493  	}
   494  	if !bytes.Equal(fb, fb2) {
   495  		c.Fatalf("Expected copied file to be duplicate of bind-mounted file")
   496  	}
   497  
   498  	// Copy bind-mounted file
   499  	_, _ = dockerCmd(c, "cp", cleanedContainerID+":/test", outDir)
   500  	fb, err = ioutil.ReadFile(outDir + "/test")
   501  	if err != nil {
   502  		c.Fatal(err)
   503  	}
   504  	fb2, err = ioutil.ReadFile(tmpDir + "/test")
   505  	if err != nil {
   506  		c.Fatal(err)
   507  	}
   508  	if !bytes.Equal(fb, fb2) {
   509  		c.Fatalf("Expected copied file to be duplicate of bind-mounted file")
   510  	}
   511  
   512  }
   513  
   514  func (s *DockerSuite) TestCpToDot(c *check.C) {
   515  	out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test")
   516  	if exitCode != 0 {
   517  		c.Fatal("failed to create a container", out)
   518  	}
   519  
   520  	cleanedContainerID := strings.TrimSpace(out)
   521  
   522  	out, _ = dockerCmd(c, "wait", cleanedContainerID)
   523  	if strings.TrimSpace(out) != "0" {
   524  		c.Fatal("failed to set up container", out)
   525  	}
   526  
   527  	tmpdir, err := ioutil.TempDir("", "docker-integration")
   528  	if err != nil {
   529  		c.Fatal(err)
   530  	}
   531  	defer os.RemoveAll(tmpdir)
   532  	cwd, err := os.Getwd()
   533  	if err != nil {
   534  		c.Fatal(err)
   535  	}
   536  	defer os.Chdir(cwd)
   537  	if err := os.Chdir(tmpdir); err != nil {
   538  		c.Fatal(err)
   539  	}
   540  	_, _ = dockerCmd(c, "cp", cleanedContainerID+":/test", ".")
   541  	content, err := ioutil.ReadFile("./test")
   542  	if string(content) != "lololol\n" {
   543  		c.Fatalf("Wrong content in copied file %q, should be %q", content, "lololol\n")
   544  	}
   545  }
   546  
   547  func (s *DockerSuite) TestCpToStdout(c *check.C) {
   548  	out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test")
   549  	if exitCode != 0 {
   550  		c.Fatalf("failed to create a container:%s\n", out)
   551  	}
   552  
   553  	cID := strings.TrimSpace(out)
   554  
   555  	out, _ = dockerCmd(c, "wait", cID)
   556  	if strings.TrimSpace(out) != "0" {
   557  		c.Fatalf("failed to set up container:%s\n", out)
   558  	}
   559  
   560  	out, _, err := runCommandPipelineWithOutput(
   561  		exec.Command(dockerBinary, "cp", cID+":/test", "-"),
   562  		exec.Command("tar", "-vtf", "-"))
   563  
   564  	if err != nil {
   565  		c.Fatalf("Failed to run commands: %s", err)
   566  	}
   567  
   568  	if !strings.Contains(out, "test") || !strings.Contains(out, "-rw") {
   569  		c.Fatalf("Missing file from tar TOC:\n%s", out)
   570  	}
   571  }
   572  
   573  func (s *DockerSuite) TestCpNameHasColon(c *check.C) {
   574  	testRequires(c, SameHostDaemon)
   575  
   576  	out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /te:s:t")
   577  	if exitCode != 0 {
   578  		c.Fatal("failed to create a container", out)
   579  	}
   580  
   581  	cleanedContainerID := strings.TrimSpace(out)
   582  
   583  	out, _ = dockerCmd(c, "wait", cleanedContainerID)
   584  	if strings.TrimSpace(out) != "0" {
   585  		c.Fatal("failed to set up container", out)
   586  	}
   587  
   588  	tmpdir, err := ioutil.TempDir("", "docker-integration")
   589  	if err != nil {
   590  		c.Fatal(err)
   591  	}
   592  	defer os.RemoveAll(tmpdir)
   593  	_, _ = dockerCmd(c, "cp", cleanedContainerID+":/te:s:t", tmpdir)
   594  	content, err := ioutil.ReadFile(tmpdir + "/te:s:t")
   595  	if string(content) != "lololol\n" {
   596  		c.Fatalf("Wrong content in copied file %q, should be %q", content, "lololol\n")
   597  	}
   598  }