Skip to content

Commit caf4ad8

Browse files
composefs-backend/uki: Handle UKI systemd-boot
We don't need to write Grub menuentries for systemd-boot. For now the operation is a no-op, but later we would want to have .conf files in `ESP/loader/entries` so we can control the order of entries. Regarding that, we would also need to place the UKIs in a separate directory and not inside `ESP/EFI/Linux`, if we don't want duplicate entries, as systemd-boot will simply list all .efi files placed in EFI/Linux unconditionally Signed-off-by: Pragyan Poudyal <[email protected]>
1 parent b50622f commit caf4ad8

File tree

1 file changed

+128
-91
lines changed
  • crates/lib/src/bootc_composefs

1 file changed

+128
-91
lines changed

crates/lib/src/bootc_composefs/boot.rs

Lines changed: 128 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use bootc_blockdev::find_parent_devices;
88
use bootc_mount::inspect_filesystem;
99
use bootc_utils::CommandRunExt;
1010
use camino::{Utf8Path, Utf8PathBuf};
11-
use cap_std_ext::{cap_std, dirext::CapStdExtDirExt};
11+
use cap_std_ext::{
12+
cap_std::{ambient_authority, fs::Dir},
13+
dirext::CapStdExtDirExt,
14+
};
1215
use clap::ValueEnum;
1316
use composefs::fs::read_file;
1417
use composefs::tree::{FileSystem, RegularFile};
@@ -174,8 +177,7 @@ fn compute_boot_digest(
174177
/// Returns the verity of the deployment that has a boot digest same as the one passed in
175178
#[context("Checking boot entry duplicates")]
176179
fn find_vmlinuz_initrd_duplicates(digest: &str) -> Result<Option<String>> {
177-
let deployments =
178-
cap_std::fs::Dir::open_ambient_dir(STATE_DIR_ABS, cap_std::ambient_authority());
180+
let deployments = Dir::open_ambient_dir(STATE_DIR_ABS, ambient_authority());
179181

180182
let deployments = match deployments {
181183
Ok(d) => d,
@@ -231,7 +233,7 @@ fn write_bls_boot_entries_to_disk(
231233
let path = boot_dir.join(&id_hex);
232234
create_dir_all(&path)?;
233235

234-
let entries_dir = cap_std::fs::Dir::open_ambient_dir(&path, cap_std::ambient_authority())
236+
let entries_dir = Dir::open_ambient_dir(&path, ambient_authority())
235237
.with_context(|| format!("Opening {path}"))?;
236238

237239
entries_dir
@@ -486,9 +488,8 @@ pub(crate) fn setup_composefs_bls_boot(
486488

487489
// Scope to allow for proper unmounting
488490
{
489-
let loader_entries_dir =
490-
cap_std::fs::Dir::open_ambient_dir(&config_path, cap_std::ambient_authority())
491-
.with_context(|| format!("Opening {config_path:?}"))?;
491+
let loader_entries_dir = Dir::open_ambient_dir(&config_path, ambient_authority())
492+
.with_context(|| format!("Opening {config_path:?}"))?;
492493

493494
loader_entries_dir.atomic_write(
494495
// SAFETY: We set sort_key above
@@ -600,7 +601,7 @@ fn write_pe_to_esp(
600601
None => efi_linux_path,
601602
};
602603

603-
let pe_dir = cap_std::fs::Dir::open_ambient_dir(&final_pe_path, cap_std::ambient_authority())
604+
let pe_dir = Dir::open_ambient_dir(&final_pe_path, ambient_authority())
604605
.with_context(|| format!("Opening {final_pe_path:?}"))?;
605606

606607
let pe_name = match pe_type {
@@ -622,6 +623,94 @@ fn write_pe_to_esp(
622623
Ok(boot_label)
623624
}
624625

626+
#[context("Writing Grub menuentry")]
627+
fn write_grub_uki_menuentry(
628+
root_path: Utf8PathBuf,
629+
setup_type: &BootSetupType,
630+
boot_label: &String,
631+
id: &Sha256HashValue,
632+
esp_device: &String,
633+
) -> Result<()> {
634+
let boot_dir = root_path.join("boot");
635+
create_dir_all(&boot_dir).context("Failed to create boot dir")?;
636+
637+
let is_upgrade = matches!(setup_type, BootSetupType::Upgrade(..));
638+
639+
let efi_uuid_source = get_efi_uuid_source();
640+
641+
let user_cfg_name = if is_upgrade {
642+
USER_CFG_STAGED
643+
} else {
644+
USER_CFG
645+
};
646+
647+
let grub_dir = Dir::open_ambient_dir(boot_dir.join("grub2"), ambient_authority())
648+
.context("opening boot/grub2")?;
649+
650+
// Iterate over all available deployments, and generate a menuentry for each
651+
//
652+
// TODO: We might find a staged deployment here
653+
if is_upgrade {
654+
let mut buffer = vec![];
655+
656+
// Shouldn't really fail so no context here
657+
buffer.write_all(efi_uuid_source.as_bytes())?;
658+
buffer.write_all(
659+
MenuEntry::new(&boot_label, &id.to_hex())
660+
.to_string()
661+
.as_bytes(),
662+
)?;
663+
664+
let mut str_buf = String::new();
665+
let boot_dir =
666+
Dir::open_ambient_dir(boot_dir, ambient_authority()).context("Opening boot dir")?;
667+
let entries = get_sorted_uki_boot_entries(&boot_dir, &mut str_buf)?;
668+
669+
// Write out only the currently booted entry, which should be the very first one
670+
// Even if we have booted into the second menuentry "boot entry", the default will be the
671+
// first one
672+
buffer.write_all(entries[0].to_string().as_bytes())?;
673+
674+
grub_dir
675+
.atomic_write(user_cfg_name, buffer)
676+
.with_context(|| format!("Writing to {user_cfg_name}"))?;
677+
678+
rustix::fs::fsync(grub_dir.reopen_as_ownedfd()?).context("fsync")?;
679+
680+
return Ok(());
681+
}
682+
683+
// Open grub2/efiuuid.cfg and write the EFI partition fs-UUID in there
684+
// This will be sourced by grub2/user.cfg to be used for `--fs-uuid`
685+
let esp_uuid = Task::new("blkid for ESP UUID", "blkid")
686+
.args(["-s", "UUID", "-o", "value", &esp_device])
687+
.read()?;
688+
689+
grub_dir.atomic_write(
690+
EFI_UUID_FILE,
691+
format!("set EFI_PART_UUID=\"{}\"", esp_uuid.trim()).as_bytes(),
692+
)?;
693+
694+
// Write to grub2/user.cfg
695+
let mut buffer = vec![];
696+
697+
// Shouldn't really fail so no context here
698+
buffer.write_all(efi_uuid_source.as_bytes())?;
699+
buffer.write_all(
700+
MenuEntry::new(&boot_label, &id.to_hex())
701+
.to_string()
702+
.as_bytes(),
703+
)?;
704+
705+
grub_dir
706+
.atomic_write(user_cfg_name, buffer)
707+
.with_context(|| format!("Writing to {user_cfg_name}"))?;
708+
709+
rustix::fs::fsync(grub_dir.reopen_as_ownedfd()?).context("fsync")?;
710+
711+
Ok(())
712+
}
713+
625714
#[context("Setting up UKI boot")]
626715
pub(crate) fn setup_composefs_uki_boot(
627716
setup_type: BootSetupType,
@@ -630,7 +719,7 @@ pub(crate) fn setup_composefs_uki_boot(
630719
id: &Sha256HashValue,
631720
entries: Vec<ComposefsBootEntry<Sha256HashValue>>,
632721
) -> Result<()> {
633-
let (root_path, esp_device, is_insecure_from_opts) = match setup_type {
722+
let (root_path, esp_device, bootloader, is_insecure_from_opts) = match setup_type {
634723
BootSetupType::Setup((root_setup, state, ..)) => {
635724
if let Some(v) = &state.config_opts.karg {
636725
if v.len() > 0 {
@@ -645,22 +734,37 @@ pub(crate) fn setup_composefs_uki_boot(
645734
.find(|p| p.parttype.as_str() == ESP_GUID)
646735
.ok_or_else(|| anyhow!("ESP partition not found"))?;
647736

737+
let bootloader = state
738+
.composefs_options
739+
.as_ref()
740+
.map(|opts| opts.bootloader.clone())
741+
.unwrap_or(Bootloader::default());
742+
743+
let is_insecure = state
744+
.composefs_options
745+
.as_ref()
746+
.map(|x| x.insecure)
747+
.unwrap_or(false);
748+
648749
(
649750
root_setup.physical_root_path.clone(),
650751
esp_part.node.clone(),
651-
state
652-
.composefs_options
653-
.as_ref()
654-
.map(|x| x.insecure)
655-
.unwrap_or(false),
752+
bootloader,
753+
is_insecure,
656754
)
657755
}
658756

659-
BootSetupType::Upgrade(..) => {
757+
BootSetupType::Upgrade((_, host)) => {
660758
let sysroot = Utf8PathBuf::from("/sysroot");
661759
let sysroot_parent = get_sysroot_parent_dev()?;
760+
let bootloader = host.require_composefs_booted()?.bootloader.clone();
662761

663-
(sysroot, get_esp_partition(&sysroot_parent)?.0, false)
762+
(
763+
sysroot,
764+
get_esp_partition(&sysroot_parent)?.0,
765+
bootloader,
766+
false,
767+
)
664768
}
665769
};
666770

@@ -705,84 +809,17 @@ pub(crate) fn setup_composefs_uki_boot(
705809
.run_inherited_with_cmd_context()
706810
.context("Unmounting ESP")?;
707811

708-
let boot_dir = root_path.join("boot");
709-
create_dir_all(&boot_dir).context("Failed to create boot dir")?;
710-
711-
let is_upgrade = matches!(setup_type, BootSetupType::Upgrade(..));
712-
713-
let efi_uuid_source = get_efi_uuid_source();
812+
match bootloader {
813+
Bootloader::Grub => {
814+
write_grub_uki_menuentry(root_path, &setup_type, &boot_label, id, &esp_device)?
815+
}
714816

715-
let user_cfg_name = if is_upgrade {
716-
USER_CFG_STAGED
717-
} else {
718-
USER_CFG
817+
Bootloader::Systemd => {
818+
// No-op for now, but later we want to have .conf files so we can control the order of
819+
// entries.
820+
}
719821
};
720822

721-
let grub_dir =
722-
cap_std::fs::Dir::open_ambient_dir(boot_dir.join("grub2"), cap_std::ambient_authority())
723-
.context("opening boot/grub2")?;
724-
725-
// Iterate over all available deployments, and generate a menuentry for each
726-
//
727-
// TODO: We might find a staged deployment here
728-
if is_upgrade {
729-
let mut buffer = vec![];
730-
731-
// Shouldn't really fail so no context here
732-
buffer.write_all(efi_uuid_source.as_bytes())?;
733-
buffer.write_all(
734-
MenuEntry::new(&boot_label, &id.to_hex())
735-
.to_string()
736-
.as_bytes(),
737-
)?;
738-
739-
let mut str_buf = String::new();
740-
let boot_dir = cap_std::fs::Dir::open_ambient_dir(boot_dir, cap_std::ambient_authority())
741-
.context("Opening boot dir")?;
742-
let entries = get_sorted_uki_boot_entries(&boot_dir, &mut str_buf)?;
743-
744-
// Write out only the currently booted entry, which should be the very first one
745-
// Even if we have booted into the second menuentry "boot entry", the default will be the
746-
// first one
747-
buffer.write_all(entries[0].to_string().as_bytes())?;
748-
749-
grub_dir
750-
.atomic_write(user_cfg_name, buffer)
751-
.with_context(|| format!("Writing to {user_cfg_name}"))?;
752-
753-
rustix::fs::fsync(grub_dir.reopen_as_ownedfd()?).context("fsync")?;
754-
755-
return Ok(());
756-
}
757-
758-
// Open grub2/efiuuid.cfg and write the EFI partition fs-UUID in there
759-
// This will be sourced by grub2/user.cfg to be used for `--fs-uuid`
760-
let esp_uuid = Task::new("blkid for ESP UUID", "blkid")
761-
.args(["-s", "UUID", "-o", "value", &esp_device])
762-
.read()?;
763-
764-
grub_dir.atomic_write(
765-
EFI_UUID_FILE,
766-
format!("set EFI_PART_UUID=\"{}\"", esp_uuid.trim()).as_bytes(),
767-
)?;
768-
769-
// Write to grub2/user.cfg
770-
let mut buffer = vec![];
771-
772-
// Shouldn't really fail so no context here
773-
buffer.write_all(efi_uuid_source.as_bytes())?;
774-
buffer.write_all(
775-
MenuEntry::new(&boot_label, &id.to_hex())
776-
.to_string()
777-
.as_bytes(),
778-
)?;
779-
780-
grub_dir
781-
.atomic_write(user_cfg_name, buffer)
782-
.with_context(|| format!("Writing to {user_cfg_name}"))?;
783-
784-
rustix::fs::fsync(grub_dir.reopen_as_ownedfd()?).context("fsync")?;
785-
786823
Ok(())
787824
}
788825

0 commit comments

Comments
 (0)