@@ -205,9 +205,11 @@ func (d *dir) fullPath() string {
205
205
}
206
206
207
207
// tarballToTree converts a tarball into a complete filesystem tree.
208
- func (d * dir ) tarballToTree (tarball io.Reader ) error {
208
+ func (d * dir ) tarballToTree (tarball io.Reader ) ( err error ) {
209
209
d .fetchFn = nil
210
210
tr := tar .NewReader (tarball )
211
+
212
+ var list []* tar.Header
211
213
for {
212
214
hdr , err := tr .Next ()
213
215
if err == io .EOF {
@@ -216,7 +218,6 @@ func (d *dir) tarballToTree(tarball io.Reader) error {
216
218
if err != nil {
217
219
return err
218
220
}
219
-
220
221
switch hdr .Typeflag {
221
222
case tar .TypeReg :
222
223
path , filename := filepath .Split (hdr .Name )
@@ -237,34 +238,60 @@ func (d *dir) tarballToTree(tarball io.Reader) error {
237
238
d .makeDirs (parts , withDirModTime (hdr .ModTime ))
238
239
}
239
240
case tar .TypeLink , tar .TypeSymlink :
240
- insertPoint := d .fullPath ()
241
- targetPathOnly , _ := filepath .Split (hdr .Name )
242
- targetParts := tarSplitPath (targetPathOnly )
243
- target := filepath .Clean (insertPoint + "/" + strings .Join (targetParts , "/" ) + "/" + hdr .Linkname )
244
- linkname := filepath .Clean (insertPoint + "/" + strings .Join (tarSplitPath (hdr .Name ), "/" ))
245
-
246
- linknamePath , linknameFile := filepath .Split (linkname )
247
- linknamePath = filepath .Clean (linknamePath )
241
+ list = append (list , hdr )
242
+ }
243
+ }
248
244
249
- targetDir , targetFile , err := d .gfs .root .find (target )
250
- if err != nil {
251
- return err
252
- }
253
- linknameDir , _ , err := d .gfs .root .find (linknamePath )
254
- if err != nil {
255
- return err
256
- }
257
- if nil != targetFile {
258
- linknameDir .children [filepath .Base (linkname )] = targetFile
259
- } else {
260
- linknameDir .children [linknameFile ] = targetDir
261
- }
245
+ // Try to add symlinks from the list until either they are all applied or
246
+ // no progress is made.
247
+ last := len (list )
248
+ for len (list ) > 0 {
249
+ list , err = d .processTarballList (list )
250
+ if err != nil {
251
+ return err
262
252
}
253
+ if len (list ) == last {
254
+ return fmt .Errorf ("unable to link to %s" , list [0 ].Linkname )
255
+ }
256
+ last = len (list )
263
257
}
264
258
265
259
return nil
266
260
}
267
261
262
+ // processTarballList takes a list of symlinks and tries to create what it can.
263
+ // Whatever it can't is returned for further processing.
264
+ func (d * dir ) processTarballList (list []* tar.Header ) (later []* tar.Header , err error ) {
265
+ for _ , hdr := range list {
266
+ insertPoint := d .fullPath ()
267
+ targetPathOnly , _ := filepath .Split (hdr .Name )
268
+ targetParts := tarSplitPath (targetPathOnly )
269
+ target := filepath .Clean (insertPoint + "/" + strings .Join (targetParts , "/" ) + "/" + hdr .Linkname )
270
+ linkname := filepath .Clean (insertPoint + "/" + strings .Join (tarSplitPath (hdr .Name ), "/" ))
271
+
272
+ linknamePath , linknameFile := filepath .Split (linkname )
273
+ linknamePath = filepath .Clean (linknamePath )
274
+
275
+ targetDir , targetFile , err := d .gfs .root .find (target )
276
+ if err != nil {
277
+ later = append (later , hdr )
278
+ continue
279
+ }
280
+ linknameDir , _ , err := d .gfs .root .find (linknamePath )
281
+ if err != nil {
282
+ later = append (later , hdr )
283
+ continue
284
+ }
285
+ if nil != targetFile {
286
+ linknameDir .children [filepath .Base (linkname )] = targetFile
287
+ } else {
288
+ linknameDir .children [linknameFile ] = targetDir
289
+ }
290
+ }
291
+
292
+ return later , nil
293
+ }
294
+
268
295
// fetch fetches the information about the directory and removes the fetch function
269
296
// so that it's not fetched again.
270
297
func (d * dir ) fetch () error {
0 commit comments