Skip to content

Commit 1e5b2ca

Browse files
committed
update: Skip bootloader update when no block devices back the root
In environments without block-backed boot filesystems (virtiofs in bcvk ephemeral, NFS root, ISO boot, etc.) there is no on-disk bootloader to manage. Previously bootloader-update.service would fail because get_devices() bailed when it could not find a block device from /boot or /sysroot. Change get_devices() to return Ok(None) instead of an error when no block-backed filesystem is found, and propagate this through prep_before_update() so the update and adopt-and-update paths exit cleanly with an informative message. The EFI validate path also skips gracefully in this case. This is more general than checking for specific filesystem types: any environment without backing block devices is handled, which also prepares for the transition to list_dev_current_root() in PR #1068. Assisted-by: OpenCode (Claude Opus 4)
1 parent 83293cf commit 1e5b2ca

3 files changed

Lines changed: 30 additions & 10 deletions

File tree

src/blockdev.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ use anyhow::{Context, Result};
55
use bootc_internal_blockdev::PartitionTable;
66
use fn_error_context::context;
77

8+
/// Try to find the block devices backing `/boot` or `/sysroot`.
9+
///
10+
/// Returns `Ok(None)` when neither path has a block-backed filesystem (e.g.
11+
/// virtiofs, NFS, ISO 9660). This is normal for environments without an
12+
/// on-disk bootloader.
813
#[context("get parent devices from mount point boot or sysroot")]
9-
pub fn get_devices<P: AsRef<Path>>(target_root: P) -> Result<Vec<String>> {
14+
pub fn get_devices<P: AsRef<Path>>(target_root: P) -> Result<Option<Vec<String>>> {
1015
let target_root = target_root.as_ref();
1116
let mut source = None;
1217

@@ -26,14 +31,14 @@ pub fn get_devices<P: AsRef<Path>>(target_root: P) -> Result<Vec<String>> {
2631

2732
let source = match source {
2833
Some(s) => s,
29-
None => anyhow::bail!("Failed to inspect filesystem from boot or sysroot"),
34+
None => return Ok(None),
3035
};
3136

3237
// Find the parent devices of the source path
3338
let parent_devices = bootc_internal_blockdev::find_parent_devices(&source)
3439
.with_context(|| format!("While looking for backing devices of {}", source))?;
3540
log::debug!("Found parent devices: {parent_devices:?}");
36-
Ok(parent_devices)
41+
Ok(Some(parent_devices))
3742
}
3843

3944
/// Find esp partition on the same device

src/bootupd.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -531,17 +531,28 @@ impl RootContext {
531531
}
532532
}
533533

534-
/// Initialize parent devices to prepare the update
535-
fn prep_before_update() -> Result<RootContext> {
534+
/// Initialize parent devices to prepare the update.
535+
///
536+
/// Returns `Ok(None)` when the root has no block-backed boot filesystem,
537+
/// meaning there is no on-disk bootloader to manage. This happens in
538+
/// environments like virtiofs (bcvk ephemeral), NFS root, ISO boot, etc.
539+
fn prep_before_update() -> Result<Option<RootContext>> {
536540
let path = "/";
537541
let sysroot = openat::Dir::open(path).context("Opening root dir")?;
538-
let devices = crate::blockdev::get_devices(path).context("get parent devices")?;
539-
Ok(RootContext::new(sysroot, path, devices))
542+
let Some(devices) = crate::blockdev::get_devices(path)? else {
543+
println!(
544+
"No block-backed boot filesystem found; bootloader update is not applicable, skipping."
545+
);
546+
return Ok(None);
547+
};
548+
Ok(Some(RootContext::new(sysroot, path, devices)))
540549
}
541550

542551
pub(crate) fn client_run_update() -> Result<()> {
543552
crate::try_fail_point!("update");
544-
let rootcxt = prep_before_update()?;
553+
let Some(rootcxt) = prep_before_update()? else {
554+
return Ok(());
555+
};
545556
let status: Status = status()?;
546557
if status.components.is_empty() && status.adoptable.is_empty() {
547558
println!("No components installed.");
@@ -596,7 +607,9 @@ pub(crate) fn client_run_update() -> Result<()> {
596607
}
597608

598609
pub(crate) fn client_run_adopt_and_update(with_static_config: bool) -> Result<()> {
599-
let rootcxt = prep_before_update()?;
610+
let Some(rootcxt) = prep_before_update()? else {
611+
return Ok(());
612+
};
600613
let status: Status = status()?;
601614
if status.adoptable.is_empty() {
602615
println!("No components are adoptable.");

src/efi.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,9 @@ impl Component for Efi {
560560
}
561561

562562
fn validate(&self, current: &InstalledContent) -> Result<ValidationResult> {
563-
let devices = crate::blockdev::get_devices("/").context("get parent devices")?;
563+
let Some(devices) = crate::blockdev::get_devices("/")? else {
564+
return Ok(ValidationResult::Skip);
565+
};
564566
let esp_devices = blockdev::find_colocated_esps(&devices)?;
565567
if !is_efi_booted()? && esp_devices.is_none() {
566568
return Ok(ValidationResult::Skip);

0 commit comments

Comments
 (0)