Skip to content

Commit 62dc60d

Browse files
committed
Fix a bug where if the symlinks are applied in the wrong order they are broken. Now the ordering of the files/symlinks shouldn't matter.
1 parent 1de2fcb commit 62dc60d

File tree

1 file changed

+50
-23
lines changed

1 file changed

+50
-23
lines changed

dir.go

+50-23
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,11 @@ func (d *dir) fullPath() string {
205205
}
206206

207207
// 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) {
209209
d.fetchFn = nil
210210
tr := tar.NewReader(tarball)
211+
212+
var list []*tar.Header
211213
for {
212214
hdr, err := tr.Next()
213215
if err == io.EOF {
@@ -216,7 +218,6 @@ func (d *dir) tarballToTree(tarball io.Reader) error {
216218
if err != nil {
217219
return err
218220
}
219-
220221
switch hdr.Typeflag {
221222
case tar.TypeReg:
222223
path, filename := filepath.Split(hdr.Name)
@@ -237,34 +238,60 @@ func (d *dir) tarballToTree(tarball io.Reader) error {
237238
d.makeDirs(parts, withDirModTime(hdr.ModTime))
238239
}
239240
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+
}
248244

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
262252
}
253+
if len(list) == last {
254+
return fmt.Errorf("unable to link to %s", list[0].Linkname)
255+
}
256+
last = len(list)
263257
}
264258

265259
return nil
266260
}
267261

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+
268295
// fetch fetches the information about the directory and removes the fetch function
269296
// so that it's not fetched again.
270297
func (d *dir) fetch() error {

0 commit comments

Comments
 (0)