archive_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. package archive
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "os/exec"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "syscall"
  13. "testing"
  14. "time"
  15. "github.com/docker/docker/pkg/system"
  16. "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
  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 TestTarFiles(t *testing.T) {
  193. // try without hardlinks
  194. if err := checkNoChanges(1000, false); err != nil {
  195. t.Fatal(err)
  196. }
  197. // try with hardlinks
  198. if err := checkNoChanges(1000, true); err != nil {
  199. t.Fatal(err)
  200. }
  201. }
  202. func checkNoChanges(fileNum int, hardlinks bool) error {
  203. srcDir, err := ioutil.TempDir("", "docker-test-srcDir")
  204. if err != nil {
  205. return err
  206. }
  207. defer os.RemoveAll(srcDir)
  208. destDir, err := ioutil.TempDir("", "docker-test-destDir")
  209. if err != nil {
  210. return err
  211. }
  212. defer os.RemoveAll(destDir)
  213. _, err = prepareUntarSourceDirectory(fileNum, srcDir, hardlinks)
  214. if err != nil {
  215. return err
  216. }
  217. err = TarUntar(srcDir, destDir)
  218. if err != nil {
  219. return err
  220. }
  221. changes, err := ChangesDirs(destDir, srcDir)
  222. if err != nil {
  223. return err
  224. }
  225. if len(changes) > 0 {
  226. return fmt.Errorf("with %d files and %v hardlinks: expected 0 changes, got %d", fileNum, hardlinks, len(changes))
  227. }
  228. return nil
  229. }
  230. func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error) {
  231. archive, err := TarWithOptions(origin, options)
  232. if err != nil {
  233. t.Fatal(err)
  234. }
  235. defer archive.Close()
  236. buf := make([]byte, 10)
  237. if _, err := archive.Read(buf); err != nil {
  238. return nil, err
  239. }
  240. wrap := io.MultiReader(bytes.NewReader(buf), archive)
  241. detectedCompression := DetectCompression(buf)
  242. compression := options.Compression
  243. if detectedCompression.Extension() != compression.Extension() {
  244. return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
  245. }
  246. tmp, err := ioutil.TempDir("", "docker-test-untar")
  247. if err != nil {
  248. return nil, err
  249. }
  250. defer os.RemoveAll(tmp)
  251. if err := Untar(wrap, tmp, nil); err != nil {
  252. return nil, err
  253. }
  254. if _, err := os.Stat(tmp); err != nil {
  255. return nil, err
  256. }
  257. return ChangesDirs(origin, tmp)
  258. }
  259. func TestTarUntar(t *testing.T) {
  260. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  261. if err != nil {
  262. t.Fatal(err)
  263. }
  264. defer os.RemoveAll(origin)
  265. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  266. t.Fatal(err)
  267. }
  268. if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
  269. t.Fatal(err)
  270. }
  271. if err := ioutil.WriteFile(path.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil {
  272. t.Fatal(err)
  273. }
  274. for _, c := range []Compression{
  275. Uncompressed,
  276. Gzip,
  277. } {
  278. changes, err := tarUntar(t, origin, &TarOptions{
  279. Compression: c,
  280. ExcludePatterns: []string{"3"},
  281. })
  282. if err != nil {
  283. t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
  284. }
  285. if len(changes) != 1 || changes[0].Path != "/3" {
  286. t.Fatalf("Unexpected differences after tarUntar: %v", changes)
  287. }
  288. }
  289. }
  290. func TestTarUntarWithXattr(t *testing.T) {
  291. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  292. if err != nil {
  293. t.Fatal(err)
  294. }
  295. defer os.RemoveAll(origin)
  296. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  297. t.Fatal(err)
  298. }
  299. if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
  300. t.Fatal(err)
  301. }
  302. if err := ioutil.WriteFile(path.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil {
  303. t.Fatal(err)
  304. }
  305. if err := system.Lsetxattr(path.Join(origin, "2"), "security.capability", []byte{0x00}, 0); err != nil {
  306. t.Fatal(err)
  307. }
  308. for _, c := range []Compression{
  309. Uncompressed,
  310. Gzip,
  311. } {
  312. changes, err := tarUntar(t, origin, &TarOptions{
  313. Compression: c,
  314. ExcludePatterns: []string{"3"},
  315. })
  316. if err != nil {
  317. t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
  318. }
  319. if len(changes) != 1 || changes[0].Path != "/3" {
  320. t.Fatalf("Unexpected differences after tarUntar: %v", changes)
  321. }
  322. capability, _ := system.Lgetxattr(path.Join(origin, "2"), "security.capability")
  323. if capability == nil && capability[0] != 0x00 {
  324. t.Fatalf("Untar should have kept the 'security.capability' xattr.")
  325. }
  326. }
  327. }
  328. func TestTarWithOptions(t *testing.T) {
  329. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  330. if err != nil {
  331. t.Fatal(err)
  332. }
  333. if _, err := ioutil.TempDir(origin, "folder"); err != nil {
  334. t.Fatal(err)
  335. }
  336. defer os.RemoveAll(origin)
  337. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  338. t.Fatal(err)
  339. }
  340. if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
  341. t.Fatal(err)
  342. }
  343. cases := []struct {
  344. opts *TarOptions
  345. numChanges int
  346. }{
  347. {&TarOptions{IncludeFiles: []string{"1"}}, 2},
  348. {&TarOptions{ExcludePatterns: []string{"2"}}, 1},
  349. {&TarOptions{ExcludePatterns: []string{"1", "folder*"}}, 2},
  350. {&TarOptions{IncludeFiles: []string{"1", "1"}}, 2},
  351. {&TarOptions{Name: "test", IncludeFiles: []string{"1"}}, 4},
  352. }
  353. for _, testCase := range cases {
  354. changes, err := tarUntar(t, origin, testCase.opts)
  355. if err != nil {
  356. t.Fatalf("Error tar/untar when testing inclusion/exclusion: %s", err)
  357. }
  358. if len(changes) != testCase.numChanges {
  359. t.Errorf("Expected %d changes, got %d for %+v:",
  360. testCase.numChanges, len(changes), testCase.opts)
  361. }
  362. }
  363. }
  364. // Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz
  365. // use PAX Global Extended Headers.
  366. // Failing prevents the archives from being uncompressed during ADD
  367. func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) {
  368. hdr := tar.Header{Typeflag: tar.TypeXGlobalHeader}
  369. tmpDir, err := ioutil.TempDir("", "docker-test-archive-pax-test")
  370. if err != nil {
  371. t.Fatal(err)
  372. }
  373. defer os.RemoveAll(tmpDir)
  374. err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true)
  375. if err != nil {
  376. t.Fatal(err)
  377. }
  378. }
  379. // Some tar have both GNU specific (huge uid) and Ustar specific (long name) things.
  380. // Not supposed to happen (should use PAX instead of Ustar for long name) but it does and it should still work.
  381. func TestUntarUstarGnuConflict(t *testing.T) {
  382. f, err := os.Open("testdata/broken.tar")
  383. if err != nil {
  384. t.Fatal(err)
  385. }
  386. found := false
  387. tr := tar.NewReader(f)
  388. // Iterate through the files in the archive.
  389. for {
  390. hdr, err := tr.Next()
  391. if err == io.EOF {
  392. // end of tar archive
  393. break
  394. }
  395. if err != nil {
  396. t.Fatal(err)
  397. }
  398. if hdr.Name == "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm" {
  399. found = true
  400. break
  401. }
  402. }
  403. if !found {
  404. t.Fatalf("%s not found in the archive", "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm")
  405. }
  406. }
  407. func TestTarWithBlockCharFifo(t *testing.T) {
  408. origin, err := ioutil.TempDir("", "docker-test-tar-hardlink")
  409. if err != nil {
  410. t.Fatal(err)
  411. }
  412. defer os.RemoveAll(origin)
  413. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  414. t.Fatal(err)
  415. }
  416. if err := system.Mknod(path.Join(origin, "2"), syscall.S_IFBLK, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  417. t.Fatal(err)
  418. }
  419. if err := system.Mknod(path.Join(origin, "3"), syscall.S_IFCHR, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  420. t.Fatal(err)
  421. }
  422. if err := system.Mknod(path.Join(origin, "4"), syscall.S_IFIFO, int(system.Mkdev(int64(12), int64(5)))); err != nil {
  423. t.Fatal(err)
  424. }
  425. dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest")
  426. if err != nil {
  427. t.Fatal(err)
  428. }
  429. defer os.RemoveAll(dest)
  430. // we'll do this in two steps to separate failure
  431. fh, err := Tar(origin, Uncompressed)
  432. if err != nil {
  433. t.Fatal(err)
  434. }
  435. // ensure we can read the whole thing with no error, before writing back out
  436. buf, err := ioutil.ReadAll(fh)
  437. if err != nil {
  438. t.Fatal(err)
  439. }
  440. bRdr := bytes.NewReader(buf)
  441. err = Untar(bRdr, dest, &TarOptions{Compression: Uncompressed})
  442. if err != nil {
  443. t.Fatal(err)
  444. }
  445. changes, err := ChangesDirs(origin, dest)
  446. if err != nil {
  447. t.Fatal(err)
  448. }
  449. if len(changes) > 0 {
  450. t.Fatalf("Tar with special device (block, char, fifo) should keep them (recreate them when untar) : %v", changes)
  451. }
  452. }
  453. func TestTarWithHardLink(t *testing.T) {
  454. origin, err := ioutil.TempDir("", "docker-test-tar-hardlink")
  455. if err != nil {
  456. t.Fatal(err)
  457. }
  458. defer os.RemoveAll(origin)
  459. if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
  460. t.Fatal(err)
  461. }
  462. if err := os.Link(path.Join(origin, "1"), path.Join(origin, "2")); err != nil {
  463. t.Fatal(err)
  464. }
  465. var i1, i2 uint64
  466. if i1, err = getNlink(path.Join(origin, "1")); err != nil {
  467. t.Fatal(err)
  468. }
  469. // sanity check that we can hardlink
  470. if i1 != 2 {
  471. t.Skipf("skipping since hardlinks don't work here; expected 2 links, got %d", i1)
  472. }
  473. dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest")
  474. if err != nil {
  475. t.Fatal(err)
  476. }
  477. defer os.RemoveAll(dest)
  478. // we'll do this in two steps to separate failure
  479. fh, err := Tar(origin, Uncompressed)
  480. if err != nil {
  481. t.Fatal(err)
  482. }
  483. // ensure we can read the whole thing with no error, before writing back out
  484. buf, err := ioutil.ReadAll(fh)
  485. if err != nil {
  486. t.Fatal(err)
  487. }
  488. bRdr := bytes.NewReader(buf)
  489. err = Untar(bRdr, dest, &TarOptions{Compression: Uncompressed})
  490. if err != nil {
  491. t.Fatal(err)
  492. }
  493. if i1, err = getInode(path.Join(dest, "1")); err != nil {
  494. t.Fatal(err)
  495. }
  496. if i2, err = getInode(path.Join(dest, "2")); err != nil {
  497. t.Fatal(err)
  498. }
  499. if i1 != i2 {
  500. t.Errorf("expected matching inodes, but got %d and %d", i1, i2)
  501. }
  502. }
  503. func getNlink(path string) (uint64, error) {
  504. stat, err := os.Stat(path)
  505. if err != nil {
  506. return 0, err
  507. }
  508. statT, ok := stat.Sys().(*syscall.Stat_t)
  509. if !ok {
  510. return 0, fmt.Errorf("expected type *syscall.Stat_t, got %t", stat.Sys())
  511. }
  512. return statT.Nlink, nil
  513. }
  514. func getInode(path string) (uint64, error) {
  515. stat, err := os.Stat(path)
  516. if err != nil {
  517. return 0, err
  518. }
  519. statT, ok := stat.Sys().(*syscall.Stat_t)
  520. if !ok {
  521. return 0, fmt.Errorf("expected type *syscall.Stat_t, got %t", stat.Sys())
  522. }
  523. return statT.Ino, nil
  524. }
  525. func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) {
  526. fileData := []byte("fooo")
  527. for n := 0; n < numberOfFiles; n++ {
  528. fileName := fmt.Sprintf("file-%d", n)
  529. if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil {
  530. return 0, err
  531. }
  532. if makeLinks {
  533. if err := os.Link(path.Join(targetPath, fileName), path.Join(targetPath, fileName+"-link")); err != nil {
  534. return 0, err
  535. }
  536. }
  537. }
  538. totalSize := numberOfFiles * len(fileData)
  539. return totalSize, nil
  540. }
  541. func BenchmarkTarUntar(b *testing.B) {
  542. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  543. if err != nil {
  544. b.Fatal(err)
  545. }
  546. tempDir, err := ioutil.TempDir("", "docker-test-untar-destination")
  547. if err != nil {
  548. b.Fatal(err)
  549. }
  550. target := path.Join(tempDir, "dest")
  551. n, err := prepareUntarSourceDirectory(100, origin, false)
  552. if err != nil {
  553. b.Fatal(err)
  554. }
  555. defer os.RemoveAll(origin)
  556. defer os.RemoveAll(tempDir)
  557. b.ResetTimer()
  558. b.SetBytes(int64(n))
  559. for n := 0; n < b.N; n++ {
  560. err := TarUntar(origin, target)
  561. if err != nil {
  562. b.Fatal(err)
  563. }
  564. os.RemoveAll(target)
  565. }
  566. }
  567. func BenchmarkTarUntarWithLinks(b *testing.B) {
  568. origin, err := ioutil.TempDir("", "docker-test-untar-origin")
  569. if err != nil {
  570. b.Fatal(err)
  571. }
  572. tempDir, err := ioutil.TempDir("", "docker-test-untar-destination")
  573. if err != nil {
  574. b.Fatal(err)
  575. }
  576. target := path.Join(tempDir, "dest")
  577. n, err := prepareUntarSourceDirectory(100, origin, true)
  578. if err != nil {
  579. b.Fatal(err)
  580. }
  581. defer os.RemoveAll(origin)
  582. defer os.RemoveAll(tempDir)
  583. b.ResetTimer()
  584. b.SetBytes(int64(n))
  585. for n := 0; n < b.N; n++ {
  586. err := TarUntar(origin, target)
  587. if err != nil {
  588. b.Fatal(err)
  589. }
  590. os.RemoveAll(target)
  591. }
  592. }
  593. func TestUntarInvalidFilenames(t *testing.T) {
  594. for i, headers := range [][]*tar.Header{
  595. {
  596. {
  597. Name: "../victim/dotdot",
  598. Typeflag: tar.TypeReg,
  599. Mode: 0644,
  600. },
  601. },
  602. {
  603. {
  604. // Note the leading slash
  605. Name: "/../victim/slash-dotdot",
  606. Typeflag: tar.TypeReg,
  607. Mode: 0644,
  608. },
  609. },
  610. } {
  611. if err := testBreakout("untar", "docker-TestUntarInvalidFilenames", headers); err != nil {
  612. t.Fatalf("i=%d. %v", i, err)
  613. }
  614. }
  615. }
  616. func TestUntarHardlinkToSymlink(t *testing.T) {
  617. for i, headers := range [][]*tar.Header{
  618. {
  619. {
  620. Name: "symlink1",
  621. Typeflag: tar.TypeSymlink,
  622. Linkname: "regfile",
  623. Mode: 0644,
  624. },
  625. {
  626. Name: "symlink2",
  627. Typeflag: tar.TypeLink,
  628. Linkname: "symlink1",
  629. Mode: 0644,
  630. },
  631. {
  632. Name: "regfile",
  633. Typeflag: tar.TypeReg,
  634. Mode: 0644,
  635. },
  636. },
  637. } {
  638. if err := testBreakout("untar", "docker-TestUntarHardlinkToSymlink", headers); err != nil {
  639. t.Fatalf("i=%d. %v", i, err)
  640. }
  641. }
  642. }
  643. func TestUntarInvalidHardlink(t *testing.T) {
  644. for i, headers := range [][]*tar.Header{
  645. { // try reading victim/hello (../)
  646. {
  647. Name: "dotdot",
  648. Typeflag: tar.TypeLink,
  649. Linkname: "../victim/hello",
  650. Mode: 0644,
  651. },
  652. },
  653. { // try reading victim/hello (/../)
  654. {
  655. Name: "slash-dotdot",
  656. Typeflag: tar.TypeLink,
  657. // Note the leading slash
  658. Linkname: "/../victim/hello",
  659. Mode: 0644,
  660. },
  661. },
  662. { // try writing victim/file
  663. {
  664. Name: "loophole-victim",
  665. Typeflag: tar.TypeLink,
  666. Linkname: "../victim",
  667. Mode: 0755,
  668. },
  669. {
  670. Name: "loophole-victim/file",
  671. Typeflag: tar.TypeReg,
  672. Mode: 0644,
  673. },
  674. },
  675. { // try reading victim/hello (hardlink, symlink)
  676. {
  677. Name: "loophole-victim",
  678. Typeflag: tar.TypeLink,
  679. Linkname: "../victim",
  680. Mode: 0755,
  681. },
  682. {
  683. Name: "symlink",
  684. Typeflag: tar.TypeSymlink,
  685. Linkname: "loophole-victim/hello",
  686. Mode: 0644,
  687. },
  688. },
  689. { // Try reading victim/hello (hardlink, hardlink)
  690. {
  691. Name: "loophole-victim",
  692. Typeflag: tar.TypeLink,
  693. Linkname: "../victim",
  694. Mode: 0755,
  695. },
  696. {
  697. Name: "hardlink",
  698. Typeflag: tar.TypeLink,
  699. Linkname: "loophole-victim/hello",
  700. Mode: 0644,
  701. },
  702. },
  703. { // Try removing victim directory (hardlink)
  704. {
  705. Name: "loophole-victim",
  706. Typeflag: tar.TypeLink,
  707. Linkname: "../victim",
  708. Mode: 0755,
  709. },
  710. {
  711. Name: "loophole-victim",
  712. Typeflag: tar.TypeReg,
  713. Mode: 0644,
  714. },
  715. },
  716. } {
  717. if err := testBreakout("untar", "docker-TestUntarInvalidHardlink", headers); err != nil {
  718. t.Fatalf("i=%d. %v", i, err)
  719. }
  720. }
  721. }
  722. func TestUntarInvalidSymlink(t *testing.T) {
  723. for i, headers := range [][]*tar.Header{
  724. { // try reading victim/hello (../)
  725. {
  726. Name: "dotdot",
  727. Typeflag: tar.TypeSymlink,
  728. Linkname: "../victim/hello",
  729. Mode: 0644,
  730. },
  731. },
  732. { // try reading victim/hello (/../)
  733. {
  734. Name: "slash-dotdot",
  735. Typeflag: tar.TypeSymlink,
  736. // Note the leading slash
  737. Linkname: "/../victim/hello",
  738. Mode: 0644,
  739. },
  740. },
  741. { // try writing victim/file
  742. {
  743. Name: "loophole-victim",
  744. Typeflag: tar.TypeSymlink,
  745. Linkname: "../victim",
  746. Mode: 0755,
  747. },
  748. {
  749. Name: "loophole-victim/file",
  750. Typeflag: tar.TypeReg,
  751. Mode: 0644,
  752. },
  753. },
  754. { // try reading victim/hello (symlink, symlink)
  755. {
  756. Name: "loophole-victim",
  757. Typeflag: tar.TypeSymlink,
  758. Linkname: "../victim",
  759. Mode: 0755,
  760. },
  761. {
  762. Name: "symlink",
  763. Typeflag: tar.TypeSymlink,
  764. Linkname: "loophole-victim/hello",
  765. Mode: 0644,
  766. },
  767. },
  768. { // try reading victim/hello (symlink, hardlink)
  769. {
  770. Name: "loophole-victim",
  771. Typeflag: tar.TypeSymlink,
  772. Linkname: "../victim",
  773. Mode: 0755,
  774. },
  775. {
  776. Name: "hardlink",
  777. Typeflag: tar.TypeLink,
  778. Linkname: "loophole-victim/hello",
  779. Mode: 0644,
  780. },
  781. },
  782. { // try removing victim directory (symlink)
  783. {
  784. Name: "loophole-victim",
  785. Typeflag: tar.TypeSymlink,
  786. Linkname: "../victim",
  787. Mode: 0755,
  788. },
  789. {
  790. Name: "loophole-victim",
  791. Typeflag: tar.TypeReg,
  792. Mode: 0644,
  793. },
  794. },
  795. { // try writing to victim/newdir/newfile with a symlink in the path
  796. {
  797. // this header needs to be before the next one, or else there is an error
  798. Name: "dir/loophole",
  799. Typeflag: tar.TypeSymlink,
  800. Linkname: "../../victim",
  801. Mode: 0755,
  802. },
  803. {
  804. Name: "dir/loophole/newdir/newfile",
  805. Typeflag: tar.TypeReg,
  806. Mode: 0644,
  807. },
  808. },
  809. } {
  810. if err := testBreakout("untar", "docker-TestUntarInvalidSymlink", headers); err != nil {
  811. t.Fatalf("i=%d. %v", i, err)
  812. }
  813. }
  814. }
  815. func TestTempArchiveCloseMultipleTimes(t *testing.T) {
  816. reader := ioutil.NopCloser(strings.NewReader("hello"))
  817. tempArchive, err := NewTempArchive(reader, "")
  818. buf := make([]byte, 10)
  819. n, err := tempArchive.Read(buf)
  820. if n != 5 {
  821. t.Fatalf("Expected to read 5 bytes. Read %d instead", n)
  822. }
  823. for i := 0; i < 3; i++ {
  824. if err = tempArchive.Close(); err != nil {
  825. t.Fatalf("i=%d. Unexpected error closing temp archive: %v", i, err)
  826. }
  827. }
  828. }