From 3c12fdfa4193051e34f0145bbadc63a3bc9f900f Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Tue, 8 Sep 2026 12:19:37 +0530 Subject: [PATCH 1/2] cfs/boot: Handle separate /boot mount If /boot is mounted as XBOOTLDR partition then grub configs are stored there as we were unconditionally searching for grub configs in `/sysroot/boot`, which would fail every time. Fix this by checking if `/boot` is a mountpoint and then checking if we have grub configs in there. This is only an issue with Grub as GrubCC and SystemdBoot both do not store anything inside of `/sysroot/boot` and will (should) always have the ESP mounted at /boot Closes: #2399 Signed-off-by: Pragyan Poudyal --- crates/lib/src/store/mod.rs | 81 +++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/crates/lib/src/store/mod.rs b/crates/lib/src/store/mod.rs index d417b17de..aad7f01d7 100644 --- a/crates/lib/src/store/mod.rs +++ b/crates/lib/src/store/mod.rs @@ -95,6 +95,7 @@ use std::ops::Deref; use std::sync::Arc; use anyhow::{Context, Result}; +use bootc_mount::run_findmnt; use bootc_mount::tempmount::TempMount; use camino::Utf8PathBuf; use cap_std_ext::cap_std; @@ -104,6 +105,7 @@ use cap_std_ext::cap_std::fs::{ use cap_std_ext::dirext::CapStdExtDirExt; use fn_error_context::context; +use ocidir::cap_std::ambient_authority; use ostree_ext::container_utils::ostree_booted; use ostree_ext::prelude::FileExt; use ostree_ext::sysroot::SysrootLock; @@ -361,6 +363,80 @@ fn sysroot_is_read_only(d: &Dir) -> Result { Ok(false) } +#[context("Finding boot for Grub")] +fn get_boot_dir_for_grub(physical_root: &Dir) -> Result { + // We have this so systemd's boot.automount shouldn't expire until + // this function finishes execution as we have a handle to /boot + let boot = + Dir::open_ambient_dir("/boot", ambient_authority()).context("Failed to open /boot")?; + + let is_boot_mntpnt = boot + .is_mountpoint(".") + .context("Checking if /boot is a mountpoint")?; + + // /boot is not a mount point for bootloader Grub, so we have to + // have stuff in /sysroot/boot + if !matches!(is_boot_mntpnt, Some(true)) { + return physical_root + .open_dir("boot") + .context("Opening boot in physical root"); + } + + // /boot is a mountpoint + // Figure out if it's the ESP or XBOOTLDR + let mnt_res = run_findmnt(&[], None, Some("/boot")).context("Finding /boot mount info")?; + + let mut boot_fs = None; + + for mount in mnt_res.filesystems { + if mount.source.starts_with("systemd") { + // systemd automount, useless for getting any info + continue; + } + + if let Some(already_found) = boot_fs { + // Really shouldn't happen, but for sanity + anyhow::bail!( + "Found multiple mounts on /boot. Found {}, already had {already_found}", + mount.fstype + ); + }; + + boot_fs = Some(mount.fstype); + } + + let boot_fs = boot_fs.ok_or_else(|| anyhow::anyhow!("Failed to get filesystem for /boot"))?; + + // NOTE: It would be ideal here to check for DPS UUID but we can't be sure that the + // device that /boot is mounted as will have DPS compatible UUID + // + // The best effort we can have is to check the fstype + match boot_fs.as_ref() { + // /boot is ESP so we have grub configs in /sysroot/boot + "vfat" => { + return physical_root + .open_dir("boot") + .context("Opening boot in physical root"); + } + + // XBOOTLDR, so grub configs should hopefully be here + // but check just in case + "ext4" | "xfs" | "btrfs" => { + if boot.is_dir("grub2") { + return Ok(boot); + } + + return physical_root + .open_dir("boot") + .context("Opening boot in physical root"); + } + + fstype => { + anyhow::bail!("Unknown fstype {fstype} for /boot") + } + }; +} + impl BootedStorage { /// Create a new booted storage accessor for the given environment. /// @@ -390,9 +466,8 @@ impl BootedStorage { }; let boot_dir = match get_bootloader()?.kind()? { - BootloaderKind::GRUBClassic => { - physical_root.open_dir("boot").context("Opening boot")? - } + // We can have a separate /boot and not /sysroot/boot + BootloaderKind::GRUBClassic => get_boot_dir_for_grub(&physical_root)?, // NOTE: Handle XBOOTLDR partitions here if and when we use it BootloaderKind::BLSCompatible => { esp_mount.fd.try_clone().context("Cloning fd")? From bf61140d43474de11cef4f071fcc18aebd137fba Mon Sep 17 00:00:00 2001 From: Pragyan Poudyal Date: Tue, 15 Sep 2026 12:13:28 +0530 Subject: [PATCH 2/2] cfs/boot: Check DPS UUID for /boot mount Check if DPS UUIDs are being used for /boot mount and if they are, use the UUIDs instead of filesystem check Also, do not restrict `/boot` fs to only ext4, btrfs and xfs Signed-off-by: Pragyan Poudyal --- crates/lib/src/store/mod.rs | 69 ++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/crates/lib/src/store/mod.rs b/crates/lib/src/store/mod.rs index aad7f01d7..cf52542ba 100644 --- a/crates/lib/src/store/mod.rs +++ b/crates/lib/src/store/mod.rs @@ -95,8 +95,8 @@ use std::ops::Deref; use std::sync::Arc; use anyhow::{Context, Result}; -use bootc_mount::run_findmnt; use bootc_mount::tempmount::TempMount; +use bootc_mount::{Filesystem, run_findmnt}; use camino::Utf8PathBuf; use cap_std_ext::cap_std; use cap_std_ext::cap_std::fs::{ @@ -386,7 +386,7 @@ fn get_boot_dir_for_grub(physical_root: &Dir) -> Result { // Figure out if it's the ESP or XBOOTLDR let mnt_res = run_findmnt(&[], None, Some("/boot")).context("Finding /boot mount info")?; - let mut boot_fs = None; + let mut boot_mount_details: Option = None; for mount in mnt_res.filesystems { if mount.source.starts_with("systemd") { @@ -394,47 +394,60 @@ fn get_boot_dir_for_grub(physical_root: &Dir) -> Result { continue; } - if let Some(already_found) = boot_fs { + if let Some(already_found) = boot_mount_details { // Really shouldn't happen, but for sanity anyhow::bail!( - "Found multiple mounts on /boot. Found {}, already had {already_found}", - mount.fstype + "Found multiple mounts on /boot. Found {}, already had {}", + mount.source, + already_found.source, ); }; - boot_fs = Some(mount.fstype); + boot_mount_details = Some(mount); } - let boot_fs = boot_fs.ok_or_else(|| anyhow::anyhow!("Failed to get filesystem for /boot"))?; + let boot_mount_details = boot_mount_details + .ok_or_else(|| anyhow::anyhow!("Failed to get mount details for /boot"))?; - // NOTE: It would be ideal here to check for DPS UUID but we can't be sure that the - // device that /boot is mounted as will have DPS compatible UUID - // - // The best effort we can have is to check the fstype - match boot_fs.as_ref() { + let boot_fs = boot_mount_details.fstype; + + let boot_dev_info = bootc_blockdev::list_dev(&Utf8PathBuf::from(boot_mount_details.source))?; + + let boot_is_esp = || { // /boot is ESP so we have grub configs in /sysroot/boot - "vfat" => { - return physical_root - .open_dir("boot") - .context("Opening boot in physical root"); - } + return physical_root + .open_dir("boot") + .context("Opening boot in physical root"); + }; + let boot_is_xbootldr = || { // XBOOTLDR, so grub configs should hopefully be here // but check just in case - "ext4" | "xfs" | "btrfs" => { - if boot.is_dir("grub2") { - return Ok(boot); - } - - return physical_root - .open_dir("boot") - .context("Opening boot in physical root"); + if boot.is_dir("grub2") { + // Remount /boot if mounted RO + return open_dir_remount_rw(&boot, ".".into()); } - fstype => { - anyhow::bail!("Unknown fstype {fstype} for /boot") - } + return physical_root + .open_dir("boot") + .context("Opening boot in physical root"); }; + + let return_boot_by_fs = || match boot_fs.as_ref() { + "vfat" => boot_is_esp(), + _ => boot_is_xbootldr(), + }; + + use crate::discoverable_partition_specification as dps; + + match boot_dev_info.parttype { + Some(parttype) => match parttype.as_str() { + dps::ESP => boot_is_esp(), + dps::XBOOTLDR => boot_is_xbootldr(), + _ => return_boot_by_fs(), + }, + None => return_boot_by_fs(), + } } impl BootedStorage {