From e9902d36c0edea292d945a0b32eb08e1d934d9f2 Mon Sep 17 00:00:00 2001 From: Wei Shi Date: Fri, 20 Mar 2026 19:20:15 +0800 Subject: [PATCH] composefs: add pre-flight disk space check for native backend The previous commit (7196079e) added a pre-flight disk space check for the ostree and ostree+unified-storage backend paths. This commit extends the same protection to the native composefs backend. - Rename check_disk_space_composefs -> check_disk_space_unified for the ostree unified-storage path (uses PreparedImportMeta), to clarify that this variant is not used by the native composefs backend. - Add check_disk_space_composefs for the native composefs backend: sums layer sizes from the raw ImageManifest and calls fstatvfs on the composefs objects directory to verify available space before pulling. - Call check_disk_space_composefs in do_upgrade() (covers bootc upgrade and bootc switch on the native composefs backend), reusing the already-opened composefs repo from booted_cfs.repo. - Call check_disk_space_composefs in install_to_filesystem_impl() before initialize_composefs_repository() (covers bootc install --composefs-backend). Note: ensure_composefs_dir() is called first to create the objects directory if it does not yet exist on the fresh install target. - Enable the existing pre-flight disk check tmt test for the composefs backend (remove fixme_skip_if_composefs). Note: the check uses compressed layer sizes from the manifest, which is a lower bound of actual disk usage (composefs stores decompressed content). This is consistent with the ostree path and the broader container ecosystem, as uncompressed sizes are not available in the OCI manifest without downloading layers. Assisted-by: AI Signed-off-by: Wei Shi --- crates/lib/src/bootc_composefs/update.rs | 7 ++++++ crates/lib/src/deploy.rs | 22 +++++++++++++--- crates/lib/src/install.rs | 25 ++++++++++++++++--- tmt/plans/integration.fmf | 1 - .../test-upgrade-preflight-disk-check.nu | 2 -- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/crates/lib/src/bootc_composefs/update.rs b/crates/lib/src/bootc_composefs/update.rs index 78a3d4717..d1990eb1e 100644 --- a/crates/lib/src/bootc_composefs/update.rs +++ b/crates/lib/src/bootc_composefs/update.rs @@ -255,6 +255,13 @@ pub(crate) async fn do_upgrade( ) -> Result<()> { start_finalize_stated_svc()?; + // Pre-flight disk space check before pulling any data. + crate::deploy::check_disk_space_composefs( + &booted_cfs.repo, + &img_manifest_config.manifest, + imgref, + )?; + let (repo, entries, id, fs) = pull_composefs_repo( &imgref.transport, &imgref.image, diff --git a/crates/lib/src/deploy.rs b/crates/lib/src/deploy.rs index acd8f8198..5487b143f 100644 --- a/crates/lib/src/deploy.rs +++ b/crates/lib/src/deploy.rs @@ -411,8 +411,9 @@ pub(crate) fn check_disk_space_ostree( ) } -/// Verify there is sufficient disk space to pull an image into the composefs store. -pub(crate) fn check_disk_space_composefs( +/// Verify there is sufficient disk space to pull an image into the composefs store +/// via the ostree unified-storage path (uses `PreparedImportMeta`). +pub(crate) fn check_disk_space_unified( cfs: &crate::store::ComposefsRepository, image_meta: &PreparedImportMeta, imgref: &ImageReference, @@ -420,6 +421,21 @@ pub(crate) fn check_disk_space_composefs( check_disk_space_inner(cfs.objects_dir()?, image_meta.bytes_to_fetch, 0, imgref) } +/// Verify there is sufficient disk space to pull an image into the composefs store +/// for the native composefs backend (uses a raw `ImageManifest`). +pub(crate) fn check_disk_space_composefs( + cfs: &crate::store::ComposefsRepository, + manifest: &ostree_ext::oci_spec::image::ImageManifest, + imgref: &ImageReference, +) -> Result<()> { + let bytes_to_fetch: u64 = manifest + .layers() + .iter() + .map(|l: &ostree_ext::oci_spec::image::Descriptor| l.size()) + .sum(); + check_disk_space_inner(cfs.objects_dir()?, bytes_to_fetch, 0, imgref) +} + pub(crate) struct PreparedImportMeta { pub imp: ImageImporter, pub prep: Box, @@ -609,7 +625,7 @@ pub(crate) async fn pull_unified( Ok(existing) } PreparedPullResult::Ready(prepared_image_meta) => { - check_disk_space_composefs( + check_disk_space_unified( store.get_ensure_composefs()?.as_ref(), &prepared_image_meta, imgref, diff --git a/crates/lib/src/install.rs b/crates/lib/src/install.rs index 5b412f990..1d59deb63 100644 --- a/crates/lib/src/install.rs +++ b/crates/lib/src/install.rs @@ -188,7 +188,11 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "install-to-disk")] use self::baseline::InstallBlockDeviceOpts; use crate::bootc_composefs::status::ComposefsCmdline; -use crate::bootc_composefs::{boot::setup_composefs_boot, repo::initialize_composefs_repository}; +use crate::bootc_composefs::{ + boot::setup_composefs_boot, + repo::{get_imgref, initialize_composefs_repository, open_composefs_repo}, + status::get_container_manifest_and_config, +}; use crate::boundimage::{BoundImage, ResolvedBoundImage}; use crate::containerenv::ContainerExecutionInfo; use crate::deploy::{MergeState, PreparedPullResult, prepare_for_pull, pull_from_prepared}; @@ -1951,8 +1955,23 @@ async fn install_to_filesystem_impl( } if state.composefs_options.composefs_backend { - // Load a fd for the mounted target physical root - + // Pre-flight disk space check for native composefs install path. + { + let imgref = &state.source.imageref; + let imgref_repr = get_imgref(&imgref.transport.to_string(), &imgref.name); + let img_manifest_config = get_container_manifest_and_config(&imgref_repr).await?; + crate::store::ensure_composefs_dir(&rootfs.physical_root)?; + let cfs_repo = open_composefs_repo(&rootfs.physical_root)?; + crate::deploy::check_disk_space_composefs( + &cfs_repo, + &img_manifest_config.manifest, + &crate::spec::ImageReference { + image: imgref.name.clone(), + transport: imgref.transport.to_string(), + signature: None, + }, + )?; + } let pull_result = initialize_composefs_repository( state, rootfs, diff --git a/tmt/plans/integration.fmf b/tmt/plans/integration.fmf index 46b14b0fa..b41800930 100644 --- a/tmt/plans/integration.fmf +++ b/tmt/plans/integration.fmf @@ -201,7 +201,6 @@ execute: how: fmf test: - /tmt/tests/tests/test-35-upgrade-preflight-disk-check - extra-fixme_skip_if_composefs: true /plan-36-rollback: summary: Test bootc rollback functionality diff --git a/tmt/tests/booted/test-upgrade-preflight-disk-check.nu b/tmt/tests/booted/test-upgrade-preflight-disk-check.nu index 2d5d5f0e7..0a084529d 100644 --- a/tmt/tests/booted/test-upgrade-preflight-disk-check.nu +++ b/tmt/tests/booted/test-upgrade-preflight-disk-check.nu @@ -2,8 +2,6 @@ # tmt: # summary: Verify pre-flight disk space check rejects images with inflated layer sizes # duration: 10m -# extra: -# fixme_skip_if_composefs: true # # This test does NOT require a reboot. # It constructs a minimal fake OCI image directory that claims to have an