diff --git a/crates/lib/src/bootc_composefs/boot.rs b/crates/lib/src/bootc_composefs/boot.rs index 5cc475b75..644f082dc 100644 --- a/crates/lib/src/bootc_composefs/boot.rs +++ b/crates/lib/src/bootc_composefs/boot.rs @@ -57,7 +57,7 @@ use crate::{ use crate::install::{RootSetup, State}; -/// Contains the EFP's filesystem UUID. Used by grub +/// Contains the ESP's filesystem UUID. Used by grub pub(crate) const EFI_UUID_FILE: &str = "efiuuid.cfg"; /// The EFI Linux directory pub(crate) const EFI_LINUX: &str = "EFI/Linux"; @@ -73,7 +73,14 @@ const VMLINUZ: &str = "vmlinuz"; /// directory specified by the BLS spec. We do this because we want systemd-boot to only look at /// our config files and not show the actual UKIs in the bootloader menu /// This is relative to the ESP -pub(crate) const SYSTEMD_UKI_DIR: &str = "EFI/Linux/bootc"; +pub(crate) const BOOTC_UKI_DIR: &str = "EFI/Linux/bootc"; + +pub(crate) const GLOBAL_UKI_ADDON_DIR: &str = "loader/addons"; + +/// Whether to install all passed in UKI Addons as global +/// For now, we don't have a reason to scope addons to a specific deployment, but we want to in the +/// future +pub const INSTALL_ADDONS_AS_GLOBAL: bool = true; pub(crate) enum BootSetupType<'a> { /// For initial setup, i.e. install to-disk @@ -497,7 +504,7 @@ pub(crate) fn setup_composefs_bls_boot( .with_title(title) .with_sort_key(default_sort_key.into()) .with_version(version) - .with_cfg(BLSConfigType::NonEFI { + .with_cfg(BLSConfigType::NonUKI { linux: entry_paths.abs_entries_path.join(&id_hex).join(VMLINUZ), initrd: vec![entry_paths.abs_entries_path.join(&id_hex).join(INITRD)], options: Some(cmdline_refs), @@ -508,7 +515,7 @@ pub(crate) fn setup_composefs_bls_boot( let symlink_to = &symlink_to[0]; match bls_config.cfg_type { - BLSConfigType::NonEFI { + BLSConfigType::NonUKI { ref mut linux, ref mut initrd, .. @@ -597,7 +604,6 @@ fn write_pe_to_esp( uki_id: &Sha512HashValue, is_insecure_from_opts: bool, mounted_efi: impl AsRef, - bootloader: &Bootloader, ) -> Result> { let efi_bin = read_file(file, &repo).context("Reading .efi binary")?; @@ -642,16 +648,19 @@ fn write_pe_to_esp( }); } - // Write the UKI to ESP - let efi_linux_path = mounted_efi.as_ref().join(match bootloader { - Bootloader::Grub => EFI_LINUX, - Bootloader::Systemd => SYSTEMD_UKI_DIR, + // Directory to write the PortableExecutable to + let pe_install_dir = mounted_efi.as_ref().join(match pe_type { + PEType::UkiAddon if INSTALL_ADDONS_AS_GLOBAL => GLOBAL_UKI_ADDON_DIR, + PEType::Uki | PEType::UkiAddon => BOOTC_UKI_DIR, }); - create_dir_all(&efi_linux_path).context("Creating EFI/Linux")?; + create_dir_all(&pe_install_dir) + .with_context(|| format!("Creating {}", pe_install_dir.display()))?; + + let final_pe_path = match (INSTALL_ADDONS_AS_GLOBAL, file_path.parent()) { + (true, ..) => pe_install_dir, - let final_pe_path = match file_path.parent() { - Some(parent) => { + (false, Some(parent)) => { let renamed_path = match parent.as_str().ends_with(EFI_ADDON_DIR_EXT) { true => { let dir_name = format!("{}{}", uki_id.to_hex(), EFI_ADDON_DIR_EXT); @@ -665,13 +674,13 @@ fn write_pe_to_esp( false => parent.to_path_buf(), }; - let full_path = efi_linux_path.join(renamed_path); + let full_path = pe_install_dir.join(renamed_path); create_dir_all(&full_path)?; full_path } - None => efi_linux_path, + (false, None) => pe_install_dir, }; let pe_dir = Dir::open_ambient_dir(&final_pe_path, ambient_authority()) @@ -796,8 +805,8 @@ fn write_systemd_uki_config( let mut bls_conf = BLSConfig::default(); bls_conf .with_title(boot_label.boot_label) - .with_cfg(BLSConfigType::EFI { - efi: format!("/{SYSTEMD_UKI_DIR}/{}{}", id.to_hex(), EFI_EXT).into(), + .with_cfg(BLSConfigType::UKI { + uki: format!("/{BOOTC_UKI_DIR}/{}{}", id.to_hex(), EFI_EXT).into(), }) .with_sort_key(default_sort_key.into()) .with_version(boot_label.version.unwrap_or(default_sort_key.into())); @@ -922,6 +931,7 @@ pub(crate) fn setup_composefs_uki_boot( })?; if !addons.iter().any(|passed_addon| passed_addon == addon_name) { + tracing::debug!("Skipping addon '{addon_name}'"); continue; } } @@ -937,7 +947,6 @@ pub(crate) fn setup_composefs_uki_boot( &id, is_insecure_from_opts, esp_mount.dir.path(), - &bootloader, )?; if let Some(label) = ret { diff --git a/crates/lib/src/bootc_composefs/delete.rs b/crates/lib/src/bootc_composefs/delete.rs index b16930e83..a45e3ee34 100644 --- a/crates/lib/src/bootc_composefs/delete.rs +++ b/crates/lib/src/bootc_composefs/delete.rs @@ -9,7 +9,7 @@ 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, + get_sysroot_parent_dev, mount_esp, BootType, BOOTC_UKI_DIR, }, gc::composefs_gc, repo::open_composefs_repo, @@ -65,20 +65,20 @@ fn delete_type1_entry(depl: &DeploymentEntry, boot_dir: &Dir, deleting_staged: b let bls_config = parse_bls_config(&cfg)?; match &bls_config.cfg_type { - BLSConfigType::EFI { efi } => { - if !efi.as_str().contains(&depl.deployment.verity) { + BLSConfigType::UKI { uki } => { + if !uki.as_str().contains(&depl.deployment.verity) { continue; } // Boot dir in case of EFI will be the ESP - tracing::debug!("Deleting EFI .conf file: {}", file_name); + tracing::debug!("Deleting UKI .conf file: {}", file_name); entry.remove_file().context("Removing .conf file")?; delete_uki(&depl.deployment.verity, boot_dir)?; break; } - BLSConfigType::NonEFI { options, .. } => { + BLSConfigType::NonUKI { options, .. } => { let options = options .as_ref() .ok_or(anyhow::anyhow!("options not found in BLS config file"))?; @@ -87,7 +87,7 @@ fn delete_type1_entry(depl: &DeploymentEntry, boot_dir: &Dir, deleting_staged: b continue; } - tracing::debug!("Deleting non-EFI .conf file: {}", file_name); + tracing::debug!("Deleting non-UKI .conf file: {}", file_name); entry.remove_file().context("Removing .conf file")?; if should_del_kernel { @@ -116,8 +116,8 @@ fn delete_type1_entry(depl: &DeploymentEntry, boot_dir: &Dir, deleting_staged: b #[fn_error_context::context("Deleting kernel and initrd")] fn delete_kernel_initrd(bls_config: &BLSConfigType, boot_dir: &Dir) -> Result<()> { - let BLSConfigType::NonEFI { linux, initrd, .. } = bls_config else { - anyhow::bail!("Found EFI config") + let BLSConfigType::NonUKI { linux, initrd, .. } = bls_config else { + anyhow::bail!("Found UKI config") }; // "linux" and "initrd" are relative to the boot_dir in our config files @@ -156,7 +156,7 @@ fn delete_kernel_initrd(bls_config: &BLSConfigType, boot_dir: &Dir) -> Result<() #[fn_error_context::context("Deleting UKI and UKI addons {uki_id}")] fn delete_uki(uki_id: &str, esp_mnt: &Dir) -> Result<()> { // TODO: We don't delete global addons here - let ukis = esp_mnt.open_dir(SYSTEMD_UKI_DIR)?; + let ukis = esp_mnt.open_dir(BOOTC_UKI_DIR)?; for entry in ukis.entries_utf8()? { let entry = entry?; diff --git a/crates/lib/src/bootc_composefs/state.rs b/crates/lib/src/bootc_composefs/state.rs index 723d6ed19..8491a627c 100644 --- a/crates/lib/src/bootc_composefs/state.rs +++ b/crates/lib/src/bootc_composefs/state.rs @@ -44,17 +44,17 @@ pub(crate) fn get_booted_bls(boot_dir: &Dir) -> Result { for entry in sorted_entries { match &entry.cfg_type { - BLSConfigType::EFI { efi } => { + BLSConfigType::UKI { uki } => { let composefs_param_value = booted.value().ok_or_else(|| { anyhow::anyhow!("Failed to get composefs kernel cmdline value") })?; - if efi.as_str().contains(composefs_param_value) { + if uki.as_str().contains(composefs_param_value) { return Ok(entry); } } - BLSConfigType::NonEFI { options, .. } => { + BLSConfigType::NonUKI { options, .. } => { let Some(opts) = options else { anyhow::bail!("options not found in bls config") }; diff --git a/crates/lib/src/bootc_composefs/status.rs b/crates/lib/src/bootc_composefs/status.rs index 29ae212ba..c09e13474 100644 --- a/crates/lib/src/bootc_composefs/status.rs +++ b/crates/lib/src/bootc_composefs/status.rs @@ -377,13 +377,13 @@ pub(crate) async fn composefs_deployment_status_from( .ok_or(anyhow::anyhow!("First boot entry not found"))?; match &bls_config.cfg_type { - BLSConfigType::NonEFI { options, .. } => !options + BLSConfigType::NonUKI { options, .. } => !options .as_ref() .ok_or(anyhow::anyhow!("options key not found in bls config"))? .contains(composefs_digest.as_ref()), - BLSConfigType::EFI { .. } => { - anyhow::bail!("Found 'efi' field in Type1 boot entry") + BLSConfigType::UKI { .. } => { + anyhow::bail!("Found 'uki' field in Type1 boot entry") } BLSConfigType::Unknown => anyhow::bail!("Unknown BLS Config Type"), } @@ -410,10 +410,10 @@ pub(crate) async fn composefs_deployment_status_from( match &bls_config.cfg_type { // For UKI boot - BLSConfigType::EFI { efi } => efi.as_str().contains(composefs_digest.as_ref()), + BLSConfigType::UKI { uki } => uki.as_str().contains(composefs_digest.as_ref()), // For boot entry Type1 - BLSConfigType::NonEFI { options, .. } => !options + BLSConfigType::NonUKI { options, .. } => !options .as_ref() .ok_or(anyhow::anyhow!("options key not found in bls config"))? .contains(composefs_digest.as_ref()), @@ -486,7 +486,7 @@ mod tests { let mut config1 = BLSConfig::default(); config1.title = Some("Fedora 42.20250623.3.1 (CoreOS)".into()); config1.sort_key = Some("1".into()); - config1.cfg_type = BLSConfigType::NonEFI { + config1.cfg_type = BLSConfigType::NonUKI { linux: "/boot/7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6/vmlinuz-5.14.10".into(), initrd: vec!["/boot/7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6/initramfs-5.14.10.img".into()], options: Some("root=UUID=abc123 rw composefs=7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6".into()), @@ -495,7 +495,7 @@ mod tests { let mut config2 = BLSConfig::default(); config2.title = Some("Fedora 41.20250214.2.0 (CoreOS)".into()); config2.sort_key = Some("2".into()); - config2.cfg_type = BLSConfigType::NonEFI { + config2.cfg_type = BLSConfigType::NonUKI { linux: "/boot/febdf62805de2ae7b6b597f2a9775d9c8a753ba1e5f09298fc8fbe0b0d13bf01/vmlinuz-5.14.10".into(), initrd: vec!["/boot/febdf62805de2ae7b6b597f2a9775d9c8a753ba1e5f09298fc8fbe0b0d13bf01/initramfs-5.14.10.img".into()], options: Some("root=UUID=abc123 rw composefs=febdf62805de2ae7b6b597f2a9775d9c8a753ba1e5f09298fc8fbe0b0d13bf01".into()) diff --git a/crates/lib/src/parsers/bls_config.rs b/crates/lib/src/parsers/bls_config.rs index 606b990c7..275baf24e 100644 --- a/crates/lib/src/parsers/bls_config.rs +++ b/crates/lib/src/parsers/bls_config.rs @@ -17,11 +17,11 @@ use crate::composefs_consts::COMPOSEFS_CMDLINE; #[derive(Debug, PartialEq, Eq, Default)] pub enum BLSConfigType { - EFI { - /// The path to the EFI binary, usually a UKI - efi: Utf8PathBuf, + UKI { + /// The path to the UKI + uki: Utf8PathBuf, }, - NonEFI { + NonUKI { /// The path to the linux kernel to boot. linux: Utf8PathBuf, /// The paths to the initrd images. @@ -102,11 +102,11 @@ impl Display for BLSConfig { writeln!(f, "version {}", self.version)?; match &self.cfg_type { - BLSConfigType::EFI { efi } => { - writeln!(f, "efi {}", efi)?; + BLSConfigType::UKI { uki } => { + writeln!(f, "uki {}", uki)?; } - BLSConfigType::NonEFI { + BLSConfigType::NonUKI { linux, initrd, options, @@ -173,16 +173,16 @@ impl BLSConfig { pub(crate) fn get_verity(&self) -> Result { match &self.cfg_type { - BLSConfigType::EFI { efi } => Ok(efi + BLSConfigType::UKI { uki } => Ok(uki .components() .last() - .ok_or(anyhow::anyhow!("Empty efi field"))? + .ok_or(anyhow::anyhow!("Empty uki field"))? .to_string() .strip_suffix(EFI_EXT) - .ok_or(anyhow::anyhow!("efi doesn't end with .efi"))? + .ok_or_else(|| anyhow::anyhow!("uki doesn't end with .efi"))? .to_string()), - BLSConfigType::NonEFI { options, .. } => { + BLSConfigType::NonUKI { options, .. } => { let options = options.as_ref().ok_or(anyhow::anyhow!("No options"))?; let cmdline = Cmdline::from(&options); @@ -209,7 +209,7 @@ pub(crate) fn parse_bls_config(input: &str) -> Result { let mut title = None; let mut version = None; let mut linux = None; - let mut efi = None; + let mut uki = None; let mut initrd = Vec::new(); let mut options = None; let mut machine_id = None; @@ -232,7 +232,7 @@ pub(crate) fn parse_bls_config(input: &str) -> Result { "options" => options = Some(CmdlineOwned::from(value)), "machine-id" => machine_id = Some(value), "sort-key" => sort_key = Some(value), - "efi" => efi = Some(Utf8PathBuf::from(value)), + "uki" => uki = Some(Utf8PathBuf::from(value)), _ => { extra.insert(key.to_string(), value); } @@ -242,10 +242,10 @@ pub(crate) fn parse_bls_config(input: &str) -> Result { let version = version.ok_or_else(|| anyhow!("Missing 'version' value"))?; - let cfg_type = match (linux, efi) { - (None, Some(efi)) => BLSConfigType::EFI { efi }, + let cfg_type = match (linux, uki) { + (None, Some(uki)) => BLSConfigType::UKI { uki }, - (Some(linux), None) => BLSConfigType::NonEFI { + (Some(linux), None) => BLSConfigType::NonUKI { linux, initrd, options, @@ -253,8 +253,8 @@ pub(crate) fn parse_bls_config(input: &str) -> Result { // The spec makes no mention of whether both can be present or not // Fow now, for us, we won't have both at the same time - (Some(_), Some(_)) => anyhow::bail!("'linux' and 'efi' values present"), - (None, None) => anyhow::bail!("Missing 'linux' or 'efi' value"), + (Some(_), Some(_)) => anyhow::bail!("'linux' and 'uki' values present"), + (None, None) => anyhow::bail!("Missing 'linux' or 'uki' value"), }; Ok(BLSConfig { @@ -285,13 +285,13 @@ mod tests { let config = parse_bls_config(input)?; - let BLSConfigType::NonEFI { + let BLSConfigType::NonUKI { linux, initrd, options, } = config.cfg_type else { - panic!("Expected non EFI variant"); + panic!("Expected non UKI variant"); }; assert_eq!( @@ -321,8 +321,8 @@ mod tests { let config = parse_bls_config(input)?; - let BLSConfigType::NonEFI { initrd, .. } = config.cfg_type else { - panic!("Expected non EFI variant"); + let BLSConfigType::NonUKI { initrd, .. } = config.cfg_type else { + panic!("Expected non UKI variant"); }; assert_eq!( diff --git a/crates/lib/src/parsers/grub_menuconfig.rs b/crates/lib/src/parsers/grub_menuconfig.rs index 41e25554c..d7f8ec5d0 100644 --- a/crates/lib/src/parsers/grub_menuconfig.rs +++ b/crates/lib/src/parsers/grub_menuconfig.rs @@ -15,6 +15,8 @@ use nom::{ Err, IResult, Parser, }; +use crate::bootc_composefs::boot::BOOTC_UKI_DIR; + /// Body content of a GRUB menuentry containing parsed commands. #[derive(Debug, PartialEq, Eq)] pub(crate) struct MenuentryBody<'a> { @@ -95,7 +97,7 @@ impl<'a> MenuEntry<'a> { title: format!("{boot_label}: ({uki_id})"), body: MenuentryBody { insmod: vec!["fat", "chain"], - chainloader: format!("/EFI/Linux/{uki_id}.efi"), + chainloader: format!("/{BOOTC_UKI_DIR}/{uki_id}.efi"), search: "--no-floppy --set=root --fs-uuid \"${EFI_PART_UUID}\"", version: 0, extra: vec![],