docker_cli_cp_to_container_test.go 23 KB

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