Skip to content

Commit

Permalink
entry: convert hard link absolute paths to relative ones
Browse files Browse the repository at this point in the history
When unpacking a docker image, some hard links may be pointing to, say,
/bin/gunzip. Those were failing the validate_inside_dst() check,
preventing to unpack the image.

Fix this by re-using the same logic as we are already using for actual
files, removing the leading '/' from the link target path.

We do this only if the absolute link is not an ancestor of the target
dir as we want to support this use case according to the existing
absolute_hardlink() test.
  • Loading branch information
Guillaume Desmottes committed May 25, 2021
1 parent 7f2a355 commit 745659b
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/entry.rs
Expand Up @@ -467,6 +467,9 @@ impl<'a> EntryFields<'a> {
// the destination of this hard link is both present and
// inside our own directory. This is needed because we want
// to make sure to not overwrite anything outside the root.
// If the link points to an absolute path, assume it's relative
// to the target dir by removing the leading '/', as we already
// do for files, see unpack_in() comments.
//
// Note that this logic is only needed for hard links
// currently. With symlinks the `validate_inside_dst` which
Expand All @@ -475,6 +478,16 @@ impl<'a> EntryFields<'a> {
// links though they're canonicalized to their existing path
// so we need to validate at this time.
Some(ref p) => {
let mut src = src.to_path_buf();
if src.is_absolute() {
let dest_canon = p.canonicalize()?;
if !src.starts_with(&dest_canon) {
// Skip root component, making the target relative to the target dir
let components = src.components().skip(1);
src = components.collect();
}
}

let link_src = p.join(src);
self.validate_inside_dst(p, &link_src)?;
link_src
Expand Down
10 changes: 10 additions & 0 deletions tests/entry.rs
Expand Up @@ -62,12 +62,22 @@ fn absolute_hardlink() {
header.set_cksum();
t!(ar.append(&header, &[][..]));

let mut header = tar::Header::new_gnu();
header.set_size(0);
header.set_entry_type(tar::EntryType::Link);
t!(header.set_path("baz"));
// This absolute path on root will be converted when unpacking
t!(header.set_link_name("/foo"));
header.set_cksum();
t!(ar.append(&header, &[][..]));

let bytes = t!(ar.into_inner());
let mut ar = tar::Archive::new(&bytes[..]);

t!(ar.unpack(td.path()));
t!(td.path().join("foo").metadata());
t!(td.path().join("bar").metadata());
t!(td.path().join("baz").metadata());
}

#[test]
Expand Down

0 comments on commit 745659b

Please sign in to comment.