1- use std:: { io:: Read , sync:: OnceLock } ;
1+ use std:: { collections :: HashSet , io:: Read , sync:: OnceLock } ;
22
33use anyhow:: { Context , Result } ;
44use bootc_kernel_cmdline:: utf8:: Cmdline ;
@@ -570,6 +570,10 @@ pub(crate) async fn composefs_deployment_status_from(
570570 // NOTE: This cannot work if we support both BLS and UKI at the same time
571571 let mut boot_type: Option < BootType > = None ;
572572
573+ // Boot entries from deployments that are neither booted nor staged deployments
574+ // Rollback deployment is in here, but may also contain stale deployment entries
575+ let mut extra_deployment_boot_entries: Vec < BootEntry > = Vec :: new ( ) ;
576+
573577 for depl in deployments {
574578 let depl = depl?;
575579
@@ -617,7 +621,7 @@ pub(crate) async fn composefs_deployment_status_from(
617621 }
618622 }
619623
620- host . status . rollback = Some ( boot_entry) ;
624+ extra_deployment_boot_entries . push ( boot_entry) ;
621625 }
622626
623627 // Shouldn't really happen, but for sanity nonetheless
@@ -627,7 +631,8 @@ pub(crate) async fn composefs_deployment_status_from(
627631
628632 let booted_cfs = host. require_composefs_booted ( ) ?;
629633
630- let ( is_rollback_queued, sorted_bls_config) = match booted_cfs. bootloader {
634+ let mut grub_menu_string = String :: new ( ) ;
635+ let ( is_rollback_queued, sorted_bls_config, grub_menu_entries) = match booted_cfs. bootloader {
631636 Bootloader :: Grub => match boot_type {
632637 BootType :: Bls => {
633638 let bls_configs = get_sorted_type1_boot_entries ( boot_dir, false ) ?;
@@ -642,7 +647,7 @@ pub(crate) async fn composefs_deployment_status_from(
642647 . ok_or_else ( || anyhow:: anyhow!( "options key not found in bls config" ) ) ?
643648 . contains ( booted_composefs_digest. as_ref ( ) ) ;
644649
645- ( is_rollback_queued, Some ( bls_configs) )
650+ ( is_rollback_queued, Some ( bls_configs) , None )
646651 }
647652
648653 BLSConfigType :: EFI { .. } => {
@@ -654,8 +659,8 @@ pub(crate) async fn composefs_deployment_status_from(
654659 }
655660
656661 BootType :: Uki => {
657- let mut s = String :: new ( ) ;
658- let menuentries = get_sorted_grub_uki_boot_entries ( boot_dir, & mut s ) ?;
662+ let menuentries =
663+ get_sorted_grub_uki_boot_entries ( boot_dir, & mut grub_menu_string ) ?;
659664
660665 let is_rollback_queued = !menuentries
661666 . first ( )
@@ -664,7 +669,7 @@ pub(crate) async fn composefs_deployment_status_from(
664669 . chainloader
665670 . contains ( booted_composefs_digest. as_ref ( ) ) ;
666671
667- ( is_rollback_queued, None )
672+ ( is_rollback_queued, None , Some ( menuentries ) )
668673 }
669674 } ,
670675
@@ -690,10 +695,40 @@ pub(crate) async fn composefs_deployment_status_from(
690695 BLSConfigType :: Unknown => anyhow:: bail!( "Unknown BLS Config Type" ) ,
691696 } ;
692697
693- ( is_rollback_queued, Some ( bls_configs) )
698+ ( is_rollback_queued, Some ( bls_configs) , None )
694699 }
695700 } ;
696701
702+ // Determine rollback deployment by matching extra deployment boot entries against entires read from /boot
703+ // This collects verity digest across bls and grub enties, we should just have one of them, but still works
704+ let bootloader_configured_verity = sorted_bls_config
705+ . iter ( )
706+ . flatten ( )
707+ . map ( |cfg| cfg. get_verity ( ) )
708+ . chain (
709+ grub_menu_entries
710+ . iter ( )
711+ . flatten ( )
712+ . map ( |menu| menu. get_verity ( ) ) ,
713+ )
714+ . collect :: < Result < HashSet < _ > > > ( ) ?;
715+ for entry in extra_deployment_boot_entries {
716+ // SAFETY: boot_entry.composefs will always be present
717+ let verity = & entry. composefs . as_ref ( ) . unwrap ( ) . verity ;
718+ if bootloader_configured_verity. contains ( verity) {
719+ match host. status . rollback {
720+ Some ( ref _entry) => {
721+ anyhow:: bail!(
722+ "Multiple extra entires in /boot, could not determine rollback entry"
723+ ) ;
724+ }
725+ None => {
726+ host. status . rollback = Some ( entry) ;
727+ }
728+ }
729+ }
730+ }
731+
697732 host. status . rollback_queued = is_rollback_queued;
698733
699734 if host. status . rollback_queued {
0 commit comments