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