Skip to content

Commit 525997c

Browse files
composefs-native/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 389a922 commit 525997c

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::{
@@ -270,7 +271,7 @@ pub(crate) fn setup_composefs_bls_boot(
270271
// TODO: Make this generic
271272
repo: ComposefsRepository<Sha256HashValue>,
272273
id: &Sha256HashValue,
273-
entry: ComposefsBootEntry<Sha256HashValue>,
274+
entry: &ComposefsBootEntry<Sha256HashValue>,
274275
) -> Result<String> {
275276
let id_hex = id.to_hex();
276277

@@ -522,13 +523,107 @@ pub(crate) fn setup_composefs_bls_boot(
522523
Ok(boot_digest)
523524
}
524525

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

@@ -562,7 +661,7 @@ pub(crate) fn setup_composefs_uki_boot(
562661
anyhow::bail!("Could not find parent device for mountpoint /sysroot");
563662
};
564663

565-
(sysroot, get_esp_partition(&parent)?.0, None)
664+
(sysroot, get_esp_partition(&parent)?.0, false)
566665
}
567666
};
568667

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

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

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

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

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

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

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

758-
let boot_type = BootType::from(&entry);
824+
let boot_type = BootType::from(entry);
759825
let mut boot_digest: Option<String> = None;
760826

761827
match boot_type {
@@ -773,7 +839,7 @@ pub(crate) fn setup_composefs_boot(
773839
BootSetupType::Setup((&root_setup, &state, &fs)),
774840
repo,
775841
&id,
776-
entry,
842+
entries,
777843
)?,
778844
};
779845

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)