archive_test.go 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. package archive
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "os/exec"
  10. "path"
  11. "path/filepath"
  12. "strings"
  13. "syscall"
  14. "testing"
  15. "time"
  16. "github.com/docker/docker/pkg/system"
  17. )
  18. func TestIsArchiveNilHeader(t *testing.T) {
  19. out := IsArchive(nil)
  20. if out {
  21. t.Fatalf("isArchive should return false as nil is not a valid archive header")
  22. }
  23. }
  24. func TestIsArchiveInvalidHeader(t *testing.T) {
  25. header := []byte{0x00, 0x01, 0x02}
  26. out := IsArchive(header)
  27. if out {
  28. t.Fatalf("isArchive should return false as %s is not a valid archive header", header)
  29. }
  30. }
  31. func TestIsArchiveBzip2(t *testing.T) {
  32. header := []byte{0x42, 0x5A, 0x68}
  33. out := IsArchive(header)
  34. if !out {
  35. t.Fatalf("isArchive should return true as %s is a bz2 header", header)
  36. }
  37. }
  38. func TestIsArchive7zip(t *testing.T) {
  39. header := []byte{0x50, 0x4b, 0x03, 0x04}
  40. out := IsArchive(header)
  41. if out {
  42. t.Fatalf("isArchive should return false as %s is a 7z header and it is not supported", header)
  43. }
  44. }
  45. func TestIsArchivePathDir(t *testing.T) {
  46. cmd := exec.Command("/bin/sh", "-c", "mkdir -p /tmp/archivedir")
  47. output, err := cmd.CombinedOutput()
  48. if err != nil {
  49. t.Fatalf("Fail to create an archive file for test : %s.", output)
  50. }
  51. if IsArchivePath("/tmp/archivedir") {
  52. t.Fatalf("Incorrectly recognised directory as an archive")
  53. }
  54. }
  55. func TestIsArchivePathInvalidFile(t *testing.T) {
  56. cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1K count=1 of=/tmp/archive && gzip --stdout /tmp/archive > /tmp/archive.gz")
  57. output, err := cmd.CombinedOutput()
  58. if err != nil {
  59. t.Fatalf("Fail to create an archive file for test : %s.", output)
  60. }
  61. if IsArchivePath("/tmp/archive") {
  62. t.Fatalf("Incorrectly recognised invalid tar path as archive")
  63. }
  64. if IsArchivePath("/tmp/archive.gz") {
  65. t.Fatalf("Incorrectly recognised invalid compressed tar path as archive")
  66. }
  67. }
  68. func TestIsArchivePathTar(t *testing.T) {
  69. cmd := exec.Command("/bin/sh", "-c", "touch /tmp/archivedata && tar -cf /tmp/archive /tmp/archivedata && gzip --stdout /tmp/archive > /tmp/archive.gz")
  70. output, err := cmd.CombinedOutput()
  71. if err != nil {
  72. t.Fatalf("Fail to create an archive file for test : %s.", output)
  73. }
  74. if !IsArchivePath("/tmp/archive") {
  75. t.Fatalf("Did not recognise valid tar path as archive")
  76. }
  77. if !IsArchivePath("/tmp/archive.gz") {
  78. t.Fatalf("Did not recognise valid compressed tar path as archive")
  79. }
  80. }
  81. func TestDecompressStreamGzip(t *testing.T) {
  82. cmd := exec.Command("/bin/sh", "-c", "touch /tmp/archive && gzip -f /tmp/archive")
  83. output, err := cmd.CombinedOutput()
  84. if err != nil {
  85. t.Fatalf("Fail to create an archive file for test : %s.", output)
  86. }
  87. archive, err := os.Open("/tmp/archive.gz")
  88. _, err = DecompressStream(archive)
  89. if err != nil {
  90. t.Fatalf("Failed to decompress a gzip file.")
  91. }
  92. }
  93. func TestDecompressStreamBzip2(t *testing.T) {
  94. cmd := exec.Command("/bin/sh", "-c", "touch /tmp/archive && bzip2 -f /tmp/archive")
  95. output, err := cmd.CombinedOutput()
  96. if err != nil {
  97. t.Fatalf("Fail to create an archive file for test : %s.", output)
  98. }
  99. archive, err := os.Open("/tmp/archive.bz2")
  100. _, err = DecompressStream(archive)
  101. if err != nil {
  102. t.Fatalf("Failed to decompress a bzip2 file.")
  103. }
  104. }
  105. func TestDecompressStreamXz(t *testing.T) {
  106. cmd := exec.Command("/bin/sh", "-c", "touch /tmp/archive && xz -f /tmp/archive")
  107. output, err := cmd.CombinedOutput()
  108. if err != nil {
  109. t.Fatalf("Fail to create an archive file for test : %s.", output)
  110. }
  111. archive, err := os.Open("/tmp/archive.xz")
  112. _, err = DecompressStream(archive)
  113. if err != nil {
  114. t.Fatalf("Failed to decompress a xz file.")
  115. }
  116. }
  117. func TestCompressStreamXzUnsuported(t *testing.T) {
  118. dest, err := os.Create("/tmp/dest")
  119. if err != nil {
  120. t.Fatalf("Fail to create the destination file")
  121. }
  122. _, err = CompressStream(dest, Xz)
  123. if err == nil {
  124. t.Fatalf("Should fail as xz is unsupported for compression format.")
  125. }
  126. }
  127. func TestCompressStreamBzip2Unsupported(t *testing.T) {
  128. dest, err := os.Create("/tmp/dest")
  129. if err != nil {
  130. t.Fatalf("Fail to create the destination file")
  131. }
  132. _, err = CompressStream(dest, Xz)
  133. if err == nil {
  134. t.Fatalf("Should fail as xz is unsupported for compression format.")
  135. }
  136. }
  137. func TestCompressStreamInvalid(t *testing.T) {
  138. dest, err := os.Create("/tmp/dest")
  139. if err != nil {
  140. t.Fatalf("Fail to create the destination file")
  141. }
  142. _, err = CompressStream(dest, -1)
  143. if err == nil {
  144. t.Fatalf("Should fail as xz is unsupported for compression format.")
  145. }
  146. }
  147. func TestExtensionInvalid(t *testing.T) {
  148. compression := Compression(-1)
  149. output := compression.Extension()
  150. if output != "" {
  151. t.Fatalf("The extension of an invalid compression should be an empty string.")
  152. }
  153. }
  154. func TestExtensionUncompressed(t *testing.T) {
  155. compression := Uncompressed
  156. output := compression.Extension()
  157. if output != "tar" {
  158. t.Fatalf("The extension of a uncompressed archive should be 'tar'.")
  159. }
  160. }
  161. func TestExtensionBzip2(t *testing.T) {
  162. compression := Bzip2
  163. output := compression.Extension()
  164. if output != "tar.bz2" {
  165. t.Fatalf("The extension of a bzip2 archive should be 'tar.bz2'")
  166. }
  167. }
  168. func TestExtensionGzip(t *testing.T) {
  169. compression := Gzip
  170. output := compression.Extension()
  171. if output != "tar.gz" {
  172. t.Fatalf("The extension of a bzip2 archive should be 'tar.gz'")
  173. }
  174. }
  175. func TestExtensionXz(t *testing.T) {
  176. compression := Xz
  177. output := compression.Extension()
  178. if output != "tar.xz" {
  179. t.Fatalf("The extension of a bzip2 archive should be 'tar.xz'")
  180. }
  181. }
  182. func TestCmdStreamLargeStderr(t *testing.T) {
  183. cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
  184. out, _, err := cmdStream(cmd, nil)
  185. if err != nil {
  186. t.Fatalf("Failed to start command: %s", err)
  187. }
  188. errCh := make(chan error)
  189. go func() {
  190. _, err := io.Copy(ioutil.Discard, out)
  191. errCh <- err
  192. }()
  193. select {
  194. case err := <-errCh:
  195. if err != nil {
  196. t.Fatalf("Command should not have failed (err=%.100s...)", err)
  197. }
  198. case <-time.After(5 * time.Second):
  199. t.Fatalf("Command did not complete in 5 seconds; probable deadlock")
  200. }
  201. }
  202. func TestCmdStreamBad(t *testing.T) {
  203. badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
  204. out, _, err := cmdStream(badCmd, nil)
  205. if err != nil {
  206. t.Fatalf("Failed to start command: %s", err)
  207. }
  208. if output, err := ioutil.ReadAll(out); err == nil {
  209. t.Fatalf("Command should have failed")
  210. } else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" {
  211. t.Fatalf("Wrong error value (%s)", err)
  212. } else if s := string(output); s != "hello\n" {
  213. t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
  214. }
  215. }
  216. func TestCmdStreamGood(t *testing.T) {
  217. cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
  218. out, _, err := cmdStream(cmd, nil)
  219. if err != nil {
  220. t.Fatal(err)
  221. }
  222. if output, err := ioutil.ReadAll(out); err != nil {
  223. t.Fatalf("Command should not have failed (err=%s)", err)
  224. } else if s := string(output); s != "hello\n" {
  225. t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
  226. }
  227. }
  228. func TestUntarPathWithInvalidDest(t *testing.T) {
  229. tempFolder, err := ioutil.TempDir("", "docker-archive-test")
  230. if err != nil {
  231. t.Fatal(err)
  232. }
  233. defer os.RemoveAll(tempFolder)
  234. invalidDestFolder := path.Join(tempFolder, "invalidDest")
  235. // Create a src file
  236. srcFile := path.Join(tempFolder, "src")
  237. tarFile := path.Join(tempFolder, "src.tar")
  238. os.Create(srcFile)
  239. os.Create(invalidDestFolder) // being a file (not dir) should cause an error
  240. cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile)
  241. _, err = cmd.CombinedOutput()
  242. if err != nil {
  243. t.Fatal(err)
  244. }
  245. err = UntarPath(tarFile, invalidDestFolder)
  246. if err == nil {
  247. t.Fatalf("UntarPath with invalid destination path should throw an error.")
  248. }
  249. }
  250. func TestUntarPathWithInvalidSrc(t *testing.T) {
  251. dest, err := ioutil.TempDir("", "docker-archive-test")
  252. if err != nil {
  253. t.Fatalf("Fail to create the destination file")
  254. }
  255. defer os.RemoveAll(dest)
  256. err = UntarPath("/invalid/path", dest)
  257. if err == nil {
  258. t.Fatalf("UntarPath with invalid src path should throw an error.")
  259. }
  260. }
  261. func TestUntarPath(t *testing.T) {
  262. tmpFolder, err := ioutil.TempDir("", "docker-archive-test")
  263. if err != nil {
  264. t.Fatal(err)
  265. }
  266. defer os.RemoveAll(tmpFolder)
  267. srcFile := path.Join(tmpFolder, "src")
  268. tarFile := path.Join(tmpFolder, "src.tar")
  269. os.Create(path.Join(tmpFolder, "src"))
  270. cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile)
  271. _, err = cmd.CombinedOutput()
  272. if err != nil {
  273. t.Fatal(err)
  274. }
  275. destFolder := path.Join(tmpFolder, "dest")
  276. err = os.MkdirAll(destFolder, 0740)
  277. if err != nil {
  278. t.Fatalf("Fail to create the destination file")
  279. }
  280. err = UntarPath(tarFile, destFolder)
  281. if err != nil {
  282. t.Fatalf("UntarPath shouldn't throw an error, %s.", err)
  283. }
  284. expectedFile := path.Join(destFolder, srcFile)
  285. _, err = os.Stat(expectedFile)
  286. if err != nil {
  287. t.Fatalf("Destination folder should contain the source file but did not.")
  288. }
  289. }
  290. // Do the same test as above but with the destination as file, it should fail
  291. func TestUntarPathWithDestinationFile(t *testing.T) {
  292. tmpFolder, err := ioutil.TempDir("", "docker-archive-test")
  293. if err != nil {
  294. t.Fatal(err)
  295. }
  296. defer os.RemoveAll(tmpFolder)
  297. srcFile := path.Join(tmpFolder, "src")
  298. tarFile := path.Join(tmpFolder, "src.tar")
  299. os.Create(path.Join(tmpFolder, "src"))
  300. cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile)
  301. _, err = cmd.CombinedOutput()
  302. if err != nil {
  303. t.Fatal(err)
  304. }
  305. destFile := path.Join(tmpFolder, "dest")
  306. _, err = os.Create(destFile)
  307. if err != nil {
  308. t.Fatalf("Fail to create the destination file")
  309. }
  310. err = UntarPath(tarFile, destFile)
  311. if err == nil {
  312. t.Fatalf("UntarPath should throw an error if the destination if a file")
  313. }
  314. }
  315. // Do the same test as above but with the destination folder already exists
  316. // and the destination file is a directory
  317. // It's working, see https://github.com/docker/docker/issues/10040
  318. func TestUntarPathWithDestinationSrcFileAsFolder(t *testing.T) {
  319. tmpFolder, err := ioutil.TempDir("", "docker-archive-test")
  320. if err != nil {
  321. t.Fatal(err)
  322. }
  323. defer os.RemoveAll(tmpFolder)
  324. srcFile := path.Join(tmpFolder, "src")
  325. tarFile := path.Join(tmpFolder, "src.tar")
  326. os.Create(srcFile)
  327. cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile)
  328. _, err = cmd.CombinedOutput()
  329. if err != nil {
  330. t.Fatal(err)
  331. }
  332. destFolder := path.Join(tmpFolder, "dest")
  333. err = os.MkdirAll(destFolder, 0740)
  334. if err != nil {
  335. t.Fatalf("Fail to create the destination folder")
  336. }
  337. // Let's create a folder that will has the same path as the extracted file (from tar)
  338. destSrcFileAsFolder := path.Join(destFolder, srcFile)
  339. err = os.MkdirAll(destSrcFileAsFolder, 0740)
  340. if err != nil {
  341. t.Fatal(err)
  342. }
  343. err = UntarPath(tarFile, destFolder)
  344. if err != nil {
  345. t.Fatalf("UntarPath should throw not throw an error if the extracted file already exists and is a folder")
  346. }
  347. }
  348. func TestCopyWithTarInvalidSrc(t *testing.T) {
  349. tempFolder, err := ioutil.TempDir("", "docker-archive-test")
  350. if err != nil {
  351. t.Fatal(nil)
  352. }
  353. destFolder := path.Join(tempFolder, "dest")
  354. invalidSrc := path.Join(tempFolder, "doesnotexists")
  355. err = os.MkdirAll(destFolder, 0740)
  356. if err != nil {
  357. t.Fatal(err)
  358. }
  359. err = CopyWithTar(invalidSrc, destFolder)
  360. if err == nil {
  361. t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.")
  362. }
  363. }
  364. func TestCopyWithTarInexistentDestWillCreateIt(t *testing.T) {
  365. tempFolder, err := ioutil.TempDir("", "docker-archive-test")
  366. if err != nil {
  367. t.Fatal(nil)
  368. }
  369. srcFolder := path.Join(tempFolder, "src")
  370. inexistentDestFolder := path.Join(tempFolder, "doesnotexists")
  371. err = os.MkdirAll(srcFolder, 0740)
  372. if err != nil {
  373. t.Fatal(err)
  374. }
  375. err = CopyWithTar(srcFolder, inexistentDestFolder)
  376. if err != nil {
  377. t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.")
  378. }
  379. _, err = os.Stat(inexistentDestFolder)
  380. if err != nil {
  381. t.Fatalf("CopyWithTar with an inexistent folder should create it.")
  382. }
  383. }
  384. // Test CopyWithTar with a file as src
  385. func TestCopyWithTarSrcFile(t *testing.T) {
  386. folder, err := ioutil.TempDir("", "docker-archive-test")
  387. if err != nil {
  388. t.Fatal(err)
  389. }
  390. defer os.RemoveAll(folder)
  391. dest := path.Join(folder, "dest")
  392. srcFolder := path.Join(folder, "src")
  393. src := path.Join(folder, path.Join("src", "src"))
  394. err = os.MkdirAll(srcFolder, 0740)
  395. if err != nil {
  396. t.Fatal(err)
  397. }
  398. err = os.MkdirAll(dest, 0740)
  399. if err != nil {
  400. t.Fatal(err)
  401. }
  402. ioutil.WriteFile(src, []byte("content"), 0777)
  403. err = CopyWithTar(src, dest)
  404. if err != nil {
  405. t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err)
  406. }
  407. _, err = os.Stat(dest)
  408. // FIXME Check the content
  409. if err != nil {
  410. t.Fatalf("Destination file should be the same as the source.")
  411. }
  412. }
  413. // Test CopyWithTar with a folder as src
  414. func TestCopyWithTarSrcFolder(t *testing.T) {
  415. folder, err := ioutil.TempDir("", "docker-archive-test")
  416. if err != nil {
  417. t.Fatal(err)
  418. }
  419. defer os.RemoveAll(folder)
  420. dest := path.Join(folder, "dest")
  421. src := path.Join(folder, path.Join("src", "folder"))
  422. err = os.MkdirAll(src, 0740)
  423. if err != nil {
  424. t.Fatal(err)
  425. }
  426. err = os.MkdirAll(dest, 0740)
  427. if err != nil {
  428. t.Fatal(err)
  429. }
  430. ioutil.WriteFile(path.Join(src, "file"), []byte("content"), 0777)
  431. err = CopyWithTar(src, dest)
  432. if err != nil {
  433. t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err)
  434. }
  435. _, err = os.Stat(dest)
  436. // FIXME Check the content (the file inside)
  437. if err != nil {
  438. t.Fatalf("Destination folder should contain the source file but did not.")
  439. }
  440. }
  441. func TestCopyFileWithTarInvalidSrc(t *testing.T) {
  442. tempFolder, err := ioutil.TempDir("", "docker-archive-test")
  443. if err != nil {
  444. t.Fatal(err)
  445. }
  446. defer os.RemoveAll(tempFolder)
  447. destFolder := path.Join(tempFolder, "dest")
  448. err = os.MkdirAll(destFolder, 0740)
  449. if err != nil {
  450. t.Fatal(err)
  451. }
  452. invalidFile := path.Join(tempFolder, "doesnotexists")
  453. err = CopyFileWithTar(invalidFile, destFolder)
  454. if err == nil {
  455. t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.")
  456. }
  457. }
  458. func TestCopyFileWithTarInexistentDestWillCreateIt(t *testing.T) {
  459. tempFolder, err := ioutil.TempDir("", "docker-archive-test")
  460. if err != nil {
  461. t.Fatal(nil)
  462. }
  463. defer os.RemoveAll(tempFolder)
  464. srcFile := path.Join(tempFolder, "src")
  465. inexistentDestFolder := path.Join(tempFolder, "doesnotexists")
  466. _, err = os.Create(srcFile)
  467. if err != nil {
  468. t.Fatal(err)
  469. }
  470. err = CopyFileWithTar(srcFile, inexistentDestFolder)
  471. if err != nil {
  472. t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.")
  473. }
  474. _, err = os.Stat(inexistentDestFolder)
  475. if err != nil {
  476. t.Fatalf("CopyWithTar with an inexistent folder should create it.")
  477. }
  478. // FIXME Test the src file and content
  479. }
  480. func TestCopyFileWithTarSrcFolder(t *testing.T) {
  481. folder, err := ioutil.TempDir("", "docker-archive-copyfilewithtar-test")
  482. if err != nil {
  483. t.Fatal(err)
  484. }
  485. defer os.RemoveAll(folder)
  486. dest := path.Join(folder, "dest")
  487. src := path.Join(folder, "srcfolder")
  488. err = os.MkdirAll(src, 0740)
  489. if err != nil {
  490. t.Fatal(err)
  491. }
  492. err = os.MkdirAll(dest, 0740)
  493. if err != nil {
  494. t.Fatal(err)
  495. }
  496. err = CopyFileWithTar(src, dest)
  497. if err == nil {
  498. t.Fatalf("CopyFileWithTar should throw an error with a folder.")
  499. }
  500. }
  501. func TestCopyFileWithTarSrcFile(t *testing.T) {
  502. folder, err := ioutil.TempDir("", "docker-archive-test")
  503. if err != nil {
  504. t.Fatal(err)
  505. }
  506. defer os.RemoveAll(folder)
  507. dest := path.Join(folder, "dest")
  508. srcFolder := path.Join(folder, "src")
  509. src := path.Join(folder, path.Join("src", "src"))
  510. err = os.MkdirAll(srcFolder, 0740)
  511. if err != nil {
  512. t.Fatal(err)
  513. }
  514. err = os.MkdirAll(dest, 0740)
  515. if err != nil {
  516. t.Fatal(err)
  517. }
  518. ioutil.WriteFile(src, []byte("content"), 0777)
  519. err = CopyWithTar(src, dest+"/")
  520. if err != nil {
  521. t.Fatalf("archiver.CopyFileWithTar shouldn't throw an error, %s.", err)
  522. }
  523. _, err = os.Stat(dest)
  524. if err != nil {
  525. t.Fatalf("Destination folder should contain the source file but did not.")
  526. }
  527. }
  528. func TestTarFiles(t *testing.T) {
  529. // try without hardlinks
  530. if err := checkNoChanges(1000, false); err != nil {
  531. t.Fatal(err)
  532. }
  533. // try with hardlinks
  534. if err := checkNoChanges(1000, true); err != nil {
  535. t.Fatal(err)
  536. }
  537. }
  538. func checkNoChanges(fileNum int, hardlinks bool) error {
  539. srcDir, err := ioutil.TempDir("", "docker-test-srcDir")
  540. if err != nil {
  541. return err
  542. }
  543. defer os.RemoveAll(srcDir)
  544. destDir, err := ioutil.TempDir("", "docker-test-destDir")
  545. if err != nil {
  546. return err
  547. }
  548. defer os.RemoveAll(destDir)
  549. _, err = prepareUntarSourceDirectory(fileNum, srcDir, hardlinks)
  550. if err != nil {
  551. return err
  552. }
  553. err = TarUntar(srcDir, destDir)
  554. if err != nil {
  555. return err
  556. }
  557. changes, err := ChangesDirs(destDir, srcDir)
  558. if err != nil {
  559. return err
  560. }
  561. if len(changes) > 0 {
  562. return fmt.Errorf("with %d files and %v hardlinks: expected 0 changes, got %d", fileNum, hardlinks, len(changes))
  563. }
  564. return nil
  565. }
  566. func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error) {
  567. archive, err := TarWithOptions(origin, options)
  568. if err != nil {
  569. t.Fatal(err)
  570. }
  571. defer archive.Close()
  572. buf := make([]byte, 10)
  573. if _, err := archive.Read(buf); err != nil {
  574. return nil, err
  575. }
  576. wrap := io.MultiReader(bytes.NewReader(buf), archive)
  577. detectedCompression := DetectCompression(buf)
  578. compression := options.Compression
  579. if detectedCompression.Extension() != compression.Extension() {
  580. return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
  581. }
  582. tmp, err := ioutil.TempDir("", "docker-test-untar")
  583. if err != nil {
  584. return nil, err
  585. }
  586. defer os.RemoveAll(tmp)
  587. if err := Untar(wrap, tmp, nil); err != nil {
  588. return nil, err
  589. }
  590. if _, err := os.Stat(tmp); err != nil {
  591. return nil, err
  592. }
  593. return ChangesDirs(origin, tmp)
  594. }
  595. func TestTarUntar(t *testing.T) {
  596. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  597. if err != nil {
  598. t.Fatal(err)
  599. }
  600. defer os.RemoveAll(origin)
  601. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  602. t.Fatal(err)
  603. }
  604. if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
  605. t.Fatal(err)
  606. }
  607. if err := ioutil.WriteFile(path.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil {
  608. t.Fatal(err)
  609. }
  610. for _, c := range []Compression{
  611. Uncompressed,
  612. Gzip,
  613. } {
  614. changes, err := tarUntar(t, origin, &TarOptions{
  615. Compression: c,
  616. ExcludePatterns: []string{"3"},
  617. })
  618. if err != nil {
  619. t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
  620. }
  621. if len(changes) != 1 || changes[0].Path != "/3" {
  622. t.Fatalf("Unexpected differences after tarUntar: %v", changes)
  623. }
  624. }
  625. }
  626. func TestTarUntarWithXattr(t *testing.T) {
  627. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  628. if err != nil {
  629. t.Fatal(err)
  630. }
  631. defer os.RemoveAll(origin)
  632. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  633. t.Fatal(err)
  634. }
  635. if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
  636. t.Fatal(err)
  637. }
  638. if err := ioutil.WriteFile(path.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil {
  639. t.Fatal(err)
  640. }
  641. if err := system.Lsetxattr(path.Join(origin, "2"), "security.capability", []byte{0x00}, 0); err != nil {
  642. t.Fatal(err)
  643. }
  644. for _, c := range []Compression{
  645. Uncompressed,
  646. Gzip,
  647. } {
  648. changes, err := tarUntar(t, origin, &TarOptions{
  649. Compression: c,
  650. ExcludePatterns: []string{"3"},
  651. })
  652. if err != nil {
  653. t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
  654. }
  655. if len(changes) != 1 || changes[0].Path != "/3" {
  656. t.Fatalf("Unexpected differences after tarUntar: %v", changes)
  657. }
  658. capability, _ := system.Lgetxattr(path.Join(origin, "2"), "security.capability")
  659. if capability == nil && capability[0] != 0x00 {
  660. t.Fatalf("Untar should have kept the 'security.capability' xattr.")
  661. }
  662. }
  663. }
  664. func TestTarWithOptions(t *testing.T) {
  665. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  666. if err != nil {
  667. t.Fatal(err)
  668. }
  669. if _, err := ioutil.TempDir(origin, "folder"); err != nil {
  670. t.Fatal(err)
  671. }
  672. defer os.RemoveAll(origin)
  673. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  674. t.Fatal(err)
  675. }
  676. if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
  677. t.Fatal(err)
  678. }
  679. cases := []struct {
  680. opts *TarOptions
  681. numChanges int
  682. }{
  683. {&TarOptions{IncludeFiles: []string{"1"}}, 2},
  684. {&TarOptions{ExcludePatterns: []string{"2"}}, 1},
  685. {&TarOptions{ExcludePatterns: []string{"1", "folder*"}}, 2},
  686. {&TarOptions{IncludeFiles: []string{"1", "1"}}, 2},
  687. {&TarOptions{IncludeFiles: []string{"1"}, RebaseNames: map[string]string{"1": "test"}}, 4},
  688. }
  689. for _, testCase := range cases {
  690. changes, err := tarUntar(t, origin, testCase.opts)
  691. if err != nil {
  692. t.Fatalf("Error tar/untar when testing inclusion/exclusion: %s", err)
  693. }
  694. if len(changes) != testCase.numChanges {
  695. t.Errorf("Expected %d changes, got %d for %+v:",
  696. testCase.numChanges, len(changes), testCase.opts)
  697. }
  698. }
  699. }
  700. // Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz
  701. // use PAX Global Extended Headers.
  702. // Failing prevents the archives from being uncompressed during ADD
  703. func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) {
  704. hdr := tar.Header{Typeflag: tar.TypeXGlobalHeader}
  705. tmpDir, err := ioutil.TempDir("", "docker-test-archive-pax-test")
  706. if err != nil {
  707. t.Fatal(err)
  708. }
  709. defer os.RemoveAll(tmpDir)
  710. err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true, nil)
  711. if err != nil {
  712. t.Fatal(err)
  713. }
  714. }
  715. // Some tar have both GNU specific (huge uid) and Ustar specific (long name) things.
  716. // Not supposed to happen (should use PAX instead of Ustar for long name) but it does and it should still work.
  717. func TestUntarUstarGnuConflict(t *testing.T) {
  718. f, err := os.Open("testdata/broken.tar")
  719. if err != nil {
  720. t.Fatal(err)
  721. }
  722. found := false
  723. tr := tar.NewReader(f)
  724. // Iterate through the files in the archive.
  725. for {
  726. hdr, err := tr.Next()
  727. if err == io.EOF {
  728. // end of tar archive
  729. break
  730. }
  731. if err != nil {
  732. t.Fatal(err)
  733. }
  734. if hdr.Name == "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm" {
  735. found = true
  736. break
  737. }
  738. }
  739. if !found {
  740. t.Fatalf("%s not found in the archive", "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm")
  741. }
  742. }
  743. func TestTarWithBlockCharFifo(t *testing.T) {
  744. origin, err := ioutil.TempDir("", "docker-test-tar-hardlink")
  745. if err != nil {
  746. t.Fatal(err)
  747. }
  748. defer os.RemoveAll(origin)
  749. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  750. t.Fatal(err)
  751. }
  752. if err := system.Mknod(path.Join(origin, "2"), syscall.S_IFBLK, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  753. t.Fatal(err)
  754. }
  755. if err := system.Mknod(path.Join(origin, "3"), syscall.S_IFCHR, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  756. t.Fatal(err)
  757. }
  758. if err := system.Mknod(path.Join(origin, "4"), syscall.S_IFIFO, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  759. t.Fatal(err)
  760. }
  761. dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest")
  762. if err != nil {
  763. t.Fatal(err)
  764. }
  765. defer os.RemoveAll(dest)
  766. // we'll do this in two steps to separate failure
  767. fh, err := Tar(origin, Uncompressed)
  768. if err != nil {
  769. t.Fatal(err)
  770. }
  771. // ensure we can read the whole thing with no error, before writing back out
  772. buf, err := ioutil.ReadAll(fh)
  773. if err != nil {
  774. t.Fatal(err)
  775. }
  776. bRdr := bytes.NewReader(buf)
  777. err = Untar(bRdr, dest, &TarOptions{Compression: Uncompressed})
  778. if err != nil {
  779. t.Fatal(err)
  780. }
  781. changes, err := ChangesDirs(origin, dest)
  782. if err != nil {
  783. t.Fatal(err)
  784. }
  785. if len(changes) > 0 {
  786. t.Fatalf("Tar with special device (block, char, fifo) should keep them (recreate them when untar) : %v", changes)
  787. }
  788. }
  789. func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) {
  790. fileData := []byte("fooo")
  791. for n := 0; n < numberOfFiles; n++ {
  792. fileName := fmt.Sprintf("file-%d", n)
  793. if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil {
  794. return 0, err
  795. }
  796. if makeLinks {
  797. if err := os.Link(path.Join(targetPath, fileName), path.Join(targetPath, fileName+"-link")); err != nil {
  798. return 0, err
  799. }
  800. }
  801. }
  802. totalSize := numberOfFiles * len(fileData)
  803. return totalSize, nil
  804. }
  805. func BenchmarkTarUntar(b *testing.B) {
  806. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  807. if err != nil {
  808. b.Fatal(err)
  809. }
  810. tempDir, err := ioutil.TempDir("", "docker-test-untar-destination")
  811. if err != nil {
  812. b.Fatal(err)
  813. }
  814. target := path.Join(tempDir, "dest")
  815. n, err := prepareUntarSourceDirectory(100, origin, false)
  816. if err != nil {
  817. b.Fatal(err)
  818. }
  819. defer os.RemoveAll(origin)
  820. defer os.RemoveAll(tempDir)
  821. b.ResetTimer()
  822. b.SetBytes(int64(n))
  823. for n := 0; n < b.N; n++ {
  824. err := TarUntar(origin, target)
  825. if err != nil {
  826. b.Fatal(err)
  827. }
  828. os.RemoveAll(target)
  829. }
  830. }
  831. func BenchmarkTarUntarWithLinks(b *testing.B) {
  832. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  833. if err != nil {
  834. b.Fatal(err)
  835. }
  836. tempDir, err := ioutil.TempDir("", "docker-test-untar-destination")
  837. if err != nil {
  838. b.Fatal(err)
  839. }
  840. target := path.Join(tempDir, "dest")
  841. n, err := prepareUntarSourceDirectory(100, origin, true)
  842. if err != nil {
  843. b.Fatal(err)
  844. }
  845. defer os.RemoveAll(origin)
  846. defer os.RemoveAll(tempDir)
  847. b.ResetTimer()
  848. b.SetBytes(int64(n))
  849. for n := 0; n < b.N; n++ {
  850. err := TarUntar(origin, target)
  851. if err != nil {
  852. b.Fatal(err)
  853. }
  854. os.RemoveAll(target)
  855. }
  856. }
  857. func TestUntarInvalidFilenames(t *testing.T) {
  858. for i, headers := range [][]*tar.Header{
  859. {
  860. {
  861. Name: "../victim/dotdot",
  862. Typeflag: tar.TypeReg,
  863. Mode: 0644,
  864. },
  865. },
  866. {
  867. {
  868. // Note the leading slash
  869. Name: "/../victim/slash-dotdot",
  870. Typeflag: tar.TypeReg,
  871. Mode: 0644,
  872. },
  873. },
  874. } {
  875. if err := testBreakout("untar", "docker-TestUntarInvalidFilenames", headers); err != nil {
  876. t.Fatalf("i=%d. %v", i, err)
  877. }
  878. }
  879. }
  880. func TestUntarHardlinkToSymlink(t *testing.T) {
  881. for i, headers := range [][]*tar.Header{
  882. {
  883. {
  884. Name: "symlink1",
  885. Typeflag: tar.TypeSymlink,
  886. Linkname: "regfile",
  887. Mode: 0644,
  888. },
  889. {
  890. Name: "symlink2",
  891. Typeflag: tar.TypeLink,
  892. Linkname: "symlink1",
  893. Mode: 0644,
  894. },
  895. {
  896. Name: "regfile",
  897. Typeflag: tar.TypeReg,
  898. Mode: 0644,
  899. },
  900. },
  901. } {
  902. if err := testBreakout("untar", "docker-TestUntarHardlinkToSymlink", headers); err != nil {
  903. t.Fatalf("i=%d. %v", i, err)
  904. }
  905. }
  906. }
  907. func TestUntarInvalidHardlink(t *testing.T) {
  908. for i, headers := range [][]*tar.Header{
  909. { // try reading victim/hello (../)
  910. {
  911. Name: "dotdot",
  912. Typeflag: tar.TypeLink,
  913. Linkname: "../victim/hello",
  914. Mode: 0644,
  915. },
  916. },
  917. { // try reading victim/hello (/../)
  918. {
  919. Name: "slash-dotdot",
  920. Typeflag: tar.TypeLink,
  921. // Note the leading slash
  922. Linkname: "/../victim/hello",
  923. Mode: 0644,
  924. },
  925. },
  926. { // try writing victim/file
  927. {
  928. Name: "loophole-victim",
  929. Typeflag: tar.TypeLink,
  930. Linkname: "../victim",
  931. Mode: 0755,
  932. },
  933. {
  934. Name: "loophole-victim/file",
  935. Typeflag: tar.TypeReg,
  936. Mode: 0644,
  937. },
  938. },
  939. { // try reading victim/hello (hardlink, symlink)
  940. {
  941. Name: "loophole-victim",
  942. Typeflag: tar.TypeLink,
  943. Linkname: "../victim",
  944. Mode: 0755,
  945. },
  946. {
  947. Name: "symlink",
  948. Typeflag: tar.TypeSymlink,
  949. Linkname: "loophole-victim/hello",
  950. Mode: 0644,
  951. },
  952. },
  953. { // Try reading victim/hello (hardlink, hardlink)
  954. {
  955. Name: "loophole-victim",
  956. Typeflag: tar.TypeLink,
  957. Linkname: "../victim",
  958. Mode: 0755,
  959. },
  960. {
  961. Name: "hardlink",
  962. Typeflag: tar.TypeLink,
  963. Linkname: "loophole-victim/hello",
  964. Mode: 0644,
  965. },
  966. },
  967. { // Try removing victim directory (hardlink)
  968. {
  969. Name: "loophole-victim",
  970. Typeflag: tar.TypeLink,
  971. Linkname: "../victim",
  972. Mode: 0755,
  973. },
  974. {
  975. Name: "loophole-victim",
  976. Typeflag: tar.TypeReg,
  977. Mode: 0644,
  978. },
  979. },
  980. } {
  981. if err := testBreakout("untar", "docker-TestUntarInvalidHardlink", headers); err != nil {
  982. t.Fatalf("i=%d. %v", i, err)
  983. }
  984. }
  985. }
  986. func TestUntarInvalidSymlink(t *testing.T) {
  987. for i, headers := range [][]*tar.Header{
  988. { // try reading victim/hello (../)
  989. {
  990. Name: "dotdot",
  991. Typeflag: tar.TypeSymlink,
  992. Linkname: "../victim/hello",
  993. Mode: 0644,
  994. },
  995. },
  996. { // try reading victim/hello (/../)
  997. {
  998. Name: "slash-dotdot",
  999. Typeflag: tar.TypeSymlink,
  1000. // Note the leading slash
  1001. Linkname: "/../victim/hello",
  1002. Mode: 0644,
  1003. },
  1004. },
  1005. { // try writing victim/file
  1006. {
  1007. Name: "loophole-victim",
  1008. Typeflag: tar.TypeSymlink,
  1009. Linkname: "../victim",
  1010. Mode: 0755,
  1011. },
  1012. {
  1013. Name: "loophole-victim/file",
  1014. Typeflag: tar.TypeReg,
  1015. Mode: 0644,
  1016. },
  1017. },
  1018. { // try reading victim/hello (symlink, symlink)
  1019. {
  1020. Name: "loophole-victim",
  1021. Typeflag: tar.TypeSymlink,
  1022. Linkname: "../victim",
  1023. Mode: 0755,
  1024. },
  1025. {
  1026. Name: "symlink",
  1027. Typeflag: tar.TypeSymlink,
  1028. Linkname: "loophole-victim/hello",
  1029. Mode: 0644,
  1030. },
  1031. },
  1032. { // try reading victim/hello (symlink, hardlink)
  1033. {
  1034. Name: "loophole-victim",
  1035. Typeflag: tar.TypeSymlink,
  1036. Linkname: "../victim",
  1037. Mode: 0755,
  1038. },
  1039. {
  1040. Name: "hardlink",
  1041. Typeflag: tar.TypeLink,
  1042. Linkname: "loophole-victim/hello",
  1043. Mode: 0644,
  1044. },
  1045. },
  1046. { // try removing victim directory (symlink)
  1047. {
  1048. Name: "loophole-victim",
  1049. Typeflag: tar.TypeSymlink,
  1050. Linkname: "../victim",
  1051. Mode: 0755,
  1052. },
  1053. {
  1054. Name: "loophole-victim",
  1055. Typeflag: tar.TypeReg,
  1056. Mode: 0644,
  1057. },
  1058. },
  1059. { // try writing to victim/newdir/newfile with a symlink in the path
  1060. {
  1061. // this header needs to be before the next one, or else there is an error
  1062. Name: "dir/loophole",
  1063. Typeflag: tar.TypeSymlink,
  1064. Linkname: "../../victim",
  1065. Mode: 0755,
  1066. },
  1067. {
  1068. Name: "dir/loophole/newdir/newfile",
  1069. Typeflag: tar.TypeReg,
  1070. Mode: 0644,
  1071. },
  1072. },
  1073. } {
  1074. if err := testBreakout("untar", "docker-TestUntarInvalidSymlink", headers); err != nil {
  1075. t.Fatalf("i=%d. %v", i, err)
  1076. }
  1077. }
  1078. }
  1079. func TestTempArchiveCloseMultipleTimes(t *testing.T) {
  1080. reader := ioutil.NopCloser(strings.NewReader("hello"))
  1081. tempArchive, err := NewTempArchive(reader, "")
  1082. buf := make([]byte, 10)
  1083. n, err := tempArchive.Read(buf)
  1084. if n != 5 {
  1085. t.Fatalf("Expected to read 5 bytes. Read %d instead", n)
  1086. }
  1087. for i := 0; i < 3; i++ {
  1088. if err = tempArchive.Close(); err != nil {
  1089. t.Fatalf("i=%d. Unexpected error closing temp archive: %v", i, err)
  1090. }
  1091. }
  1092. }