docker_cli_cp_test.go 21 KB

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