docker_cli_cp_test.go 22 KB

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