Skip to content

Commit

Permalink
manager: Add automatic mounting of ESP/XBOOTLDR in native mode
Browse files Browse the repository at this point in the history
Signed-off-by: Ikey Doherty <ikey@serpentos.com>
  • Loading branch information
ikeycode committed Jul 6, 2024
1 parent f49ce49 commit 4d86141
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ members = [
log = "0.4.21"
gpt = "3.1.0"
thiserror = "1"
nix = { version = "0.28.0", features = ["fs"] }
nix = { version = "0.28.0", features = ["fs", "mount"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = "1.8.0", features = ["v8"] }
Expand Down
4 changes: 3 additions & 1 deletion blsctl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ fn inspect_root(config: &Configuration) -> color_eyre::Result<()> {
log::info!("Kernels: {kernels:?}");

// Query the manager
let _ = Manager::new(config)?.with_kernels(kernels);
let manager = Manager::new(config)?.with_kernels(kernels);
let _parts = manager.mount_partitions()?;
eprintln!("manager = {manager:?}");

Ok(())
}
Expand Down
11 changes: 8 additions & 3 deletions blsforme/src/bootenv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ pub struct BootEnvironment {
esp: Option<PathBuf>,

/// Firmware in use
firmware: Firmware,
pub(crate) firmware: Firmware,

esp_mountpoint: Option<PathBuf>,
xboot_mountpoint: Option<PathBuf>,
pub(crate) esp_mountpoint: Option<PathBuf>,
pub(crate) xboot_mountpoint: Option<PathBuf>,
}

impl BootEnvironment {
Expand Down Expand Up @@ -190,4 +190,9 @@ impl BootEnvironment {
pub fn esp(&self) -> Option<&PathBuf> {
self.esp.as_ref()
}

/// Return the XBOOTLDR partition (UEFI only)
pub fn xbootldr(&self) -> Option<&PathBuf> {
self.xbootldr.as_ref()
}
}
3 changes: 3 additions & 0 deletions blsforme/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub enum Error {
#[error("boot loader protocol: {0}")]
BootLoaderProtocol(#[from] systemd_boot::interface::Error),

#[error("c stdlib: {0}")]
C(#[from] nix::errno::Errno),

#[error("undetected xbootldr")]
NoXBOOTLDR,

Expand Down
104 changes: 103 additions & 1 deletion blsforme/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,24 @@

//! Boot loader management entry APIs
use std::{
fs::create_dir_all,
path::{Path, PathBuf},
};

use nix::mount::{mount, umount, MsFlags};
use topology::disk;

use crate::{BootEnvironment, Configuration, Error, Kernel};
use crate::{BootEnvironment, Configuration, Error, Kernel, Root};

#[derive(Debug)]
struct Mounts {
xbootldr: Option<PathBuf>,
esp: Option<PathBuf>,
}

/// Encapsulate the entirety of the boot management core APIs
#[derive(Debug)]
pub struct Manager<'a> {
config: &'a Configuration,

Expand All @@ -17,6 +30,8 @@ pub struct Manager<'a> {

/// Our detected boot environment
boot_env: BootEnvironment,

mounts: Mounts,
}

impl<'a> Manager<'a> {
Expand All @@ -32,10 +47,35 @@ impl<'a> Manager<'a> {
let boot_env = BootEnvironment::new(&probe, disk_parent, config)?;
log::trace!("boot env: {boot_env:?}");

let mut mounts = Mounts {
xbootldr: if let Some(point) = boot_env.xboot_mountpoint.as_ref() {
Some(point.clone())
} else {
Some(config.root.path().join("boot"))
},
esp: if let Some(point) = boot_env.esp_mountpoint.as_ref() {
Some(point.clone())
} else {
Some(config.root.path().join("efi"))
},
};

log::trace!("selected mountpoints: {mounts:?}");

// So, we got a `/boot` mount for ESP, legacy style. We can't stick xbootldr there...
if let Some(xbootldr) = mounts.xbootldr.as_ref() {
if let Some(esp) = mounts.esp.as_ref() {
if esp == xbootldr && boot_env.xbootldr().is_none() {
mounts.xbootldr = Some(config.root.path().join("xboot"))
}
}
}

Ok(Self {
config,
system_kernels: vec![],
boot_env,
mounts,
})
}

Expand All @@ -46,4 +86,66 @@ impl<'a> Manager<'a> {
..self
}
}

/// Mount any required partitions (ESP/XBOOTLDR)
pub fn mount_partitions(&self) -> Result<Vec<ScopedMount>, Error> {
let mut mounted_paths = vec![];

// Stop silly buggers with image based mounting
if let Root::Image(_) = self.config.root {
log::warn!("Refusing to auto-mount partitions in image mode");
return Ok(mounted_paths);
}

// Got the ESP, not mounted.
if let Some(hw) = self.boot_env.esp() {
if self.boot_env.esp_mountpoint.is_none() {
let mount_point = self.mounts.esp.clone().ok_or_else(|| Error::NoESP)?;
mounted_paths.insert(0, self.mount_vfat_partition(hw, &mount_point)?);
}
}
// Got an XBOOTLDR, not mounted..
if let Some(hw) = self.boot_env.xbootldr() {
if self.boot_env.xboot_mountpoint.is_none() {
let mount_point = self.mounts.xbootldr.clone().ok_or_else(|| Error::NoXBOOTLDR)?;
mounted_paths.insert(0, self.mount_vfat_partition(hw, &mount_point)?);
}
}

Ok(mounted_paths)
}

/// Mount an fat filesystem
#[inline]
fn mount_vfat_partition(&self, source: &Path, target: &Path) -> Result<ScopedMount, Error> {
let options: Option<&str> = None;
if !target.exists() {
create_dir_all(target)?;
}
mount(Some(source), target, Some("vfat"), MsFlags::MS_MGC_VAL, options)?;
log::info!("Mounted vfat partition {} at {}", source.display(), target.display());
Ok(ScopedMount {
point: target.into(),
mounted: true,
})
}
}

/// Encapsulated mountpoint to ensure auto-unmount (Scoped)
pub struct ScopedMount {
point: PathBuf,
mounted: bool,
}

impl Drop for ScopedMount {
fn drop(&mut self) {
if !self.mounted {
return;
}
self.mounted = true;
match umount(&self.point) {
Ok(_) => log::info!("Unmounted {}", self.point.display()),
Err(err) => log::error!("Failed to umount {}: {}", self.point.display(), err.to_string()),
}
}
}

0 comments on commit 4d86141

Please sign in to comment.