|
|
@@ -30,91 +30,98 @@ func unpackImage(dest string, img v1.Image) error {
|
|
|
// 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]
|
|
|
-
|
|
|
- size, err := layer.Size()
|
|
|
+ err := extractLayer(dest, layers[i], written, removed, chown)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- digest, err := layer.Digest()
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func extractLayer(dest string, layer v1.Layer, written, removed map[string]struct{}, chown bool) error {
|
|
|
+ size, err := layer.Size()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ digest, err := layer.Digest()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ r, err := layer.Compressed()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ bar := pb.New64(size).SetUnits(pb.U_BYTES)
|
|
|
+ bar.Output = os.Stderr
|
|
|
+ bar.Prefix(digest.Hex[0:12])
|
|
|
+ bar.SetWidth(98)
|
|
|
+
|
|
|
+ bar.Start()
|
|
|
+ defer bar.Finish()
|
|
|
|
|
|
- bar := pb.New64(size).SetUnits(pb.U_BYTES)
|
|
|
- bar.Output = os.Stderr
|
|
|
- bar.Prefix(digest.Hex[0:12])
|
|
|
+ gr, err := gzip.NewReader(bar.NewProxyReader(r))
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ tr := tar.NewReader(gr)
|
|
|
+
|
|
|
+ for {
|
|
|
+ hdr, err := tr.Next()
|
|
|
+ if err == io.EOF {
|
|
|
+ break
|
|
|
+ }
|
|
|
|
|
|
- r, err := layer.Compressed()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- bar.Start()
|
|
|
+ path := filepath.Join(dest, filepath.Clean(hdr.Name))
|
|
|
+ base := filepath.Base(path)
|
|
|
+ dir := filepath.Dir(path)
|
|
|
|
|
|
- gr, err := gzip.NewReader(bar.NewProxyReader(r))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
+ if strings.HasPrefix(base, whiteoutPrefix) {
|
|
|
+ // layer has marked a file as deleted
|
|
|
+ name := strings.TrimPrefix(base, whiteoutPrefix)
|
|
|
+ removed[filepath.Join(dir, name)] = struct{}{}
|
|
|
+ continue
|
|
|
}
|
|
|
|
|
|
- tr := tar.NewReader(gr)
|
|
|
-
|
|
|
- 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 hdr.Typeflag == tar.TypeBlock || hdr.Typeflag == tar.TypeChar {
|
|
|
- // devices can't be created in a user namespace
|
|
|
- logrus.Infof("skipping device %s", hdr.Name)
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- if err := tarfs.ExtractEntry(hdr, dest, tr, chown); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ if pathIsRemoved(path, removed) {
|
|
|
+ // path has been removed by lower layer
|
|
|
+ continue
|
|
|
}
|
|
|
|
|
|
- err = gr.Close()
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
+ if _, ok := written[path]; ok {
|
|
|
+ // path has already been written by lower layer
|
|
|
+ continue
|
|
|
}
|
|
|
|
|
|
- err = r.Close()
|
|
|
- if err != nil {
|
|
|
+ written[path] = struct{}{}
|
|
|
+
|
|
|
+ if hdr.Typeflag == tar.TypeBlock || hdr.Typeflag == tar.TypeChar {
|
|
|
+ // devices can't be created in a user namespace
|
|
|
+ logrus.Debugf("skipping device %s", hdr.Name)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := tarfs.ExtractEntry(hdr, dest, tr, chown); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ err = gr.Close()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
|
|
|
- bar.Finish()
|
|
|
+ err = r.Close()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
return nil
|