Skip to content

Commit

Permalink
Ensure files and links have missing parent dirs created. Support hard…
Browse files Browse the repository at this point in the history
… links

Signed-off-by: Jose R. Gonzalez <komish@flutes.dev>
  • Loading branch information
komish committed Jun 26, 2024
1 parent b926353 commit 69a1942
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 3 deletions.
41 changes: 38 additions & 3 deletions internal/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,13 @@ func untar(ctx context.Context, dst string, r io.Reader) error {

// if it's a file create it
case tar.TypeReg:
// If the file's parent dir doesn't exist, create it.
dirname := filepath.Dir(target)
if _, err := os.Stat(dirname); err != nil {
if err := os.MkdirAll(dirname, 0o755); err != nil {
return err
}
}
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return err
Expand All @@ -428,16 +435,44 @@ func untar(ctx context.Context, dst string, r io.Reader) error {
f.Close()

// if it's a link create it
case tar.TypeSymlink:
err := os.Symlink(header.Linkname, filepath.Join(dst, header.Name))
case tar.TypeSymlink, tar.TypeLink:
ln := os.Symlink
if header.Typeflag == tar.TypeLink {
ln = os.Link
}

oldNoBase, newNoBase := resolveLinkPaths(header.Linkname, header.Name)
old := filepath.Join(dst, oldNoBase)
new := filepath.Join(dst, newNoBase)
// Create the new link's directory if it doesn't exist.
dirname := filepath.Dir(new)
if _, err := os.Stat(dirname); err != nil {
if err := os.MkdirAll(dirname, 0o755); err != nil {
return err
}
}
err := ln(old, new)
if err != nil {
logger.V(log.DBG).Info(fmt.Sprintf("Error creating link: %s. Ignoring.", header.Name))
logger.V(log.DBG).Info(fmt.Sprintf("Error creating link: %s. Ignoring.", header.Name), "link", new, "linkedTo", old)
continue
}
}
}
}

// resolveLinkPaths resolves relative pathing in linkname relative to filename.
// Relative link names are resolved such that the linkname is no longer relative
// to filename. E.g etc/c/foo -> ../../usr/lib/foo would become etc/c/foo and usr/lib/foo,
// making both paths relative to their shared base.
func resolveLinkPaths(oldname, newname string) (string, string) {
if !strings.HasPrefix(oldname, "..") {
return oldname, newname
}

linkDir := filepath.Dir(newname)
return filepath.Join(linkDir, oldname), newname
}

// writeCertImage takes imageRef and writes it to disk as JSON representing a pyxis.CertImage
// struct. The file is written at path certification.DefaultCertImageFilename.
//
Expand Down
14 changes: 14 additions & 0 deletions internal/engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,20 @@ var _ = Describe("Check Name Queries", func() {
})
})

var _ = Describe("Link Path Resolution", func() {
DescribeTable(
"Link targets should resolve correctly",
func(old, new, expectedOld, expectedNew string) {
resO, resN := resolveLinkPaths(old, new)
Expect(resO).To(Equal(expectedOld))
Expect(resN).To(Equal(expectedNew))
},
Entry("Up one level", "../var/lib/file", "etc/file", "var/lib/file", "etc/file"),
Entry("Up two levels", "../../var/lib/file", "etc/foo/file", "var/lib/file", "etc/foo/file"),
Entry("Up no levels", "var/lib/file", "etc/file", "var/lib/file", "etc/file"),
)
})

// writeTarball writes a tar archive to out with filename containing contents at the base path
// with extra bytes written at the end of length extraBytes.
// note: this should only be used as a helper function in tests
Expand Down

0 comments on commit 69a1942

Please sign in to comment.