Skip to content

Commit 40431e8

Browse files
authored
Merge pull request #680 from cgwalters/fix-hardlinked-sysroot-absolute
tar: Handle absolute hardlinked paths to sysroot
2 parents 003039d + f3c1c15 commit 40431e8

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

lib/src/tar/write.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ pub(crate) fn filter_tar(
272272
let header = entry.header();
273273
let path = entry.path()?;
274274
let path: &Utf8Path = (&*path).try_into()?;
275+
// Force all paths to relative
276+
let path = path.strip_prefix("/").unwrap_or(path);
275277

276278
let is_modified = header.mtime().unwrap_or_default() > 0;
277279
let is_regular = header.entry_type() == tar::EntryType::Regular;
@@ -302,6 +304,8 @@ pub(crate) fn filter_tar(
302304
.link_name()?
303305
.ok_or_else(|| anyhow!("Invalid empty hardlink"))?;
304306
let target: &Utf8Path = (&*target).try_into()?;
307+
// Canonicalize to a relative path
308+
let target = path.strip_prefix("/").unwrap_or(target);
305309
// If this is a hardlink into /sysroot...
306310
if target.strip_prefix(crate::tar::REPO_PREFIX).is_ok() {
307311
// And we found a previously processed modified file there

lib/tests/it/main.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,7 +1132,16 @@ async fn test_container_var_content() -> Result<()> {
11321132
}
11331133

11341134
#[tokio::test]
1135-
async fn test_container_etc_hardlinked() -> Result<()> {
1135+
async fn test_container_etc_hardlinked_absolute() -> Result<()> {
1136+
test_container_etc_hardlinked(true).await
1137+
}
1138+
1139+
#[tokio::test]
1140+
async fn test_container_etc_hardlinked_relative() -> Result<()> {
1141+
test_container_etc_hardlinked(false).await
1142+
}
1143+
1144+
async fn test_container_etc_hardlinked(absolute: bool) -> Result<()> {
11361145
let fixture = Fixture::new_v1()?;
11371146

11381147
let imgref = fixture.export_container().await.unwrap().0;
@@ -1174,6 +1183,7 @@ async fn test_container_etc_hardlinked() -> Result<()> {
11741183
// Another case where we have /etc/dnf.conf and a hardlinked /ostree/repo/objects
11751184
// link into it - in this case we should ignore the hardlinked one.
11761185
let testdata = "hardlinked into object store";
1186+
let mut h = tar::Header::new_ustar();
11771187
h.set_mode(0o644);
11781188
h.set_mtime(42);
11791189
h.set_size(testdata.len().try_into().unwrap());
@@ -1186,7 +1196,20 @@ async fn test_container_etc_hardlinked() -> Result<()> {
11861196
h.set_entry_type(tar::EntryType::Link);
11871197
h.set_mtime(42);
11881198
h.set_size(0);
1189-
layer_tar.append_link(&mut h.clone(), "sysroot/ostree/repo/objects/45/7279b28b541ca20358bec8487c81baac6a3d5ed3cea019aee675137fab53cb.file", "etc/dnf.conf")?;
1199+
let path = "sysroot/ostree/repo/objects/45/7279b28b541ca20358bec8487c81baac6a3d5ed3cea019aee675137fab53cb.file";
1200+
let target = "etc/dnf.conf";
1201+
if absolute {
1202+
let ustarname = &mut h.as_ustar_mut().unwrap().name;
1203+
// The tar crate doesn't let us set absolute paths in tar archives, so we bypass
1204+
// it and just write to the path buffer directly.
1205+
assert!(path.len() < ustarname.len());
1206+
ustarname[0..path.len()].copy_from_slice(path.as_bytes());
1207+
h.set_link_name(target)?;
1208+
h.set_cksum();
1209+
layer_tar.append(&mut h.clone(), std::io::empty())?;
1210+
} else {
1211+
layer_tar.append_link(&mut h.clone(), path, target)?;
1212+
}
11901213
layer_tar.finish()?;
11911214
Ok(())
11921215
},
@@ -1221,6 +1244,10 @@ async fn test_container_etc_hardlinked() -> Result<()> {
12211244
bar.ensure_resolved()?;
12221245
assert_eq!(foo.checksum(), bar.checksum());
12231246

1247+
let dnfconf = r.resolve_relative_path("usr/etc/dnf.conf");
1248+
let dnfconf: &ostree::RepoFile = dnfconf.downcast_ref::<ostree::RepoFile>().unwrap();
1249+
dnfconf.ensure_resolved()?;
1250+
12241251
Ok(())
12251252
}
12261253

0 commit comments

Comments
 (0)