diff --git a/crates/lib/src/bootloader.rs b/crates/lib/src/bootloader.rs index 6126cef9e..57c0f16f6 100644 --- a/crates/lib/src/bootloader.rs +++ b/crates/lib/src/bootloader.rs @@ -8,6 +8,7 @@ use fn_error_context::context; use bootc_blockdev::{Partition, PartitionTable}; use bootc_mount as mount; +use rustix::mount::UnmountFlags; use crate::bootc_composefs::boot::mount_esp; use crate::{discoverable_partition_specification, utils}; @@ -50,22 +51,60 @@ pub(crate) fn install_via_bootupd( // bootc defaults to only targeting the platform boot method. let bootupd_opts = (!configopts.generic_image).then_some(["--update-firmware", "--auto"]); - let abs_deployment_path = deployment_path.map(|v| rootfs.join(v)); - let src_root_arg = if let Some(p) = abs_deployment_path.as_deref() { - vec!["--src-root", p.as_str()] + let abs_deployment_path = deployment_path.map(|deploy| rootfs.join(deploy)); + // When not running inside the target container (through `--src-imgref`) we chroot + // into the deployment before running bootupd. This makes sure we use binaries + // from the target image rather than the buildroot + let bind_mount_dirs = ["/dev", "/run", "/proc", "/sys"]; + let chroot_args = if let Some(target_root) = abs_deployment_path.as_deref() { + tracing::debug!("Setting up bind-mounts before chrooting to the target deployment"); + for src in bind_mount_dirs { + let dest = target_root + // joining an absolute path + // makes it replace self, so we strip the prefix + .join_os(src.strip_prefix("/").unwrap()); + tracing::debug!("bind mounting {}", dest.display()); + rustix::mount::mount_bind_recursive(src, dest)?; + } + // Append the `bootupctl` command, it will be passed as + // an argument to chroot + vec![target_root.as_str(), "bootupctl"] } else { vec![] }; + let devpath = device.path(); println!("Installing bootloader via bootupd"); - Command::new("bootupctl") + let mut bootupctl = if abs_deployment_path.is_some() { + Command::new("chroot") + } else { + Command::new("bootupctl") + }; + let install_result = bootupctl + .args(chroot_args) .args(["backend", "install", "--write-uuid"]) .args(verbose) .args(bootupd_opts.iter().copied().flatten()) - .args(src_root_arg) .args(["--device", devpath.as_str(), rootfs.as_str()]) .log_debug() - .run_inherited_with_cmd_context() + .run_inherited_with_cmd_context(); + + // Clean up the mounts after ourselves + if let Some(target_root) = abs_deployment_path { + let mut unmount_res = Ok(()); + for dir in bind_mount_dirs { + let mount = target_root + .join(dir.strip_prefix("/").unwrap()) + .into_std_path_buf(); + if let Err(e) = rustix::mount::unmount(&mount, UnmountFlags::DETACH) { + tracing::warn!("Error unmounting {}: {e}", mount.display()); + unmount_res = Err(e.into()); + } + } + install_result.and(unmount_res) + } else { + install_result + } } #[context("Installing bootloader")]