docker_cli_cp_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "os/exec"
  8. "path"
  9. "path/filepath"
  10. "strings"
  11. "github.com/go-check/check"
  12. )
  13. const (
  14. cpTestPathParent = "/some"
  15. cpTestPath = "/some/path"
  16. cpTestName = "test"
  17. cpFullPath = "/some/path/test"
  18. cpContainerContents = "holla, i am the container"
  19. cpHostContents = "hello, i am the host"
  20. )
  21. // Ensure that an all-local path case returns an error.
  22. func (s *DockerSuite) TestCpLocalOnly(c *check.C) {
  23. err := runDockerCp(c, "foo", "bar")
  24. if err == nil {
  25. c.Fatal("expected failure, got success")
  26. }
  27. if !strings.Contains(err.Error(), "must specify at least one container source") {
  28. c.Fatalf("unexpected output: %s", err.Error())
  29. }
  30. }
  31. // Test for #5656
  32. // Check that garbage paths don't escape the container's rootfs
  33. func (s *DockerSuite) TestCpGarbagePath(c *check.C) {
  34. testRequires(c, DaemonIsLinux)
  35. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
  36. if exitCode != 0 {
  37. c.Fatal("failed to create a container", out)
  38. }
  39. cleanedContainerID := strings.TrimSpace(out)
  40. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  41. if strings.TrimSpace(out) != "0" {
  42. c.Fatal("failed to set up container", out)
  43. }
  44. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  45. c.Fatal(err)
  46. }
  47. hostFile, err := os.Create(cpFullPath)
  48. if err != nil {
  49. c.Fatal(err)
  50. }
  51. defer hostFile.Close()
  52. defer os.RemoveAll(cpTestPathParent)
  53. fmt.Fprintf(hostFile, "%s", cpHostContents)
  54. tmpdir, err := ioutil.TempDir("", "docker-integration")
  55. if err != nil {
  56. c.Fatal(err)
  57. }
  58. tmpname := filepath.Join(tmpdir, cpTestName)
  59. defer os.RemoveAll(tmpdir)
  60. path := path.Join("../../../../../../../../../../../../", cpFullPath)
  61. dockerCmd(c, "cp", cleanedContainerID+":"+path, tmpdir)
  62. file, _ := os.Open(tmpname)
  63. defer file.Close()
  64. test, err := ioutil.ReadAll(file)
  65. if err != nil {
  66. c.Fatal(err)
  67. }
  68. if string(test) == cpHostContents {
  69. c.Errorf("output matched host file -- garbage path can escape container rootfs")
  70. }
  71. if string(test) != cpContainerContents {
  72. c.Errorf("output doesn't match the input for garbage path")
  73. }
  74. }
  75. // Check that relative paths are relative to the container's rootfs
  76. func (s *DockerSuite) TestCpRelativePath(c *check.C) {
  77. testRequires(c, DaemonIsLinux)
  78. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
  79. if exitCode != 0 {
  80. c.Fatal("failed to create a container", out)
  81. }
  82. cleanedContainerID := strings.TrimSpace(out)
  83. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  84. if strings.TrimSpace(out) != "0" {
  85. c.Fatal("failed to set up container", out)
  86. }
  87. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  88. c.Fatal(err)
  89. }
  90. hostFile, err := os.Create(cpFullPath)
  91. if err != nil {
  92. c.Fatal(err)
  93. }
  94. defer hostFile.Close()
  95. defer os.RemoveAll(cpTestPathParent)
  96. fmt.Fprintf(hostFile, "%s", cpHostContents)
  97. tmpdir, err := ioutil.TempDir("", "docker-integration")
  98. if err != nil {
  99. c.Fatal(err)
  100. }
  101. tmpname := filepath.Join(tmpdir, cpTestName)
  102. defer os.RemoveAll(tmpdir)
  103. var relPath string
  104. if path.IsAbs(cpFullPath) {
  105. // normally this is `filepath.Rel("/", cpFullPath)` but we cannot
  106. // get this unix-path manipulation on windows with filepath.
  107. relPath = cpFullPath[1:]
  108. } else {
  109. c.Fatalf("path %s was assumed to be an absolute path", cpFullPath)
  110. }
  111. dockerCmd(c, "cp", cleanedContainerID+":"+relPath, tmpdir)
  112. file, _ := os.Open(tmpname)
  113. defer file.Close()
  114. test, err := ioutil.ReadAll(file)
  115. if err != nil {
  116. c.Fatal(err)
  117. }
  118. if string(test) == cpHostContents {
  119. c.Errorf("output matched host file -- relative path can escape container rootfs")
  120. }
  121. if string(test) != cpContainerContents {
  122. c.Errorf("output doesn't match the input for relative path")
  123. }
  124. }
  125. // Check that absolute paths are relative to the container's rootfs
  126. func (s *DockerSuite) TestCpAbsolutePath(c *check.C) {
  127. testRequires(c, DaemonIsLinux)
  128. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
  129. if exitCode != 0 {
  130. c.Fatal("failed to create a container", out)
  131. }
  132. cleanedContainerID := strings.TrimSpace(out)
  133. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  134. if strings.TrimSpace(out) != "0" {
  135. c.Fatal("failed to set up container", out)
  136. }
  137. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  138. c.Fatal(err)
  139. }
  140. hostFile, err := os.Create(cpFullPath)
  141. if err != nil {
  142. c.Fatal(err)
  143. }
  144. defer hostFile.Close()
  145. defer os.RemoveAll(cpTestPathParent)
  146. fmt.Fprintf(hostFile, "%s", cpHostContents)
  147. tmpdir, err := ioutil.TempDir("", "docker-integration")
  148. if err != nil {
  149. c.Fatal(err)
  150. }
  151. tmpname := filepath.Join(tmpdir, cpTestName)
  152. defer os.RemoveAll(tmpdir)
  153. path := cpFullPath
  154. dockerCmd(c, "cp", cleanedContainerID+":"+path, tmpdir)
  155. file, _ := os.Open(tmpname)
  156. defer file.Close()
  157. test, err := ioutil.ReadAll(file)
  158. if err != nil {
  159. c.Fatal(err)
  160. }
  161. if string(test) == cpHostContents {
  162. c.Errorf("output matched host file -- absolute path can escape container rootfs")
  163. }
  164. if string(test) != cpContainerContents {
  165. c.Errorf("output doesn't match the input for absolute path")
  166. }
  167. }
  168. // Test for #5619
  169. // Check that absolute symlinks are still relative to the container's rootfs
  170. func (s *DockerSuite) TestCpAbsoluteSymlink(c *check.C) {
  171. testRequires(c, DaemonIsLinux)
  172. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpFullPath+" container_path")
  173. if exitCode != 0 {
  174. c.Fatal("failed to create a container", out)
  175. }
  176. cleanedContainerID := strings.TrimSpace(out)
  177. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  178. if strings.TrimSpace(out) != "0" {
  179. c.Fatal("failed to set up container", out)
  180. }
  181. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  182. c.Fatal(err)
  183. }
  184. hostFile, err := os.Create(cpFullPath)
  185. if err != nil {
  186. c.Fatal(err)
  187. }
  188. defer hostFile.Close()
  189. defer os.RemoveAll(cpTestPathParent)
  190. fmt.Fprintf(hostFile, "%s", cpHostContents)
  191. tmpdir, err := ioutil.TempDir("", "docker-integration")
  192. if err != nil {
  193. c.Fatal(err)
  194. }
  195. tmpname := filepath.Join(tmpdir, "container_path")
  196. defer os.RemoveAll(tmpdir)
  197. path := path.Join("/", "container_path")
  198. dockerCmd(c, "cp", cleanedContainerID+":"+path, tmpdir)
  199. // We should have copied a symlink *NOT* the file itself!
  200. linkTarget, err := os.Readlink(tmpname)
  201. if err != nil {
  202. c.Fatal(err)
  203. }
  204. if linkTarget != filepath.FromSlash(cpFullPath) {
  205. c.Errorf("symlink target was %q, but expected: %q", linkTarget, cpFullPath)
  206. }
  207. }
  208. // Check that symlinks to a directory behave as expected when copying one from
  209. // a container.
  210. func (s *DockerSuite) TestCpFromSymlinkToDirectory(c *check.C) {
  211. testRequires(c, DaemonIsLinux)
  212. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpTestPathParent+" /dir_link")
  213. if exitCode != 0 {
  214. c.Fatal("failed to create a container", out)
  215. }
  216. cleanedContainerID := strings.TrimSpace(out)
  217. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  218. if strings.TrimSpace(out) != "0" {
  219. c.Fatal("failed to set up container", out)
  220. }
  221. testDir, err := ioutil.TempDir("", "test-cp-from-symlink-to-dir-")
  222. if err != nil {
  223. c.Fatal(err)
  224. }
  225. defer os.RemoveAll(testDir)
  226. // This copy command should copy the symlink, not the target, into the
  227. // temporary directory.
  228. dockerCmd(c, "cp", cleanedContainerID+":"+"/dir_link", testDir)
  229. expectedPath := filepath.Join(testDir, "dir_link")
  230. linkTarget, err := os.Readlink(expectedPath)
  231. if err != nil {
  232. c.Fatalf("unable to read symlink at %q: %v", expectedPath, err)
  233. }
  234. if linkTarget != filepath.FromSlash(cpTestPathParent) {
  235. c.Errorf("symlink target was %q, but expected: %q", linkTarget, cpTestPathParent)
  236. }
  237. os.Remove(expectedPath)
  238. // This copy command should resolve the symlink (note the trailing
  239. // separator), copying the target into the temporary directory.
  240. dockerCmd(c, "cp", cleanedContainerID+":"+"/dir_link/", testDir)
  241. // It *should not* have copied the directory using the target's name, but
  242. // used the given name instead.
  243. unexpectedPath := filepath.Join(testDir, cpTestPathParent)
  244. if stat, err := os.Lstat(unexpectedPath); err == nil {
  245. c.Fatalf("target name was copied: %q - %q", stat.Mode(), stat.Name())
  246. }
  247. // It *should* have copied the directory using the asked name "dir_link".
  248. stat, err := os.Lstat(expectedPath)
  249. if err != nil {
  250. c.Fatalf("unable to stat resource at %q: %v", expectedPath, err)
  251. }
  252. if !stat.IsDir() {
  253. c.Errorf("should have copied a directory but got %q instead", stat.Mode())
  254. }
  255. }
  256. // Check that symlinks to a directory behave as expected when copying one to a
  257. // container.
  258. func (s *DockerSuite) TestCpToSymlinkToDirectory(c *check.C) {
  259. testRequires(c, DaemonIsLinux)
  260. testRequires(c, SameHostDaemon) // Requires local volume mount bind.
  261. testVol, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-")
  262. if err != nil {
  263. c.Fatal(err)
  264. }
  265. defer os.RemoveAll(testVol)
  266. // Create a test container with a local volume. We will test by copying
  267. // to the volume path in the container which we can then verify locally.
  268. out, exitCode := dockerCmd(c, "create", "-v", testVol+":/testVol", "busybox")
  269. if exitCode != 0 {
  270. c.Fatal("failed to create a container", out)
  271. }
  272. cleanedContainerID := strings.TrimSpace(out)
  273. // Create a temp directory to hold a test file nested in a direcotry.
  274. testDir, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-")
  275. if err != nil {
  276. c.Fatal(err)
  277. }
  278. defer os.RemoveAll(testDir)
  279. // This file will be at "/testDir/some/path/test" and will be copied into
  280. // the test volume later.
  281. hostTestFilename := filepath.Join(testDir, cpFullPath)
  282. if err := os.MkdirAll(filepath.Dir(hostTestFilename), os.FileMode(0700)); err != nil {
  283. c.Fatal(err)
  284. }
  285. if err := ioutil.WriteFile(hostTestFilename, []byte(cpHostContents), os.FileMode(0600)); err != nil {
  286. c.Fatal(err)
  287. }
  288. // Now create another temp directory to hold a symlink to the
  289. // "/testDir/some" directory.
  290. linkDir, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-")
  291. if err != nil {
  292. c.Fatal(err)
  293. }
  294. defer os.RemoveAll(linkDir)
  295. // Then symlink "/linkDir/dir_link" to "/testdir/some".
  296. linkTarget := filepath.Join(testDir, cpTestPathParent)
  297. localLink := filepath.Join(linkDir, "dir_link")
  298. if err := os.Symlink(linkTarget, localLink); err != nil {
  299. c.Fatal(err)
  300. }
  301. // Now copy that symlink into the test volume in the container.
  302. dockerCmd(c, "cp", localLink, cleanedContainerID+":/testVol")
  303. // This copy command should have copied the symlink *not* the target.
  304. expectedPath := filepath.Join(testVol, "dir_link")
  305. actualLinkTarget, err := os.Readlink(expectedPath)
  306. if err != nil {
  307. c.Fatalf("unable to read symlink at %q: %v", expectedPath, err)
  308. }
  309. if actualLinkTarget != linkTarget {
  310. c.Errorf("symlink target was %q, but expected: %q", actualLinkTarget, linkTarget)
  311. }
  312. // Good, now remove that copied link for the next test.
  313. os.Remove(expectedPath)
  314. // This copy command should resolve the symlink (note the trailing
  315. // separator), copying the target into the test volume directory in the
  316. // container.
  317. dockerCmd(c, "cp", localLink+"/", cleanedContainerID+":/testVol")
  318. // It *should not* have copied the directory using the target's name, but
  319. // used the given name instead.
  320. unexpectedPath := filepath.Join(testVol, cpTestPathParent)
  321. if stat, err := os.Lstat(unexpectedPath); err == nil {
  322. c.Fatalf("target name was copied: %q - %q", stat.Mode(), stat.Name())
  323. }
  324. // It *should* have copied the directory using the asked name "dir_link".
  325. stat, err := os.Lstat(expectedPath)
  326. if err != nil {
  327. c.Fatalf("unable to stat resource at %q: %v", expectedPath, err)
  328. }
  329. if !stat.IsDir() {
  330. c.Errorf("should have copied a directory but got %q instead", stat.Mode())
  331. }
  332. // And this directory should contain the file copied from the host at the
  333. // expected location: "/testVol/dir_link/path/test"
  334. expectedFilepath := filepath.Join(testVol, "dir_link/path/test")
  335. fileContents, err := ioutil.ReadFile(expectedFilepath)
  336. if err != nil {
  337. c.Fatal(err)
  338. }
  339. if string(fileContents) != cpHostContents {
  340. c.Fatalf("file contains %q but expected %q", string(fileContents), cpHostContents)
  341. }
  342. }
  343. // Test for #5619
  344. // Check that symlinks which are part of the resource path are still relative to the container's rootfs
  345. func (s *DockerSuite) TestCpSymlinkComponent(c *check.C) {
  346. testRequires(c, DaemonIsLinux)
  347. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpTestPath+" container_path")
  348. if exitCode != 0 {
  349. c.Fatal("failed to create a container", out)
  350. }
  351. cleanedContainerID := strings.TrimSpace(out)
  352. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  353. if strings.TrimSpace(out) != "0" {
  354. c.Fatal("failed to set up container", out)
  355. }
  356. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  357. c.Fatal(err)
  358. }
  359. hostFile, err := os.Create(cpFullPath)
  360. if err != nil {
  361. c.Fatal(err)
  362. }
  363. defer hostFile.Close()
  364. defer os.RemoveAll(cpTestPathParent)
  365. fmt.Fprintf(hostFile, "%s", cpHostContents)
  366. tmpdir, err := ioutil.TempDir("", "docker-integration")
  367. if err != nil {
  368. c.Fatal(err)
  369. }
  370. tmpname := filepath.Join(tmpdir, cpTestName)
  371. defer os.RemoveAll(tmpdir)
  372. path := path.Join("/", "container_path", cpTestName)
  373. dockerCmd(c, "cp", cleanedContainerID+":"+path, tmpdir)
  374. file, _ := os.Open(tmpname)
  375. defer file.Close()
  376. test, err := ioutil.ReadAll(file)
  377. if err != nil {
  378. c.Fatal(err)
  379. }
  380. if string(test) == cpHostContents {
  381. c.Errorf("output matched host file -- symlink path component can escape container rootfs")
  382. }
  383. if string(test) != cpContainerContents {
  384. c.Errorf("output doesn't match the input for symlink path component")
  385. }
  386. }
  387. // Check that cp with unprivileged user doesn't return any error
  388. func (s *DockerSuite) TestCpUnprivilegedUser(c *check.C) {
  389. testRequires(c, DaemonIsLinux)
  390. testRequires(c, UnixCli) // uses chmod/su: not available on windows
  391. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "touch "+cpTestName)
  392. if exitCode != 0 {
  393. c.Fatal("failed to create a container", out)
  394. }
  395. cleanedContainerID := strings.TrimSpace(out)
  396. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  397. if strings.TrimSpace(out) != "0" {
  398. c.Fatal("failed to set up container", out)
  399. }
  400. tmpdir, err := ioutil.TempDir("", "docker-integration")
  401. if err != nil {
  402. c.Fatal(err)
  403. }
  404. defer os.RemoveAll(tmpdir)
  405. if err = os.Chmod(tmpdir, 0777); err != nil {
  406. c.Fatal(err)
  407. }
  408. path := cpTestName
  409. _, _, err = runCommandWithOutput(exec.Command("su", "unprivilegeduser", "-c", dockerBinary+" cp "+cleanedContainerID+":"+path+" "+tmpdir))
  410. if err != nil {
  411. c.Fatalf("couldn't copy with unprivileged user: %s:%s %s", cleanedContainerID, path, err)
  412. }
  413. }
  414. func (s *DockerSuite) TestCpSpecialFiles(c *check.C) {
  415. testRequires(c, DaemonIsLinux)
  416. testRequires(c, SameHostDaemon)
  417. outDir, err := ioutil.TempDir("", "cp-test-special-files")
  418. if err != nil {
  419. c.Fatal(err)
  420. }
  421. defer os.RemoveAll(outDir)
  422. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "touch /foo")
  423. if exitCode != 0 {
  424. c.Fatal("failed to create a container", out)
  425. }
  426. cleanedContainerID := strings.TrimSpace(out)
  427. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  428. if strings.TrimSpace(out) != "0" {
  429. c.Fatal("failed to set up container", out)
  430. }
  431. // Copy actual /etc/resolv.conf
  432. dockerCmd(c, "cp", cleanedContainerID+":/etc/resolv.conf", outDir)
  433. expected, err := ioutil.ReadFile("/var/lib/docker/containers/" + cleanedContainerID + "/resolv.conf")
  434. actual, err := ioutil.ReadFile(outDir + "/resolv.conf")
  435. if !bytes.Equal(actual, expected) {
  436. c.Fatalf("Expected copied file to be duplicate of the container resolvconf")
  437. }
  438. // Copy actual /etc/hosts
  439. dockerCmd(c, "cp", cleanedContainerID+":/etc/hosts", outDir)
  440. expected, err = ioutil.ReadFile("/var/lib/docker/containers/" + cleanedContainerID + "/hosts")
  441. actual, err = ioutil.ReadFile(outDir + "/hosts")
  442. if !bytes.Equal(actual, expected) {
  443. c.Fatalf("Expected copied file to be duplicate of the container hosts")
  444. }
  445. // Copy actual /etc/resolv.conf
  446. dockerCmd(c, "cp", cleanedContainerID+":/etc/hostname", outDir)
  447. expected, err = ioutil.ReadFile("/var/lib/docker/containers/" + cleanedContainerID + "/hostname")
  448. actual, err = ioutil.ReadFile(outDir + "/hostname")
  449. if !bytes.Equal(actual, expected) {
  450. c.Fatalf("Expected copied file to be duplicate of the container resolvconf")
  451. }
  452. }
  453. func (s *DockerSuite) TestCpVolumePath(c *check.C) {
  454. testRequires(c, DaemonIsLinux)
  455. testRequires(c, SameHostDaemon)
  456. tmpDir, err := ioutil.TempDir("", "cp-test-volumepath")
  457. if err != nil {
  458. c.Fatal(err)
  459. }
  460. defer os.RemoveAll(tmpDir)
  461. outDir, err := ioutil.TempDir("", "cp-test-volumepath-out")
  462. if err != nil {
  463. c.Fatal(err)
  464. }
  465. defer os.RemoveAll(outDir)
  466. _, err = os.Create(tmpDir + "/test")
  467. if err != nil {
  468. c.Fatal(err)
  469. }
  470. out, exitCode := dockerCmd(c, "run", "-d", "-v", "/foo", "-v", tmpDir+"/test:/test", "-v", tmpDir+":/baz", "busybox", "/bin/sh", "-c", "touch /foo/bar")
  471. if exitCode != 0 {
  472. c.Fatal("failed to create a container", out)
  473. }
  474. cleanedContainerID := strings.TrimSpace(out)
  475. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  476. if strings.TrimSpace(out) != "0" {
  477. c.Fatal("failed to set up container", out)
  478. }
  479. // Copy actual volume path
  480. dockerCmd(c, "cp", cleanedContainerID+":/foo", outDir)
  481. stat, err := os.Stat(outDir + "/foo")
  482. if err != nil {
  483. c.Fatal(err)
  484. }
  485. if !stat.IsDir() {
  486. c.Fatal("expected copied content to be dir")
  487. }
  488. stat, err = os.Stat(outDir + "/foo/bar")
  489. if err != nil {
  490. c.Fatal(err)
  491. }
  492. if stat.IsDir() {
  493. c.Fatal("Expected file `bar` to be a file")
  494. }
  495. // Copy file nested in volume
  496. dockerCmd(c, "cp", cleanedContainerID+":/foo/bar", outDir)
  497. stat, err = os.Stat(outDir + "/bar")
  498. if err != nil {
  499. c.Fatal(err)
  500. }
  501. if stat.IsDir() {
  502. c.Fatal("Expected file `bar` to be a file")
  503. }
  504. // Copy Bind-mounted dir
  505. dockerCmd(c, "cp", cleanedContainerID+":/baz", outDir)
  506. stat, err = os.Stat(outDir + "/baz")
  507. if err != nil {
  508. c.Fatal(err)
  509. }
  510. if !stat.IsDir() {
  511. c.Fatal("Expected `baz` to be a dir")
  512. }
  513. // Copy file nested in bind-mounted dir
  514. dockerCmd(c, "cp", cleanedContainerID+":/baz/test", outDir)
  515. fb, err := ioutil.ReadFile(outDir + "/baz/test")
  516. if err != nil {
  517. c.Fatal(err)
  518. }
  519. fb2, err := ioutil.ReadFile(tmpDir + "/test")
  520. if err != nil {
  521. c.Fatal(err)
  522. }
  523. if !bytes.Equal(fb, fb2) {
  524. c.Fatalf("Expected copied file to be duplicate of bind-mounted file")
  525. }
  526. // Copy bind-mounted file
  527. dockerCmd(c, "cp", cleanedContainerID+":/test", outDir)
  528. fb, err = ioutil.ReadFile(outDir + "/test")
  529. if err != nil {
  530. c.Fatal(err)
  531. }
  532. fb2, err = ioutil.ReadFile(tmpDir + "/test")
  533. if err != nil {
  534. c.Fatal(err)
  535. }
  536. if !bytes.Equal(fb, fb2) {
  537. c.Fatalf("Expected copied file to be duplicate of bind-mounted file")
  538. }
  539. }
  540. func (s *DockerSuite) TestCpToDot(c *check.C) {
  541. testRequires(c, DaemonIsLinux)
  542. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test")
  543. if exitCode != 0 {
  544. c.Fatal("failed to create a container", out)
  545. }
  546. cleanedContainerID := strings.TrimSpace(out)
  547. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  548. if strings.TrimSpace(out) != "0" {
  549. c.Fatal("failed to set up container", out)
  550. }
  551. tmpdir, err := ioutil.TempDir("", "docker-integration")
  552. if err != nil {
  553. c.Fatal(err)
  554. }
  555. defer os.RemoveAll(tmpdir)
  556. cwd, err := os.Getwd()
  557. if err != nil {
  558. c.Fatal(err)
  559. }
  560. defer os.Chdir(cwd)
  561. if err := os.Chdir(tmpdir); err != nil {
  562. c.Fatal(err)
  563. }
  564. dockerCmd(c, "cp", cleanedContainerID+":/test", ".")
  565. content, err := ioutil.ReadFile("./test")
  566. if string(content) != "lololol\n" {
  567. c.Fatalf("Wrong content in copied file %q, should be %q", content, "lololol\n")
  568. }
  569. }
  570. func (s *DockerSuite) TestCpToStdout(c *check.C) {
  571. testRequires(c, DaemonIsLinux)
  572. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test")
  573. if exitCode != 0 {
  574. c.Fatalf("failed to create a container:%s\n", out)
  575. }
  576. cID := strings.TrimSpace(out)
  577. out, _ = dockerCmd(c, "wait", cID)
  578. if strings.TrimSpace(out) != "0" {
  579. c.Fatalf("failed to set up container:%s\n", out)
  580. }
  581. out, _, err := runCommandPipelineWithOutput(
  582. exec.Command(dockerBinary, "cp", cID+":/test", "-"),
  583. exec.Command("tar", "-vtf", "-"))
  584. if err != nil {
  585. c.Fatalf("Failed to run commands: %s", err)
  586. }
  587. if !strings.Contains(out, "test") || !strings.Contains(out, "-rw") {
  588. c.Fatalf("Missing file from tar TOC:\n%s", out)
  589. }
  590. }
  591. func (s *DockerSuite) TestCpNameHasColon(c *check.C) {
  592. testRequires(c, SameHostDaemon)
  593. out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /te:s:t")
  594. if exitCode != 0 {
  595. c.Fatal("failed to create a container", out)
  596. }
  597. cleanedContainerID := strings.TrimSpace(out)
  598. out, _ = dockerCmd(c, "wait", cleanedContainerID)
  599. if strings.TrimSpace(out) != "0" {
  600. c.Fatal("failed to set up container", out)
  601. }
  602. tmpdir, err := ioutil.TempDir("", "docker-integration")
  603. if err != nil {
  604. c.Fatal(err)
  605. }
  606. defer os.RemoveAll(tmpdir)
  607. dockerCmd(c, "cp", cleanedContainerID+":/te:s:t", tmpdir)
  608. content, err := ioutil.ReadFile(tmpdir + "/te:s:t")
  609. if string(content) != "lololol\n" {
  610. c.Fatalf("Wrong content in copied file %q, should be %q", content, "lololol\n")
  611. }
  612. }
  613. func (s *DockerSuite) TestCopyAndRestart(c *check.C) {
  614. testRequires(c, DaemonIsLinux)
  615. expectedMsg := "hello"
  616. out, _ := dockerCmd(c, "run", "-d", "busybox", "echo", expectedMsg)
  617. id := strings.TrimSpace(string(out))
  618. out, _ = dockerCmd(c, "wait", id)
  619. status := strings.TrimSpace(out)
  620. if status != "0" {
  621. c.Fatalf("container exited with status %s", status)
  622. }
  623. tmpDir, err := ioutil.TempDir("", "test-docker-restart-after-copy-")
  624. if err != nil {
  625. c.Fatalf("unable to make temporary directory: %s", err)
  626. }
  627. defer os.RemoveAll(tmpDir)
  628. dockerCmd(c, "cp", fmt.Sprintf("%s:/etc/issue", id), tmpDir)
  629. out, _ = dockerCmd(c, "start", "-a", id)
  630. msg := strings.TrimSpace(out)
  631. if msg != expectedMsg {
  632. c.Fatalf("expected %q but got %q", expectedMsg, msg)
  633. }
  634. }
  635. func (s *DockerSuite) TestCopyCreatedContainer(c *check.C) {
  636. testRequires(c, DaemonIsLinux)
  637. dockerCmd(c, "create", "--name", "test_cp", "-v", "/test", "busybox")
  638. tmpDir, err := ioutil.TempDir("", "test")
  639. if err != nil {
  640. c.Fatalf("unable to make temporary directory: %s", err)
  641. }
  642. defer os.RemoveAll(tmpDir)
  643. dockerCmd(c, "cp", "test_cp:/bin/sh", tmpDir)
  644. }