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