@@ -17,13 +17,15 @@ limitations under the License.
1717package util
1818
1919import (
20+ "archive/tar"
21+ "bytes"
2022 "context"
2123 "encoding/json"
22- "errors"
2324 "fmt"
25+ "io"
2426 "io/ioutil"
2527 "os"
26- "path/filepath "
28+ "strings "
2729
2830 "github.com/docker/docker/client"
2931 "github.com/golang/glog"
@@ -49,56 +51,73 @@ func NewClient() (*client.Client, error) {
4951 return cli , nil
5052}
5153
52- func getLayersFromManifest (manifestPath string ) ([]string , error ) {
54+ func getLayersFromManifest (r io. Reader ) ([]string , error ) {
5355 type Manifest struct {
5456 Layers []string
5557 }
5658
57- manifestJSON , err := ioutil .ReadFile ( manifestPath )
59+ manifestJSON , err := ioutil .ReadAll ( r )
5860 if err != nil {
59- errMsg := fmt .Sprintf ("Could not open manifest to get layer order: %s" , err )
60- return []string {}, errors .New (errMsg )
61+ return nil , err
6162 }
6263
6364 var imageManifest []Manifest
64- err = json .Unmarshal (manifestJSON , & imageManifest )
65- if err != nil {
66- errMsg := fmt .Sprintf ("Could not unmarshal manifest to get layer order: %s" , err )
67- return []string {}, errors .New (errMsg )
65+ if err := json .Unmarshal (manifestJSON , & imageManifest ); err != nil {
66+ return []string {}, fmt .Errorf ("Could not unmarshal manifest to get layer order: %s" , err )
6867 }
6968 return imageManifest [0 ].Layers , nil
7069}
7170
7271func unpackDockerSave (tarPath string , target string ) error {
7372 if _ , ok := os .Stat (target ); ok != nil {
74- os .MkdirAll (target , 0777 )
73+ os .MkdirAll (target , 0644 )
7574 }
76-
77- tempLayerDir , err := ioutil .TempDir ("" , ".container-diff" )
75+ f , err := os .Open (tarPath )
7876 if err != nil {
7977 return err
8078 }
81- defer os .RemoveAll (tempLayerDir )
8279
83- if err := UnTar (tarPath , tempLayerDir ); err != nil {
84- errMsg := fmt .Sprintf ("Could not unpack saved Docker image %s: %s" , tarPath , err )
85- return errors .New (errMsg )
86- }
80+ tr := tar .NewReader (f )
8781
88- manifest := filepath .Join (tempLayerDir , "manifest.json" )
89- layers , err := getLayersFromManifest (manifest )
90- if err != nil {
91- return err
92- }
82+ // Unpack the layers into a map, since we need to sort out the order later.
83+ var layers []string
84+ layerMap := map [string ][]byte {}
85+ for {
86+ hdr , err := tr .Next ()
87+ if err == io .EOF {
88+ break
89+ }
90+ if err != nil {
91+ return err
92+ }
9393
94- for _ , layer := range layers {
95- layerTar := filepath .Join (tempLayerDir , layer )
96- if _ , err := os .Stat (layerTar ); err != nil {
97- glog .Infof ("Did not unpack layer %s because no layer.tar found" , layer )
94+ // Docker save contains files and directories. Ignore the directories.
95+ // We care about the layers and the manifest. The layers look like:
96+ // $SHA/layer.tar
97+ // and they are referenced that way in the manifest.
98+ switch t := hdr .Typeflag ; t {
99+ case tar .TypeReg :
100+ if hdr .Name == "manifest.json" {
101+ layers , err = getLayersFromManifest (tr )
102+ if err != nil {
103+ return err
104+ }
105+ } else if strings .HasSuffix (hdr .Name , ".tar" ) {
106+ layerMap [hdr .Name ], err = ioutil .ReadAll (tr )
107+ if err != nil {
108+ return err
109+ }
110+ }
111+ case tar .TypeDir :
98112 continue
113+ default :
114+ return fmt .Errorf ("unsupported file type %v found in file %s tar %s" , t , hdr .Name , tarPath )
99115 }
100- if err = UnTar (layerTar , target ); err != nil {
101- glog .Errorf ("Could not unpack layer %s: %s" , layer , err )
116+ }
117+
118+ for _ , layer := range layers {
119+ if err = UnTar (bytes .NewReader (layerMap [layer ]), target ); err != nil {
120+ return fmt .Errorf ("Could not unpack layer %s: %s" , layer , err )
102121 }
103122 }
104123 return nil
0 commit comments