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