github.com/afbjorklund/moby@v20.10.5+incompatible/pkg/archive/archive_test.go (about) 1 package archive // import "github.com/docker/docker/pkg/archive" 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "compress/gzip" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "reflect" 15 "runtime" 16 "strings" 17 "testing" 18 "time" 19 20 "github.com/containerd/containerd/sys" 21 "github.com/docker/docker/pkg/idtools" 22 "github.com/docker/docker/pkg/ioutils" 23 "gotest.tools/v3/assert" 24 is "gotest.tools/v3/assert/cmp" 25 "gotest.tools/v3/skip" 26 ) 27 28 var tmp string 29 30 func init() { 31 tmp = "/tmp/" 32 if runtime.GOOS == "windows" { 33 tmp = os.Getenv("TEMP") + `\` 34 } 35 } 36 37 var defaultArchiver = NewDefaultArchiver() 38 39 func defaultTarUntar(src, dst string) error { 40 return defaultArchiver.TarUntar(src, dst) 41 } 42 43 func defaultUntarPath(src, dst string) error { 44 return defaultArchiver.UntarPath(src, dst) 45 } 46 47 func defaultCopyFileWithTar(src, dst string) (err error) { 48 return defaultArchiver.CopyFileWithTar(src, dst) 49 } 50 51 func defaultCopyWithTar(src, dst string) error { 52 return defaultArchiver.CopyWithTar(src, dst) 53 } 54 55 func TestIsArchivePathDir(t *testing.T) { 56 cmd := exec.Command("sh", "-c", "mkdir -p /tmp/archivedir") 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 + "archivedir") { 62 t.Fatalf("Incorrectly recognised directory as an archive") 63 } 64 } 65 66 func TestIsArchivePathInvalidFile(t *testing.T) { 67 cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1024 count=1 of=/tmp/archive && gzip --stdout /tmp/archive > /tmp/archive.gz") 68 output, err := cmd.CombinedOutput() 69 if err != nil { 70 t.Fatalf("Fail to create an archive file for test : %s.", output) 71 } 72 if IsArchivePath(tmp + "archive") { 73 t.Fatalf("Incorrectly recognised invalid tar path as archive") 74 } 75 if IsArchivePath(tmp + "archive.gz") { 76 t.Fatalf("Incorrectly recognised invalid compressed tar path as archive") 77 } 78 } 79 80 func TestIsArchivePathTar(t *testing.T) { 81 whichTar := "tar" 82 cmdStr := fmt.Sprintf("touch /tmp/archivedata && %s -cf /tmp/archive /tmp/archivedata && gzip --stdout /tmp/archive > /tmp/archive.gz", whichTar) 83 cmd := exec.Command("sh", "-c", cmdStr) 84 output, err := cmd.CombinedOutput() 85 if err != nil { 86 t.Fatalf("Fail to create an archive file for test : %s.", output) 87 } 88 if !IsArchivePath(tmp + "/archive") { 89 t.Fatalf("Did not recognise valid tar path as archive") 90 } 91 if !IsArchivePath(tmp + "archive.gz") { 92 t.Fatalf("Did not recognise valid compressed tar path as archive") 93 } 94 } 95 96 func testDecompressStream(t *testing.T, ext, compressCommand string) io.Reader { 97 cmd := exec.Command("sh", "-c", 98 fmt.Sprintf("touch /tmp/archive && %s /tmp/archive", compressCommand)) 99 output, err := cmd.CombinedOutput() 100 if err != nil { 101 t.Fatalf("Failed to create an archive file for test : %s.", output) 102 } 103 filename := "archive." + ext 104 archive, err := os.Open(tmp + filename) 105 if err != nil { 106 t.Fatalf("Failed to open file %s: %v", filename, err) 107 } 108 defer archive.Close() 109 110 r, err := DecompressStream(archive) 111 if err != nil { 112 t.Fatalf("Failed to decompress %s: %v", filename, err) 113 } 114 if _, err = ioutil.ReadAll(r); err != nil { 115 t.Fatalf("Failed to read the decompressed stream: %v ", err) 116 } 117 if err = r.Close(); err != nil { 118 t.Fatalf("Failed to close the decompressed stream: %v ", err) 119 } 120 121 return r 122 } 123 124 func TestDecompressStreamGzip(t *testing.T) { 125 testDecompressStream(t, "gz", "gzip -f") 126 } 127 128 func TestDecompressStreamBzip2(t *testing.T) { 129 testDecompressStream(t, "bz2", "bzip2 -f") 130 } 131 132 func TestDecompressStreamXz(t *testing.T) { 133 if runtime.GOOS == "windows" { 134 t.Skip("Xz not present in msys2") 135 } 136 testDecompressStream(t, "xz", "xz -f") 137 } 138 139 func TestCompressStreamXzUnsupported(t *testing.T) { 140 dest, err := os.Create(tmp + "dest") 141 if err != nil { 142 t.Fatalf("Fail to create the destination file") 143 } 144 defer dest.Close() 145 146 _, err = CompressStream(dest, Xz) 147 if err == nil { 148 t.Fatalf("Should fail as xz is unsupported for compression format.") 149 } 150 } 151 152 func TestCompressStreamBzip2Unsupported(t *testing.T) { 153 dest, err := os.Create(tmp + "dest") 154 if err != nil { 155 t.Fatalf("Fail to create the destination file") 156 } 157 defer dest.Close() 158 159 _, err = CompressStream(dest, Bzip2) 160 if err == nil { 161 t.Fatalf("Should fail as bzip2 is unsupported for compression format.") 162 } 163 } 164 165 func TestCompressStreamInvalid(t *testing.T) { 166 dest, err := os.Create(tmp + "dest") 167 if err != nil { 168 t.Fatalf("Fail to create the destination file") 169 } 170 defer dest.Close() 171 172 _, err = CompressStream(dest, -1) 173 if err == nil { 174 t.Fatalf("Should fail as xz is unsupported for compression format.") 175 } 176 } 177 178 func TestExtensionInvalid(t *testing.T) { 179 compression := Compression(-1) 180 output := compression.Extension() 181 if output != "" { 182 t.Fatalf("The extension of an invalid compression should be an empty string.") 183 } 184 } 185 186 func TestExtensionUncompressed(t *testing.T) { 187 compression := Uncompressed 188 output := compression.Extension() 189 if output != "tar" { 190 t.Fatalf("The extension of an uncompressed archive should be 'tar'.") 191 } 192 } 193 func TestExtensionBzip2(t *testing.T) { 194 compression := Bzip2 195 output := compression.Extension() 196 if output != "tar.bz2" { 197 t.Fatalf("The extension of a bzip2 archive should be 'tar.bz2'") 198 } 199 } 200 func TestExtensionGzip(t *testing.T) { 201 compression := Gzip 202 output := compression.Extension() 203 if output != "tar.gz" { 204 t.Fatalf("The extension of a gzip archive should be 'tar.gz'") 205 } 206 } 207 func TestExtensionXz(t *testing.T) { 208 compression := Xz 209 output := compression.Extension() 210 if output != "tar.xz" { 211 t.Fatalf("The extension of a xz archive should be 'tar.xz'") 212 } 213 } 214 215 func TestCmdStreamLargeStderr(t *testing.T) { 216 cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello") 217 out, err := cmdStream(cmd, nil) 218 if err != nil { 219 t.Fatalf("Failed to start command: %s", err) 220 } 221 errCh := make(chan error, 1) 222 go func() { 223 _, err := io.Copy(ioutil.Discard, out) 224 errCh <- err 225 }() 226 select { 227 case err := <-errCh: 228 if err != nil { 229 t.Fatalf("Command should not have failed (err=%.100s...)", err) 230 } 231 case <-time.After(5 * time.Second): 232 t.Fatalf("Command did not complete in 5 seconds; probable deadlock") 233 } 234 } 235 236 func TestCmdStreamBad(t *testing.T) { 237 // TODO Windows: Figure out why this is failing in CI but not locally 238 if runtime.GOOS == "windows" { 239 t.Skip("Failing on Windows CI machines") 240 } 241 badCmd := exec.Command("sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1") 242 out, err := cmdStream(badCmd, nil) 243 if err != nil { 244 t.Fatalf("Failed to start command: %s", err) 245 } 246 if output, err := ioutil.ReadAll(out); err == nil { 247 t.Fatalf("Command should have failed") 248 } else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" { 249 t.Fatalf("Wrong error value (%s)", err) 250 } else if s := string(output); s != "hello\n" { 251 t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output) 252 } 253 } 254 255 func TestCmdStreamGood(t *testing.T) { 256 cmd := exec.Command("sh", "-c", "echo hello; exit 0") 257 out, err := cmdStream(cmd, nil) 258 if err != nil { 259 t.Fatal(err) 260 } 261 if output, err := ioutil.ReadAll(out); err != nil { 262 t.Fatalf("Command should not have failed (err=%s)", err) 263 } else if s := string(output); s != "hello\n" { 264 t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output) 265 } 266 } 267 268 func TestUntarPathWithInvalidDest(t *testing.T) { 269 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 270 assert.NilError(t, err) 271 defer os.RemoveAll(tempFolder) 272 invalidDestFolder := filepath.Join(tempFolder, "invalidDest") 273 // Create a src file 274 srcFile := filepath.Join(tempFolder, "src") 275 tarFile := filepath.Join(tempFolder, "src.tar") 276 os.Create(srcFile) 277 os.Create(invalidDestFolder) // being a file (not dir) should cause an error 278 279 // Translate back to Unix semantics as next exec.Command is run under sh 280 srcFileU := srcFile 281 tarFileU := tarFile 282 if runtime.GOOS == "windows" { 283 tarFileU = "/tmp/" + filepath.Base(filepath.Dir(tarFile)) + "/src.tar" 284 srcFileU = "/tmp/" + filepath.Base(filepath.Dir(srcFile)) + "/src" 285 } 286 287 cmd := exec.Command("sh", "-c", "tar cf "+tarFileU+" "+srcFileU) 288 _, err = cmd.CombinedOutput() 289 assert.NilError(t, err) 290 291 err = defaultUntarPath(tarFile, invalidDestFolder) 292 if err == nil { 293 t.Fatalf("UntarPath with invalid destination path should throw an error.") 294 } 295 } 296 297 func TestUntarPathWithInvalidSrc(t *testing.T) { 298 dest, err := ioutil.TempDir("", "docker-archive-test") 299 if err != nil { 300 t.Fatalf("Fail to create the destination file") 301 } 302 defer os.RemoveAll(dest) 303 err = defaultUntarPath("/invalid/path", dest) 304 if err == nil { 305 t.Fatalf("UntarPath with invalid src path should throw an error.") 306 } 307 } 308 309 func TestUntarPath(t *testing.T) { 310 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 311 tmpFolder, err := ioutil.TempDir("", "docker-archive-test") 312 assert.NilError(t, err) 313 defer os.RemoveAll(tmpFolder) 314 srcFile := filepath.Join(tmpFolder, "src") 315 tarFile := filepath.Join(tmpFolder, "src.tar") 316 os.Create(filepath.Join(tmpFolder, "src")) 317 318 destFolder := filepath.Join(tmpFolder, "dest") 319 err = os.MkdirAll(destFolder, 0740) 320 if err != nil { 321 t.Fatalf("Fail to create the destination file") 322 } 323 324 // Translate back to Unix semantics as next exec.Command is run under sh 325 srcFileU := srcFile 326 tarFileU := tarFile 327 if runtime.GOOS == "windows" { 328 tarFileU = "/tmp/" + filepath.Base(filepath.Dir(tarFile)) + "/src.tar" 329 srcFileU = "/tmp/" + filepath.Base(filepath.Dir(srcFile)) + "/src" 330 } 331 cmd := exec.Command("sh", "-c", "tar cf "+tarFileU+" "+srcFileU) 332 _, err = cmd.CombinedOutput() 333 assert.NilError(t, err) 334 335 err = defaultUntarPath(tarFile, destFolder) 336 if err != nil { 337 t.Fatalf("UntarPath shouldn't throw an error, %s.", err) 338 } 339 expectedFile := filepath.Join(destFolder, srcFileU) 340 _, err = os.Stat(expectedFile) 341 if err != nil { 342 t.Fatalf("Destination folder should contain the source file but did not.") 343 } 344 } 345 346 // Do the same test as above but with the destination as file, it should fail 347 func TestUntarPathWithDestinationFile(t *testing.T) { 348 tmpFolder, err := ioutil.TempDir("", "docker-archive-test") 349 if err != nil { 350 t.Fatal(err) 351 } 352 defer os.RemoveAll(tmpFolder) 353 srcFile := filepath.Join(tmpFolder, "src") 354 tarFile := filepath.Join(tmpFolder, "src.tar") 355 os.Create(filepath.Join(tmpFolder, "src")) 356 357 // Translate back to Unix semantics as next exec.Command is run under sh 358 srcFileU := srcFile 359 tarFileU := tarFile 360 if runtime.GOOS == "windows" { 361 tarFileU = "/tmp/" + filepath.Base(filepath.Dir(tarFile)) + "/src.tar" 362 srcFileU = "/tmp/" + filepath.Base(filepath.Dir(srcFile)) + "/src" 363 } 364 cmd := exec.Command("sh", "-c", "tar cf "+tarFileU+" "+srcFileU) 365 _, err = cmd.CombinedOutput() 366 if err != nil { 367 t.Fatal(err) 368 } 369 destFile := filepath.Join(tmpFolder, "dest") 370 _, err = os.Create(destFile) 371 if err != nil { 372 t.Fatalf("Fail to create the destination file") 373 } 374 err = defaultUntarPath(tarFile, destFile) 375 if err == nil { 376 t.Fatalf("UntarPath should throw an error if the destination if a file") 377 } 378 } 379 380 // Do the same test as above but with the destination folder already exists 381 // and the destination file is a directory 382 // It's working, see https://github.com/docker/docker/issues/10040 383 func TestUntarPathWithDestinationSrcFileAsFolder(t *testing.T) { 384 tmpFolder, err := ioutil.TempDir("", "docker-archive-test") 385 if err != nil { 386 t.Fatal(err) 387 } 388 defer os.RemoveAll(tmpFolder) 389 srcFile := filepath.Join(tmpFolder, "src") 390 tarFile := filepath.Join(tmpFolder, "src.tar") 391 os.Create(srcFile) 392 393 // Translate back to Unix semantics as next exec.Command is run under sh 394 srcFileU := srcFile 395 tarFileU := tarFile 396 if runtime.GOOS == "windows" { 397 tarFileU = "/tmp/" + filepath.Base(filepath.Dir(tarFile)) + "/src.tar" 398 srcFileU = "/tmp/" + filepath.Base(filepath.Dir(srcFile)) + "/src" 399 } 400 401 cmd := exec.Command("sh", "-c", "tar cf "+tarFileU+" "+srcFileU) 402 _, err = cmd.CombinedOutput() 403 if err != nil { 404 t.Fatal(err) 405 } 406 destFolder := filepath.Join(tmpFolder, "dest") 407 err = os.MkdirAll(destFolder, 0740) 408 if err != nil { 409 t.Fatalf("Fail to create the destination folder") 410 } 411 // Let's create a folder that will has the same path as the extracted file (from tar) 412 destSrcFileAsFolder := filepath.Join(destFolder, srcFileU) 413 err = os.MkdirAll(destSrcFileAsFolder, 0740) 414 if err != nil { 415 t.Fatal(err) 416 } 417 err = defaultUntarPath(tarFile, destFolder) 418 if err != nil { 419 t.Fatalf("UntarPath should throw not throw an error if the extracted file already exists and is a folder") 420 } 421 } 422 423 func TestCopyWithTarInvalidSrc(t *testing.T) { 424 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 425 if err != nil { 426 t.Fatal(nil) 427 } 428 destFolder := filepath.Join(tempFolder, "dest") 429 invalidSrc := filepath.Join(tempFolder, "doesnotexists") 430 err = os.MkdirAll(destFolder, 0740) 431 if err != nil { 432 t.Fatal(err) 433 } 434 err = defaultCopyWithTar(invalidSrc, destFolder) 435 if err == nil { 436 t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.") 437 } 438 } 439 440 func TestCopyWithTarInexistentDestWillCreateIt(t *testing.T) { 441 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 442 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 443 if err != nil { 444 t.Fatal(nil) 445 } 446 srcFolder := filepath.Join(tempFolder, "src") 447 inexistentDestFolder := filepath.Join(tempFolder, "doesnotexists") 448 err = os.MkdirAll(srcFolder, 0740) 449 if err != nil { 450 t.Fatal(err) 451 } 452 err = defaultCopyWithTar(srcFolder, inexistentDestFolder) 453 if err != nil { 454 t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.") 455 } 456 _, err = os.Stat(inexistentDestFolder) 457 if err != nil { 458 t.Fatalf("CopyWithTar with an inexistent folder should create it.") 459 } 460 } 461 462 // Test CopyWithTar with a file as src 463 func TestCopyWithTarSrcFile(t *testing.T) { 464 folder, err := ioutil.TempDir("", "docker-archive-test") 465 if err != nil { 466 t.Fatal(err) 467 } 468 defer os.RemoveAll(folder) 469 dest := filepath.Join(folder, "dest") 470 srcFolder := filepath.Join(folder, "src") 471 src := filepath.Join(folder, filepath.Join("src", "src")) 472 err = os.MkdirAll(srcFolder, 0740) 473 if err != nil { 474 t.Fatal(err) 475 } 476 err = os.MkdirAll(dest, 0740) 477 if err != nil { 478 t.Fatal(err) 479 } 480 ioutil.WriteFile(src, []byte("content"), 0777) 481 err = defaultCopyWithTar(src, dest) 482 if err != nil { 483 t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) 484 } 485 _, err = os.Stat(dest) 486 // FIXME Check the content 487 if err != nil { 488 t.Fatalf("Destination file should be the same as the source.") 489 } 490 } 491 492 // Test CopyWithTar with a folder as src 493 func TestCopyWithTarSrcFolder(t *testing.T) { 494 folder, err := ioutil.TempDir("", "docker-archive-test") 495 if err != nil { 496 t.Fatal(err) 497 } 498 defer os.RemoveAll(folder) 499 dest := filepath.Join(folder, "dest") 500 src := filepath.Join(folder, filepath.Join("src", "folder")) 501 err = os.MkdirAll(src, 0740) 502 if err != nil { 503 t.Fatal(err) 504 } 505 err = os.MkdirAll(dest, 0740) 506 if err != nil { 507 t.Fatal(err) 508 } 509 ioutil.WriteFile(filepath.Join(src, "file"), []byte("content"), 0777) 510 err = defaultCopyWithTar(src, dest) 511 if err != nil { 512 t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) 513 } 514 _, err = os.Stat(dest) 515 // FIXME Check the content (the file inside) 516 if err != nil { 517 t.Fatalf("Destination folder should contain the source file but did not.") 518 } 519 } 520 521 func TestCopyFileWithTarInvalidSrc(t *testing.T) { 522 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 523 if err != nil { 524 t.Fatal(err) 525 } 526 defer os.RemoveAll(tempFolder) 527 destFolder := filepath.Join(tempFolder, "dest") 528 err = os.MkdirAll(destFolder, 0740) 529 if err != nil { 530 t.Fatal(err) 531 } 532 invalidFile := filepath.Join(tempFolder, "doesnotexists") 533 err = defaultCopyFileWithTar(invalidFile, destFolder) 534 if err == nil { 535 t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.") 536 } 537 } 538 539 func TestCopyFileWithTarInexistentDestWillCreateIt(t *testing.T) { 540 tempFolder, err := ioutil.TempDir("", "docker-archive-test") 541 if err != nil { 542 t.Fatal(nil) 543 } 544 defer os.RemoveAll(tempFolder) 545 srcFile := filepath.Join(tempFolder, "src") 546 inexistentDestFolder := filepath.Join(tempFolder, "doesnotexists") 547 _, err = os.Create(srcFile) 548 if err != nil { 549 t.Fatal(err) 550 } 551 err = defaultCopyFileWithTar(srcFile, inexistentDestFolder) 552 if err != nil { 553 t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.") 554 } 555 _, err = os.Stat(inexistentDestFolder) 556 if err != nil { 557 t.Fatalf("CopyWithTar with an inexistent folder should create it.") 558 } 559 // FIXME Test the src file and content 560 } 561 562 func TestCopyFileWithTarSrcFolder(t *testing.T) { 563 folder, err := ioutil.TempDir("", "docker-archive-copyfilewithtar-test") 564 if err != nil { 565 t.Fatal(err) 566 } 567 defer os.RemoveAll(folder) 568 dest := filepath.Join(folder, "dest") 569 src := filepath.Join(folder, "srcfolder") 570 err = os.MkdirAll(src, 0740) 571 if err != nil { 572 t.Fatal(err) 573 } 574 err = os.MkdirAll(dest, 0740) 575 if err != nil { 576 t.Fatal(err) 577 } 578 err = defaultCopyFileWithTar(src, dest) 579 if err == nil { 580 t.Fatalf("CopyFileWithTar should throw an error with a folder.") 581 } 582 } 583 584 func TestCopyFileWithTarSrcFile(t *testing.T) { 585 folder, err := ioutil.TempDir("", "docker-archive-test") 586 if err != nil { 587 t.Fatal(err) 588 } 589 defer os.RemoveAll(folder) 590 dest := filepath.Join(folder, "dest") 591 srcFolder := filepath.Join(folder, "src") 592 src := filepath.Join(folder, filepath.Join("src", "src")) 593 err = os.MkdirAll(srcFolder, 0740) 594 if err != nil { 595 t.Fatal(err) 596 } 597 err = os.MkdirAll(dest, 0740) 598 if err != nil { 599 t.Fatal(err) 600 } 601 ioutil.WriteFile(src, []byte("content"), 0777) 602 err = defaultCopyWithTar(src, dest+"/") 603 if err != nil { 604 t.Fatalf("archiver.CopyFileWithTar shouldn't throw an error, %s.", err) 605 } 606 _, err = os.Stat(dest) 607 if err != nil { 608 t.Fatalf("Destination folder should contain the source file but did not.") 609 } 610 } 611 612 func TestTarFiles(t *testing.T) { 613 // try without hardlinks 614 if err := checkNoChanges(1000, false); err != nil { 615 t.Fatal(err) 616 } 617 // try with hardlinks 618 if err := checkNoChanges(1000, true); err != nil { 619 t.Fatal(err) 620 } 621 } 622 623 func checkNoChanges(fileNum int, hardlinks bool) error { 624 srcDir, err := ioutil.TempDir("", "docker-test-srcDir") 625 if err != nil { 626 return err 627 } 628 defer os.RemoveAll(srcDir) 629 630 destDir, err := ioutil.TempDir("", "docker-test-destDir") 631 if err != nil { 632 return err 633 } 634 defer os.RemoveAll(destDir) 635 636 _, err = prepareUntarSourceDirectory(fileNum, srcDir, hardlinks) 637 if err != nil { 638 return err 639 } 640 641 err = defaultTarUntar(srcDir, destDir) 642 if err != nil { 643 return err 644 } 645 646 changes, err := ChangesDirs(destDir, srcDir) 647 if err != nil { 648 return err 649 } 650 if len(changes) > 0 { 651 return fmt.Errorf("with %d files and %v hardlinks: expected 0 changes, got %d", fileNum, hardlinks, len(changes)) 652 } 653 return nil 654 } 655 656 func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error) { 657 archive, err := TarWithOptions(origin, options) 658 if err != nil { 659 t.Fatal(err) 660 } 661 defer archive.Close() 662 663 buf := make([]byte, 10) 664 if _, err := archive.Read(buf); err != nil { 665 return nil, err 666 } 667 wrap := io.MultiReader(bytes.NewReader(buf), archive) 668 669 detectedCompression := DetectCompression(buf) 670 compression := options.Compression 671 if detectedCompression.Extension() != compression.Extension() { 672 return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension()) 673 } 674 675 tmp, err := ioutil.TempDir("", "docker-test-untar") 676 if err != nil { 677 return nil, err 678 } 679 defer os.RemoveAll(tmp) 680 if err := Untar(wrap, tmp, nil); err != nil { 681 return nil, err 682 } 683 if _, err := os.Stat(tmp); err != nil { 684 return nil, err 685 } 686 687 return ChangesDirs(origin, tmp) 688 } 689 690 func TestTarUntar(t *testing.T) { 691 origin, err := ioutil.TempDir("", "docker-test-untar-origin") 692 if err != nil { 693 t.Fatal(err) 694 } 695 defer os.RemoveAll(origin) 696 if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil { 697 t.Fatal(err) 698 } 699 if err := ioutil.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { 700 t.Fatal(err) 701 } 702 if err := ioutil.WriteFile(filepath.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil { 703 t.Fatal(err) 704 } 705 706 for _, c := range []Compression{ 707 Uncompressed, 708 Gzip, 709 } { 710 changes, err := tarUntar(t, origin, &TarOptions{ 711 Compression: c, 712 ExcludePatterns: []string{"3"}, 713 }) 714 715 if err != nil { 716 t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err) 717 } 718 719 if len(changes) != 1 || changes[0].Path != string(filepath.Separator)+"3" { 720 t.Fatalf("Unexpected differences after tarUntar: %v", changes) 721 } 722 } 723 } 724 725 func TestTarWithOptionsChownOptsAlwaysOverridesIdPair(t *testing.T) { 726 origin, err := ioutil.TempDir("", "docker-test-tar-chown-opt") 727 assert.NilError(t, err) 728 729 defer os.RemoveAll(origin) 730 filePath := filepath.Join(origin, "1") 731 err = ioutil.WriteFile(filePath, []byte("hello world"), 0700) 732 assert.NilError(t, err) 733 734 idMaps := []idtools.IDMap{ 735 0: { 736 ContainerID: 0, 737 HostID: 0, 738 Size: 65536, 739 }, 740 1: { 741 ContainerID: 0, 742 HostID: 100000, 743 Size: 65536, 744 }, 745 } 746 747 cases := []struct { 748 opts *TarOptions 749 expectedUID int 750 expectedGID int 751 }{ 752 {&TarOptions{ChownOpts: &idtools.Identity{UID: 1337, GID: 42}}, 1337, 42}, 753 {&TarOptions{ChownOpts: &idtools.Identity{UID: 100001, GID: 100001}, UIDMaps: idMaps, GIDMaps: idMaps}, 100001, 100001}, 754 {&TarOptions{ChownOpts: &idtools.Identity{UID: 0, GID: 0}, NoLchown: false}, 0, 0}, 755 {&TarOptions{ChownOpts: &idtools.Identity{UID: 1, GID: 1}, NoLchown: true}, 1, 1}, 756 {&TarOptions{ChownOpts: &idtools.Identity{UID: 1000, GID: 1000}, NoLchown: true}, 1000, 1000}, 757 } 758 for _, testCase := range cases { 759 reader, err := TarWithOptions(filePath, testCase.opts) 760 assert.NilError(t, err) 761 tr := tar.NewReader(reader) 762 defer reader.Close() 763 for { 764 hdr, err := tr.Next() 765 if err == io.EOF { 766 // end of tar archive 767 break 768 } 769 assert.NilError(t, err) 770 assert.Check(t, is.Equal(hdr.Uid, testCase.expectedUID), "Uid equals expected value") 771 assert.Check(t, is.Equal(hdr.Gid, testCase.expectedGID), "Gid equals expected value") 772 } 773 } 774 } 775 776 func TestTarWithOptions(t *testing.T) { 777 origin, err := ioutil.TempDir("", "docker-test-untar-origin") 778 if err != nil { 779 t.Fatal(err) 780 } 781 if _, err := ioutil.TempDir(origin, "folder"); err != nil { 782 t.Fatal(err) 783 } 784 defer os.RemoveAll(origin) 785 if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil { 786 t.Fatal(err) 787 } 788 if err := ioutil.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { 789 t.Fatal(err) 790 } 791 792 cases := []struct { 793 opts *TarOptions 794 numChanges int 795 }{ 796 {&TarOptions{IncludeFiles: []string{"1"}}, 2}, 797 {&TarOptions{ExcludePatterns: []string{"2"}}, 1}, 798 {&TarOptions{ExcludePatterns: []string{"1", "folder*"}}, 2}, 799 {&TarOptions{IncludeFiles: []string{"1", "1"}}, 2}, 800 {&TarOptions{IncludeFiles: []string{"1"}, RebaseNames: map[string]string{"1": "test"}}, 4}, 801 } 802 for _, testCase := range cases { 803 changes, err := tarUntar(t, origin, testCase.opts) 804 if err != nil { 805 t.Fatalf("Error tar/untar when testing inclusion/exclusion: %s", err) 806 } 807 if len(changes) != testCase.numChanges { 808 t.Errorf("Expected %d changes, got %d for %+v:", 809 testCase.numChanges, len(changes), testCase.opts) 810 } 811 } 812 } 813 814 // Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz 815 // use PAX Global Extended Headers. 816 // Failing prevents the archives from being uncompressed during ADD 817 func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) { 818 hdr := tar.Header{Typeflag: tar.TypeXGlobalHeader} 819 tmpDir, err := ioutil.TempDir("", "docker-test-archive-pax-test") 820 if err != nil { 821 t.Fatal(err) 822 } 823 defer os.RemoveAll(tmpDir) 824 err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true, nil, false) 825 if err != nil { 826 t.Fatal(err) 827 } 828 } 829 830 // Some tar have both GNU specific (huge uid) and Ustar specific (long name) things. 831 // Not supposed to happen (should use PAX instead of Ustar for long name) but it does and it should still work. 832 func TestUntarUstarGnuConflict(t *testing.T) { 833 f, err := os.Open("testdata/broken.tar") 834 if err != nil { 835 t.Fatal(err) 836 } 837 defer f.Close() 838 839 found := false 840 tr := tar.NewReader(f) 841 // Iterate through the files in the archive. 842 for { 843 hdr, err := tr.Next() 844 if err == io.EOF { 845 // end of tar archive 846 break 847 } 848 if err != nil { 849 t.Fatal(err) 850 } 851 if hdr.Name == "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm" { 852 found = true 853 break 854 } 855 } 856 if !found { 857 t.Fatalf("%s not found in the archive", "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm") 858 } 859 } 860 861 func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) { 862 fileData := []byte("fooo") 863 for n := 0; n < numberOfFiles; n++ { 864 fileName := fmt.Sprintf("file-%d", n) 865 if err := ioutil.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil { 866 return 0, err 867 } 868 if makeLinks { 869 if err := os.Link(filepath.Join(targetPath, fileName), filepath.Join(targetPath, fileName+"-link")); err != nil { 870 return 0, err 871 } 872 } 873 } 874 totalSize := numberOfFiles * len(fileData) 875 return totalSize, nil 876 } 877 878 func BenchmarkTarUntar(b *testing.B) { 879 origin, err := ioutil.TempDir("", "docker-test-untar-origin") 880 if err != nil { 881 b.Fatal(err) 882 } 883 tempDir, err := ioutil.TempDir("", "docker-test-untar-destination") 884 if err != nil { 885 b.Fatal(err) 886 } 887 target := filepath.Join(tempDir, "dest") 888 n, err := prepareUntarSourceDirectory(100, origin, false) 889 if err != nil { 890 b.Fatal(err) 891 } 892 defer os.RemoveAll(origin) 893 defer os.RemoveAll(tempDir) 894 895 b.ResetTimer() 896 b.SetBytes(int64(n)) 897 for n := 0; n < b.N; n++ { 898 err := defaultTarUntar(origin, target) 899 if err != nil { 900 b.Fatal(err) 901 } 902 os.RemoveAll(target) 903 } 904 } 905 906 func BenchmarkTarUntarWithLinks(b *testing.B) { 907 origin, err := ioutil.TempDir("", "docker-test-untar-origin") 908 if err != nil { 909 b.Fatal(err) 910 } 911 tempDir, err := ioutil.TempDir("", "docker-test-untar-destination") 912 if err != nil { 913 b.Fatal(err) 914 } 915 target := filepath.Join(tempDir, "dest") 916 n, err := prepareUntarSourceDirectory(100, origin, true) 917 if err != nil { 918 b.Fatal(err) 919 } 920 defer os.RemoveAll(origin) 921 defer os.RemoveAll(tempDir) 922 923 b.ResetTimer() 924 b.SetBytes(int64(n)) 925 for n := 0; n < b.N; n++ { 926 err := defaultTarUntar(origin, target) 927 if err != nil { 928 b.Fatal(err) 929 } 930 os.RemoveAll(target) 931 } 932 } 933 934 func TestUntarInvalidFilenames(t *testing.T) { 935 for i, headers := range [][]*tar.Header{ 936 { 937 { 938 Name: "../victim/dotdot", 939 Typeflag: tar.TypeReg, 940 Mode: 0644, 941 }, 942 }, 943 { 944 { 945 // Note the leading slash 946 Name: "/../victim/slash-dotdot", 947 Typeflag: tar.TypeReg, 948 Mode: 0644, 949 }, 950 }, 951 } { 952 if err := testBreakout("untar", "docker-TestUntarInvalidFilenames", headers); err != nil { 953 t.Fatalf("i=%d. %v", i, err) 954 } 955 } 956 } 957 958 func TestUntarHardlinkToSymlink(t *testing.T) { 959 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 960 for i, headers := range [][]*tar.Header{ 961 { 962 { 963 Name: "symlink1", 964 Typeflag: tar.TypeSymlink, 965 Linkname: "regfile", 966 Mode: 0644, 967 }, 968 { 969 Name: "symlink2", 970 Typeflag: tar.TypeLink, 971 Linkname: "symlink1", 972 Mode: 0644, 973 }, 974 { 975 Name: "regfile", 976 Typeflag: tar.TypeReg, 977 Mode: 0644, 978 }, 979 }, 980 } { 981 if err := testBreakout("untar", "docker-TestUntarHardlinkToSymlink", headers); err != nil { 982 t.Fatalf("i=%d. %v", i, err) 983 } 984 } 985 } 986 987 func TestUntarInvalidHardlink(t *testing.T) { 988 for i, headers := range [][]*tar.Header{ 989 { // try reading victim/hello (../) 990 { 991 Name: "dotdot", 992 Typeflag: tar.TypeLink, 993 Linkname: "../victim/hello", 994 Mode: 0644, 995 }, 996 }, 997 { // try reading victim/hello (/../) 998 { 999 Name: "slash-dotdot", 1000 Typeflag: tar.TypeLink, 1001 // Note the leading slash 1002 Linkname: "/../victim/hello", 1003 Mode: 0644, 1004 }, 1005 }, 1006 { // try writing victim/file 1007 { 1008 Name: "loophole-victim", 1009 Typeflag: tar.TypeLink, 1010 Linkname: "../victim", 1011 Mode: 0755, 1012 }, 1013 { 1014 Name: "loophole-victim/file", 1015 Typeflag: tar.TypeReg, 1016 Mode: 0644, 1017 }, 1018 }, 1019 { // try reading victim/hello (hardlink, symlink) 1020 { 1021 Name: "loophole-victim", 1022 Typeflag: tar.TypeLink, 1023 Linkname: "../victim", 1024 Mode: 0755, 1025 }, 1026 { 1027 Name: "symlink", 1028 Typeflag: tar.TypeSymlink, 1029 Linkname: "loophole-victim/hello", 1030 Mode: 0644, 1031 }, 1032 }, 1033 { // Try reading victim/hello (hardlink, hardlink) 1034 { 1035 Name: "loophole-victim", 1036 Typeflag: tar.TypeLink, 1037 Linkname: "../victim", 1038 Mode: 0755, 1039 }, 1040 { 1041 Name: "hardlink", 1042 Typeflag: tar.TypeLink, 1043 Linkname: "loophole-victim/hello", 1044 Mode: 0644, 1045 }, 1046 }, 1047 { // Try removing victim directory (hardlink) 1048 { 1049 Name: "loophole-victim", 1050 Typeflag: tar.TypeLink, 1051 Linkname: "../victim", 1052 Mode: 0755, 1053 }, 1054 { 1055 Name: "loophole-victim", 1056 Typeflag: tar.TypeReg, 1057 Mode: 0644, 1058 }, 1059 }, 1060 } { 1061 if err := testBreakout("untar", "docker-TestUntarInvalidHardlink", headers); err != nil { 1062 t.Fatalf("i=%d. %v", i, err) 1063 } 1064 } 1065 } 1066 1067 func TestUntarInvalidSymlink(t *testing.T) { 1068 for i, headers := range [][]*tar.Header{ 1069 { // try reading victim/hello (../) 1070 { 1071 Name: "dotdot", 1072 Typeflag: tar.TypeSymlink, 1073 Linkname: "../victim/hello", 1074 Mode: 0644, 1075 }, 1076 }, 1077 { // try reading victim/hello (/../) 1078 { 1079 Name: "slash-dotdot", 1080 Typeflag: tar.TypeSymlink, 1081 // Note the leading slash 1082 Linkname: "/../victim/hello", 1083 Mode: 0644, 1084 }, 1085 }, 1086 { // try writing victim/file 1087 { 1088 Name: "loophole-victim", 1089 Typeflag: tar.TypeSymlink, 1090 Linkname: "../victim", 1091 Mode: 0755, 1092 }, 1093 { 1094 Name: "loophole-victim/file", 1095 Typeflag: tar.TypeReg, 1096 Mode: 0644, 1097 }, 1098 }, 1099 { // try reading victim/hello (symlink, symlink) 1100 { 1101 Name: "loophole-victim", 1102 Typeflag: tar.TypeSymlink, 1103 Linkname: "../victim", 1104 Mode: 0755, 1105 }, 1106 { 1107 Name: "symlink", 1108 Typeflag: tar.TypeSymlink, 1109 Linkname: "loophole-victim/hello", 1110 Mode: 0644, 1111 }, 1112 }, 1113 { // try reading victim/hello (symlink, hardlink) 1114 { 1115 Name: "loophole-victim", 1116 Typeflag: tar.TypeSymlink, 1117 Linkname: "../victim", 1118 Mode: 0755, 1119 }, 1120 { 1121 Name: "hardlink", 1122 Typeflag: tar.TypeLink, 1123 Linkname: "loophole-victim/hello", 1124 Mode: 0644, 1125 }, 1126 }, 1127 { // try removing victim directory (symlink) 1128 { 1129 Name: "loophole-victim", 1130 Typeflag: tar.TypeSymlink, 1131 Linkname: "../victim", 1132 Mode: 0755, 1133 }, 1134 { 1135 Name: "loophole-victim", 1136 Typeflag: tar.TypeReg, 1137 Mode: 0644, 1138 }, 1139 }, 1140 { // try writing to victim/newdir/newfile with a symlink in the path 1141 { 1142 // this header needs to be before the next one, or else there is an error 1143 Name: "dir/loophole", 1144 Typeflag: tar.TypeSymlink, 1145 Linkname: "../../victim", 1146 Mode: 0755, 1147 }, 1148 { 1149 Name: "dir/loophole/newdir/newfile", 1150 Typeflag: tar.TypeReg, 1151 Mode: 0644, 1152 }, 1153 }, 1154 } { 1155 if err := testBreakout("untar", "docker-TestUntarInvalidSymlink", headers); err != nil { 1156 t.Fatalf("i=%d. %v", i, err) 1157 } 1158 } 1159 } 1160 1161 func TestTempArchiveCloseMultipleTimes(t *testing.T) { 1162 reader := ioutil.NopCloser(strings.NewReader("hello")) 1163 tempArchive, err := NewTempArchive(reader, "") 1164 assert.NilError(t, err) 1165 buf := make([]byte, 10) 1166 n, err := tempArchive.Read(buf) 1167 assert.NilError(t, err) 1168 if n != 5 { 1169 t.Fatalf("Expected to read 5 bytes. Read %d instead", n) 1170 } 1171 for i := 0; i < 3; i++ { 1172 if err = tempArchive.Close(); err != nil { 1173 t.Fatalf("i=%d. Unexpected error closing temp archive: %v", i, err) 1174 } 1175 } 1176 } 1177 1178 // TestXGlobalNoParent is a regression test to check parent directories are not crated for PAX headers 1179 func TestXGlobalNoParent(t *testing.T) { 1180 buf := &bytes.Buffer{} 1181 w := tar.NewWriter(buf) 1182 err := w.WriteHeader(&tar.Header{ 1183 Name: "foo/bar", 1184 Typeflag: tar.TypeXGlobalHeader, 1185 }) 1186 assert.NilError(t, err) 1187 tmpDir, err := ioutil.TempDir("", "pax-test") 1188 assert.NilError(t, err) 1189 defer os.RemoveAll(tmpDir) 1190 err = Untar(buf, tmpDir, nil) 1191 assert.NilError(t, err) 1192 1193 _, err = os.Lstat(filepath.Join(tmpDir, "foo")) 1194 assert.Check(t, err != nil) 1195 assert.Check(t, errors.Is(err, os.ErrNotExist)) 1196 } 1197 1198 func TestReplaceFileTarWrapper(t *testing.T) { 1199 filesInArchive := 20 1200 testcases := []struct { 1201 doc string 1202 filename string 1203 modifier TarModifierFunc 1204 expected string 1205 fileCount int 1206 }{ 1207 { 1208 doc: "Modifier creates a new file", 1209 filename: "newfile", 1210 modifier: createModifier(t), 1211 expected: "the new content", 1212 fileCount: filesInArchive + 1, 1213 }, 1214 { 1215 doc: "Modifier replaces a file", 1216 filename: "file-2", 1217 modifier: createOrReplaceModifier, 1218 expected: "the new content", 1219 fileCount: filesInArchive, 1220 }, 1221 { 1222 doc: "Modifier replaces the last file", 1223 filename: fmt.Sprintf("file-%d", filesInArchive-1), 1224 modifier: createOrReplaceModifier, 1225 expected: "the new content", 1226 fileCount: filesInArchive, 1227 }, 1228 { 1229 doc: "Modifier appends to a file", 1230 filename: "file-3", 1231 modifier: appendModifier, 1232 expected: "fooo\nnext line", 1233 fileCount: filesInArchive, 1234 }, 1235 } 1236 1237 for _, testcase := range testcases { 1238 sourceArchive, cleanup := buildSourceArchive(t, filesInArchive) 1239 defer cleanup() 1240 1241 resultArchive := ReplaceFileTarWrapper( 1242 sourceArchive, 1243 map[string]TarModifierFunc{testcase.filename: testcase.modifier}) 1244 1245 actual := readFileFromArchive(t, resultArchive, testcase.filename, testcase.fileCount, testcase.doc) 1246 assert.Check(t, is.Equal(testcase.expected, actual), testcase.doc) 1247 } 1248 } 1249 1250 // TestPrefixHeaderReadable tests that files that could be created with the 1251 // version of this package that was built with <=go17 are still readable. 1252 func TestPrefixHeaderReadable(t *testing.T) { 1253 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 1254 skip.If(t, sys.RunningInUserNS(), "skipping test that requires more than 010000000 UIDs, which is unlikely to be satisfied when running in userns") 1255 // https://gist.github.com/stevvooe/e2a790ad4e97425896206c0816e1a882#file-out-go 1256 var testFile = []byte("\x1f\x8b\x08\x08\x44\x21\x68\x59\x00\x03\x74\x2e\x74\x61\x72\x00\x4b\xcb\xcf\x67\xa0\x35\x30\x80\x00\x86\x06\x10\x47\x01\xc1\x37\x40\x00\x54\xb6\xb1\xa1\xa9\x99\x09\x48\x25\x1d\x40\x69\x71\x49\x62\x91\x02\xe5\x76\xa1\x79\x84\x21\x91\xd6\x80\x72\xaf\x8f\x82\x51\x30\x0a\x46\x36\x00\x00\xf0\x1c\x1e\x95\x00\x06\x00\x00") 1257 1258 tmpDir, err := ioutil.TempDir("", "prefix-test") 1259 assert.NilError(t, err) 1260 defer os.RemoveAll(tmpDir) 1261 err = Untar(bytes.NewReader(testFile), tmpDir, nil) 1262 assert.NilError(t, err) 1263 1264 baseName := "foo" 1265 pth := strings.Repeat("a", 100-len(baseName)) + "/" + baseName 1266 1267 _, err = os.Lstat(filepath.Join(tmpDir, pth)) 1268 assert.NilError(t, err) 1269 } 1270 1271 func buildSourceArchive(t *testing.T, numberOfFiles int) (io.ReadCloser, func()) { 1272 srcDir, err := ioutil.TempDir("", "docker-test-srcDir") 1273 assert.NilError(t, err) 1274 1275 _, err = prepareUntarSourceDirectory(numberOfFiles, srcDir, false) 1276 assert.NilError(t, err) 1277 1278 sourceArchive, err := TarWithOptions(srcDir, &TarOptions{}) 1279 assert.NilError(t, err) 1280 return sourceArchive, func() { 1281 os.RemoveAll(srcDir) 1282 sourceArchive.Close() 1283 } 1284 } 1285 1286 func createOrReplaceModifier(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) { 1287 return &tar.Header{ 1288 Mode: 0600, 1289 Typeflag: tar.TypeReg, 1290 }, []byte("the new content"), nil 1291 } 1292 1293 func createModifier(t *testing.T) TarModifierFunc { 1294 return func(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) { 1295 assert.Check(t, is.Nil(content)) 1296 return createOrReplaceModifier(path, header, content) 1297 } 1298 } 1299 1300 func appendModifier(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) { 1301 buffer := bytes.Buffer{} 1302 if content != nil { 1303 if _, err := buffer.ReadFrom(content); err != nil { 1304 return nil, nil, err 1305 } 1306 } 1307 buffer.WriteString("\nnext line") 1308 return &tar.Header{Mode: 0600, Typeflag: tar.TypeReg}, buffer.Bytes(), nil 1309 } 1310 1311 func readFileFromArchive(t *testing.T, archive io.ReadCloser, name string, expectedCount int, doc string) string { 1312 skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") 1313 destDir, err := ioutil.TempDir("", "docker-test-destDir") 1314 assert.NilError(t, err) 1315 defer os.RemoveAll(destDir) 1316 1317 err = Untar(archive, destDir, nil) 1318 assert.NilError(t, err) 1319 1320 files, _ := ioutil.ReadDir(destDir) 1321 assert.Check(t, is.Len(files, expectedCount), doc) 1322 1323 content, err := ioutil.ReadFile(filepath.Join(destDir, name)) 1324 assert.Check(t, err) 1325 return string(content) 1326 } 1327 1328 func TestDisablePigz(t *testing.T) { 1329 _, err := exec.LookPath("unpigz") 1330 if err != nil { 1331 t.Log("Test will not check full path when Pigz not installed") 1332 } 1333 1334 os.Setenv("MOBY_DISABLE_PIGZ", "true") 1335 defer os.Unsetenv("MOBY_DISABLE_PIGZ") 1336 1337 r := testDecompressStream(t, "gz", "gzip -f") 1338 // For the bufio pool 1339 outsideReaderCloserWrapper := r.(*ioutils.ReadCloserWrapper) 1340 // For the context canceller 1341 contextReaderCloserWrapper := outsideReaderCloserWrapper.Reader.(*ioutils.ReadCloserWrapper) 1342 1343 assert.Equal(t, reflect.TypeOf(contextReaderCloserWrapper.Reader), reflect.TypeOf(&gzip.Reader{})) 1344 } 1345 1346 func TestPigz(t *testing.T) { 1347 r := testDecompressStream(t, "gz", "gzip -f") 1348 // For the bufio pool 1349 outsideReaderCloserWrapper := r.(*ioutils.ReadCloserWrapper) 1350 // For the context canceller 1351 contextReaderCloserWrapper := outsideReaderCloserWrapper.Reader.(*ioutils.ReadCloserWrapper) 1352 1353 _, err := exec.LookPath("unpigz") 1354 if err == nil { 1355 t.Log("Tested whether Pigz is used, as it installed") 1356 // For the command wait wrapper 1357 cmdWaitCloserWrapper := contextReaderCloserWrapper.Reader.(*ioutils.ReadCloserWrapper) 1358 assert.Equal(t, reflect.TypeOf(cmdWaitCloserWrapper.Reader), reflect.TypeOf(&io.PipeReader{})) 1359 } else { 1360 t.Log("Tested whether Pigz is not used, as it not installed") 1361 assert.Equal(t, reflect.TypeOf(contextReaderCloserWrapper.Reader), reflect.TypeOf(&gzip.Reader{})) 1362 } 1363 }