@@ -7,7 +7,9 @@ use serde::{Deserialize, Serialize};
77
88use crate :: {
99 bootc_composefs:: { boot:: BootType , repo:: get_imgref} ,
10- composefs_consts:: { COMPOSEFS_CMDLINE , ORIGIN_KEY_BOOT_DIGEST , TYPE1_ENT_PATH , USER_CFG } ,
10+ composefs_consts:: {
11+ COMPOSEFS_CMDLINE , ORIGIN_KEY_BOOT_DIGEST , TYPE1_ENT_PATH , TYPE1_ENT_PATH_STAGED , USER_CFG ,
12+ } ,
1113 install:: EFI_LOADER_INFO ,
1214 parsers:: {
1315 bls_config:: { parse_bls_config, BLSConfig , BLSConfigType } ,
@@ -100,14 +102,43 @@ pub(crate) fn get_sorted_grub_uki_boot_entries<'a>(
100102 parse_grub_menuentry_file ( str)
101103}
102104
103- #[ context( "Getting sorted Type1 boot entries" ) ]
104105pub ( crate ) fn get_sorted_type1_boot_entries (
105106 boot_dir : & Dir ,
106107 ascending : bool ,
108+ ) -> Result < Vec < BLSConfig > > {
109+ get_sorted_type1_boot_entries_helper ( boot_dir, ascending, false )
110+ }
111+
112+ pub ( crate ) fn get_sorted_staged_type1_boot_entries (
113+ boot_dir : & Dir ,
114+ ascending : bool ,
115+ ) -> Result < Vec < BLSConfig > > {
116+ get_sorted_type1_boot_entries_helper ( boot_dir, ascending, true )
117+ }
118+
119+ #[ context( "Getting sorted Type1 boot entries" ) ]
120+ fn get_sorted_type1_boot_entries_helper (
121+ boot_dir : & Dir ,
122+ ascending : bool ,
123+ get_staged_entries : bool ,
107124) -> Result < Vec < BLSConfig > > {
108125 let mut all_configs = vec ! [ ] ;
109126
110- for entry in boot_dir. read_dir ( TYPE1_ENT_PATH ) ? {
127+ let dir = match get_staged_entries {
128+ true => {
129+ let dir = boot_dir. open_dir_optional ( TYPE1_ENT_PATH_STAGED ) ?;
130+
131+ let Some ( dir) = dir else {
132+ return Ok ( all_configs) ;
133+ } ;
134+
135+ dir. read_dir ( "." ) ?
136+ }
137+
138+ false => boot_dir. read_dir ( TYPE1_ENT_PATH ) ?,
139+ } ;
140+
141+ for entry in dir {
111142 let entry = entry?;
112143
113144 let file_name = entry. file_name ( ) ;
@@ -302,12 +333,140 @@ pub(crate) async fn get_composefs_status(
302333 composefs_deployment_status_from ( & storage, booted_cfs. cmdline ) . await
303334}
304335
336+ fn set_soft_reboot_capable_bls (
337+ storage : & Storage ,
338+ host : & mut Host ,
339+ bls_entries : & Vec < BLSConfig > ,
340+ cmdline : & ComposefsCmdline ,
341+ ) -> Result < ( ) > {
342+ let booted = host. require_composefs_booted ( ) ?;
343+
344+ match booted. boot_type {
345+ BootType :: Bls => {
346+ set_reboot_capable_type1_deployments ( storage, cmdline, host, bls_entries) ?;
347+ }
348+
349+ BootType :: Uki => match booted. bootloader {
350+ Bootloader :: Grub => todo ! ( ) ,
351+ Bootloader :: Systemd => todo ! ( ) ,
352+ } ,
353+ } ;
354+
355+ Ok ( ( ) )
356+ }
357+
358+ fn find_bls_entry < ' a > (
359+ verity : & str ,
360+ bls_entries : & ' a Vec < BLSConfig > ,
361+ ) -> Result < Option < & ' a BLSConfig > > {
362+ for ent in bls_entries {
363+ if ent. get_verity ( ) ? == * verity {
364+ return Ok ( Some ( ent) ) ;
365+ }
366+ }
367+
368+ Ok ( None )
369+ }
370+
371+ /// Compares cmdline `first` and `second` skipping `composefs=`
372+ fn compare_cmdline_skip_cfs ( first : & Cmdline < ' _ > , second : & Cmdline < ' _ > ) -> bool {
373+ for param in first {
374+ if param. key ( ) == COMPOSEFS_CMDLINE . into ( ) {
375+ continue ;
376+ }
377+
378+ let second_param = second. iter ( ) . find ( |b| * b == param) ;
379+
380+ let Some ( found_param) = second_param else {
381+ return false ;
382+ } ;
383+
384+ if found_param. value ( ) != param. value ( ) {
385+ return false ;
386+ }
387+ }
388+
389+ return true ;
390+ }
391+
392+ fn set_soft_reboot_capable_type1 (
393+ deployment : & mut BootEntry ,
394+ bls_entries : & Vec < BLSConfig > ,
395+ booted_bls_entry : & BLSConfig ,
396+ booted_boot_digest : & String ,
397+ ) -> Result < ( ) > {
398+ let deployment_cfs = deployment. require_composefs ( ) ?;
399+
400+ // TODO: Unwrap
401+ if deployment_cfs. boot_digest . as_ref ( ) . unwrap ( ) != booted_boot_digest {
402+ deployment. soft_reboot_capable = false ;
403+ return Ok ( ( ) ) ;
404+ }
405+
406+ let entry = find_bls_entry ( & deployment_cfs. verity , bls_entries) ?
407+ . ok_or_else ( || anyhow:: anyhow!( "Entry not found" ) ) ?;
408+
409+ let opts = entry. get_cmdline ( ) ?;
410+ let booted_cmdline_opts = booted_bls_entry. get_cmdline ( ) ?;
411+
412+ if opts. len ( ) != booted_cmdline_opts. len ( ) {
413+ tracing:: debug!( "Soft reboot not allowed due to differing cmdline" ) ;
414+ deployment. soft_reboot_capable = false ;
415+ return Ok ( ( ) ) ;
416+ }
417+
418+ deployment. soft_reboot_capable = compare_cmdline_skip_cfs ( opts, booted_cmdline_opts)
419+ && compare_cmdline_skip_cfs ( booted_cmdline_opts, opts) ;
420+
421+ return Ok ( ( ) ) ;
422+ }
423+
424+ fn set_reboot_capable_type1_deployments (
425+ storage : & Storage ,
426+ cmdline : & ComposefsCmdline ,
427+ host : & mut Host ,
428+ bls_entries : & Vec < BLSConfig > ,
429+ ) -> Result < ( ) > {
430+ let booted = host
431+ . status
432+ . booted
433+ . as_ref ( )
434+ . ok_or_else ( || anyhow:: anyhow!( "Failed to find booted entry" ) ) ?;
435+
436+ let booted_boot_digest = booted. composefs_boot_digest ( ) ?;
437+
438+ let booted_bls_entry = find_bls_entry ( & * cmdline. digest , bls_entries) ?
439+ . ok_or_else ( || anyhow:: anyhow!( "Booted bls entry not found" ) ) ?;
440+
441+ if let Some ( staged) = host. status . staged . as_mut ( ) {
442+ let staged_entries =
443+ get_sorted_staged_type1_boot_entries ( storage. require_boot_dir ( ) ?, true ) ?;
444+
445+ set_soft_reboot_capable_type1 (
446+ staged,
447+ & staged_entries,
448+ booted_bls_entry,
449+ booted_boot_digest,
450+ ) ?;
451+ }
452+
453+ if let Some ( rollback) = & mut host. status . rollback {
454+ set_soft_reboot_capable_type1 ( rollback, bls_entries, booted_bls_entry, booted_boot_digest) ?;
455+ }
456+
457+ for depl in & mut host. status . other_deployments {
458+ set_soft_reboot_capable_type1 ( depl, bls_entries, booted_bls_entry, booted_boot_digest) ?;
459+ }
460+
461+ Ok ( ( ) )
462+ }
463+
305464#[ context( "Getting composefs deployment status" ) ]
306465pub ( crate ) async fn composefs_deployment_status_from (
307466 storage : & Storage ,
308467 cmdline : & ComposefsCmdline ,
309468) -> Result < Host > {
310- let composefs_digest = & cmdline. digest ;
469+ let booted_composefs_digest = & cmdline. digest ;
311470
312471 let boot_dir = storage. require_boot_dir ( ) ?;
313472
@@ -373,7 +532,7 @@ pub(crate) async fn composefs_deployment_status_from(
373532 }
374533 } ;
375534
376- if depl. file_name ( ) == composefs_digest . as_ref ( ) {
535+ if depl. file_name ( ) == booted_composefs_digest . as_ref ( ) {
377536 host. spec . image = boot_entry. image . as_ref ( ) . map ( |x| x. image . clone ( ) ) ;
378537 host. status . booted = Some ( boot_entry) ;
379538 continue ;
@@ -394,60 +553,72 @@ pub(crate) async fn composefs_deployment_status_from(
394553 anyhow:: bail!( "Could not determine boot type" ) ;
395554 } ;
396555
397- let booted = host. require_composefs_booted ( ) ?;
556+ let booted_cfs = host. require_composefs_booted ( ) ?;
398557
399- let is_rollback_queued = match booted . bootloader {
558+ let ( is_rollback_queued, sorted_bls_config ) = match booted_cfs . bootloader {
400559 Bootloader :: Grub => match boot_type {
401560 BootType :: Bls => {
402- let bls_config = get_sorted_type1_boot_entries ( boot_dir, false ) ?;
403- let bls_config = bls_config
561+ let bls_configs = get_sorted_type1_boot_entries ( boot_dir, false ) ?;
562+ let bls_config = bls_configs
404563 . first ( )
405- . ok_or ( anyhow:: anyhow!( "First boot entry not found" ) ) ?;
564+ . ok_or_else ( || anyhow:: anyhow!( "First boot entry not found" ) ) ?;
406565
407566 match & bls_config. cfg_type {
408- BLSConfigType :: NonEFI { options, .. } => !options
409- . as_ref ( )
410- . ok_or ( anyhow:: anyhow!( "options key not found in bls config" ) ) ?
411- . contains ( composefs_digest. as_ref ( ) ) ,
567+ BLSConfigType :: NonEFI { options, .. } => {
568+ let is_rollback_queued = !options
569+ . as_ref ( )
570+ . ok_or_else ( || anyhow:: anyhow!( "options key not found in bls config" ) ) ?
571+ . contains ( booted_composefs_digest. as_ref ( ) ) ;
572+
573+ ( is_rollback_queued, Some ( bls_configs) )
574+ }
412575
413576 BLSConfigType :: EFI { .. } => {
414577 anyhow:: bail!( "Found 'efi' field in Type1 boot entry" )
415578 }
579+
416580 BLSConfigType :: Unknown => anyhow:: bail!( "Unknown BLS Config Type" ) ,
417581 }
418582 }
419583
420584 BootType :: Uki => {
421585 let mut s = String :: new ( ) ;
586+ let menuentries = get_sorted_grub_uki_boot_entries ( boot_dir, & mut s) ?;
422587
423- ! get_sorted_grub_uki_boot_entries ( boot_dir , & mut s ) ?
588+ let is_rollback_queued = !menuentries
424589 . first ( )
425590 . ok_or ( anyhow:: anyhow!( "First boot entry not found" ) ) ?
426591 . body
427592 . chainloader
428- . contains ( composefs_digest. as_ref ( ) )
593+ . contains ( booted_composefs_digest. as_ref ( ) ) ;
594+
595+ ( is_rollback_queued, None )
429596 }
430597 } ,
431598
432599 // We will have BLS stuff and the UKI stuff in the same DIR
433600 Bootloader :: Systemd => {
434- let bls_config = get_sorted_type1_boot_entries ( boot_dir, false ) ?;
435- let bls_config = bls_config
601+ let bls_configs = get_sorted_type1_boot_entries ( boot_dir, true ) ?;
602+ let bls_config = bls_configs
436603 . first ( )
437604 . ok_or ( anyhow:: anyhow!( "First boot entry not found" ) ) ?;
438605
439- match & bls_config. cfg_type {
606+ let is_rollback_queued = match & bls_config. cfg_type {
440607 // For UKI boot
441- BLSConfigType :: EFI { efi } => efi. as_str ( ) . contains ( composefs_digest. as_ref ( ) ) ,
608+ BLSConfigType :: EFI { efi } => {
609+ efi. as_str ( ) . contains ( booted_composefs_digest. as_ref ( ) )
610+ }
442611
443612 // For boot entry Type1
444613 BLSConfigType :: NonEFI { options, .. } => !options
445614 . as_ref ( )
446615 . ok_or ( anyhow:: anyhow!( "options key not found in bls config" ) ) ?
447- . contains ( composefs_digest . as_ref ( ) ) ,
616+ . contains ( booted_composefs_digest . as_ref ( ) ) ,
448617
449618 BLSConfigType :: Unknown => anyhow:: bail!( "Unknown BLS Config Type" ) ,
450- }
619+ } ;
620+
621+ ( is_rollback_queued, Some ( bls_configs) )
451622 }
452623 } ;
453624
@@ -457,6 +628,10 @@ pub(crate) async fn composefs_deployment_status_from(
457628 host. spec . boot_order = BootOrder :: Rollback
458629 } ;
459630
631+ if let Some ( bls_configs) = sorted_bls_config {
632+ set_soft_reboot_capable_bls ( storage, & mut host, & bls_configs, cmdline) ?;
633+ }
634+
460635 Ok ( host)
461636}
462637
0 commit comments