Skip to content
This repository has been archived by the owner on Nov 7, 2024. It is now read-only.

Commit

Permalink
tar: Propagate PAX extensions (including xattrs)
Browse files Browse the repository at this point in the history
This relies on the new tar API.

Closes: #655

Signed-off-by: Colin Walters <[email protected]>
  • Loading branch information
cgwalters committed Nov 1, 2024
1 parent 993a583 commit 530cd19
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ debug = true
codegen-units = 1
inherits = "release"
lto = "yes"

[patch.crates-io]
tar = { git = "https://github.com/alexcrichton/tar-rs", rev = "2b1d3741f9b1f6f9787721ced7bbf10b611feda4" }
11 changes: 10 additions & 1 deletion lib/src/tar/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const EXCLUDED_TOPLEVEL_PATHS: &[&str] = &["run", "tmp", "proc", "sys", "dev"];
/// Copy a tar entry to a new tar archive, optionally using a different filesystem path.
#[context("Copying entry")]
pub(crate) fn copy_entry(
entry: tar::Entry<impl std::io::Read>,
mut entry: tar::Entry<impl std::io::Read>,
dest: &mut tar::Builder<impl std::io::Write>,
path: Option<&Path>,
) -> Result<()> {
Expand All @@ -45,6 +45,15 @@ pub(crate) fn copy_entry(
(*entry.path()?).to_owned()
};
let mut header = entry.header().clone();
if let Some(headers) = entry.pax_extensions()? {
let extensions = headers
.map(|ext| {
let ext = ext?;
Ok((ext.key()?, ext.value_bytes()))
})
.collect::<Result<Vec<_>>>()?;
dest.append_pax_extensions(extensions.as_slice().iter().copied())?;
}

// Need to use the entry.link_name() not the header.link_name()
// api as the header api does not handle long paths:
Expand Down
62 changes: 62 additions & 0 deletions lib/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,68 @@ async fn test_old_code_parses_new_export() -> Result<()> {
Ok(())
}

/// Test for https://github.com/ostreedev/ostree-rs-ext/issues/655
#[tokio::test]
async fn test_container_xattr() -> Result<()> {
let fixture = Fixture::new_v1()?;
let sh = fixture.new_shell()?;
let baseimg = &fixture.export_container().await?.0;
let basepath = &match baseimg.transport {
Transport::OciDir => fixture.path.join(baseimg.name.as_str()),
_ => unreachable!(),
};

// Build a derived image
let derived_path = &fixture.path.join("derived.oci");
oci_clone(basepath, derived_path).await?;
ostree_ext::integrationtest::generate_derived_oci_from_tar(
derived_path,
|w| {
let mut tar = tar::Builder::new(w);
let mut h = tar::Header::new_gnu();
h.set_entry_type(tar::EntryType::Regular);
h.set_uid(0);
h.set_gid(0);
h.set_mode(0o644);
h.set_mtime(0);
let data = b"hello";
h.set_size(data.len() as u64);
tar.append_pax_extensions([("SCHILY.xattr.user.foo", b"bar".as_slice())])
.unwrap();
tar.append_data(&mut h, "usr/bin/testxattr", std::io::Cursor::new(data))
.unwrap();
Ok::<_, anyhow::Error>(())
},
None,
None,
)?;
let derived_ref = &OstreeImageReference {
sigverify: SignatureSource::ContainerPolicyAllowInsecure,
imgref: ImageReference {
transport: Transport::OciDir,
name: derived_path.to_string(),
},
};
let mut imp =
store::ImageImporter::new(fixture.destrepo(), derived_ref, Default::default()).await?;
let prep = match imp.prepare().await.context("Init prep derived")? {
store::PrepareResult::AlreadyPresent(_) => panic!("should not be already imported"),
store::PrepareResult::Ready(r) => r,
};
let import = imp.import(prep).await.unwrap();
let merge_commit = import.merge_commit;

// Yeah we just scrape the output of ostree because it's easy
let out = cmd!(
sh,
"ostree --repo=dest/repo ls -X {merge_commit} /usr/bin/testxattr"
)
.read()?;
assert!(out.contains("'user.foo', [byte 0x62, 0x61, 0x72]"));

Ok(())
}

#[ignore]
#[tokio::test]
// Verify that we can push and pull to a registry, not just oci-archive:.
Expand Down

0 comments on commit 530cd19

Please sign in to comment.