diff --git a/lib/src/cli.rs b/lib/src/cli.rs index 8c172e37..3e66fda3 100644 --- a/lib/src/cli.rs +++ b/lib/src/cli.rs @@ -573,18 +573,10 @@ async fn container_store( let _ = printer.await; } let import = import?; - let commit = &repo.load_commit(&import.merge_commit)?.0; - let commit_meta = &glib::VariantDict::new(Some(&commit.child_value(0))); - let filtered = commit_meta.lookup::( - ostree_container::store::META_FILTERED, - )?; - if let Some(filtered) = filtered { - for (layerid, filtered) in filtered { - eprintln!("Unsupported paths filtered from {}:", layerid); - for (prefix, count) in filtered { - eprintln!(" {}: {}", prefix, count); - } - } + if let Some(msg) = + ostree_container::store::image_filtered_content_warning(repo, &imgref.imgref)? + { + eprintln!("{msg}") } println!("Wrote: {} => {}", imgref, import.merge_commit); Ok(()) @@ -793,6 +785,7 @@ where } => { let sysroot = &ostree::Sysroot::new(Some(&gio::File::for_path(&sysroot))); sysroot.load(gio::NONE_CANCELLABLE)?; + let repo = &sysroot.repo().unwrap(); let kargs = karg.as_deref(); let kargs = kargs.map(|v| { let r: Vec<_> = v.iter().map(|s| s.as_str()).collect(); @@ -811,6 +804,12 @@ where Some(options), ) .await?; + if let Some(msg) = ostree_container::store::image_filtered_content_warning( + repo, + &imgref.imgref, + )? { + eprintln!("{msg}") + } if let Some(p) = write_commitid_to { std::fs::write(&p, state.merge_commit.as_bytes()) .with_context(|| format!("Failed to write commitid to {}", p))?; diff --git a/lib/src/container/store.rs b/lib/src/container/store.rs index 61fdb344..0cbfd871 100644 --- a/lib/src/container/store.rs +++ b/lib/src/container/store.rs @@ -969,6 +969,39 @@ fn prune_image(repo: &ostree::Repo, image: &ImageReference) -> Result<()> { Ok(()) } +/// Given an image, if it has any non-ostree compatible content, return a suitable +/// warning message. +pub fn image_filtered_content_warning( + repo: &ostree::Repo, + image: &ImageReference, +) -> Result> { + use std::fmt::Write; + + let ostree_ref = ref_for_image(image)?; + let rev = repo.require_rev(&ostree_ref)?; + let commit_obj = repo.load_commit(rev.as_str())?.0; + let commit_meta = &glib::VariantDict::new(Some(&commit_obj.child_value(0))); + + let r = commit_meta + .lookup::(META_FILTERED)? + .filter(|v| !v.is_empty()) + .map(|v| { + let mut filtered = HashMap::<&String, u32>::new(); + for paths in v.values() { + for (k, v) in paths { + let e = filtered.entry(k).or_default(); + *e += v; + } + } + let mut buf = "Image contains non-ostree compatible file paths:".to_string(); + for (k, v) in filtered { + write!(buf, " {k}: {v}").unwrap(); + } + buf + }); + Ok(r) +} + /// Remove the specified image references. /// /// This function assumes no transaction is active on the repository. diff --git a/lib/tests/it/main.rs b/lib/tests/it/main.rs index a573e139..a18e7728 100644 --- a/lib/tests/it/main.rs +++ b/lib/tests/it/main.rs @@ -746,6 +746,12 @@ async fn impl_test_container_chunked(format: ExportLayout) -> Result<()> { assert_eq!(store::list_images(fixture.destrepo()).unwrap().len(), 1); + assert!( + store::image_filtered_content_warning(fixture.destrepo(), &imgref.imgref) + .unwrap() + .is_none() + ); + const ADDITIONS: &str = indoc::indoc! { " r usr/bin/bash bash-v0 "}; @@ -835,6 +841,12 @@ r usr/bin/bash bash-v0 let _import = imp.import(prep).await.unwrap(); assert_eq!(store::list_images(fixture.destrepo()).unwrap().len(), 2); + assert!( + store::image_filtered_content_warning(fixture.destrepo(), &derived_imgref.imgref) + .unwrap() + .is_none() + ); + // Should only be new layers let n_removed = store::gc_image_layers(fixture.destrepo())?; assert_eq!(n_removed, 0); @@ -861,6 +873,58 @@ r usr/bin/bash bash-v0 Ok(()) } +#[tokio::test] +async fn test_container_var_content() -> Result<()> { + let fixture = Fixture::new_v1()?; + + let imgref = fixture.export_container(ExportLayout::V1).await.unwrap().0; + let imgref = OstreeImageReference { + sigverify: SignatureSource::ContainerPolicyAllowInsecure, + imgref, + }; + + // Build a derived image + let derived_path = &fixture.path.join("derived.oci"); + let srcpath = imgref.imgref.name.as_str(); + oci_clone(srcpath, derived_path).await.unwrap(); + let temproot = &fixture.path.join("temproot"); + || -> Result<_> { + std::fs::create_dir(temproot)?; + let temprootd = Dir::open_ambient_dir(temproot, cap_std::ambient_authority())?; + let mut db = DirBuilder::new(); + db.mode(0o755); + db.recursive(true); + temprootd.create_dir_with("var/lib", &db)?; + temprootd.write("var/lib/foo", "junk var data")?; + Ok(()) + }() + .context("generating temp content")?; + ostree_ext::integrationtest::generate_derived_oci(derived_path, temproot)?; + + let derived_imgref = OstreeImageReference { + sigverify: SignatureSource::ContainerPolicyAllowInsecure, + imgref: ImageReference { + transport: Transport::OciDir, + name: derived_path.to_string(), + }, + }; + let mut imp = + store::ImageImporter::new(fixture.destrepo(), &derived_imgref, Default::default()).await?; + let prep = match imp.prepare().await.unwrap() { + store::PrepareResult::AlreadyPresent(_) => panic!("should not be already imported"), + store::PrepareResult::Ready(r) => r, + }; + let _import = imp.import(prep).await.unwrap(); + + assert!( + store::image_filtered_content_warning(fixture.destrepo(), &derived_imgref.imgref) + .unwrap() + .is_some() + ); + + Ok(()) +} + /// Copy an OCI directory. async fn oci_clone(src: impl AsRef, dest: impl AsRef) -> Result<()> { let src = src.as_ref();