archive_test.go 31 KB

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