From f48158e31f39edf515823e25880cf71e34639963 Mon Sep 17 00:00:00 2001 From: Dudecake Date: Wed, 10 Jun 2026 12:30:58 +0000 Subject: [PATCH 1/3] Filter out BLS entries not managed by bootc --- crates/lib/src/bootc_composefs/status.rs | 19 ++++++++++++++----- crates/lib/src/composefs_consts.rs | 3 +++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/crates/lib/src/bootc_composefs/status.rs b/crates/lib/src/bootc_composefs/status.rs index 16b469206..64ac317fc 100644 --- a/crates/lib/src/bootc_composefs/status.rs +++ b/crates/lib/src/bootc_composefs/status.rs @@ -19,6 +19,7 @@ use crate::{ composefs_consts::{ COMPOSEFS_CMDLINE, ORIGIN_KEY_BOOT_DIGEST, ORIGIN_KEY_IMAGE, ORIGIN_KEY_MANIFEST_DIGEST, TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED, USER_CFG, USER_CFG_STAGED, + BLS_ENTRY_PREFIX, }, install::EFI_LOADER_INFO, parsers::{ @@ -304,7 +305,7 @@ fn get_sorted_type1_boot_entries_helper( .to_str() .ok_or(anyhow::anyhow!("Found non UTF-8 characters in filename"))?; - if !file_name.ends_with(".conf") { + if !(file_name.starts_with(BLS_ENTRY_PREFIX) && file_name.ends_with(".conf")) { continue; } @@ -1094,8 +1095,16 @@ mod tests { "loader/entries/random_file.txt", "Random file that we won't parse", )?; - tempdir.atomic_write("loader/entries/entry1.conf", entry1)?; - tempdir.atomic_write("loader/entries/entry2.conf", entry2)?; + tempdir.atomic_write( + "loader/entries/random_file.conf", + "Random file that we won't parse", + )?; + tempdir.atomic_write( + "loader/entries/bootc_random_file.txt", + "Random file that we won't parse", + )?; + tempdir.atomic_write("loader/entries/bootc_entry1.conf", entry1)?; + tempdir.atomic_write("loader/entries/bootc_entry2.conf", entry2)?; let result = get_sorted_type1_boot_entries_helper(&tempdir, true, false, Bootloader::Systemd) @@ -1257,8 +1266,8 @@ mod tests { tempdir.create_dir_all("loader/entries")?; tempdir.create_dir_all("loader/entries.staged")?; - tempdir.atomic_write("loader/entries/active.conf", active_entry)?; - tempdir.atomic_write("loader/entries.staged/staged.conf", staged_entry)?; + tempdir.atomic_write("loader/entries/bootc_active.conf", active_entry)?; + tempdir.atomic_write("loader/entries.staged/bootc_staged.conf", staged_entry)?; let result = list_type1_entries(&tempdir)?; assert_eq!(result.len(), 2); diff --git a/crates/lib/src/composefs_consts.rs b/crates/lib/src/composefs_consts.rs index 8617f1005..d53255572 100644 --- a/crates/lib/src/composefs_consts.rs +++ b/crates/lib/src/composefs_consts.rs @@ -48,6 +48,9 @@ pub(crate) const TYPE1_BOOT_DIR_PREFIX: &str = "bootc_composefs-"; /// The prefix for names of UKI and UKI Addons pub(crate) const UKI_NAME_PREFIX: &str = TYPE1_BOOT_DIR_PREFIX; +/// The prefix for BLS file entries +pub(crate) const BLS_ENTRY_PREFIX: &str = "bootc_"; + /// Prefix for OCI tags owned by bootc in the composefs repository. /// /// Tags are created as `localhost/bootc-` to act as GC roots From e50f288f3d07044a4dffa7d1b67139efc975b317 Mon Sep 17 00:00:00 2001 From: Dudecake Date: Wed, 10 Jun 2026 12:37:32 +0000 Subject: [PATCH 2/3] Copy unmanaged BLS entries to staging dir on upgrade --- .../backwards_compat/bcompat_boot.rs | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/crates/lib/src/bootc_composefs/backwards_compat/bcompat_boot.rs b/crates/lib/src/bootc_composefs/backwards_compat/bcompat_boot.rs index f624bbead..22315f1df 100644 --- a/crates/lib/src/bootc_composefs/backwards_compat/bcompat_boot.rs +++ b/crates/lib/src/bootc_composefs/backwards_compat/bcompat_boot.rs @@ -13,8 +13,7 @@ use crate::{ }, }, composefs_consts::{ - ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_TYPE, STATE_DIR_RELATIVE, TYPE1_BOOT_DIR_PREFIX, - TYPE1_ENT_PATH_STAGED, UKI_NAME_PREFIX, USER_CFG_STAGED, + BLS_ENTRY_PREFIX, ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_TYPE, STATE_DIR_RELATIVE, TYPE1_BOOT_DIR_PREFIX, TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED, UKI_NAME_PREFIX, USER_CFG_STAGED }, parsers::bls_config::{BLSConfig, BLSConfigType}, spec::Bootloader, @@ -240,6 +239,15 @@ fn create_staged_bls_entries(boot_dir: &Dir, entries: &Vec<(String, BLSConfig)>) staged_entries.atomic_write(filename, new_entry.to_string().as_bytes())?; } + let original_entries = boot_dir.open_dir(TYPE1_ENT_PATH)?; + for entry in original_entries.entries()? { + let entry = entry?; + let entry_name = entry.file_name(); + if entry.file_type()?.is_file() && !entry_name.to_string_lossy().starts_with(BLS_ENTRY_PREFIX) { + original_entries.copy(entry.file_name(), &staged_entries, entry_name)?; + } + } + fsync(staged_entries.reopen_as_ownedfd()?).context("fsync") } @@ -394,3 +402,58 @@ pub(crate) async fn prepend_custom_prefix( Ok(()) } + +#[cfg(test)] +mod tests { + use anyhow::Result; + use cap_std_ext::{cap_std, dirext::CapStdExtDirExt}; + use crate::bootc_composefs::backwards_compat::bcompat_boot::*; + use crate::parsers::bls_config::parse_bls_config; + + #[test] + fn test_create_staged_bls_entries() -> Result<()> { + let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?; + + let entry1 = r#" + title Fedora 42.20250623.3.1 (CoreOS) + version fedora-42.0 + sort-key 1 + linux /boot/7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6/vmlinuz-5.14.10 + initrd /boot/7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6/initramfs-5.14.10.img + options root=UUID=abc123 rw composefs=7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6 + "#; + let entry2 = r#" + title Fedora 41.20250214.2.0 (CoreOS) + version fedora-42.0 + sort-key 2 + efi /EFI/Linux/f7415d75017a12a387a39d2281e033a288fc15775108250ef70a01dcadb93346.efi + options root=UUID=abc123 rw composefs=febdf62805de2ae7b6b597f2a9775d9c8a753ba1e5f09298fc8fbe0b0d13bf01 + "#; + let entry3 = r#" + title Test + version 1 + sort-key 9 + efi /EFI/boot/test.efi + "#; + + tempdir.create_dir_all("loader/entries")?; + tempdir.atomic_write("loader/entries/bootc_entry1.conf", entry1)?; + tempdir.atomic_write("loader/entries/bootc_entry2.conf", entry2)?; + tempdir.atomic_write("loader/entries/entry3.conf", entry3)?; + + let entries = vec![ + ("bootc_entry1.conf".to_string(), parse_bls_config(entry1).unwrap()), + ("bootc_entry2.conf".to_string(), parse_bls_config(entry2).unwrap()), + ]; + + create_staged_bls_entries(&tempdir, &entries).unwrap(); + + assert!(tempdir.exists("loader/entries.staged/bootc_entry1.conf")); + assert!(tempdir.exists("loader/entries.staged/bootc_entry2.conf")); + assert!(tempdir.exists("loader/entries.staged/entry3.conf")); + + assert_eq!(str::from_utf8(&tempdir.read("loader/entries.staged/entry3.conf")?[..])?, entry3); + + Ok(()) + } +} From 80be97c88f17021544501b3df189927cf404a278 Mon Sep 17 00:00:00 2001 From: Dudecake Date: Wed, 10 Jun 2026 12:37:32 +0000 Subject: [PATCH 3/3] Added tests for non-confirming efi BLS entries --- crates/lib/src/parsers/bls_config.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/crates/lib/src/parsers/bls_config.rs b/crates/lib/src/parsers/bls_config.rs index c72674a08..5cfe66055 100644 --- a/crates/lib/src/parsers/bls_config.rs +++ b/crates/lib/src/parsers/bls_config.rs @@ -810,6 +810,34 @@ mod tests { Ok(()) } + #[test] + fn test_boot_artifact_verity_efi_no_prefix() -> Result<()> { + let input = r#" + title Test + version 1 + efi /EFI/boot/test.efi + "#; + let config = parse_bls_config(&input)?; + + let result = config.get_verity(); + assert!(result.is_err()); + Ok(()) + } + + #[test] + fn test_boot_artifact_verity_efi_no_extension() -> Result<()> { + let input = r#" + title Test + version 1 + efi /EFI/boot/test + "#; + let config = parse_bls_config(&input)?; + + let result = config.get_verity(); + assert!(result.is_err()); + Ok(()) + } + /// Test that Non-EFI boot_artifact_name fails when linux path has no parent #[test] fn test_boot_artifact_name_non_efi_no_parent() {