diff --git a/Cargo.toml b/Cargo.toml index 0441b93c..a50a90bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/lib/src/tar/write.rs b/lib/src/tar/write.rs index b87249fe..e769ebe9 100644 --- a/lib/src/tar/write.rs +++ b/lib/src/tar/write.rs @@ -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, + mut entry: tar::Entry, dest: &mut tar::Builder, path: Option<&Path>, ) -> Result<()> { @@ -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::>>()?; + 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: diff --git a/lib/tests/it/main.rs b/lib/tests/it/main.rs index d2bd27f6..cccc5c5f 100644 --- a/lib/tests/it/main.rs +++ b/lib/tests/it/main.rs @@ -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:.