Skip to content

Commit b50622f

Browse files
composefs-backend/uki: Handle UKI addons
If we find UKI addons in the boot entries list, write them to ESP along with the UKI Signed-off-by: Pragyan Poudyal <[email protected]>
1 parent 4f49e6a commit b50622f

File tree

3 files changed

+134
-68
lines changed

3 files changed

+134
-68
lines changed

crates/lib/src/bootc_composefs/boot.rs

Lines changed: 128 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use camino::{Utf8Path, Utf8PathBuf};
1111
use cap_std_ext::{cap_std, dirext::CapStdExtDirExt};
1212
use clap::ValueEnum;
1313
use composefs::fs::read_file;
14-
use composefs::tree::FileSystem;
14+
use composefs::tree::{FileSystem, RegularFile};
15+
use composefs_boot::bootloader::{PEType, EFI_ADDON_DIR_EXT, EFI_ADDON_FILE_EXT, EFI_EXT};
1516
use composefs_boot::BootOps;
1617
use fn_error_context::context;
1718
use ostree_ext::composefs::{
@@ -283,7 +284,7 @@ pub(crate) fn setup_composefs_bls_boot(
283284
// TODO: Make this generic
284285
repo: ComposefsRepository<Sha256HashValue>,
285286
id: &Sha256HashValue,
286-
entry: ComposefsBootEntry<Sha256HashValue>,
287+
entry: &ComposefsBootEntry<Sha256HashValue>,
287288
) -> Result<String> {
288289
let id_hex = id.to_hex();
289290

@@ -527,13 +528,107 @@ pub(crate) fn setup_composefs_bls_boot(
527528
Ok(boot_digest)
528529
}
529530

531+
/// Writes a PortableExecutable to ESP along with any PE specific or Global addons
532+
fn write_pe_to_esp(
533+
repo: &ComposefsRepository<Sha256HashValue>,
534+
file: &RegularFile<Sha256HashValue>,
535+
file_path: &PathBuf,
536+
pe_type: PEType,
537+
uki_id: &String,
538+
is_insecure_from_opts: bool,
539+
mounted_efi: &PathBuf,
540+
) -> Result<Option<String>> {
541+
let efi_bin = read_file(file, &repo).context("Reading .efi binary")?;
542+
543+
let mut boot_label = None;
544+
545+
// UKI Extension might not even have a cmdline
546+
// TODO: UKI Addon might also have a composefs= cmdline?
547+
if matches!(pe_type, PEType::Uki) {
548+
let cmdline = uki::get_cmdline(&efi_bin).context("Getting UKI cmdline")?;
549+
550+
let (composefs_cmdline, insecure) = get_cmdline_composefs::<Sha256HashValue>(cmdline)?;
551+
552+
// If the UKI cmdline does not match what the user has passed as cmdline option
553+
// NOTE: This will only be checked for new installs and now upgrades/switches
554+
match is_insecure_from_opts {
555+
true if !insecure => {
556+
tracing::warn!("--insecure passed as option but UKI cmdline does not support it");
557+
}
558+
559+
false if insecure => {
560+
tracing::warn!("UKI cmdline has composefs set as insecure");
561+
}
562+
563+
_ => { /* no-op */ }
564+
}
565+
566+
if composefs_cmdline.to_hex() != *uki_id {
567+
anyhow::bail!(
568+
"The UKI has the wrong composefs= parameter (is '{composefs_cmdline:?}', should be {uki_id:?})"
569+
);
570+
}
571+
572+
boot_label = Some(uki::get_boot_label(&efi_bin).context("Getting UKI boot label")?);
573+
}
574+
575+
// Write the UKI to ESP
576+
let efi_linux_path = mounted_efi.join(EFI_LINUX);
577+
create_dir_all(&efi_linux_path).context("Creating EFI/Linux")?;
578+
579+
let final_pe_path = match file_path.parent() {
580+
Some(parent) => {
581+
let renamed_path = match parent.as_str()?.ends_with(EFI_ADDON_DIR_EXT) {
582+
true => {
583+
let dir_name = format!("{}{}", uki_id, EFI_ADDON_DIR_EXT);
584+
585+
parent
586+
.parent()
587+
.map(|p| p.join(&dir_name))
588+
.unwrap_or(dir_name.into())
589+
}
590+
591+
false => parent.to_path_buf(),
592+
};
593+
594+
let full_path = efi_linux_path.join(renamed_path);
595+
create_dir_all(&full_path)?;
596+
597+
full_path
598+
}
599+
600+
None => efi_linux_path,
601+
};
602+
603+
let pe_dir = cap_std::fs::Dir::open_ambient_dir(&final_pe_path, cap_std::ambient_authority())
604+
.with_context(|| format!("Opening {final_pe_path:?}"))?;
605+
606+
let pe_name = match pe_type {
607+
PEType::Uki => format!("{}{}", uki_id, EFI_EXT),
608+
PEType::UkiAddon => format!("{}{}", uki_id, EFI_ADDON_FILE_EXT),
609+
};
610+
611+
pe_dir
612+
.atomic_write(pe_name, efi_bin)
613+
.context("Writing UKI")?;
614+
615+
rustix::fs::fsync(
616+
pe_dir
617+
.reopen_as_ownedfd()
618+
.context("Reopening as owned fd")?,
619+
)
620+
.context("fsync")?;
621+
622+
Ok(boot_label)
623+
}
624+
530625
#[context("Setting up UKI boot")]
531626
pub(crate) fn setup_composefs_uki_boot(
532627
setup_type: BootSetupType,
533628
// TODO: Make this generic
534629
repo: ComposefsRepository<Sha256HashValue>,
535630
id: &Sha256HashValue,
536-
entry: ComposefsBootEntry<Sha256HashValue>,
631+
entries: Vec<ComposefsBootEntry<Sha256HashValue>>,
537632
) -> Result<()> {
538633
let (root_path, esp_device, is_insecure_from_opts) = match setup_type {
539634
BootSetupType::Setup((root_setup, state, ..)) => {
@@ -553,15 +648,19 @@ pub(crate) fn setup_composefs_uki_boot(
553648
(
554649
root_setup.physical_root_path.clone(),
555650
esp_part.node.clone(),
556-
state.composefs_options.as_ref().map(|x| x.insecure),
651+
state
652+
.composefs_options
653+
.as_ref()
654+
.map(|x| x.insecure)
655+
.unwrap_or(false),
557656
)
558657
}
559658

560659
BootSetupType::Upgrade(..) => {
561660
let sysroot = Utf8PathBuf::from("/sysroot");
562661
let sysroot_parent = get_sysroot_parent_dev()?;
563662

564-
(sysroot, get_esp_partition(&sysroot_parent)?.0, None)
663+
(sysroot, get_esp_partition(&sysroot_parent)?.0, false)
565664
}
566665
};
567666

@@ -573,65 +672,32 @@ pub(crate) fn setup_composefs_uki_boot(
573672
.args([&PathBuf::from(&esp_device), &mounted_efi.clone()])
574673
.run()?;
575674

576-
let boot_label = match entry {
577-
ComposefsBootEntry::Type1(..) => unimplemented!(),
578-
ComposefsBootEntry::UsrLibModulesVmLinuz(..) => unimplemented!(),
579-
580-
ComposefsBootEntry::Type2(type2_entry) => {
581-
let uki = read_file(&type2_entry.file, &repo).context("Reading UKI")?;
582-
let cmdline = uki::get_cmdline(&uki).context("Getting UKI cmdline")?;
583-
let (composefs_cmdline, insecure) = get_cmdline_composefs::<Sha256HashValue>(cmdline)?;
584-
585-
// If the UKI cmdline does not match what the user has passed as cmdline option
586-
// NOTE: This will only be checked for new installs and now upgrades/switches
587-
if let Some(is_insecure_from_opts) = is_insecure_from_opts {
588-
match is_insecure_from_opts {
589-
true => {
590-
if !insecure {
591-
tracing::warn!(
592-
"--insecure passed as option but UKI cmdline does not support it"
593-
)
594-
}
595-
}
675+
let mut boot_label = String::new();
596676

597-
false => {
598-
if insecure {
599-
tracing::warn!("UKI cmdline has composefs set as insecure")
600-
}
601-
}
602-
}
677+
for entry in entries {
678+
match entry {
679+
ComposefsBootEntry::Type1(..) => tracing::debug!("Skipping Type1 Entry"),
680+
ComposefsBootEntry::UsrLibModulesVmLinuz(..) => {
681+
tracing::debug!("Skipping vmlinuz in /usr/lib/modules")
603682
}
604683

605-
let boot_label = uki::get_boot_label(&uki).context("Getting UKI boot label")?;
684+
ComposefsBootEntry::Type2(entry) => {
685+
let ret = write_pe_to_esp(
686+
&repo,
687+
&entry.file,
688+
&entry.file_path,
689+
entry.pe_type,
690+
&id.to_hex(),
691+
is_insecure_from_opts,
692+
&mounted_efi,
693+
)?;
606694

607-
if composefs_cmdline != *id {
608-
anyhow::bail!(
609-
"The UKI has the wrong composefs= parameter (is '{composefs_cmdline:?}', should be {id:?})"
610-
);
695+
if let Some(label) = ret {
696+
boot_label = label;
697+
}
611698
}
612-
613-
// Write the UKI to ESP
614-
let efi_linux_path = mounted_efi.join(EFI_LINUX);
615-
create_dir_all(&efi_linux_path).context("Creating EFI/Linux")?;
616-
617-
let efi_linux =
618-
cap_std::fs::Dir::open_ambient_dir(&efi_linux_path, cap_std::ambient_authority())
619-
.with_context(|| format!("Opening {efi_linux_path:?}"))?;
620-
621-
efi_linux
622-
.atomic_write(format!("{}.efi", id.to_hex()), uki)
623-
.context("Writing UKI")?;
624-
625-
rustix::fs::fsync(
626-
efi_linux
627-
.reopen_as_ownedfd()
628-
.context("Reopening as owned fd")?,
629-
)
630-
.context("fsync")?;
631-
632-
boot_label
633-
}
634-
};
699+
};
700+
}
635701

636702
Command::new("umount")
637703
.arg(&mounted_efi)
@@ -750,11 +816,11 @@ pub(crate) fn setup_composefs_boot(
750816
let entries = fs.transform_for_boot(&repo)?;
751817
let id = fs.commit_image(&repo, None)?;
752818

753-
let Some(entry) = entries.into_iter().next() else {
819+
let Some(entry) = entries.iter().next() else {
754820
anyhow::bail!("No boot entries!");
755821
};
756822

757-
let boot_type = BootType::from(&entry);
823+
let boot_type = BootType::from(entry);
758824
let mut boot_digest: Option<String> = None;
759825

760826
match boot_type {
@@ -772,7 +838,7 @@ pub(crate) fn setup_composefs_boot(
772838
BootSetupType::Setup((&root_setup, &state, &fs)),
773839
repo,
774840
&id,
775-
entry,
841+
entries,
776842
)?,
777843
};
778844

crates/lib/src/bootc_composefs/switch.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ pub(crate) async fn switch_composefs(opts: SwitchOpts) -> Result<()> {
3939
let (repo, entries, id, fs) =
4040
pull_composefs_repo(&target_imgref.transport, &target_imgref.image).await?;
4141

42-
let Some(entry) = entries.into_iter().next() else {
42+
let Some(entry) = entries.iter().next() else {
4343
anyhow::bail!("No boot entries!");
4444
};
4545

46-
let boot_type = BootType::from(&entry);
46+
let boot_type = BootType::from(entry);
4747
let mut boot_digest = None;
4848

4949
match boot_type {
@@ -56,7 +56,7 @@ pub(crate) async fn switch_composefs(opts: SwitchOpts) -> Result<()> {
5656
)?)
5757
}
5858
BootType::Uki => {
59-
setup_composefs_uki_boot(BootSetupType::Upgrade((&fs, &host)), repo, &id, entry)?
59+
setup_composefs_uki_boot(BootSetupType::Upgrade((&fs, &host)), repo, &id, entries)?
6060
}
6161
};
6262

crates/lib/src/bootc_composefs/update.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ pub(crate) async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
2929

3030
let (repo, entries, id, fs) = pull_composefs_repo(&imgref.transport, &imgref.image).await?;
3131

32-
let Some(entry) = entries.into_iter().next() else {
32+
let Some(entry) = entries.iter().next() else {
3333
anyhow::bail!("No boot entries!");
3434
};
3535

36-
let boot_type = BootType::from(&entry);
36+
let boot_type = BootType::from(entry);
3737
let mut boot_digest = None;
3838

3939
match boot_type {
@@ -47,7 +47,7 @@ pub(crate) async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
4747
}
4848

4949
BootType::Uki => {
50-
setup_composefs_uki_boot(BootSetupType::Upgrade((&fs, &host)), repo, &id, entry)?
50+
setup_composefs_uki_boot(BootSetupType::Upgrade((&fs, &host)), repo, &id, entries)?
5151
}
5252
};
5353

0 commit comments

Comments
 (0)