Skip to content

Commit c59c776

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 525997c commit c59c776

File tree

1 file changed

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

1 file changed

+124
-91
lines changed

crates/lib/src/bootc_composefs/boot.rs

Lines changed: 124 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};
@@ -161,8 +164,7 @@ fn compute_boot_digest(
161164
/// Returns the verity of the deployment that has a boot digest same as the one passed in
162165
#[context("Checking boot entry duplicates")]
163166
fn find_vmlinuz_initrd_duplicates(digest: &str) -> Result<Option<String>> {
164-
let deployments =
165-
cap_std::fs::Dir::open_ambient_dir(STATE_DIR_ABS, cap_std::ambient_authority());
167+
let deployments = Dir::open_ambient_dir(STATE_DIR_ABS, ambient_authority());
166168

167169
let deployments = match deployments {
168170
Ok(d) => d,
@@ -218,7 +220,7 @@ fn write_bls_boot_entries_to_disk(
218220
let path = boot_dir.join(&id_hex);
219221
create_dir_all(&path)?;
220222

221-
let entries_dir = cap_std::fs::Dir::open_ambient_dir(&path, cap_std::ambient_authority())
223+
let entries_dir = Dir::open_ambient_dir(&path, ambient_authority())
222224
.with_context(|| format!("Opening {path}"))?;
223225

224226
entries_dir
@@ -481,9 +483,8 @@ pub(crate) fn setup_composefs_bls_boot(
481483

482484
// Scope to allow for proper unmounting
483485
{
484-
let loader_entries_dir =
485-
cap_std::fs::Dir::open_ambient_dir(&config_path, cap_std::ambient_authority())
486-
.with_context(|| format!("Opening {config_path:?}"))?;
486+
let loader_entries_dir = Dir::open_ambient_dir(&config_path, ambient_authority())
487+
.with_context(|| format!("Opening {config_path:?}"))?;
487488

488489
loader_entries_dir.atomic_write(
489490
// SAFETY: We set sort_key above
@@ -595,7 +596,7 @@ fn write_pe_to_esp(
595596
None => efi_linux_path,
596597
};
597598

598-
let pe_dir = cap_std::fs::Dir::open_ambient_dir(&final_pe_path, cap_std::ambient_authority())
599+
let pe_dir = Dir::open_ambient_dir(&final_pe_path, ambient_authority())
599600
.with_context(|| format!("Opening {final_pe_path:?}"))?;
600601

601602
let pe_name = match pe_type {
@@ -617,6 +618,94 @@ fn write_pe_to_esp(
617618
Ok(boot_label)
618619
}
619620

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

732+
let bootloader = state
733+
.composefs_options
734+
.as_ref()
735+
.map(|opts| opts.bootloader.clone())
736+
.unwrap_or(Bootloader::default());
737+
738+
let is_insecure = state
739+
.composefs_options
740+
.as_ref()
741+
.map(|x| x.insecure)
742+
.unwrap_or(false);
743+
643744
(
644745
root_setup.physical_root_path.clone(),
645746
esp_part.node.clone(),
646-
state
647-
.composefs_options
648-
.as_ref()
649-
.map(|x| x.insecure)
650-
.unwrap_or(false),
747+
bootloader,
748+
is_insecure,
651749
)
652750
}
653751

654-
BootSetupType::Upgrade(..) => {
752+
BootSetupType::Upgrade((_, host)) => {
655753
let sysroot = Utf8PathBuf::from("/sysroot");
656754

657755
let fsinfo = inspect_filesystem(&sysroot)?;
@@ -661,7 +759,9 @@ pub(crate) fn setup_composefs_uki_boot(
661759
anyhow::bail!("Could not find parent device for mountpoint /sysroot");
662760
};
663761

664-
(sysroot, get_esp_partition(&parent)?.0, false)
762+
let bootloader = host.require_composefs_booted()?.bootloader.clone();
763+
764+
(sysroot, get_esp_partition(&parent)?.0, bootloader, false)
665765
}
666766
};
667767

@@ -706,84 +806,17 @@ pub(crate) fn setup_composefs_uki_boot(
706806
.run_inherited_with_cmd_context()
707807
.context("Unmounting ESP")?;
708808

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

716-
let user_cfg_name = if is_upgrade {
717-
USER_CFG_STAGED
718-
} else {
719-
USER_CFG
814+
Bootloader::Systemd => {
815+
// No-op for now, but later we want to have .conf files so we can control the order of
816+
// entries.
817+
}
720818
};
721819

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

0 commit comments

Comments
 (0)