From 47020c7b927f2508b431664ba629172daa8a39e3 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sun, 4 Oct 2026 10:41:47 -0700 Subject: [PATCH] fix(bls): keep the Sprout name and version fields outside strict mode --- crates/bls/src/lib.rs | 22 ++++++++++++++++------ crates/bls/src/uki.rs | 25 +++++++++++++++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/crates/bls/src/lib.rs b/crates/bls/src/lib.rs index a1a0727..dbef8d2 100644 --- a/crates/bls/src/lib.rs +++ b/crates/bls/src/lib.rs @@ -817,20 +817,30 @@ mod tests { } #[test] - fn uki_title_and_version_use_every_systemd_fallback() { + fn uki_title_and_version_use_every_systemd_fallback_in_strict_mode() { let sections = uki_sections(&[( ".osrel", "IMAGE_ID=img\nNAME=N\nID=i\nVERSION=7.1\nVERSION_ID=7\n", )]); - let entry = BlsEntry::from_uki(§ions, "/a.efi"); + let entry = BlsEntry::from_uki_with(§ions, "/a.efi", true); assert_eq!(entry.title.as_deref(), Some("img")); assert_eq!(entry.version.as_deref(), Some("7.1")); let sections = uki_sections(&[(".osrel", "NAME=N\nID=i\n")]); - assert_eq!( - BlsEntry::from_uki(§ions, "/a.efi").title.as_deref(), - Some("N") - ); + let entry = BlsEntry::from_uki_with(§ions, "/a.efi", true); + assert_eq!(entry.title.as_deref(), Some("N")); + } + + #[test] + fn uki_title_and_version_keep_the_sprout_fallbacks_outside_strict_mode() { + // The long VERSION would be added to the title, so it is not used. + let sections = uki_sections(&[( + ".osrel", + "IMAGE_ID=img\nNAME=N\nID=i\nVERSION=\"24.04 LTS (Noble)\"\nVERSION_ID=7\n", + )]); + let entry = BlsEntry::from_uki(§ions, "/a.efi"); + assert_eq!(entry.title.as_deref(), Some("i")); + assert_eq!(entry.version.as_deref(), Some("7")); } #[test] diff --git a/crates/bls/src/uki.rs b/crates/bls/src/uki.rs index bb2b157..b391957 100644 --- a/crates/bls/src/uki.rs +++ b/crates/bls/src/uki.rs @@ -97,10 +97,11 @@ impl BlsEntry { } /// Produces an entry for the unified kernel image at `uki_path`, from its PE `sections`. - /// The title comes from `PRETTY_NAME`, `IMAGE_ID`, `NAME`, then `ID`. The version comes from - /// `IMAGE_VERSION`, `VERSION`, `VERSION_ID`, then `BUILD_ID`, as in systemd-boot, and then - /// from the `.uname` section unless `strict` is set. The sort key comes from `IMAGE_ID`, then - /// `ID`. The embedded command line is kept in `cmdline` and not in `options`, as the image + /// With `strict`, the title comes from `PRETTY_NAME`, `IMAGE_ID`, `NAME`, then `ID`, and the + /// version from `IMAGE_VERSION`, `VERSION`, `VERSION_ID`, then `BUILD_ID`, as in systemd-boot. + /// Otherwise the title comes from `PRETTY_NAME`, then `ID`, and the version from + /// `IMAGE_VERSION`, `VERSION_ID`, `BUILD_ID`, then the `.uname` section. The sort key comes + /// from `IMAGE_ID`, then `ID`. The embedded command line is kept in `cmdline` and not in `options`, as the image /// reads its own command line. pub fn from_uki_with( sections: &BTreeMap>, @@ -117,10 +118,22 @@ impl BlsEntry { .map(ToString::to_string) }; let uname = section_text(sections, ".uname"); - let version = first(&["IMAGE_VERSION", "VERSION", "VERSION_ID", "BUILD_ID"]); + // systemd-boot looks at more fields. Outside of strict mode the version is not taken from + // VERSION, which is often a long name that would be added to the title. + let (title, version) = if strict { + ( + first(&["PRETTY_NAME", "IMAGE_ID", "NAME", "ID"]), + first(&["IMAGE_VERSION", "VERSION", "VERSION_ID", "BUILD_ID"]), + ) + } else { + ( + first(&["PRETTY_NAME", "ID"]), + first(&["IMAGE_VERSION", "VERSION_ID", "BUILD_ID"]), + ) + }; Self { - title: first(&["PRETTY_NAME", "IMAGE_ID", "NAME", "ID"]), + title, version: version.or_else(|| uname.clone().filter(|_| !strict)), sort_key: first(&["IMAGE_ID", "ID"]), uki: Some(uki_path.to_string()),