diff --git a/src/bios.rs b/src/bios.rs index fbe12856..5dbb6eac 100644 --- a/src/bios.rs +++ b/src/bios.rs @@ -8,10 +8,23 @@ use crate::packagesystem; use anyhow::{bail, Result}; use crate::util; +use serde::{Deserialize, Serialize}; // grub2-install file path pub(crate) const GRUB_BIN: &str = "usr/sbin/grub2-install"; +#[derive(Serialize, Deserialize, Debug)] +struct BlockDevice { + path: String, + pttype: String, + parttypename: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Devices { + blockdevices: Vec, +} + #[derive(Default)] pub(crate) struct Bios {} @@ -22,12 +35,11 @@ impl Bios { #[cfg(target_arch = "x86_64")] { // find /boot partition - let boot_dir = Path::new("/").join("boot"); cmd = Command::new("findmnt"); cmd.arg("--noheadings") .arg("--output") .arg("SOURCE") - .arg(boot_dir); + .arg("/boot"); let partition = util::cmd_output(&mut cmd)?; // lsblk to find parent device @@ -81,6 +93,38 @@ impl Bios { } Ok(()) } + + // check bios_boot partition on gpt type disk + fn get_bios_boot_partition(&self) -> Result> { + let target = self.get_device()?; + // lsblk to list children with bios_boot + let output = Command::new("lsblk") + .args([ + "--json", + "--output", + "PATH,PTTYPE,PARTTYPENAME", + target.trim(), + ]) + .output()?; + if !output.status.success() { + std::io::stderr().write_all(&output.stderr)?; + bail!("Failed to run lsblk"); + } + + let output = String::from_utf8(output.stdout)?; + // Parse the JSON string into the `Devices` struct + let devices: Devices = serde_json::from_str(&output).expect("JSON was not well-formatted"); + + // Find the device with the parttypename "BIOS boot" + for device in devices.blockdevices { + if let Some(parttypename) = &device.parttypename { + if parttypename == "BIOS boot" && device.pttype == "gpt" { + return Ok(Some(device.path)); + } + } + } + Ok(None) + } } impl Component for Bios { @@ -120,6 +164,10 @@ impl Component for Bios { } fn query_adopt(&self) -> Result> { + if crate::component::is_efi_booted()? && self.get_bios_boot_partition()?.is_none() { + log::debug!("Skip BIOS adopt"); + return Ok(None); + } crate::component::query_adopt_state() } diff --git a/src/bootupd.rs b/src/bootupd.rs index dd441096..6f407f4e 100644 --- a/src/bootupd.rs +++ b/src/bootupd.rs @@ -136,7 +136,7 @@ pub(crate) fn get_components_impl(auto: bool) -> Components { #[cfg(target_arch = "x86_64")] { if auto { - let is_efi_booted = crate::efi::is_efi_booted().unwrap(); + let is_efi_booted = crate::component::is_efi_booted().unwrap(); log::info!( "System boot method: {}", if is_efi_booted { "EFI" } else { "BIOS" } @@ -380,11 +380,7 @@ pub(crate) fn print_status(status: &Status) -> Result<()> { #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] { - let boot_method = if Path::new("/sys/firmware/efi").exists() { - "EFI" - } else { - "BIOS" - }; + let boot_method = if crate::component::is_efi_booted()? { "EFI" } else { "BIOS" }; println!("Boot method: {}", boot_method); } diff --git a/src/component.rs b/src/component.rs index dfff20d5..8f800814 100644 --- a/src/component.rs +++ b/src/component.rs @@ -77,6 +77,13 @@ pub(crate) trait Component { fn get_efi_vendor(&self, sysroot: &openat::Dir) -> Result>; } +/// Return `true` if the system is booted via EFI +pub(crate) fn is_efi_booted() -> Result { + Path::new("/sys/firmware/efi") + .try_exists() + .map_err(Into::into) +} + /// Given a component name, create an implementation. pub(crate) fn new_from_name(name: &str) -> Result> { let r: Box = match name { diff --git a/src/efi.rs b/src/efi.rs index 2ab46c24..422a88c7 100644 --- a/src/efi.rs +++ b/src/efi.rs @@ -44,13 +44,6 @@ pub(crate) const ANACONDA_ESP_PART_LABEL: &str = "EFI\\x20System\\x20Partition"; const LOADER_INFO_VAR_STR: &str = "LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"; const STUB_INFO_VAR_STR: &str = "StubInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"; -/// Return `true` if the system is booted via EFI -pub(crate) fn is_efi_booted() -> Result { - Path::new("/sys/firmware/efi") - .try_exists() - .map_err(Into::into) -} - #[derive(Default)] pub(crate) struct Efi { mountpoint: RefCell>, @@ -63,6 +56,10 @@ impl Efi { } fn open_esp_optional(&self) -> Result> { + if !crate::component::is_efi_booted()? && self.get_esp_device().is_none() { + log::debug!("Skip EFI"); + return Ok(None); + } let sysroot = openat::Dir::open("/")?; let esp = sysroot.sub_dir_optional(&self.esp_path()?)?; Ok(esp) @@ -75,6 +72,20 @@ impl Efi { Ok(esp) } + fn get_esp_device(&self) -> Option { + let esp_devices = [COREOS_ESP_PART_LABEL, ANACONDA_ESP_PART_LABEL] + .into_iter() + .map(|p| Path::new("/dev/disk/by-partlabel/").join(p)); + let mut esp_device = None; + for path in esp_devices { + if path.exists() { + esp_device = Some(path); + break; + } + } + return esp_device; + } + pub(crate) fn ensure_mounted_esp(&self, root: &Path) -> Result { let mut mountpoint = self.mountpoint.borrow_mut(); if let Some(mountpoint) = mountpoint.as_deref() { @@ -94,17 +105,9 @@ impl Efi { return Ok(mnt); } - let esp_devices = [COREOS_ESP_PART_LABEL, ANACONDA_ESP_PART_LABEL] - .into_iter() - .map(|p| Path::new("/dev/disk/by-partlabel/").join(p)); - let mut esp_device = None; - for path in esp_devices { - if path.exists() { - esp_device = Some(path); - break; - } - } - let esp_device = esp_device.ok_or_else(|| anyhow::anyhow!("Failed to find ESP device"))?; + let esp_device = self + .get_esp_device() + .ok_or_else(|| anyhow::anyhow!("Failed to find ESP device"))?; for &mnt in ESP_MOUNTS.iter() { let mnt = root.join(mnt); if !mnt.exists() { @@ -137,7 +140,7 @@ impl Efi { #[context("Updating EFI firmware variables")] fn update_firmware(&self, device: &str, espdir: &openat::Dir, vendordir: &str) -> Result<()> { - if !is_efi_booted()? { + if !crate::component::is_efi_booted()? { log::debug!("Not booted via EFI, skipping firmware update"); return Ok(()); } @@ -386,6 +389,9 @@ impl Component for Efi { } fn validate(&self, current: &InstalledContent) -> Result { + if !crate::component::is_efi_booted()? && self.get_esp_device().is_none() { + return Ok(ValidationResult::Skip); + } let currentf = current .filetree .as_ref()