@@ -11,7 +11,8 @@ use std::path::{Path, PathBuf};
1111use std:: process:: Command ;
1212
1313use anyhow:: { anyhow, bail, Context , Result } ;
14- use bootc_utils:: CommandRunExt ;
14+ use bootc_internal_utils:: CommandRunExt ;
15+ use camino:: { Utf8Path , Utf8PathBuf } ;
1516use cap_std:: fs:: Dir ;
1617use cap_std_ext:: cap_std;
1718use chrono:: prelude:: * ;
@@ -465,14 +466,106 @@ impl Component for Efi {
465466 let Some ( esp_devices) = blockdev:: find_colocated_esps ( & rootcxt. devices ) ? else {
466467 anyhow:: bail!( "Failed to find all esp devices" ) ;
467468 } ;
469+ let mut updated_firmware = BTreeMap :: new ( ) ;
468470
469- for esp in esp_devices {
470- let destpath = & self . ensure_mounted_esp ( rootcxt. path . as_ref ( ) , Path :: new ( & esp) ) ?;
471+ for esp in esp_devices. iter ( ) {
472+ let destpath = & self . ensure_mounted_esp ( rootcxt. path . as_ref ( ) , Path :: new ( esp) ) ?;
471473 let destdir = openat:: Dir :: open ( & destpath. join ( "EFI" ) ) . context ( "opening EFI dir" ) ?;
472474 validate_esp_fstype ( & destdir) ?;
473475 log:: trace!( "applying diff: {}" , & diff) ;
474476 filetree:: apply_diff ( & updated, & destdir, & diff, None )
475477 . context ( "applying filesystem changes" ) ?;
478+ // update firmware
479+ let firmware_base_dir_path = Path :: new ( "usr/lib/efi/firmware" ) ;
480+
481+ let available_payloads = {
482+ let mut payloads = BTreeMap :: new ( ) ;
483+ if rootcxt. sysroot . exists ( firmware_base_dir_path) ? {
484+ let firmware_base_dir = rootcxt. sysroot . sub_dir ( firmware_base_dir_path) ?;
485+ for pkg_entry in firmware_base_dir. list_dir ( "." ) ?. flatten ( ) {
486+ if firmware_base_dir. get_file_type ( & pkg_entry) ? == openat:: SimpleType :: Dir {
487+ let pkg_name = pkg_entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
488+ payloads. insert ( pkg_name, pkg_entry. file_name ( ) . to_owned ( ) ) ;
489+ }
490+ }
491+ }
492+ payloads
493+ } ;
494+
495+ let old_keys: std:: collections:: HashSet < _ > = current. firmware . keys ( ) . collect ( ) ;
496+ let new_keys: std:: collections:: HashSet < _ > = available_payloads. keys ( ) . collect ( ) ;
497+ let all_keys: std:: collections:: HashSet < _ > = old_keys. union ( & new_keys) . collect ( ) ;
498+
499+ // determine if it should be added, updated, or removed.
500+ for pkg_name in all_keys {
501+ let old_payload = current. firmware . get ( * pkg_name) ;
502+ let new_payload_path = available_payloads. get ( * pkg_name) ;
503+
504+ let ( diff, src_dir, new_content) = match ( old_payload, new_payload_path) {
505+ // Payload exists in both old state and new source.
506+ ( Some ( old) , Some ( new_path) ) => {
507+ let new_ver_dir = rootcxt
508+ . sysroot
509+ . sub_dir ( firmware_base_dir_path) ?
510+ . sub_dir ( new_path. as_os_str ( ) ) ?;
511+ let new_payload_dir = new_ver_dir. sub_dir ( "EFI" ) ?;
512+ let new_ft = crate :: filetree:: FileTree :: new_from_dir ( & new_payload_dir) ?;
513+ let old_ft = old. filetree . as_ref ( ) . unwrap_or ( & new_ft) ;
514+ let diff = old_ft. diff ( & new_ft) ?;
515+
516+ let meta: ContentMetadata =
517+ serde_json:: from_reader ( new_ver_dir. open_file ( "EFI.json" ) ?) ?;
518+ let content = Box :: new ( InstalledContent {
519+ meta,
520+ filetree : Some ( new_ft) ,
521+ adopted_from : None ,
522+ firmware : BTreeMap :: new ( ) ,
523+ } ) ;
524+ ( diff, Some ( new_payload_dir) , Some ( content) )
525+ }
526+ // add as old payload is none
527+ ( None , Some ( new_path) ) => {
528+ let new_ver_dir = rootcxt
529+ . sysroot
530+ . sub_dir ( firmware_base_dir_path) ?
531+ . sub_dir ( new_path. as_os_str ( ) ) ?;
532+ let new_payload_dir = new_ver_dir. sub_dir ( "EFI" ) ?;
533+ let new_ft = crate :: filetree:: FileTree :: new_from_dir ( & new_payload_dir) ?;
534+ let empty_ft = crate :: filetree:: FileTree {
535+ children : BTreeMap :: new ( ) ,
536+ } ;
537+ let diff = empty_ft. diff ( & new_ft) ?;
538+
539+ let meta: ContentMetadata =
540+ serde_json:: from_reader ( new_ver_dir. open_file ( "EFI.json" ) ?) ?;
541+ let content = Box :: new ( InstalledContent {
542+ meta,
543+ filetree : Some ( new_ft) ,
544+ adopted_from : None ,
545+ firmware : BTreeMap :: new ( ) ,
546+ } ) ;
547+ ( diff, Some ( new_payload_dir) , Some ( content) )
548+ }
549+ // continue with old firmware
550+ ( Some ( _old) , None ) => continue ,
551+ // Should not happen.
552+ ( None , None ) => continue ,
553+ } ;
554+
555+ //apply the above diffs
556+ for esp in esp_devices. iter ( ) {
557+ let destpath =
558+ & self . ensure_mounted_esp ( rootcxt. path . as_ref ( ) , Path :: new ( esp) ) ?;
559+ let destdir = openat:: Dir :: open ( destpath) ?;
560+ let src_dir = src_dir. as_ref ( ) . unwrap_or ( & destdir) ;
561+ filetree:: apply_diff ( src_dir, & destdir, & diff, None )
562+ . context ( format ! ( "applying firmware diff for {}" , pkg_name) ) ?;
563+ }
564+
565+ if let Some ( content) = new_content {
566+ updated_firmware. insert ( pkg_name. to_string ( ) , content) ;
567+ }
568+ }
476569
477570 // Do the sync before unmount
478571 fsfreeze_thaw_cycle ( destdir. open_file ( "." ) ?) ?;
@@ -485,7 +578,7 @@ impl Component for Efi {
485578 meta : updatemeta,
486579 filetree : Some ( updatef) ,
487580 adopted_from,
488- firmware : BTreeMap :: new ( ) ,
581+ firmware : updated_firmware ,
489582 } )
490583 }
491584
@@ -993,7 +1086,9 @@ Boot0003* test";
9931086 paths,
9941087 [ "usr/lib/efi/FOO/1.1/EFI" , "usr/lib/efi/BAR/1.1/EFI" ]
9951088 ) ;
1089+ Ok ( ( ) )
9961090 }
1091+
9971092 #[ test]
9981093 fn test_extend_payload ( ) -> Result < ( ) > {
9991094 use std:: fs;
@@ -1159,4 +1254,3 @@ exit 1
11591254 Ok ( ( ) )
11601255 }
11611256}
1162-
0 commit comments