feat(bls): initial support for sorting of entries, not using version comparison

This commit is contained in:
2025-11-01 20:41:30 -04:00
parent 8a5dc33b5a
commit 3a54970386
2 changed files with 100 additions and 5 deletions

View File

@@ -4,6 +4,7 @@ use crate::generators::bls::entry::BlsEntry;
use crate::utils; use crate::utils;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use uefi::cstr16; use uefi::cstr16;
@@ -40,6 +41,55 @@ fn quirk_initrd_remove_tuned(input: String) -> String {
input.replace("$tuned_initrd", "").trim().to_string() input.replace("$tuned_initrd", "").trim().to_string()
} }
/// Sorts two entries according to the BLS sort system.
/// Reference: https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting
fn sort_entries(a: &(BlsEntry, BootableEntry), b: &(BlsEntry, BootableEntry)) -> Ordering {
// Grab the components of both entries.
let (a_bls, a_boot) = a;
let (b_bls, b_boot) = b;
// Grab the sort keys from both entries.
let a_sort_key = a_bls.sort_key();
let b_sort_key = b_bls.sort_key();
// Compare the sort keys of both entries.
match a_sort_key.cmp(&b_sort_key) {
// If A and B sort keys are equal, sort by machine-id.
Ordering::Equal => {
// Grab the machine-id from both entries.
let a_machine_id = a_bls.machine_id();
let b_machine_id = b_bls.machine_id();
// Compare the machine-id of both entries.
match a_machine_id.cmp(&b_machine_id) {
// If both machine-id values are equal, sort by version.
Ordering::Equal => {
// Grab the version from both entries.
let a_version = a_bls.version();
let b_version = b_bls.version();
// Compare the version of both entries, sorting newer versions first.
match b_version.cmp(&a_version) {
// If both versions are equal, sort by file name in reverse order.
Ordering::Equal => {
// Grab the file name from both entries.
let a_name = a_boot.name();
let b_name = b_boot.name();
// Compare the file names of both entries, sorting newer entries first.
b_name.cmp(a_name)
}
other => other,
}
}
other => other,
}
}
other => other,
}
}
/// Generates entries from the BLS entries directory using the specified `bls` configuration and /// Generates entries from the BLS entries directory using the specified `bls` configuration and
/// `context`. The BLS conversion is best-effort and will ignore any unsupported entries. /// `context`. The BLS conversion is best-effort and will ignore any unsupported entries.
pub fn generate(context: Rc<SproutContext>, bls: &BlsConfiguration) -> Result<Vec<BootableEntry>> { pub fn generate(context: Rc<SproutContext>, bls: &BlsConfiguration) -> Result<Vec<BootableEntry>> {
@@ -132,9 +182,11 @@ pub fn generate(context: Rc<SproutContext>, bls: &BlsConfiguration) -> Result<Ve
context.set("chainload", chainload); context.set("chainload", chainload);
context.set("options", options); context.set("options", options);
context.set("initrd", initrd); context.set("initrd", initrd);
context.set("version", entry.version().unwrap_or_default());
context.set("machine-id", entry.machine_id().unwrap_or_default());
// Produce a new bootable entry. // Produce a new bootable entry.
let mut entry = BootableEntry::new( let mut boot = BootableEntry::new(
name, name,
bls.entry.title.clone(), bls.entry.title.clone(),
context.freeze(), context.freeze(),
@@ -144,11 +196,15 @@ pub fn generate(context: Rc<SproutContext>, bls: &BlsConfiguration) -> Result<Ve
// Pin the entry name to prevent prefixing. // Pin the entry name to prevent prefixing.
// This is needed as the bootloader interface requires the name to be // This is needed as the bootloader interface requires the name to be
// the same as the entry file name, minus the .conf extension. // the same as the entry file name, minus the .conf extension.
entry.mark_pin_name(); boot.mark_pin_name();
// Add the entry to the list with a frozen context. // Add the BLS entry to the list, along with the bootable entry.
entries.push(entry); entries.push((entry, boot));
} }
Ok(entries) // Sort all the entries according to the BLS sort system.
entries.sort_by(sort_entries);
// Collect all the bootable entries and return them.
Ok(entries.into_iter().map(|(_, boot)| boot).collect())
} }

View File

@@ -15,6 +15,12 @@ pub struct BlsEntry {
pub initrd: Option<String>, pub initrd: Option<String>,
/// The path to an EFI image. /// The path to an EFI image.
pub efi: Option<String>, pub efi: Option<String>,
/// The sort key for the entry.
pub sort_key: Option<String>,
/// The version of the entry.
pub version: Option<String>,
/// The machine id of the entry.
pub machine_id: Option<String>,
} }
/// Parser for a BLS entry. /// Parser for a BLS entry.
@@ -30,6 +36,9 @@ impl FromStr for BlsEntry {
let mut linux: Option<String> = None; let mut linux: Option<String> = None;
let mut initrd: Option<String> = None; let mut initrd: Option<String> = None;
let mut efi: Option<String> = None; let mut efi: Option<String> = None;
let mut sort_key: Option<String> = None;
let mut version: Option<String> = None;
let mut machine_id: Option<String> = None;
// Iterate over each line in the input and parse it. // Iterate over each line in the input and parse it.
for line in input.lines() { for line in input.lines() {
@@ -74,6 +83,18 @@ impl FromStr for BlsEntry {
efi = Some(value.trim().to_string()); efi = Some(value.trim().to_string());
} }
"sort-key" => {
sort_key = Some(value.trim().to_string());
}
"version" => {
version = Some(value.trim().to_string());
}
"machine-id" => {
machine_id = Some(value.trim().to_string());
}
// Ignore any other key. // Ignore any other key.
_ => { _ => {
continue; continue;
@@ -88,6 +109,9 @@ impl FromStr for BlsEntry {
linux, linux,
initrd, initrd,
efi, efi,
sort_key,
version,
machine_id,
}) })
} }
} }
@@ -125,4 +149,19 @@ impl BlsEntry {
pub fn title(&self) -> Option<String> { pub fn title(&self) -> Option<String> {
self.title.clone() self.title.clone()
} }
/// Fetches the sort key of the entry, if any.
pub fn sort_key(&self) -> Option<String> {
self.sort_key.clone()
}
/// Fetches the version of the entry, if any.
pub fn version(&self) -> Option<String> {
self.version.clone()
}
/// Fetches the machine id of the entry, if any.
pub fn machine_id(&self) -> Option<String> {
self.machine_id.clone()
}
} }