github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/integration-cli/docker_cli_cp_to_container_test.go (about)

     1  package main
     2  
     3  import (
     4  	"os"
     5  	"runtime"
     6  	"strings"
     7  
     8  	"github.com/docker/docker/integration-cli/checker"
     9  	"github.com/go-check/check"
    10  )
    11  
    12  // docker cp LOCALPATH CONTAINER:PATH
    13  
    14  // Try all of the test cases from the archive package which implements the
    15  // internals of `docker cp` and ensure that the behavior matches when actually
    16  // copying to and from containers.
    17  
    18  // Basic assumptions about SRC and DST:
    19  // 1. SRC must exist.
    20  // 2. If SRC ends with a trailing separator, it must be a directory.
    21  // 3. DST parent directory must exist.
    22  // 4. If DST exists as a file, it must not end with a trailing separator.
    23  
    24  // First get these easy error cases out of the way.
    25  
    26  // Test for error when SRC does not exist.
    27  func (s *DockerSuite) TestCpToErrSrcNotExists(c *check.C) {
    28  	containerID := makeTestContainer(c, testContainerOptions{})
    29  
    30  	tmpDir := getTestDir(c, "test-cp-to-err-src-not-exists")
    31  	defer os.RemoveAll(tmpDir)
    32  
    33  	srcPath := cpPath(tmpDir, "file1")
    34  	dstPath := containerCpPath(containerID, "file1")
    35  	_, srcStatErr := os.Stat(srcPath)
    36  	c.Assert(os.IsNotExist(srcStatErr), checker.True)
    37  
    38  	err := runDockerCp(c, srcPath, dstPath, nil)
    39  	if runtime.GOOS == "windows" {
    40  		// Go 1.9+ on Windows returns a different error for `os.Stat()`, see
    41  		// https://github.com/golang/go/commit/6144c7270e5812d9de8fb97456ee4e5ae657fcbb#diff-f63e1a4b4377b2fe0b05011db3df9599
    42  		//
    43  		// Go 1.8: CreateFile C:\not-exist: The system cannot find the file specified.
    44  		// Go 1.9: GetFileAttributesEx C:\not-exist: The system cannot find the file specified.
    45  		//
    46  		// Due to the CLI using a different version than the daemon, comparing the
    47  		// error message won't work, so just hard-code the common part here.
    48  		//
    49  		// TODO this should probably be a test in the CLI repository instead
    50  		c.Assert(strings.ToLower(err.Error()), checker.Contains, "cannot find the file specified")
    51  		c.Assert(strings.ToLower(err.Error()), checker.Contains, strings.ToLower(tmpDir))
    52  	} else {
    53  		c.Assert(strings.ToLower(err.Error()), checker.Contains, strings.ToLower(srcStatErr.Error()))
    54  	}
    55  }
    56  
    57  // Test for error when SRC ends in a trailing
    58  // path separator but it exists as a file.
    59  func (s *DockerSuite) TestCpToErrSrcNotDir(c *check.C) {
    60  	containerID := makeTestContainer(c, testContainerOptions{})
    61  
    62  	tmpDir := getTestDir(c, "test-cp-to-err-src-not-dir")
    63  	defer os.RemoveAll(tmpDir)
    64  
    65  	makeTestContentInDir(c, tmpDir)
    66  
    67  	srcPath := cpPathTrailingSep(tmpDir, "file1")
    68  	dstPath := containerCpPath(containerID, "testDir")
    69  
    70  	err := runDockerCp(c, srcPath, dstPath, nil)
    71  	c.Assert(err, checker.NotNil)
    72  
    73  	c.Assert(isCpNotDir(err), checker.True, check.Commentf("expected IsNotDir error, but got %T: %s", err, err))
    74  }
    75  
    76  // Test for error when SRC is a valid file or directory,
    77  // but the DST parent directory does not exist.
    78  func (s *DockerSuite) TestCpToErrDstParentNotExists(c *check.C) {
    79  	testRequires(c, DaemonIsLinux)
    80  	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
    81  
    82  	tmpDir := getTestDir(c, "test-cp-to-err-dst-parent-not-exists")
    83  	defer os.RemoveAll(tmpDir)
    84  
    85  	makeTestContentInDir(c, tmpDir)
    86  
    87  	// Try with a file source.
    88  	srcPath := cpPath(tmpDir, "file1")
    89  	dstPath := containerCpPath(containerID, "/notExists", "file1")
    90  
    91  	err := runDockerCp(c, srcPath, dstPath, nil)
    92  	c.Assert(err, checker.NotNil)
    93  
    94  	c.Assert(isCpNotExist(err), checker.True, check.Commentf("expected IsNotExist error, but got %T: %s", err, err))
    95  
    96  	// Try with a directory source.
    97  	srcPath = cpPath(tmpDir, "dir1")
    98  
    99  	err = runDockerCp(c, srcPath, dstPath, nil)
   100  	c.Assert(err, checker.NotNil)
   101  
   102  	c.Assert(isCpNotExist(err), checker.True, check.Commentf("expected IsNotExist error, but got %T: %s", err, err))
   103  }
   104  
   105  // Test for error when DST ends in a trailing path separator but exists as a
   106  // file. Also test that we cannot overwrite an existing directory with a
   107  // non-directory and cannot overwrite an existing
   108  func (s *DockerSuite) TestCpToErrDstNotDir(c *check.C) {
   109  	testRequires(c, DaemonIsLinux)
   110  	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
   111  
   112  	tmpDir := getTestDir(c, "test-cp-to-err-dst-not-dir")
   113  	defer os.RemoveAll(tmpDir)
   114  
   115  	makeTestContentInDir(c, tmpDir)
   116  
   117  	// Try with a file source.
   118  	srcPath := cpPath(tmpDir, "dir1/file1-1")
   119  	dstPath := containerCpPathTrailingSep(containerID, "file1")
   120  
   121  	// The client should encounter an error trying to stat the destination
   122  	// and then be unable to copy since the destination is asserted to be a
   123  	// directory but does not exist.
   124  	err := runDockerCp(c, srcPath, dstPath, nil)
   125  	c.Assert(err, checker.NotNil)
   126  
   127  	c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExist error, but got %T: %s", err, err))
   128  
   129  	// Try with a directory source.
   130  	srcPath = cpPath(tmpDir, "dir1")
   131  
   132  	// The client should encounter an error trying to stat the destination and
   133  	// then decide to extract to the parent directory instead with a rebased
   134  	// name in the source archive, but this directory would overwrite the
   135  	// existing file with the same name.
   136  	err = runDockerCp(c, srcPath, dstPath, nil)
   137  	c.Assert(err, checker.NotNil)
   138  
   139  	c.Assert(isCannotOverwriteNonDirWithDir(err), checker.True, check.Commentf("expected CannotOverwriteNonDirWithDir error, but got %T: %s", err, err))
   140  }
   141  
   142  // Check that copying from a local path to a symlink in a container copies to
   143  // the symlink target and does not overwrite the container symlink itself.
   144  func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
   145  	//  stat /tmp/test-cp-to-symlink-destination-262430901/vol3 gets permission denied for the user
   146  	testRequires(c, NotUserNamespace)
   147  	testRequires(c, DaemonIsLinux)
   148  	testRequires(c, SameHostDaemon) // Requires local volume mount bind.
   149  
   150  	testVol := getTestDir(c, "test-cp-to-symlink-destination-")
   151  	defer os.RemoveAll(testVol)
   152  
   153  	makeTestContentInDir(c, testVol)
   154  
   155  	containerID := makeTestContainer(c, testContainerOptions{
   156  		volumes: defaultVolumes(testVol), // Our bind mount is at /vol2
   157  	})
   158  
   159  	// First, copy a local file to a symlink to a file in the container. This
   160  	// should overwrite the symlink target contents with the source contents.
   161  	srcPath := cpPath(testVol, "file2")
   162  	dstPath := containerCpPath(containerID, "/vol2/symlinkToFile1")
   163  
   164  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   165  
   166  	// The symlink should not have been modified.
   167  	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToFile1"), "file1"), checker.IsNil)
   168  
   169  	// The file should have the contents of "file2" now.
   170  	c.Assert(fileContentEquals(c, cpPath(testVol, "file1"), "file2\n"), checker.IsNil)
   171  
   172  	// Next, copy a local file to a symlink to a directory in the container.
   173  	// This should copy the file into the symlink target directory.
   174  	dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1")
   175  
   176  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   177  
   178  	// The symlink should not have been modified.
   179  	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil)
   180  
   181  	// The file should have the contents of "file2" now.
   182  	c.Assert(fileContentEquals(c, cpPath(testVol, "file2"), "file2\n"), checker.IsNil)
   183  
   184  	// Next, copy a file to a symlink to a file that does not exist (a broken
   185  	// symlink) in the container. This should create the target file with the
   186  	// contents of the source file.
   187  	dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToFileX")
   188  
   189  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   190  
   191  	// The symlink should not have been modified.
   192  	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToFileX"), "fileX"), checker.IsNil)
   193  
   194  	// The file should have the contents of "file2" now.
   195  	c.Assert(fileContentEquals(c, cpPath(testVol, "fileX"), "file2\n"), checker.IsNil)
   196  
   197  	// Next, copy a local directory to a symlink to a directory in the
   198  	// container. This should copy the directory into the symlink target
   199  	// directory and not modify the symlink.
   200  	srcPath = cpPath(testVol, "/dir2")
   201  	dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1")
   202  
   203  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   204  
   205  	// The symlink should not have been modified.
   206  	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil)
   207  
   208  	// The directory should now contain a copy of "dir2".
   209  	c.Assert(fileContentEquals(c, cpPath(testVol, "dir1/dir2/file2-1"), "file2-1\n"), checker.IsNil)
   210  
   211  	// Next, copy a local directory to a symlink to a local directory that does
   212  	// not exist (a broken symlink) in the container. This should create the
   213  	// target as a directory with the contents of the source directory. It
   214  	// should not modify the symlink.
   215  	dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToDirX")
   216  
   217  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   218  
   219  	// The symlink should not have been modified.
   220  	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToDirX"), "dirX"), checker.IsNil)
   221  
   222  	// The "dirX" directory should now be a copy of "dir2".
   223  	c.Assert(fileContentEquals(c, cpPath(testVol, "dirX/file2-1"), "file2-1\n"), checker.IsNil)
   224  }
   225  
   226  // Possibilities are reduced to the remaining 10 cases:
   227  //
   228  //  case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action
   229  // ===================================================================================================
   230  //   A   |  no      |  -              |  no       |  -       |  no      |  create file
   231  //   B   |  no      |  -              |  no       |  -       |  yes     |  error
   232  //   C   |  no      |  -              |  yes      |  no      |  -       |  overwrite file
   233  //   D   |  no      |  -              |  yes      |  yes     |  -       |  create file in dst dir
   234  //   E   |  yes     |  no             |  no       |  -       |  -       |  create dir, copy contents
   235  //   F   |  yes     |  no             |  yes      |  no      |  -       |  error
   236  //   G   |  yes     |  no             |  yes      |  yes     |  -       |  copy dir and contents
   237  //   H   |  yes     |  yes            |  no       |  -       |  -       |  create dir, copy contents
   238  //   I   |  yes     |  yes            |  yes      |  no      |  -       |  error
   239  //   J   |  yes     |  yes            |  yes      |  yes     |  -       |  copy dir contents
   240  //
   241  
   242  // A. SRC specifies a file and DST (no trailing path separator) doesn't
   243  //    exist. This should create a file with the name DST and copy the
   244  //    contents of the source file into it.
   245  func (s *DockerSuite) TestCpToCaseA(c *check.C) {
   246  	containerID := makeTestContainer(c, testContainerOptions{
   247  		workDir: "/root", command: makeCatFileCommand("itWorks.txt"),
   248  	})
   249  
   250  	tmpDir := getTestDir(c, "test-cp-to-case-a")
   251  	defer os.RemoveAll(tmpDir)
   252  
   253  	makeTestContentInDir(c, tmpDir)
   254  
   255  	srcPath := cpPath(tmpDir, "file1")
   256  	dstPath := containerCpPath(containerID, "/root/itWorks.txt")
   257  
   258  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   259  
   260  	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
   261  }
   262  
   263  // B. SRC specifies a file and DST (with trailing path separator) doesn't
   264  //    exist. This should cause an error because the copy operation cannot
   265  //    create a directory when copying a single file.
   266  func (s *DockerSuite) TestCpToCaseB(c *check.C) {
   267  	containerID := makeTestContainer(c, testContainerOptions{
   268  		command: makeCatFileCommand("testDir/file1"),
   269  	})
   270  
   271  	tmpDir := getTestDir(c, "test-cp-to-case-b")
   272  	defer os.RemoveAll(tmpDir)
   273  
   274  	makeTestContentInDir(c, tmpDir)
   275  
   276  	srcPath := cpPath(tmpDir, "file1")
   277  	dstDir := containerCpPathTrailingSep(containerID, "testDir")
   278  
   279  	err := runDockerCp(c, srcPath, dstDir, nil)
   280  	c.Assert(err, checker.NotNil)
   281  
   282  	c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExists error, but got %T: %s", err, err))
   283  }
   284  
   285  // C. SRC specifies a file and DST exists as a file. This should overwrite
   286  //    the file at DST with the contents of the source file.
   287  func (s *DockerSuite) TestCpToCaseC(c *check.C) {
   288  	testRequires(c, DaemonIsLinux)
   289  	containerID := makeTestContainer(c, testContainerOptions{
   290  		addContent: true, workDir: "/root",
   291  		command: makeCatFileCommand("file2"),
   292  	})
   293  
   294  	tmpDir := getTestDir(c, "test-cp-to-case-c")
   295  	defer os.RemoveAll(tmpDir)
   296  
   297  	makeTestContentInDir(c, tmpDir)
   298  
   299  	srcPath := cpPath(tmpDir, "file1")
   300  	dstPath := containerCpPath(containerID, "/root/file2")
   301  
   302  	// Ensure the container's file starts with the original content.
   303  	c.Assert(containerStartOutputEquals(c, containerID, "file2\n"), checker.IsNil)
   304  
   305  	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
   306  
   307  	// Should now contain file1's contents.
   308  	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
   309  }
   310  
   311  // D. SRC specifies a file and DST exists as a directory. This should place
   312  //    a copy of the source file inside it using the basename from SRC. Ensure
   313  //    this works whether DST has a trailing path separator or not.
   314  func (s *DockerSuite) TestCpToCaseD(c *check.C) {
   315  	testRequires(c, DaemonIsLinux)
   316  	containerID := makeTestContainer(c, testContainerOptions{
   317  		addContent: true,
   318  		command:    makeCatFileCommand("/dir1/file1"),
   319  	})
   320  
   321  	tmpDir := getTestDir(c, "test-cp-to-case-d")
   322  	defer os.RemoveAll(tmpDir)
   323  
   324  	makeTestContentInDir(c, tmpDir)
   325  
   326  	srcPath := cpPath(tmpDir, "file1")
   327  	dstDir := containerCpPath(containerID, "dir1")
   328  
   329  	// Ensure that dstPath doesn't exist.
   330  	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
   331  
   332  	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
   333  
   334  	// Should now contain file1's contents.
   335  	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
   336  
   337  	// Now try again but using a trailing path separator for dstDir.
   338  
   339  	// Make new destination container.
   340  	containerID = makeTestContainer(c, testContainerOptions{
   341  		addContent: true,
   342  		command:    makeCatFileCommand("/dir1/file1"),
   343  	})
   344  
   345  	dstDir = containerCpPathTrailingSep(containerID, "dir1")
   346  
   347  	// Ensure that dstPath doesn't exist.
   348  	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
   349  
   350  	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
   351  
   352  	// Should now contain file1's contents.
   353  	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
   354  }
   355  
   356  // E. SRC specifies a directory and DST does not exist. This should create a
   357  //    directory at DST and copy the contents of the SRC directory into the DST
   358  //    directory. Ensure this works whether DST has a trailing path separator or
   359  //    not.
   360  func (s *DockerSuite) TestCpToCaseE(c *check.C) {
   361  	containerID := makeTestContainer(c, testContainerOptions{
   362  		command: makeCatFileCommand("/testDir/file1-1"),
   363  	})
   364  
   365  	tmpDir := getTestDir(c, "test-cp-to-case-e")
   366  	defer os.RemoveAll(tmpDir)
   367  
   368  	makeTestContentInDir(c, tmpDir)
   369  
   370  	srcDir := cpPath(tmpDir, "dir1")
   371  	dstDir := containerCpPath(containerID, "testDir")
   372  
   373  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   374  
   375  	// Should now contain file1-1's contents.
   376  	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
   377  
   378  	// Now try again but using a trailing path separator for dstDir.
   379  
   380  	// Make new destination container.
   381  	containerID = makeTestContainer(c, testContainerOptions{
   382  		command: makeCatFileCommand("/testDir/file1-1"),
   383  	})
   384  
   385  	dstDir = containerCpPathTrailingSep(containerID, "testDir")
   386  
   387  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   388  
   389  	// Should now contain file1-1's contents.
   390  	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
   391  }
   392  
   393  // F. SRC specifies a directory and DST exists as a file. This should cause an
   394  //    error as it is not possible to overwrite a file with a directory.
   395  func (s *DockerSuite) TestCpToCaseF(c *check.C) {
   396  	testRequires(c, DaemonIsLinux)
   397  	containerID := makeTestContainer(c, testContainerOptions{
   398  		addContent: true, workDir: "/root",
   399  	})
   400  
   401  	tmpDir := getTestDir(c, "test-cp-to-case-f")
   402  	defer os.RemoveAll(tmpDir)
   403  
   404  	makeTestContentInDir(c, tmpDir)
   405  
   406  	srcDir := cpPath(tmpDir, "dir1")
   407  	dstFile := containerCpPath(containerID, "/root/file1")
   408  
   409  	err := runDockerCp(c, srcDir, dstFile, nil)
   410  	c.Assert(err, checker.NotNil)
   411  
   412  	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
   413  }
   414  
   415  // G. SRC specifies a directory and DST exists as a directory. This should copy
   416  //    the SRC directory and all its contents to the DST directory. Ensure this
   417  //    works whether DST has a trailing path separator or not.
   418  func (s *DockerSuite) TestCpToCaseG(c *check.C) {
   419  	testRequires(c, DaemonIsLinux)
   420  	containerID := makeTestContainer(c, testContainerOptions{
   421  		addContent: true, workDir: "/root",
   422  		command: makeCatFileCommand("dir2/dir1/file1-1"),
   423  	})
   424  
   425  	tmpDir := getTestDir(c, "test-cp-to-case-g")
   426  	defer os.RemoveAll(tmpDir)
   427  
   428  	makeTestContentInDir(c, tmpDir)
   429  
   430  	srcDir := cpPath(tmpDir, "dir1")
   431  	dstDir := containerCpPath(containerID, "/root/dir2")
   432  
   433  	// Ensure that dstPath doesn't exist.
   434  	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
   435  
   436  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   437  
   438  	// Should now contain file1-1's contents.
   439  	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
   440  
   441  	// Now try again but using a trailing path separator for dstDir.
   442  
   443  	// Make new destination container.
   444  	containerID = makeTestContainer(c, testContainerOptions{
   445  		addContent: true,
   446  		command:    makeCatFileCommand("/dir2/dir1/file1-1"),
   447  	})
   448  
   449  	dstDir = containerCpPathTrailingSep(containerID, "/dir2")
   450  
   451  	// Ensure that dstPath doesn't exist.
   452  	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
   453  
   454  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   455  
   456  	// Should now contain file1-1's contents.
   457  	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
   458  }
   459  
   460  // H. SRC specifies a directory's contents only and DST does not exist. This
   461  //    should create a directory at DST and copy the contents of the SRC
   462  //    directory (but not the directory itself) into the DST directory. Ensure
   463  //    this works whether DST has a trailing path separator or not.
   464  func (s *DockerSuite) TestCpToCaseH(c *check.C) {
   465  	containerID := makeTestContainer(c, testContainerOptions{
   466  		command: makeCatFileCommand("/testDir/file1-1"),
   467  	})
   468  
   469  	tmpDir := getTestDir(c, "test-cp-to-case-h")
   470  	defer os.RemoveAll(tmpDir)
   471  
   472  	makeTestContentInDir(c, tmpDir)
   473  
   474  	srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
   475  	dstDir := containerCpPath(containerID, "testDir")
   476  
   477  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   478  
   479  	// Should now contain file1-1's contents.
   480  	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
   481  
   482  	// Now try again but using a trailing path separator for dstDir.
   483  
   484  	// Make new destination container.
   485  	containerID = makeTestContainer(c, testContainerOptions{
   486  		command: makeCatFileCommand("/testDir/file1-1"),
   487  	})
   488  
   489  	dstDir = containerCpPathTrailingSep(containerID, "testDir")
   490  
   491  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   492  
   493  	// Should now contain file1-1's contents.
   494  	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
   495  }
   496  
   497  // I. SRC specifies a directory's contents only and DST exists as a file. This
   498  //    should cause an error as it is not possible to overwrite a file with a
   499  //    directory.
   500  func (s *DockerSuite) TestCpToCaseI(c *check.C) {
   501  	testRequires(c, DaemonIsLinux)
   502  	containerID := makeTestContainer(c, testContainerOptions{
   503  		addContent: true, workDir: "/root",
   504  	})
   505  
   506  	tmpDir := getTestDir(c, "test-cp-to-case-i")
   507  	defer os.RemoveAll(tmpDir)
   508  
   509  	makeTestContentInDir(c, tmpDir)
   510  
   511  	srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
   512  	dstFile := containerCpPath(containerID, "/root/file1")
   513  
   514  	err := runDockerCp(c, srcDir, dstFile, nil)
   515  	c.Assert(err, checker.NotNil)
   516  
   517  	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
   518  }
   519  
   520  // J. SRC specifies a directory's contents only and DST exists as a directory.
   521  //    This should copy the contents of the SRC directory (but not the directory
   522  //    itself) into the DST directory. Ensure this works whether DST has a
   523  //    trailing path separator or not.
   524  func (s *DockerSuite) TestCpToCaseJ(c *check.C) {
   525  	testRequires(c, DaemonIsLinux)
   526  	containerID := makeTestContainer(c, testContainerOptions{
   527  		addContent: true, workDir: "/root",
   528  		command: makeCatFileCommand("/dir2/file1-1"),
   529  	})
   530  
   531  	tmpDir := getTestDir(c, "test-cp-to-case-j")
   532  	defer os.RemoveAll(tmpDir)
   533  
   534  	makeTestContentInDir(c, tmpDir)
   535  
   536  	srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
   537  	dstDir := containerCpPath(containerID, "/dir2")
   538  
   539  	// Ensure that dstPath doesn't exist.
   540  	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
   541  
   542  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   543  
   544  	// Should now contain file1-1's contents.
   545  	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
   546  
   547  	// Now try again but using a trailing path separator for dstDir.
   548  
   549  	// Make new destination container.
   550  	containerID = makeTestContainer(c, testContainerOptions{
   551  		command: makeCatFileCommand("/dir2/file1-1"),
   552  	})
   553  
   554  	dstDir = containerCpPathTrailingSep(containerID, "/dir2")
   555  
   556  	// Ensure that dstPath doesn't exist.
   557  	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
   558  
   559  	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
   560  
   561  	// Should now contain file1-1's contents.
   562  	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
   563  }
   564  
   565  // The `docker cp` command should also ensure that you cannot
   566  // write to a container rootfs that is marked as read-only.
   567  func (s *DockerSuite) TestCpToErrReadOnlyRootfs(c *check.C) {
   568  	// --read-only + userns has remount issues
   569  	testRequires(c, DaemonIsLinux, NotUserNamespace)
   570  	tmpDir := getTestDir(c, "test-cp-to-err-read-only-rootfs")
   571  	defer os.RemoveAll(tmpDir)
   572  
   573  	makeTestContentInDir(c, tmpDir)
   574  
   575  	containerID := makeTestContainer(c, testContainerOptions{
   576  		readOnly: true, workDir: "/root",
   577  		command: makeCatFileCommand("shouldNotExist"),
   578  	})
   579  
   580  	srcPath := cpPath(tmpDir, "file1")
   581  	dstPath := containerCpPath(containerID, "/root/shouldNotExist")
   582  
   583  	err := runDockerCp(c, srcPath, dstPath, nil)
   584  	c.Assert(err, checker.NotNil)
   585  
   586  	c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrContainerRootfsReadonly error, but got %T: %s", err, err))
   587  
   588  	// Ensure that dstPath doesn't exist.
   589  	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
   590  }
   591  
   592  // The `docker cp` command should also ensure that you
   593  // cannot write to a volume that is mounted as read-only.
   594  func (s *DockerSuite) TestCpToErrReadOnlyVolume(c *check.C) {
   595  	// --read-only + userns has remount issues
   596  	testRequires(c, DaemonIsLinux, NotUserNamespace)
   597  	tmpDir := getTestDir(c, "test-cp-to-err-read-only-volume")
   598  	defer os.RemoveAll(tmpDir)
   599  
   600  	makeTestContentInDir(c, tmpDir)
   601  
   602  	containerID := makeTestContainer(c, testContainerOptions{
   603  		volumes: defaultVolumes(tmpDir), workDir: "/root",
   604  		command: makeCatFileCommand("/vol_ro/shouldNotExist"),
   605  	})
   606  
   607  	srcPath := cpPath(tmpDir, "file1")
   608  	dstPath := containerCpPath(containerID, "/vol_ro/shouldNotExist")
   609  
   610  	err := runDockerCp(c, srcPath, dstPath, nil)
   611  	c.Assert(err, checker.NotNil)
   612  
   613  	c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrVolumeReadonly error, but got %T: %s", err, err))
   614  
   615  	// Ensure that dstPath doesn't exist.
   616  	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
   617  }