archive.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "path/filepath"
  7. "github.com/docker/docker/pkg/archive"
  8. "github.com/docker/docker/pkg/idtools"
  9. )
  10. // NewArchiver returns a new Archiver which uses chrootarchive.Untar
  11. func NewArchiver(idMapping idtools.IdentityMapping) *archive.Archiver {
  12. return &archive.Archiver{
  13. Untar: Untar,
  14. IDMapping: idMapping,
  15. }
  16. }
  17. // Untar reads a stream of bytes from `archive`, parses it as a tar archive,
  18. // and unpacks it into the directory at `dest`.
  19. // The archive may be compressed with one of the following algorithms:
  20. // identity (uncompressed), gzip, bzip2, xz.
  21. func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
  22. return untarHandler(tarArchive, dest, options, true, dest)
  23. }
  24. // UntarWithRoot is the same as `Untar`, but allows you to pass in a root directory
  25. // The root directory is the directory that will be chrooted to.
  26. // `dest` must be a path within `root`, if it is not an error will be returned.
  27. //
  28. // `root` should set to a directory which is not controlled by any potentially
  29. // malicious process.
  30. //
  31. // This should be used to prevent a potential attacker from manipulating `dest`
  32. // such that it would provide access to files outside of `dest` through things
  33. // like symlinks. Normally `ResolveSymlinksInScope` would handle this, however
  34. // sanitizing symlinks in this manner is inherrently racey:
  35. // ref: CVE-2018-15664
  36. func UntarWithRoot(tarArchive io.Reader, dest string, options *archive.TarOptions, root string) error {
  37. return untarHandler(tarArchive, dest, options, true, root)
  38. }
  39. // UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive,
  40. // and unpacks it into the directory at `dest`.
  41. // The archive must be an uncompressed stream.
  42. func UntarUncompressed(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
  43. return untarHandler(tarArchive, dest, options, false, dest)
  44. }
  45. // Handler for teasing out the automatic decompression
  46. func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool, root string) error {
  47. if tarArchive == nil {
  48. return fmt.Errorf("Empty archive")
  49. }
  50. if options == nil {
  51. options = &archive.TarOptions{}
  52. }
  53. if options.ExcludePatterns == nil {
  54. options.ExcludePatterns = []string{}
  55. }
  56. // If dest is inside a root then directory is created within chroot by extractor.
  57. // This case is only currently used by cp.
  58. if dest == root {
  59. rootIDs := options.IDMap.RootPair()
  60. dest = filepath.Clean(dest)
  61. if _, err := os.Stat(dest); os.IsNotExist(err) {
  62. if err := idtools.MkdirAllAndChownNew(dest, 0o755, rootIDs); err != nil {
  63. return err
  64. }
  65. }
  66. }
  67. r := io.NopCloser(tarArchive)
  68. if decompress {
  69. decompressedArchive, err := archive.DecompressStream(tarArchive)
  70. if err != nil {
  71. return err
  72. }
  73. defer decompressedArchive.Close()
  74. r = decompressedArchive
  75. }
  76. return invokeUnpack(r, dest, options, root)
  77. }
  78. // Tar tars the requested path while chrooted to the specified root.
  79. func Tar(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) {
  80. if options == nil {
  81. options = &archive.TarOptions{}
  82. }
  83. return invokePack(srcPath, options, root)
  84. }