diff --git a/crates/lib/src/bootc_composefs/boot.rs b/crates/lib/src/bootc_composefs/boot.rs index efd9e18c8..1196c1311 100644 --- a/crates/lib/src/bootc_composefs/boot.rs +++ b/crates/lib/src/bootc_composefs/boot.rs @@ -5,7 +5,7 @@ use std::path::Path; use anyhow::{anyhow, Context, Result}; use bootc_blockdev::find_parent_devices; -use bootc_kernel_cmdline::utf8::Cmdline; +use bootc_kernel_cmdline::utf8::{Cmdline, Parameter}; use bootc_mount::inspect_filesystem_of_dir; use bootc_mount::tempmount::TempMount; use camino::{Utf8Path, Utf8PathBuf}; @@ -33,6 +33,7 @@ use rustix::{mount::MountFlags, path::Arg}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use crate::bootc_kargs::kargs_from_composefs_filesystem; use crate::composefs_consts::{TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED}; use crate::parsers::bls_config::{BLSConfig, BLSConfigType}; use crate::parsers::grub_menuconfig::MenuEntry; @@ -51,7 +52,6 @@ use crate::{ BOOT_LOADER_ENTRIES, COMPOSEFS_CMDLINE, ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_DIGEST, STAGED_BOOT_LOADER_ENTRIES, STATE_DIR_ABS, USER_CFG, USER_CFG_STAGED, }, - install::RW_KARG, spec::{Bootloader, Host}, }; @@ -381,10 +381,11 @@ pub(crate) fn setup_composefs_bls_boot( repo: crate::store::ComposefsRepository, id: &Sha512HashValue, entry: &ComposefsBootEntry, + mounted_erofs: &Dir, ) -> Result { let id_hex = id.to_hex(); - let (root_path, esp_device, cmdline_refs, fs, bootloader) = match setup_type { + let (root_path, esp_device, mut cmdline_refs, fs, bootloader) = match setup_type { BootSetupType::Setup((root_setup, state, postfetch, fs)) => { // root_setup.kargs has [root=UUID=, "rw"] let mut cmdline_options = Cmdline::new(); @@ -415,10 +416,30 @@ pub(crate) fn setup_composefs_bls_boot( let sysroot_parent = get_sysroot_parent_dev(&storage.physical_root)?; let bootloader = host.require_composefs_booted()?.bootloader.clone(); + let boot_dir = storage.require_boot_dir()?; + let current_cfg = get_booted_bls(&boot_dir)?; + + let mut cmdline = match current_cfg.cfg_type { + BLSConfigType::NonEFI { options, .. } => { + let options = options + .ok_or_else(|| anyhow::anyhow!("No 'options' found in BLS Config"))?; + + Cmdline::from(options) + } + + _ => anyhow::bail!("Found NonEFI config"), + }; + + // Copy all cmdline args, replacing only `composefs=` + let param = format!("{COMPOSEFS_CMDLINE}={id_hex}"); + let param = + Parameter::parse(¶m).context("Failed to create 'composefs=' parameter")?; + cmdline.add_or_modify(¶m); + ( Utf8PathBuf::from("/sysroot"), get_esp_partition(&sysroot_parent)?.0, - Cmdline::from(format!("{RW_KARG} {COMPOSEFS_CMDLINE}={id_hex}")), + cmdline, fs, bootloader, ) @@ -427,6 +448,14 @@ pub(crate) fn setup_composefs_bls_boot( let is_upgrade = matches!(setup_type, BootSetupType::Upgrade(..)); + let current_root = if is_upgrade { + Some(&Dir::open_ambient_dir("/", ambient_authority()).context("Opening root")?) + } else { + None + }; + + kargs_from_composefs_filesystem(mounted_erofs, current_root, &mut cmdline_refs)?; + let (entry_paths, _tmpdir_guard) = match bootloader { Bootloader::Grub => { let root = Dir::open_ambient_dir(&root_path, ambient_authority()) @@ -1053,6 +1082,7 @@ pub(crate) fn setup_composefs_boot( repo, &id, entry, + &mounted_fs, )?; boot_digest = Some(digest); diff --git a/crates/lib/src/bootc_composefs/delete.rs b/crates/lib/src/bootc_composefs/delete.rs index b16930e83..f1a31101c 100644 --- a/crates/lib/src/bootc_composefs/delete.rs +++ b/crates/lib/src/bootc_composefs/delete.rs @@ -7,10 +7,7 @@ use composefs_boot::bootloader::{EFI_ADDON_DIR_EXT, EFI_EXT}; use crate::{ bootc_composefs::{ - boot::{ - find_vmlinuz_initrd_duplicates, get_efi_uuid_source, get_esp_partition, - get_sysroot_parent_dev, mount_esp, BootType, SYSTEMD_UKI_DIR, - }, + boot::{find_vmlinuz_initrd_duplicates, get_efi_uuid_source, BootType, SYSTEMD_UKI_DIR}, gc::composefs_gc, repo::open_composefs_repo, rollback::{composefs_rollback, rename_exchange_user_cfg}, @@ -215,40 +212,34 @@ fn remove_grub_menucfg_entry(id: &str, boot_dir: &Dir, deleting_staged: bool) -> #[fn_error_context::context("Deleting boot entries for deployment {}", deployment.deployment.verity)] fn delete_depl_boot_entries( deployment: &DeploymentEntry, - physical_root: &Dir, + storage: &Storage, deleting_staged: bool, ) -> Result<()> { - match deployment.deployment.bootloader { - Bootloader::Grub => { - let boot_dir = physical_root.open_dir("boot").context("Opening boot dir")?; + let boot_dir = storage.require_boot_dir()?; - match deployment.deployment.boot_type { - BootType::Bls => delete_type1_entry(deployment, &boot_dir, deleting_staged), + match deployment.deployment.bootloader { + Bootloader::Grub => match deployment.deployment.boot_type { + BootType::Bls => delete_type1_entry(deployment, boot_dir, deleting_staged), - BootType::Uki => { - let device = get_sysroot_parent_dev(physical_root)?; - let (esp_part, ..) = get_esp_partition(&device)?; - let esp_mount = mount_esp(&esp_part)?; + BootType::Uki => { + let esp = storage + .esp + .as_ref() + .ok_or_else(|| anyhow::anyhow!("ESP not found"))?; - remove_grub_menucfg_entry( - &deployment.deployment.verity, - &boot_dir, - deleting_staged, - )?; + remove_grub_menucfg_entry( + &deployment.deployment.verity, + boot_dir, + deleting_staged, + )?; - delete_uki(&deployment.deployment.verity, &esp_mount.fd) - } + delete_uki(&deployment.deployment.verity, &esp.fd) } - } + }, Bootloader::Systemd => { - let device = get_sysroot_parent_dev(physical_root)?; - let (esp_part, ..) = get_esp_partition(&device)?; - - let esp_mount = mount_esp(&esp_part)?; - // For Systemd UKI as well, we use .conf files - delete_type1_entry(deployment, &esp_mount.fd, deleting_staged) + delete_type1_entry(deployment, boot_dir, deleting_staged) } } } @@ -362,7 +353,7 @@ pub(crate) async fn delete_composefs_deployment( tracing::info!("Deleting {kind}deployment '{deployment_id}'"); - delete_depl_boot_entries(&depl_to_del, &storage.physical_root, deleting_staged)?; + delete_depl_boot_entries(&depl_to_del, &storage, deleting_staged)?; composefs_gc(storage, booted_cfs).await?; diff --git a/crates/lib/src/bootc_composefs/finalize.rs b/crates/lib/src/bootc_composefs/finalize.rs index d397c9f5c..027ffb5ee 100644 --- a/crates/lib/src/bootc_composefs/finalize.rs +++ b/crates/lib/src/bootc_composefs/finalize.rs @@ -1,8 +1,6 @@ use std::path::Path; -use crate::bootc_composefs::boot::{ - get_esp_partition, get_sysroot_parent_dev, mount_esp, BootType, -}; +use crate::bootc_composefs::boot::BootType; use crate::bootc_composefs::rollback::{rename_exchange_bls_entries, rename_exchange_user_cfg}; use crate::bootc_composefs::status::get_composefs_status; use crate::composefs_consts::STATE_DIR_ABS; @@ -86,15 +84,12 @@ pub(crate) async fn composefs_backend_finalize( // Unmount EROFS drop(erofs_tmp_mnt); - let sysroot_parent = get_sysroot_parent_dev(&storage.physical_root)?; - // NOTE: Assumption here that ESP will always be present - let (esp_part, ..) = get_esp_partition(&sysroot_parent)?; + let boot_dir = storage.require_boot_dir()?; - let esp_mount = mount_esp(&esp_part)?; - let boot_dir = storage - .physical_root - .open_dir("boot") - .context("Opening boot")?; + let esp_mount = storage + .esp + .as_ref() + .ok_or_else(|| anyhow::anyhow!("ESP not found"))?; // NOTE: Assuming here we won't have two bootloaders at the same time match booted_composefs.bootloader { @@ -103,21 +98,17 @@ pub(crate) async fn composefs_backend_finalize( let entries_dir = boot_dir.open_dir("loader")?; rename_exchange_bls_entries(&entries_dir)?; } - BootType::Uki => finalize_staged_grub_uki(&esp_mount.fd, &boot_dir)?, + BootType::Uki => finalize_staged_grub_uki(&esp_mount.fd, boot_dir)?, }, - Bootloader::Systemd => match staged_composefs.boot_type { - BootType::Bls => { - let entries_dir = esp_mount.fd.open_dir("loader")?; - rename_exchange_bls_entries(&entries_dir)?; - } - BootType::Uki => { + Bootloader::Systemd => { + if matches!(staged_composefs.boot_type, BootType::Uki) { rename_staged_uki_entries(&esp_mount.fd)?; - - let entries_dir = esp_mount.fd.open_dir("loader")?; - rename_exchange_bls_entries(&entries_dir)?; } - }, + + let entries_dir = boot_dir.open_dir("loader")?; + rename_exchange_bls_entries(&entries_dir)?; + } }; Ok(()) diff --git a/crates/lib/src/bootc_composefs/gc.rs b/crates/lib/src/bootc_composefs/gc.rs index 8195cade2..7926d250c 100644 --- a/crates/lib/src/bootc_composefs/gc.rs +++ b/crates/lib/src/bootc_composefs/gc.rs @@ -10,7 +10,6 @@ use composefs::fsverity::{FsVerityHashValue, Sha512HashValue}; use crate::{ bootc_composefs::{ - boot::{get_esp_partition, get_sysroot_parent_dev, mount_esp}, delete::{delete_image, delete_staged, delete_state_dir, get_image_objects}, status::{ get_bootloader, get_composefs_status, get_sorted_grub_uki_boot_entries, @@ -44,20 +43,19 @@ fn list_erofs_images(sysroot: &Dir) -> Result> { /// # Returns /// The fsverity of EROFS images corresponding to boot entries #[fn_error_context::context("Listing bootloader entries")] -fn list_bootloader_entries(physical_root: &Dir) -> Result> { +fn list_bootloader_entries(storage: &Storage) -> Result> { let bootloader = get_bootloader()?; + let boot_dir = storage.require_boot_dir()?; let entries = match bootloader { Bootloader::Grub => { - let boot_dir = physical_root.open_dir("boot").context("Opening boot dir")?; - // Grub entries are always in boot let grub_dir = boot_dir.open_dir("grub2").context("Opening grub dir")?; if grub_dir.exists(USER_CFG) { // Grub UKI let mut s = String::new(); - let boot_entries = get_sorted_grub_uki_boot_entries(&boot_dir, &mut s)?; + let boot_entries = get_sorted_grub_uki_boot_entries(boot_dir, &mut s)?; boot_entries .into_iter() @@ -65,7 +63,7 @@ fn list_bootloader_entries(physical_root: &Dir) -> Result> { .collect::, _>>()? } else { // Type1 Entry - let boot_entries = get_sorted_type1_boot_entries(&boot_dir, true)?; + let boot_entries = get_sorted_type1_boot_entries(boot_dir, true)?; boot_entries .into_iter() @@ -75,11 +73,7 @@ fn list_bootloader_entries(physical_root: &Dir) -> Result> { } Bootloader::Systemd => { - let device = get_sysroot_parent_dev(physical_root)?; - let (esp_part, ..) = get_esp_partition(&device)?; - let esp_mount = mount_esp(&esp_part)?; - - let boot_entries = get_sorted_type1_boot_entries(&esp_mount.fd, true)?; + let boot_entries = get_sorted_type1_boot_entries(boot_dir, true)?; boot_entries .into_iter() @@ -175,7 +169,7 @@ pub(crate) async fn composefs_gc(storage: &Storage, booted_cfs: &BootedComposefs let sysroot = &storage.physical_root; - let bootloader_entries = list_bootloader_entries(&storage.physical_root)?; + let bootloader_entries = list_bootloader_entries(&storage)?; let images = list_erofs_images(&sysroot)?; // Collect the deployments that have an image but no bootloader entry diff --git a/crates/lib/src/bootc_composefs/rollback.rs b/crates/lib/src/bootc_composefs/rollback.rs index 6338bf9b5..5cfd60c39 100644 --- a/crates/lib/src/bootc_composefs/rollback.rs +++ b/crates/lib/src/bootc_composefs/rollback.rs @@ -6,9 +6,7 @@ use cap_std_ext::dirext::CapStdExtDirExt; use fn_error_context::context; use rustix::fs::{fsync, renameat_with, AtFlags, RenameFlags}; -use crate::bootc_composefs::boot::{ - get_esp_partition, get_sysroot_parent_dev, mount_esp, type1_entry_conf_file_name, BootType, -}; +use crate::bootc_composefs::boot::{type1_entry_conf_file_name, BootType}; use crate::bootc_composefs::status::{get_composefs_status, get_sorted_type1_boot_entries}; use crate::composefs_consts::TYPE1_ENT_PATH_STAGED; use crate::spec::Bootloader; @@ -196,31 +194,21 @@ pub(crate) async fn composefs_rollback( anyhow::bail!("Rollback deployment not a composefs deployment") }; + let boot_dir = storage.require_boot_dir()?; + match &rollback_entry.bootloader { - Bootloader::Grub => { - let boot_dir = storage - .physical_root - .open_dir("boot") - .context("Opening boot dir")?; - - match rollback_entry.boot_type { - BootType::Bls => { - rollback_composefs_entries(&boot_dir, rollback_entry.bootloader.clone())?; - } - - BootType::Uki => { - rollback_grub_uki_entries(&boot_dir)?; - } + Bootloader::Grub => match rollback_entry.boot_type { + BootType::Bls => { + rollback_composefs_entries(boot_dir, rollback_entry.bootloader.clone())?; } - } + BootType::Uki => { + rollback_grub_uki_entries(boot_dir)?; + } + }, Bootloader::Systemd => { - let parent = get_sysroot_parent_dev(&storage.physical_root)?; - let (esp_part, ..) = get_esp_partition(&parent)?; - let esp_mount = mount_esp(&esp_part)?; - // We use BLS entries for systemd UKI as well - rollback_composefs_entries(&esp_mount.fd, rollback_entry.bootloader.clone())?; + rollback_composefs_entries(boot_dir, rollback_entry.bootloader.clone())?; } } diff --git a/crates/lib/src/bootc_composefs/status.rs b/crates/lib/src/bootc_composefs/status.rs index 29ae212ba..40ec1b757 100644 --- a/crates/lib/src/bootc_composefs/status.rs +++ b/crates/lib/src/bootc_composefs/status.rs @@ -5,7 +5,7 @@ use bootc_kernel_cmdline::utf8::Cmdline; use fn_error_context::context; use crate::{ - bootc_composefs::boot::{get_esp_partition, get_sysroot_parent_dev, mount_esp, BootType}, + bootc_composefs::boot::BootType, composefs_consts::{COMPOSEFS_CMDLINE, ORIGIN_KEY_BOOT_DIGEST, TYPE1_ENT_PATH, USER_CFG}, install::EFI_LOADER_INFO, parsers::{ @@ -13,6 +13,7 @@ use crate::{ grub_menuconfig::{parse_grub_menuentry_file, MenuEntry}, }, spec::{BootEntry, BootOrder, Host, HostSpec, ImageReference, ImageStatus}, + store::Storage, utils::{read_uefi_var, EfiError}, }; @@ -254,17 +255,20 @@ pub(crate) async fn get_composefs_status( storage: &crate::store::Storage, booted_cfs: &crate::store::BootedComposefs, ) -> Result { - composefs_deployment_status_from(&storage.physical_root, booted_cfs.cmdline).await + composefs_deployment_status_from(&storage, booted_cfs.cmdline).await } #[context("Getting composefs deployment status")] pub(crate) async fn composefs_deployment_status_from( - sysroot: &Dir, + storage: &Storage, cmdline: &ComposefsCmdline, ) -> Result { let composefs_digest = &cmdline.digest; - let deployments = sysroot + let boot_dir = storage.require_boot_dir()?; + + let deployments = storage + .physical_root .read_dir(STATE_DIR_RELATIVE) .with_context(|| format!("Reading sysroot {STATE_DIR_RELATIVE}"))?; @@ -348,30 +352,10 @@ pub(crate) async fn composefs_deployment_status_from( let booted = host.require_composefs_booted()?; - let (boot_dir, _temp_guard) = match booted.bootloader { - Bootloader::Grub => (sysroot.open_dir("boot").context("Opening boot dir")?, None), - - // TODO: This is redundant as we should already have ESP mounted at `/efi/` accoding to - // spec; currently we do not - // - // See: https://uapi-group.org/specifications/specs/boot_loader_specification/#mount-points - Bootloader::Systemd => { - let parent = get_sysroot_parent_dev(sysroot)?; - let (esp_part, ..) = get_esp_partition(&parent)?; - - let esp_mount = mount_esp(&esp_part)?; - - let dir = esp_mount.fd.try_clone().context("Cloning fd")?; - let guard = Some(esp_mount); - - (dir, guard) - } - }; - let is_rollback_queued = match booted.bootloader { Bootloader::Grub => match boot_type { BootType::Bls => { - let bls_config = get_sorted_type1_boot_entries(&boot_dir, false)?; + let bls_config = get_sorted_type1_boot_entries(boot_dir, false)?; let bls_config = bls_config .first() .ok_or(anyhow::anyhow!("First boot entry not found"))?; @@ -392,7 +376,7 @@ pub(crate) async fn composefs_deployment_status_from( BootType::Uki => { let mut s = String::new(); - !get_sorted_grub_uki_boot_entries(&boot_dir, &mut s)? + !get_sorted_grub_uki_boot_entries(boot_dir, &mut s)? .first() .ok_or(anyhow::anyhow!("First boot entry not found"))? .body @@ -403,7 +387,7 @@ pub(crate) async fn composefs_deployment_status_from( // We will have BLS stuff and the UKI stuff in the same DIR Bootloader::Systemd => { - let bls_config = get_sorted_type1_boot_entries(&boot_dir, false)?; + let bls_config = get_sorted_type1_boot_entries(boot_dir, false)?; let bls_config = bls_config .first() .ok_or(anyhow::anyhow!("First boot entry not found"))?; diff --git a/crates/lib/src/bootc_composefs/switch.rs b/crates/lib/src/bootc_composefs/switch.rs index 5cb1e2c1f..28ca67c48 100644 --- a/crates/lib/src/bootc_composefs/switch.rs +++ b/crates/lib/src/bootc_composefs/switch.rs @@ -1,5 +1,7 @@ use anyhow::{Context, Result}; use camino::Utf8PathBuf; +use cap_std_ext::cap_std::fs::Dir; +use composefs::fsverity::FsVerityHashValue; use fn_error_context::context; use crate::{ @@ -54,6 +56,12 @@ pub(crate) async fn switch_composefs( let boot_type = BootType::from(entry); let mut boot_digest = None; + let mounted_fs = Dir::reopen_dir( + &repo + .mount(&id.to_hex()) + .context("Failed to mount composefs image")?, + )?; + match boot_type { BootType::Bls => { boot_digest = Some(setup_composefs_bls_boot( @@ -61,6 +69,7 @@ pub(crate) async fn switch_composefs( repo, &id, entry, + &mounted_fs, )?) } BootType::Uki => setup_composefs_uki_boot( diff --git a/crates/lib/src/bootc_composefs/update.rs b/crates/lib/src/bootc_composefs/update.rs index eebfd3faa..aa2d70ac8 100644 --- a/crates/lib/src/bootc_composefs/update.rs +++ b/crates/lib/src/bootc_composefs/update.rs @@ -1,6 +1,10 @@ use anyhow::{Context, Result}; use camino::Utf8PathBuf; -use composefs::util::{parse_sha256, Sha256Digest}; +use cap_std_ext::cap_std::fs::Dir; +use composefs::{ + fsverity::FsVerityHashValue, + util::{parse_sha256, Sha256Digest}, +}; use fn_error_context::context; use ostree_ext::oci_spec::image::{ImageConfiguration, ImageManifest}; @@ -154,6 +158,12 @@ pub(crate) async fn upgrade_composefs( anyhow::bail!("No boot entries!"); }; + let mounted_fs = Dir::reopen_dir( + &repo + .mount(&id.to_hex()) + .context("Failed to mount composefs image")?, + )?; + let boot_type = BootType::from(entry); let mut boot_digest = None; @@ -164,6 +174,7 @@ pub(crate) async fn upgrade_composefs( repo, &id, entry, + &mounted_fs, )?) } diff --git a/crates/lib/src/bootc_kargs.rs b/crates/lib/src/bootc_kargs.rs index af709f1af..8aaf991a4 100644 --- a/crates/lib/src/bootc_kargs.rs +++ b/crates/lib/src/bootc_kargs.rs @@ -45,6 +45,52 @@ impl Config { } } +/// Compute the diff between existing and remote kargs +/// Apply the diff to the new kargs +fn compute_apply_kargs_diff( + existing_kargs: &Cmdline, + remote_kargs: &Cmdline, + new_kargs: &mut Cmdline, +) { + // Calculate the diff between the existing and remote kargs + let added_kargs: Vec<_> = remote_kargs + .iter() + .filter(|item| !existing_kargs.iter().any(|existing| *item == existing)) + .collect(); + let removed_kargs: Vec<_> = existing_kargs + .iter() + .filter(|item| !remote_kargs.iter().any(|remote| *item == remote)) + .collect(); + + tracing::debug!("kargs: added={:?} removed={:?}", added_kargs, removed_kargs); + + // Apply the diff to the system kargs + for arg in &removed_kargs { + new_kargs.remove_exact(arg); + } + for arg in &added_kargs { + new_kargs.add(arg); + } +} + +/// Looks for files in usr/lib/bootc/kargs.d and parses cmdline agruments +pub(crate) fn kargs_from_composefs_filesystem( + new_fs: &Dir, + current_root: Option<&Dir>, + new_kargs: &mut Cmdline, +) -> Result<()> { + let remote_kargs = get_kargs_in_root(new_fs, std::env::consts::ARCH)?; + + let existing_kargs = match current_root { + Some(root) => get_kargs_in_root(root, std::env::consts::ARCH)?, + None => Cmdline::new(), + }; + + compute_apply_kargs_diff(&existing_kargs, &remote_kargs, new_kargs); + + Ok(()) +} + /// Load and parse all bootc kargs.d files in the specified root, returning /// a combined list. pub(crate) fn get_kargs_in_root(d: &Dir, sys_arch: &str) -> Result { @@ -175,29 +221,7 @@ pub(crate) fn get_kargs( // Fetch the kernel arguments from the new root let remote_kargs = get_kargs_from_ostree(repo, &fetched_tree, sys_arch)?; - // Calculate the diff between the existing and remote kargs - let added_kargs: Vec<_> = remote_kargs - .iter() - .filter(|item| !existing_kargs.iter().any(|existing| *item == existing)) - .collect(); - let removed_kargs: Vec<_> = existing_kargs - .iter() - .filter(|item| !remote_kargs.iter().any(|remote| *item == remote)) - .collect(); - - tracing::debug!( - "kargs: added={:?} removed={:?}", - &added_kargs, - removed_kargs - ); - - // Apply the diff to the system kargs - for arg in &removed_kargs { - kargs.remove_exact(arg); - } - for arg in &added_kargs { - kargs.add(arg); - } + compute_apply_kargs_diff(&existing_kargs, &remote_kargs, &mut kargs); Ok(kargs) } diff --git a/crates/lib/src/store/mod.rs b/crates/lib/src/store/mod.rs index eb65e6eb9..4779093c5 100644 --- a/crates/lib/src/store/mod.rs +++ b/crates/lib/src/store/mod.rs @@ -21,6 +21,7 @@ use std::ops::Deref; use std::sync::Arc; use anyhow::{Context, Result}; +use bootc_mount::tempmount::TempMount; use cap_std_ext::cap_std; use cap_std_ext::cap_std::fs::{Dir, DirBuilder, DirBuilderExt as _}; use cap_std_ext::dirext::CapStdExtDirExt; @@ -31,10 +32,11 @@ use ostree_ext::sysroot::SysrootLock; use ostree_ext::{gio, ostree}; use rustix::fs::Mode; -use crate::bootc_composefs::status::{composefs_booted, ComposefsCmdline}; +use crate::bootc_composefs::boot::{get_esp_partition, get_sysroot_parent_dev, mount_esp}; +use crate::bootc_composefs::status::{composefs_booted, get_bootloader, ComposefsCmdline}; use crate::lsm; use crate::podstorage::CStorage; -use crate::spec::ImageStatus; +use crate::spec::{Bootloader, ImageStatus}; use crate::utils::{deployment_fd, open_dir_remount_rw}; /// See https://github.com/containers/composefs-rs/issues/159 @@ -169,9 +171,23 @@ impl BootedStorage { } let composefs = Arc::new(composefs); + // NOTE: This is assuming that we'll only have composefs in a UEFI system + // We do have this assumptions in a lot of other places + let parent = get_sysroot_parent_dev(&physical_root)?; + let (esp_part, ..) = get_esp_partition(&parent)?; + let esp_mount = mount_esp(&esp_part)?; + + let boot_dir = match get_bootloader()? { + Bootloader::Grub => physical_root.open_dir("boot").context("Opening boot")?, + // NOTE: Handle XBOOTLDR partitions here if and when we use it + Bootloader::Systemd => esp_mount.fd.try_clone().context("Cloning fd")?, + }; + let storage = Storage { physical_root, run, + boot_dir: Some(boot_dir), + esp: Some(esp_mount), ostree: Default::default(), composefs: OnceCell::from(composefs), imgstore: Default::default(), @@ -194,6 +210,8 @@ impl BootedStorage { let storage = Storage { physical_root, run, + boot_dir: None, + esp: None, ostree: OnceCell::from(sysroot), composefs: Default::default(), imgstore: Default::default(), @@ -235,6 +253,15 @@ impl BootedStorage { pub(crate) struct Storage { /// Directory holding the physical root pub physical_root: Dir, + + /// The 'boot' directory, useful and `Some` only for composefs systems + /// For grub booted systems, this points to `/sysroot/boot` + /// For systemd booted systems, this points to the ESP + pub boot_dir: Option, + + /// The ESP mounted at a tmp location + pub esp: Option, + /// Our runtime state run: Dir, @@ -286,12 +313,21 @@ impl Storage { Ok(Self { physical_root, run, + boot_dir: None, + esp: None, ostree: ostree_cell, composefs: Default::default(), imgstore: Default::default(), }) } + /// Returns `boot_dir` if it exists + pub(crate) fn require_boot_dir(&self) -> Result<&Dir> { + self.boot_dir + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Boot dir not found")) + } + /// Access the underlying ostree repository pub(crate) fn get_ostree(&self) -> Result<&SysrootLock> { self.ostree