From edefd33ddfdfbc8dab75f25424b6040f1e92d209 Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Mon, 20 Jul 2026 14:48:05 +0530 Subject: [PATCH] cfs: Use bootupd for all installs if available Check if we have a new enough version of bootupd (by checking if the binary provides a `--bootloader` flag). If we have a new enough bootupd, use it to install grub-cc and systemd-boot We do not have this version of bootupd as an rpm package, so this will not break any existing systems. This is mostly for reverse dep testing in bootupd as testing this for bootc requires packaging bootupd, updating grub-cc and systemd-boot rpms to install their respective EFI binaries in the correct place which they don't right now Signed-off-by: Pragyan Poudyal --- crates/lib/src/bootc_composefs/boot.rs | 12 +++++ crates/lib/src/bootloader.rs | 71 +++++++++++++++++++------- crates/lib/src/install.rs | 1 + 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/crates/lib/src/bootc_composefs/boot.rs b/crates/lib/src/bootc_composefs/boot.rs index dd8c15e74..0d7d13b33 100644 --- a/crates/lib/src/bootc_composefs/boot.rs +++ b/crates/lib/src/bootc_composefs/boot.rs @@ -96,6 +96,7 @@ use serde::{Deserialize, Serialize}; use crate::bootc_composefs::state::{get_booted_bls, write_composefs_state}; use crate::bootc_composefs::status::ComposefsCmdline; use crate::bootc_kargs::compute_new_kargs; +use crate::bootloader::bootupd_supports_bootloader_flag; use crate::composefs_consts::{TYPE1_BOOT_DIR_PREFIX, TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED}; use crate::parsers::bls_config::{BLSConfig, BLSConfigType, EFIKey}; use crate::spec::BootloaderKind; @@ -1388,6 +1389,16 @@ pub(crate) async fn setup_composefs_boot( &root_setup.device_info.require_single_root()?, boot_uuid, )?; + } else if bootupd_supports_bootloader_flag(&root_setup.physical_root_path, None) + .is_ok_and(|v| v) + { + crate::bootloader::install_via_bootupd( + &root_setup.device_info, + &root_setup.physical_root_path, + &state.config_opts, + None, + Some(postfetch.detected_bootloader), + )?; } else if matches!( postfetch.detected_bootloader, Bootloader::Grub | Bootloader::GrubCC @@ -1397,6 +1408,7 @@ pub(crate) async fn setup_composefs_boot( &root_setup.physical_root_path, &state.config_opts, None, + None, )?; // FIXME: Remove this hack once we have support in bootupd diff --git a/crates/lib/src/bootloader.rs b/crates/lib/src/bootloader.rs index 4109e4aff..e0ec2307c 100644 --- a/crates/lib/src/bootloader.rs +++ b/crates/lib/src/bootloader.rs @@ -12,6 +12,7 @@ use fn_error_context::context; use bootc_mount as mount; use crate::bootc_composefs::boot::{MountedImageRoot, SecurebootKeys}; +use crate::spec::Bootloader; use crate::utils; /// The name of the mountpoint for efi (as a subdirectory of /boot, or at the toplevel) @@ -95,13 +96,11 @@ pub(crate) fn supports_bootupd(root: &Dir) -> Result { Ok(r) } -/// Check whether the target bootupd supports `--filesystem`. -/// -/// Runs `bootupctl backend install --help` and looks for `--filesystem` in the -/// output. When `deployment_path` is set the command runs inside a chroot -/// (via [`ChrootCmd`]) so we probe the binary from the target image. -fn bootupd_supports_filesystem(rootfs: &Utf8Path, deployment_path: Option<&str>) -> Result { +fn bootupd_install_help(rootfs: &Utf8Path, deployment_path: Option<&str>) -> Result { + static STATUS: std::sync::OnceLock = std::sync::OnceLock::new(); + let help_args = ["bootupctl", "backend", "install", "--help"]; + let output = if let Some(deploy) = deployment_path { let target_root = rootfs.join(deploy); ChrootCmd::new(&target_root) @@ -114,6 +113,17 @@ fn bootupd_supports_filesystem(rootfs: &Utf8Path, deployment_path: Option<&str>) .run_get_string()? }; + Ok(STATUS.get_or_init(|| output).to_string()) +} + +/// Check whether the target bootupd supports `--filesystem`. +/// +/// Runs `bootupctl backend install --help` and looks for `--filesystem` in the +/// output. When `deployment_path` is set the command runs inside a chroot +/// (via [`ChrootCmd`]) so we probe the binary from the target image. +fn bootupd_supports_filesystem(rootfs: &Utf8Path, deployment_path: Option<&str>) -> Result { + let output = bootupd_install_help(rootfs, deployment_path)?; + let use_filesystem = output.contains("--filesystem"); if use_filesystem { @@ -125,6 +135,24 @@ fn bootupd_supports_filesystem(rootfs: &Utf8Path, deployment_path: Option<&str>) Ok(use_filesystem) } +/// Check whether the target bootupd supports `--bootloader` for installs +pub(crate) fn bootupd_supports_bootloader_flag( + rootfs: &Utf8Path, + deployment_path: Option<&str>, +) -> Result { + let output = bootupd_install_help(rootfs, deployment_path)?; + + let supports_bootloader = output.contains("--bootloader"); + + if supports_bootloader { + tracing::debug!("bootupd supports --bootloader"); + } else { + tracing::debug!("bootupd does not support --bootloader"); + } + + Ok(supports_bootloader) +} + /// Install the bootloader via bootupd. /// /// When the target bootupd supports `--filesystem` we pass it pointing at a @@ -140,10 +168,12 @@ pub(crate) fn install_via_bootupd( rootfs: &Utf8Path, configopts: &crate::install::InstallConfigOpts, deployment_path: Option<&str>, + bootloader: Option, ) -> Result<()> { let verbose = std::env::var_os("BOOTC_BOOTLOADER_DEBUG").map(|_| "-vvvv"); // bootc defaults to only targeting the platform boot method. - let bootupd_opts = (!configopts.generic_image).then_some(["--update-firmware", "--auto"]); + let bootupd_opts = (!configopts.generic_image) + .then_some(["--update-firmware", "--auto"].map(|x| x.to_string())); // When not running inside the target container (through `--src-imgref`) we // run bootupctl from the deployment via a chroot ([`ChrootCmd`]). @@ -159,20 +189,25 @@ pub(crate) fn install_via_bootupd( println!("Installing bootloader via bootupd"); // Build the bootupctl arguments - let mut bootupd_args: Vec<&str> = vec!["backend", "install"]; + let mut bootupd_args: Vec = vec!["backend".into(), "install".into()]; if configopts.bootupd_skip_boot_uuid { - bootupd_args.push("--with-static-configs") + bootupd_args.push("--with-static-configs".into()) } else { - bootupd_args.push("--write-uuid"); + bootupd_args.push("--write-uuid".into()); } if let Some(v) = verbose { - bootupd_args.push(v); + bootupd_args.push(v.into()); } - if let Some(ref opts) = bootupd_opts { - bootupd_args.extend(opts.iter().copied()); + if let Some(opts) = bootupd_opts { + bootupd_args.extend(opts); } + if let Some(b) = bootloader { + bootupd_args.push("--bootloader".into()); + bootupd_args.push(b.to_string()); + }; + // When the target bootupd lacks --filesystem support, fall back to the // legacy --device flag. For --device we need the whole-disk device path // (e.g. /dev/vda), not a partition (e.g. /dev/vda3), so resolve the @@ -188,12 +223,12 @@ pub(crate) fn install_via_bootupd( }; if let Some(ref dev) = root_device_path { tracing::debug!("bootupd does not support --filesystem, falling back to --device {dev}"); - bootupd_args.extend(["--device", dev]); - bootupd_args.push(rootfs_mount); + bootupd_args.extend(["--device".into(), dev.into()]); + bootupd_args.push(rootfs_mount.into()); } else { tracing::debug!("bootupd supports --filesystem"); - bootupd_args.extend(["--filesystem", rootfs_mount]); - bootupd_args.push(rootfs_mount); + bootupd_args.extend(["--filesystem".into(), rootfs_mount.into()]); + bootupd_args.push(rootfs_mount.into()); } // Run inside a chroot ([`ChrootCmd`]). It sets up a fresh mount @@ -209,7 +244,7 @@ pub(crate) fn install_via_bootupd( // Prepend "bootupctl" to the args (ChrootCmd's calling // convention puts the program in args[0]). - let mut chroot_args = vec!["bootupctl"]; + let mut chroot_args = vec!["bootupctl".to_string()]; chroot_args.extend(bootupd_args); let mut cmd = ChrootCmd::new(&target_root) diff --git a/crates/lib/src/install.rs b/crates/lib/src/install.rs index 78974fef5..83466d9bc 100644 --- a/crates/lib/src/install.rs +++ b/crates/lib/src/install.rs @@ -1876,6 +1876,7 @@ async fn install_with_sysroot( .unwrap_or(rootfs.physical_root_path.clone()), &state.config_opts, Some(&deployment_path.as_str()), + None, )?; } Bootloader::Systemd | Bootloader::GrubCC => {