unpack.go 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package main
  2. import (
  3. "archive/tar"
  4. "io"
  5. "path/filepath"
  6. "strings"
  7. "github.com/concourse/go-archive/tarfs"
  8. "github.com/google/go-containerregistry/pkg/v1"
  9. )
  10. const whiteoutPrefix = ".wh."
  11. func unpackImage(dest string, img v1.Image) error {
  12. layers, err := img.Layers()
  13. if err != nil {
  14. return err
  15. }
  16. written := map[string]struct{}{}
  17. removed := map[string]struct{}{}
  18. // iterate over layers in reverse order; no need to write things files that
  19. // are modified by later layers anyway
  20. for i := len(layers) - 1; i >= 0; i-- {
  21. layer := layers[i]
  22. r, err := layer.Uncompressed()
  23. if err != nil {
  24. return err
  25. }
  26. tr := tar.NewReader(r)
  27. for {
  28. hdr, err := tr.Next()
  29. if err == io.EOF {
  30. break
  31. }
  32. if err != nil {
  33. return err
  34. }
  35. path := filepath.Join(dest, filepath.Clean(hdr.Name))
  36. base := filepath.Base(path)
  37. dir := filepath.Dir(path)
  38. if strings.HasPrefix(base, whiteoutPrefix) {
  39. // layer has marked a file as deleted
  40. name := strings.TrimPrefix(base, whiteoutPrefix)
  41. removed[filepath.Join(dir, name)] = struct{}{}
  42. continue
  43. }
  44. if pathIsRemoved(path, removed) {
  45. // path has been removed by lower layer
  46. continue
  47. }
  48. if _, ok := written[path]; ok {
  49. // path has already been written by lower layer
  50. continue
  51. }
  52. written[path] = struct{}{}
  53. if err := tarfs.ExtractEntry(hdr, dest, tr, true); err != nil {
  54. return err
  55. }
  56. }
  57. }
  58. return nil
  59. }
  60. func pathIsRemoved(path string, removed map[string]struct{}) bool {
  61. if _, ok := removed[path]; ok {
  62. return true
  63. }
  64. // check if parent dir has been removed
  65. for wd := range removed {
  66. if strings.HasPrefix(path, wd) {
  67. return true
  68. }
  69. }
  70. return false
  71. }