mirror of
https://github.com/edera-dev/sprout.git
synced 2026-11-04 16:42:20 +00:00
feat(bls): follow the systemd-boot fallbacks for unified kernel image names
This commit is contained in:
1 parent
7e1207097b
commit
bd37e9058c
2 files changed
+76
-7
No files matched your search
@@ -199,6 +199,16 @@ impl BlsEntry {
|
|||||||
self.linux.is_some() || self.efi.is_some() || self.uki.is_some()
|
self.linux.is_some() || self.efi.is_some() || self.uki.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the entry names more than one of `linux`, `efi` and `uki` to boot.
|
||||||
|
/// systemd-boot considers such an entry broken and does not show it.
|
||||||
|
pub fn mixes_boot_targets(&self) -> bool {
|
||||||
|
[&self.linux, &self.efi, &self.uki]
|
||||||
|
.iter()
|
||||||
|
.filter(|target| target.is_some())
|
||||||
|
.count()
|
||||||
|
> 1
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the entry only boots a unified kernel image, which carries its own initrd.
|
/// Whether the entry only boots a unified kernel image, which carries its own initrd.
|
||||||
fn is_uki_only(&self) -> bool {
|
fn is_uki_only(&self) -> bool {
|
||||||
self.linux.is_none() && self.efi.is_none() && self.uki.is_some()
|
self.linux.is_none() && self.efi.is_none() && self.uki.is_some()
|
||||||
@@ -806,6 +816,49 @@ mod tests {
|
|||||||
assert!(entry.is_valid());
|
assert!(entry.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uki_title_and_version_use_every_systemd_fallback() {
|
||||||
|
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");
|
||||||
|
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")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uki_version_only_falls_back_to_uname_outside_strict_mode() {
|
||||||
|
let sections = uki_sections(&[(".osrel", "ID=a\n"), (".uname", "6.1.2\n")]);
|
||||||
|
let loose = BlsEntry::from_uki_with(§ions, "/a.efi", false);
|
||||||
|
assert_eq!(loose.version.as_deref(), Some("6.1.2"));
|
||||||
|
let strict = BlsEntry::from_uki_with(§ions, "/a.efi", true);
|
||||||
|
assert_eq!(strict.version, None);
|
||||||
|
// The kernel version is still there, it is just not the version of the entry.
|
||||||
|
assert_eq!(strict.uname.as_deref(), Some("6.1.2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn entries_that_mix_boot_targets_are_found() {
|
||||||
|
for (input, mixed) in [
|
||||||
|
("linux /v\n", false),
|
||||||
|
("efi /e.efi\n", false),
|
||||||
|
("uki /u.efi\n", false),
|
||||||
|
("linux /v\nefi /e.efi\n", true),
|
||||||
|
("linux /v\nuki /u.efi\n", true),
|
||||||
|
("efi /e.efi\nuki /u.efi\n", true),
|
||||||
|
] {
|
||||||
|
let entry: BlsEntry = input.parse().unwrap();
|
||||||
|
assert_eq!(entry.mixes_boot_targets(), mixed, "{input}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn uki_entry_falls_back_through_the_fields() {
|
fn uki_entry_falls_back_through_the_fields() {
|
||||||
let sections = uki_sections(&[(".osrel", "ID=arch\nBUILD_ID=rolling\n")]);
|
let sections = uki_sections(&[(".osrel", "ID=arch\nBUILD_ID=rolling\n")]);
|
||||||
|
|||||||
+23
-7
@@ -75,7 +75,12 @@ impl BlsEntry {
|
|||||||
/// Produces an entry for one profile of the unified kernel image at `uki_path`.
|
/// Produces an entry for one profile of the unified kernel image at `uki_path`.
|
||||||
/// A profile after the first is booted with `@<number>` in its load options.
|
/// A profile after the first is booted with `@<number>` in its load options.
|
||||||
pub fn from_uki_profile(profile: &UkiProfile, uki_path: &str) -> Self {
|
pub fn from_uki_profile(profile: &UkiProfile, uki_path: &str) -> Self {
|
||||||
let mut entry = Self::from_uki(&profile.sections, uki_path);
|
Self::from_uki_profile_with(profile, uki_path, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like [BlsEntry::from_uki_profile], and `strict` follows systemd-boot exactly.
|
||||||
|
pub fn from_uki_profile_with(profile: &UkiProfile, uki_path: &str, strict: bool) -> Self {
|
||||||
|
let mut entry = Self::from_uki_with(&profile.sections, uki_path, strict);
|
||||||
if let Some(index) = profile.index {
|
if let Some(index) = profile.index {
|
||||||
entry.profile = (index > 0).then(|| index.to_string());
|
entry.profile = (index > 0).then(|| index.to_string());
|
||||||
entry.title = entry
|
entry.title = entry
|
||||||
@@ -87,11 +92,21 @@ impl BlsEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Produces an entry for the unified kernel image at `uki_path`, from its PE `sections`.
|
/// Produces an entry for the unified kernel image at `uki_path`, from its PE `sections`.
|
||||||
/// The title comes from `PRETTY_NAME`, then `ID`. The version comes 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(sections: &BTreeMap<String, Vec<u8>>, uki_path: &str) -> Self {
|
pub fn from_uki(sections: &BTreeMap<String, Vec<u8>>, uki_path: &str) -> Self {
|
||||||
|
Self::from_uki_with(sections, uki_path, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// reads its own command line.
|
||||||
|
pub fn from_uki_with(
|
||||||
|
sections: &BTreeMap<String, Vec<u8>>,
|
||||||
|
uki_path: &str,
|
||||||
|
strict: bool,
|
||||||
|
) -> Self {
|
||||||
let os_release = section_text(sections, ".osrel")
|
let os_release = section_text(sections, ".osrel")
|
||||||
.map(|text| OsRelease::parse(&text))
|
.map(|text| OsRelease::parse(&text))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
@@ -102,10 +117,11 @@ impl BlsEntry {
|
|||||||
.map(ToString::to_string)
|
.map(ToString::to_string)
|
||||||
};
|
};
|
||||||
let uname = section_text(sections, ".uname");
|
let uname = section_text(sections, ".uname");
|
||||||
|
let version = first(&["IMAGE_VERSION", "VERSION", "VERSION_ID", "BUILD_ID"]);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
title: first(&["PRETTY_NAME", "ID"]),
|
title: first(&["PRETTY_NAME", "IMAGE_ID", "NAME", "ID"]),
|
||||||
version: first(&["IMAGE_VERSION", "VERSION_ID", "BUILD_ID"]).or_else(|| uname.clone()),
|
version: version.or_else(|| uname.clone().filter(|_| !strict)),
|
||||||
sort_key: first(&["IMAGE_ID", "ID"]),
|
sort_key: first(&["IMAGE_ID", "ID"]),
|
||||||
uki: Some(uki_path.to_string()),
|
uki: Some(uki_path.to_string()),
|
||||||
cmdline: section_text(sections, ".cmdline"),
|
cmdline: section_text(sections, ".cmdline"),
|
||||||
|
|||||||
Reference in new issue
Block a user