github.com/goern/docker@v1.9.0-rc1/integration-cli/docker_cli_cp_to_container_test.go (about)

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