Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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
Expand Down Expand Up @@ -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),
Expand All @@ -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,
..
Expand Down Expand Up @@ -597,7 +604,6 @@ fn write_pe_to_esp(
uki_id: &Sha512HashValue,
is_insecure_from_opts: bool,
mounted_efi: impl AsRef<Path>,
bootloader: &Bootloader,
) -> Result<Option<UKILabels>> {
let efi_bin = read_file(file, &repo).context("Reading .efi binary")?;

Expand Down Expand Up @@ -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);
Expand All @@ -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())
Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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 {
Expand Down
18 changes: 9 additions & 9 deletions crates/lib/src/bootc_composefs/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"))?;
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?;
Expand Down
6 changes: 3 additions & 3 deletions crates/lib/src/bootc_composefs/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ pub(crate) fn get_booted_bls(boot_dir: &Dir) -> Result<BLSConfig> {

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")
};
Expand Down
14 changes: 7 additions & 7 deletions crates/lib/src/bootc_composefs/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}
Expand All @@ -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()),
Expand Down Expand Up @@ -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()),
Expand All @@ -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())
Expand Down
44 changes: 22 additions & 22 deletions crates/lib/src/parsers/bls_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -173,16 +173,16 @@ impl BLSConfig {

pub(crate) fn get_verity(&self) -> Result<String> {
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);
Expand All @@ -209,7 +209,7 @@ pub(crate) fn parse_bls_config(input: &str) -> Result<BLSConfig> {
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;
Expand All @@ -232,7 +232,7 @@ pub(crate) fn parse_bls_config(input: &str) -> Result<BLSConfig> {
"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);
}
Expand All @@ -242,19 +242,19 @@ pub(crate) fn parse_bls_config(input: &str) -> Result<BLSConfig> {

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,
},

// 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 {
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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!(
Expand Down
4 changes: 3 additions & 1 deletion crates/lib/src/parsers/grub_menuconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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![],
Expand Down
Loading