Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions crates/lib/src/bootc_composefs/status.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{io::Read, sync::OnceLock};
use std::{collections::HashSet, io::Read, sync::OnceLock};

use anyhow::{Context, Result};
use bootc_kernel_cmdline::utf8::Cmdline;
Expand Down Expand Up @@ -570,6 +570,10 @@ pub(crate) async fn composefs_deployment_status_from(
// NOTE: This cannot work if we support both BLS and UKI at the same time
let mut boot_type: Option<BootType> = None;

// Boot entries from deployments that are neither booted nor staged deployments
// Rollback deployment is in here, but may also contain stale deployment entries
let mut extra_deployment_boot_entries: Vec<BootEntry> = Vec::new();

for depl in deployments {
let depl = depl?;

Expand Down Expand Up @@ -617,7 +621,7 @@ pub(crate) async fn composefs_deployment_status_from(
}
}

host.status.rollback = Some(boot_entry);
extra_deployment_boot_entries.push(boot_entry);
}

// Shouldn't really happen, but for sanity nonetheless
Expand All @@ -627,7 +631,8 @@ pub(crate) async fn composefs_deployment_status_from(

let booted_cfs = host.require_composefs_booted()?;

let (is_rollback_queued, sorted_bls_config) = match booted_cfs.bootloader {
let mut grub_menu_string = String::new();
let (is_rollback_queued, sorted_bls_config, grub_menu_entries) = match booted_cfs.bootloader {
Bootloader::Grub => match boot_type {
BootType::Bls => {
let bls_configs = get_sorted_type1_boot_entries(boot_dir, false)?;
Expand All @@ -642,7 +647,7 @@ pub(crate) async fn composefs_deployment_status_from(
.ok_or_else(|| anyhow::anyhow!("options key not found in bls config"))?
.contains(booted_composefs_digest.as_ref());

(is_rollback_queued, Some(bls_configs))
(is_rollback_queued, Some(bls_configs), None)
}

BLSConfigType::EFI { .. } => {
Expand All @@ -654,8 +659,8 @@ pub(crate) async fn composefs_deployment_status_from(
}

BootType::Uki => {
let mut s = String::new();
let menuentries = get_sorted_grub_uki_boot_entries(boot_dir, &mut s)?;
let menuentries =
get_sorted_grub_uki_boot_entries(boot_dir, &mut grub_menu_string)?;

let is_rollback_queued = !menuentries
.first()
Expand All @@ -664,7 +669,7 @@ pub(crate) async fn composefs_deployment_status_from(
.chainloader
.contains(booted_composefs_digest.as_ref());

(is_rollback_queued, None)
(is_rollback_queued, None, Some(menuentries))
}
},

Expand All @@ -690,10 +695,41 @@ pub(crate) async fn composefs_deployment_status_from(
BLSConfigType::Unknown => anyhow::bail!("Unknown BLS Config Type"),
};

(is_rollback_queued, Some(bls_configs))
(is_rollback_queued, Some(bls_configs), None)
}
};

// Determine rollback deployment by matching extra deployment boot entries against entires read from /boot
// This collects verity digest across bls and grub enties, we should just have one of them, but still works
let bootloader_configured_verity = sorted_bls_config
.iter()
.flatten()
.map(|cfg| cfg.get_verity())
.chain(
grub_menu_entries
.iter()
.flatten()
.map(|menu| menu.get_verity()),
)
.collect::<Result<HashSet<_>>>()?;
let rollback_candidates: Vec<_> = extra_deployment_boot_entries
.into_iter()
.filter(|entry| {
let verity = &entry
.composefs
.as_ref()
.expect("composefs is always Some for composefs deployments")
.verity;
bootloader_configured_verity.contains(verity)
})
.collect();

if rollback_candidates.len() > 1 {
anyhow::bail!("Multiple extra entries in /boot, could not determine rollback entry");
} else if let Some(rollback_entry) = rollback_candidates.into_iter().next() {
host.status.rollback = Some(rollback_entry);
}

host.status.rollback_queued = is_rollback_queued;

if host.status.rollback_queued {
Expand Down