docker_cli_cp_to_container_test.go 23 KB

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