@@ -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 .getDigest () != 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,30 @@ 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+ // After calling filepath.Clean(hdr.Name) above, hdr.Name will now be in
154+ // the filepath format for the OS on which the daemon is running. Hence
155+ // the check for a slash-suffix MUST be done in an OS-agnostic way.
156+ if ! strings .HasSuffix (hdr .Name , string (os .PathSeparator )) {
157+ // Not the root directory, ensure that the parent directory exists
158+ parent := filepath .Dir (hdr .Name )
159+ parentPath := filepath .Join (dest , parent )
160+ if _ , err := os .Lstat (parentPath ); err != nil && os .IsNotExist (err ) {
161+ err = os .MkdirAll (parentPath , 0777 )
162+ if err != nil {
163+ return err
164+ }
165+ }
166+ }
167+ path := filepath .Join (dest , hdr .Name )
168+ rel , err := filepath .Rel (dest , path )
169+ if err != nil {
170+ return err
171+ }
152172 info := hdr .FileInfo ()
173+ if strings .HasPrefix (rel , ".." + string (os .PathSeparator )) {
174+ return fmt .Errorf ("%q is outside of %q" , hdr .Name , dest )
175+ }
153176
154177 if strings .HasPrefix (info .Name (), ".wh." ) {
155178 path = strings .Replace (path , ".wh." , "" , 1 )
@@ -163,12 +186,14 @@ loop:
163186
164187 switch hdr .Typeflag {
165188 case tar .TypeDir :
166- if err := os .MkdirAll (path , info .Mode ()); err != nil {
167- return errors .Wrap (err , "error creating directory" )
189+ if fi , err := os .Lstat (path ); ! (err == nil && fi .IsDir ()) {
190+ if err := os .MkdirAll (path , info .Mode ()); err != nil {
191+ return errors .Wrap (err , "error creating directory" )
192+ }
168193 }
169194
170195 case tar .TypeReg , tar .TypeRegA :
171- f , err := os .OpenFile (path , os .O_CREATE | os .O_WRONLY | os . O_TRUNC , info .Mode ())
196+ f , err := os .OpenFile (path , os .O_CREATE | os .O_WRONLY , info .Mode ())
172197 if err != nil {
173198 return errors .Wrap (err , "unable to open file" )
174199 }
@@ -200,13 +225,24 @@ loop:
200225 if err := os .Symlink (hdr .Linkname , path ); err != nil {
201226 return err
202227 }
203-
228+ case tar .TypeXGlobalHeader :
229+ return nil
230+ }
231+ // Directory mtimes must be handled at the end to avoid further
232+ // file creation in them to modify the directory mtime
233+ if hdr .Typeflag == tar .TypeDir {
234+ dirs = append (dirs , hdr )
204235 }
236+ }
237+ for _ , hdr := range dirs {
238+ path := filepath .Join (dest , hdr .Name )
205239
206- if err := os .Chtimes (path , time .Now ().UTC (), info .ModTime ()); err != nil {
240+ finfo := hdr .FileInfo ()
241+ // I believe the old version was using time.Now().UTC() to overcome an
242+ // invalid error from chtimes.....but here we lose hdr.AccessTime like this...
243+ if err := os .Chtimes (path , time .Now ().UTC (), finfo .ModTime ()); err != nil {
207244 return errors .Wrap (err , "error changing time" )
208245 }
209246 }
210-
211247 return nil
212248}
0 commit comments