@@ -38,7 +38,7 @@ type manifest struct {
3838
3939func findManifest (w walker , d * descriptor ) (* manifest , error ) {
4040 var m manifest
41- mpath := filepath .Join ("blobs" , d .getDigest ())
41+ mpath := filepath .Join ("blobs" , d .normalizeDigest ())
4242
4343 f := func (path string , info os.FileInfo , r io.Reader ) error {
4444 if info .IsDir () {
@@ -107,7 +107,7 @@ func (m *manifest) unpack(w walker, dest string) error {
107107 }
108108
109109 dd , err := filepath .Rel ("blobs" , filepath .Clean (path ))
110- if err != nil || d .Digest != dd {
110+ if err != nil || d .normalizeDigest () != dd {
111111 return nil // ignore
112112 }
113113
@@ -134,6 +134,7 @@ func unpackLayer(dest string, r io.Reader) error {
134134 }
135135 defer gz .Close ()
136136
137+ var dirs []* tar.Header
137138 tr := tar .NewReader (gz )
138139
139140loop:
@@ -148,8 +149,26 @@ loop:
148149 return errors .Wrapf (err , "error advancing tar stream" )
149150 }
150151
151- path := filepath .Join (dest , filepath .Clean (hdr .Name ))
152+ hdr .Name = filepath .Clean (hdr .Name )
153+ if ! strings .HasSuffix (hdr .Name , string (os .PathSeparator )) {
154+ // Not the root directory, ensure that the parent directory exists
155+ parent := filepath .Dir (hdr .Name )
156+ parentPath := filepath .Join (dest , parent )
157+ if _ , err := os .Lstat (parentPath ); err != nil && os .IsNotExist (err ) {
158+ if err2 := os .MkdirAll (parentPath , 0777 ); err2 != nil {
159+ return err2
160+ }
161+ }
162+ }
163+ path := filepath .Join (dest , hdr .Name )
164+ rel , err := filepath .Rel (dest , path )
165+ if err != nil {
166+ return err
167+ }
152168 info := hdr .FileInfo ()
169+ if strings .HasPrefix (rel , ".." + string (os .PathSeparator )) {
170+ return fmt .Errorf ("%q is outside of %q" , hdr .Name , dest )
171+ }
153172
154173 if strings .HasPrefix (info .Name (), ".wh." ) {
155174 path = strings .Replace (path , ".wh." , "" , 1 )
@@ -163,12 +182,14 @@ loop:
163182
164183 switch hdr .Typeflag {
165184 case tar .TypeDir :
166- if err := os .MkdirAll (path , info .Mode ()); err != nil {
167- return errors .Wrap (err , "error creating directory" )
185+ if fi , err := os .Lstat (path ); ! (err == nil && fi .IsDir ()) {
186+ if err2 := os .MkdirAll (path , info .Mode ()); err2 != nil {
187+ return errors .Wrap (err2 , "error creating directory" )
188+ }
168189 }
169190
170191 case tar .TypeReg , tar .TypeRegA :
171- f , err := os .OpenFile (path , os .O_CREATE | os .O_WRONLY | os . O_TRUNC , info .Mode ())
192+ f , err := os .OpenFile (path , os .O_CREATE | os .O_WRONLY , info .Mode ())
172193 if err != nil {
173194 return errors .Wrap (err , "unable to open file" )
174195 }
@@ -200,13 +221,24 @@ loop:
200221 if err := os .Symlink (hdr .Linkname , path ); err != nil {
201222 return err
202223 }
203-
224+ case tar .TypeXGlobalHeader :
225+ return nil
226+ }
227+ // Directory mtimes must be handled at the end to avoid further
228+ // file creation in them to modify the directory mtime
229+ if hdr .Typeflag == tar .TypeDir {
230+ dirs = append (dirs , hdr )
204231 }
232+ }
233+ for _ , hdr := range dirs {
234+ path := filepath .Join (dest , hdr .Name )
205235
206- if err := os .Chtimes (path , time .Now ().UTC (), info .ModTime ()); err != nil {
236+ finfo := hdr .FileInfo ()
237+ // I believe the old version was using time.Now().UTC() to overcome an
238+ // invalid error from chtimes.....but here we lose hdr.AccessTime like this...
239+ if err := os .Chtimes (path , time .Now ().UTC (), finfo .ModTime ()); err != nil {
207240 return errors .Wrap (err , "error changing time" )
208241 }
209242 }
210-
211243 return nil
212244}
0 commit comments