Skip to content

Commit

Permalink
efi: Skip adopting for systemd-boot and systemd-stub
Browse files Browse the repository at this point in the history
If the system is booted with systemd-boot or systemd-stub then `bootctl`
will handle updating the boot components. Both of these components write
identifying information to EFI variables as NUL terminated UTF-16
strings. Check these variables to detect if either systemd-boot or
systemd-stub are in use and skip adoption if so.
  • Loading branch information
dbnicholson committed Apr 11, 2023
1 parent 9c9d855 commit a6d0404
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ openssl = "^0.10"
serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"
tempfile = "^3.4"
widestring = "1.0.2"

[profile.release]
# We assume we're being delivered via e.g. RPM which supports split debuginfo
Expand Down
74 changes: 74 additions & 0 deletions src/efi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::process::Command;

use anyhow::{bail, Context, Result};
use openat_ext::OpenatDirExt;
use widestring::U16CString;

use crate::component::*;
use crate::filetree;
Expand All @@ -27,6 +28,10 @@ pub(crate) const ESP_MOUNTS: &[&str] = &["boot/efi", "efi"];
pub(crate) const COREOS_ESP_PART_LABEL: &str = "EFI-SYSTEM";
pub(crate) const ANACONDA_ESP_PART_LABEL: &str = "EFI\\x20System\\x20Partition";

/// Systemd boot loader info EFI variable names
const LOADER_INFO_VAR_STR: &str = "LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f";
const STUB_INFO_VAR_STR: &str = "StubInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f";

#[derive(Default)]
pub(crate) struct Efi {
mountpoint: RefCell<Option<PathBuf>>,
Expand Down Expand Up @@ -105,6 +110,70 @@ impl Efi {
}
}

/// Convert a nul-terminated UTF-16 byte array to a String.
fn string_from_utf16_bytes(slice: &[u8]) -> String {
// For some reason, systemd appends 3 nul bytes after the string.
// Drop the last byte if there's an odd number.
let size = slice.len() / 2;
let v: Vec<u16> = (0..size)
.map(|i| u16::from_ne_bytes([slice[2 * i], slice[2 * i + 1]]))
.collect();
U16CString::from_vec(v).unwrap().to_string_lossy()
}

/// Read a nul-terminated UTF-16 string from an EFI variable.
fn read_efi_var_utf16_string(name: &str) -> Option<String> {
let efivars = Path::new("/sys/firmware/efi/efivars");
if !efivars.exists() {
log::warn!("No efivars mount at {:?}", efivars);
return None;
}
let path = efivars.join(name);
if !path.exists() {
log::trace!("No EFI variable {name}");
return None;
}
match std::fs::read(&path) {
Ok(buf) => {
// Skip the first 4 bytes, those are the EFI variable attributes.
if buf.len() < 4 {
log::warn!("Read less than 4 bytes from {:?}", path);
return None;
}
Some(string_from_utf16_bytes(&buf[4..]))
}
Err(reason) => {
log::warn!("Failed reading {:?}: {reason}", path);
None
}
}
}

/// Read the LoaderInfo EFI variable if it exists.
fn get_loader_info() -> Option<String> {
read_efi_var_utf16_string(LOADER_INFO_VAR_STR)
}

/// Read the StubInfo EFI variable if it exists.
fn get_stub_info() -> Option<String> {
read_efi_var_utf16_string(STUB_INFO_VAR_STR)
}

/// Whether to skip adoption if a systemd bootloader is found.
fn skip_systemd_bootloaders() -> bool {
if let Some(loader_info) = get_loader_info() {
if loader_info.starts_with("systemd") {
log::trace!("Skipping adoption for {:?}", loader_info);
return true;
}
}
if let Some(stub_info) = get_stub_info() {
log::trace!("Skipping adoption for {:?}", stub_info);
return true;
}
false
}

impl Component for Efi {
fn name(&self) -> &'static str {
"EFI"
Expand All @@ -130,6 +199,11 @@ impl Component for Efi {
} else {
log::trace!("No CoreOS aleph detected");
}
// Don't adopt if the system is booted with systemd-boot or
// systemd-stub since those will be managed with bootctl.
if skip_systemd_bootloaders() {
return Ok(None);
}
let ostree_deploy_dir = Path::new("/ostree/deploy");
if ostree_deploy_dir.exists() {
let btime = ostree_deploy_dir.metadata()?.created()?;
Expand Down

0 comments on commit a6d0404

Please sign in to comment.