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