diff --git a/lib/src/container/store.rs b/lib/src/container/store.rs index a2d35972..ca6d1899 100644 --- a/lib/src/container/store.rs +++ b/lib/src/container/store.rs @@ -166,6 +166,8 @@ pub struct ImageImporter { disable_gc: bool, // If true, don't prune unused image layers /// If true, require the image has the bootable flag require_bootable: bool, + /// If true, we have ostree v2024.3 or newer. + ostree_v2024_3: bool, pub(crate) proxy_img: OpenedImage, layer_progress: Option>, @@ -471,6 +473,7 @@ impl ImageImporter { proxy_img, target_imgref: None, no_imgref: false, + ostree_v2024_3: ostree::check_version(2024, 3), disable_gc: false, require_bootable: false, imgref: imgref.clone(), @@ -496,6 +499,11 @@ impl ImageImporter { self.require_bootable = true; } + /// Override the ostree version being targeted + pub fn set_ostree_version(&mut self, year: u32, v: u32) { + self.ostree_v2024_3 = (year > 2024) || (year == 2024 && v >= 3) + } + /// Do not prune image layers. pub fn disable_gc(&mut self) { self.disable_gc = true; @@ -864,6 +872,7 @@ impl ImageImporter { base: Some(base_commit.clone()), selinux: true, allow_nonusr: root_is_transient, + retain_var: self.ostree_v2024_3, }; let r = crate::tar::write_tar(&self.repo, blob, layer.ostree_ref.as_str(), Some(opts)); diff --git a/lib/src/fixture.rs b/lib/src/fixture.rs index 3322d04b..cc001993 100644 --- a/lib/src/fixture.rs +++ b/lib/src/fixture.rs @@ -439,6 +439,10 @@ impl Fixture { pub fn clear_destrepo(&self) -> Result<()> { self.destrepo() .set_ref_immediate(None, self.testref(), None, gio::Cancellable::NONE)?; + for (r, _) in self.destrepo().list_refs(None, gio::Cancellable::NONE)? { + self.destrepo() + .set_ref_immediate(None, &r, None, gio::Cancellable::NONE)?; + } self.destrepo() .prune(ostree::RepoPruneFlags::REFS_ONLY, 0, gio::Cancellable::NONE)?; Ok(()) diff --git a/lib/src/tar/write.rs b/lib/src/tar/write.rs index 9ad97139..82c887ec 100644 --- a/lib/src/tar/write.rs +++ b/lib/src/tar/write.rs @@ -68,6 +68,9 @@ pub struct WriteTarOptions { pub selinux: bool, /// Allow content not in /usr; this should be paired with ostree rootfs.transient = true pub allow_nonusr: bool, + /// If true, do not move content in /var to /usr/share/factory/var. This should be used + /// with ostree v2024.3 or newer. + pub retain_var: bool, } /// The result of writing a tar stream. @@ -112,6 +115,7 @@ enum NormalizedPathResult<'a> { #[derive(Debug, Clone, PartialEq, Eq, Default)] pub(crate) struct TarImportConfig { allow_nonusr: bool, + remap_factory_var: bool, } fn normalize_validate_path<'a>( @@ -150,9 +154,13 @@ fn normalize_validate_path<'a>( "etc" => { ret.push("usr/etc"); } - // Content in /var will get copied by a systemd tmpfiles.d unit "var" => { - ret.push("usr/share/factory/var"); + // Content in /var will get copied by a systemd tmpfiles.d unit + if config.remap_factory_var { + ret.push("usr/share/factory/var"); + } else { + ret.push(part) + } } o if EXCLUDED_TOPLEVEL_PATHS.contains(&o) => { return Ok(NormalizedPathResult::Filtered(part)); @@ -373,6 +381,7 @@ pub async fn write_tar( // Copy the filtered tar stream to child stdin let mut import_config = TarImportConfig::default(); import_config.allow_nonusr = options.allow_nonusr; + import_config.remap_factory_var = !options.retain_var; let filtered_result = filter_tar_async(src, child_stdin, &import_config); let output_copier = async move { // Gather stdout/stderr to buffers @@ -429,8 +438,18 @@ mod tests { #[test] fn test_normalize_path() { - let imp_default = &TarImportConfig::default(); - let allow_nonusr = &TarImportConfig { allow_nonusr: true }; + let imp_default = &TarImportConfig { + allow_nonusr: false, + remap_factory_var: true, + }; + let allow_nonusr = &TarImportConfig { + allow_nonusr: true, + remap_factory_var: true, + }; + let composefs_and_new_ostree = &TarImportConfig { + allow_nonusr: true, + remap_factory_var: false, + }; let valid_all = &[ ("/usr/bin/blah", "./usr/bin/blah"), ("usr/bin/blah", "./usr/bin/blah"), @@ -475,6 +494,10 @@ mod tests { assert!(normalize_validate_path(k.into(), allow_nonusr).is_err()); assert!(normalize_validate_path(k.into(), imp_default).is_err()); } + assert!(matches!( + normalize_validate_path("var/lib/foo".into(), composefs_and_new_ostree).unwrap(), + NormalizedPathResult::Normal(_) + )); } #[tokio::test] diff --git a/lib/tests/it/main.rs b/lib/tests/it/main.rs index 3a0bfdab..2c4380f9 100644 --- a/lib/tests/it/main.rs +++ b/lib/tests/it/main.rs @@ -935,6 +935,7 @@ async fn test_container_var_content() -> Result<()> { }; let mut imp = store::ImageImporter::new(fixture.destrepo(), &derived_imgref, Default::default()).await?; + imp.set_ostree_version(2023, 11); let prep = match imp.prepare().await.unwrap() { store::PrepareResult::AlreadyPresent(_) => panic!("should not be already imported"), store::PrepareResult::Ready(r) => r, @@ -963,6 +964,29 @@ async fn test_container_var_content() -> Result<()> { .is_none() ); + // Reset things + fixture.clear_destrepo()?; + + let mut imp = + store::ImageImporter::new(fixture.destrepo(), &derived_imgref, Default::default()).await?; + imp.set_ostree_version(2024, 3); + 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(); + let ostree_root = fixture + .destrepo() + .read_commit(&import.merge_commit, gio::Cancellable::NONE)? + .0; + let varfile = ostree_root + .child("usr/share/factory/var/lib/foo") + .downcast::() + .unwrap(); + assert!(!varfile.query_exists(gio::Cancellable::NONE)); + assert!(ostree_root + .child("var/lib/foo") + .query_exists(gio::Cancellable::NONE)); Ok(()) }