| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- package main
- import (
- "archive/tar"
- "io"
- "path/filepath"
- "strings"
- "github.com/concourse/go-archive/tarfs"
- "github.com/google/go-containerregistry/pkg/v1"
- )
- const whiteoutPrefix = ".wh."
- func unpackImage(dest string, img v1.Image) error {
- layers, err := img.Layers()
- if err != nil {
- return err
- }
- written := map[string]struct{}{}
- removed := map[string]struct{}{}
- // iterate over layers in reverse order; no need to write things files that
- // are modified by later layers anyway
- for i := len(layers) - 1; i >= 0; i-- {
- layer := layers[i]
- r, err := layer.Uncompressed()
- if err != nil {
- return err
- }
- tr := tar.NewReader(r)
- for {
- hdr, err := tr.Next()
- if err == io.EOF {
- break
- }
- if err != nil {
- return err
- }
- path := filepath.Join(dest, filepath.Clean(hdr.Name))
- base := filepath.Base(path)
- dir := filepath.Dir(path)
- if strings.HasPrefix(base, whiteoutPrefix) {
- // layer has marked a file as deleted
- name := strings.TrimPrefix(base, whiteoutPrefix)
- removed[filepath.Join(dir, name)] = struct{}{}
- continue
- }
- if pathIsRemoved(path, removed) {
- // path has been removed by lower layer
- continue
- }
- if _, ok := written[path]; ok {
- // path has already been written by lower layer
- continue
- }
- written[path] = struct{}{}
- if err := tarfs.ExtractEntry(hdr, dest, tr, true); err != nil {
- return err
- }
- }
- }
- return nil
- }
- func pathIsRemoved(path string, removed map[string]struct{}) bool {
- if _, ok := removed[path]; ok {
- return true
- }
- // check if parent dir has been removed
- for wd := range removed {
- if strings.HasPrefix(path, wd) {
- return true
- }
- }
- return false
- }
|