From 2d877c7b416440fc5cc0830967d08cc85c5ca121 Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Thu, 18 Sep 2025 15:35:44 +0530 Subject: [PATCH] efivars: Parse efivar as UTF-16 LE bytes Signed-off-by: Pragyan Poudyal --- crates/lib/src/bootc_composefs/status.rs | 18 +++----- crates/lib/src/utils.rs | 57 ++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/crates/lib/src/bootc_composefs/status.rs b/crates/lib/src/bootc_composefs/status.rs index f99009a7b..7e8d67517 100644 --- a/crates/lib/src/bootc_composefs/status.rs +++ b/crates/lib/src/bootc_composefs/status.rs @@ -12,6 +12,7 @@ use crate::{ grub_menuconfig::{parse_grub_menuentry_file, MenuEntry}, }, spec::{BootEntry, BootOrder, Host, HostSpec, ImageReference, ImageStatus}, + utils::{read_uefi_var, EfiError}, }; use std::str::FromStr; @@ -32,7 +33,6 @@ use crate::composefs_consts::{ COMPOSEFS_STAGED_DEPLOYMENT_FNAME, COMPOSEFS_TRANSIENT_STATE_DIR, ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_TYPE, STATE_DIR_RELATIVE, }; -use crate::install::EFIVARFS; use crate::spec::Bootloader; /// A parsed composefs command line @@ -153,16 +153,9 @@ async fn get_container_manifest_and_config( #[context("Getting bootloader")] fn get_bootloader() -> Result { - let efivarfs = match Dir::open_ambient_dir(EFIVARFS, ambient_authority()) { - Ok(dir) => dir, - // Most likely using BIOS - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Bootloader::Grub), - Err(e) => Err(e).context(format!("Opening {EFIVARFS}"))?, - }; - const EFI_LOADER_INFO: &str = "LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"; - match efivarfs.read_to_string(EFI_LOADER_INFO) { + match read_uefi_var(EFI_LOADER_INFO) { Ok(loader) => { if loader.to_lowercase().contains("systemd-boot") { return Ok(Bootloader::Systemd); @@ -171,9 +164,12 @@ fn get_bootloader() -> Result { return Ok(Bootloader::Grub); } - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Bootloader::Grub), + Err(efi_error) => match efi_error { + EfiError::SystemNotUEFI => return Ok(Bootloader::Grub), + EfiError::MissingVar => return Ok(Bootloader::Grub), - Err(e) => Err(e).context(format!("Opening {EFI_LOADER_INFO}"))?, + e => return Err(anyhow::anyhow!("Failed to read EfiLoaderInfo: {e:?}")), + }, } } diff --git a/crates/lib/src/utils.rs b/crates/lib/src/utils.rs index 712a1fc1f..8f5664a41 100644 --- a/crates/lib/src/utils.rs +++ b/crates/lib/src/utils.rs @@ -188,6 +188,63 @@ pub(crate) fn digested_pullspec(image: &str, digest: &str) -> String { format!("{image}@{digest}") } +#[cfg(feature = "composefs-backend")] +#[derive(Debug)] +pub enum EfiError { + SystemNotUEFI, + MissingVar, + #[allow(dead_code)] + InvalidData(&'static str), + #[allow(dead_code)] + Io(std::io::Error), +} + +#[cfg(feature = "composefs-backend")] +impl From for EfiError { + fn from(e: std::io::Error) -> Self { + EfiError::Io(e) + } +} + +#[cfg(feature = "composefs-backend")] +pub fn read_uefi_var(var_name: &str) -> Result { + use crate::install::EFIVARFS; + use cap_std_ext::cap_std::ambient_authority; + + let efivarfs = match Dir::open_ambient_dir(EFIVARFS, ambient_authority()) { + Ok(dir) => dir, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Err(EfiError::SystemNotUEFI), + Err(e) => Err(e)?, + }; + + match efivarfs.read(var_name) { + Ok(loader_bytes) => { + if loader_bytes.len() % 2 != 0 { + return Err(EfiError::InvalidData( + "EFI var length is not valid UTF-16 LE", + )); + } + + // EFI vars are UTF-16 LE + let loader_u16_bytes: Vec = loader_bytes + .chunks_exact(2) + .map(|x| u16::from_le_bytes([x[0], x[1]])) + .collect(); + + let loader = String::from_utf16(&loader_u16_bytes) + .map_err(|_| EfiError::InvalidData("EFI var is not UTF-16"))?; + + return Ok(loader); + } + + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return Err(EfiError::MissingVar); + } + + Err(e) => Err(e)?, + } +} + /// Computes a relative path from `from` to `to`. /// /// Both `from` and `to` must be absolute paths.