@@ -462,15 +462,117 @@ impl Component for Efi {
462462 anyhow:: bail!( "Failed to find all esp devices" ) ;
463463 } ;
464464
465- for esp in esp_devices {
466- let destpath = & self . ensure_mounted_esp ( rootcxt. path . as_ref ( ) , Path :: new ( & esp) ) ?;
465+ for esp in esp_devices. iter ( ) {
466+ let destpath = & self . ensure_mounted_esp ( rootcxt. path . as_ref ( ) , Path :: new ( esp) ) ?;
467467 let destdir = openat:: Dir :: open ( & destpath. join ( "EFI" ) ) . context ( "opening EFI dir" ) ?;
468468 validate_esp_fstype ( & destdir) ?;
469469 log:: trace!( "applying diff: {}" , & diff) ;
470470 filetree:: apply_diff ( & updated, & destdir, & diff, None )
471471 . context ( "applying filesystem changes" ) ?;
472+ }
472473
473- // Do the sync before unmount
474+ // update firmware
475+ let mut updated_firmware = BTreeMap :: new ( ) ;
476+ let firmware_base_dir_path = Path :: new ( "usr/lib/efi/firmware" ) ;
477+
478+ let available_payloads = {
479+ let mut payloads = BTreeMap :: new ( ) ;
480+ if rootcxt. sysroot . exists ( firmware_base_dir_path) ? {
481+ let firmware_base_dir = rootcxt. sysroot . sub_dir ( firmware_base_dir_path) ?;
482+ for pkg_entry in firmware_base_dir. list_dir ( "." ) ?. flatten ( ) {
483+ if firmware_base_dir. get_file_type ( & pkg_entry) ? == openat:: SimpleType :: Dir {
484+ let pkg_name = pkg_entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
485+ payloads. insert ( pkg_name, pkg_entry. file_name ( ) . to_owned ( ) ) ;
486+ }
487+ }
488+ }
489+ payloads
490+ } ;
491+
492+ let old_keys: std:: collections:: HashSet < _ > = current. firmware . keys ( ) . collect ( ) ;
493+ let new_keys: std:: collections:: HashSet < _ > = available_payloads. keys ( ) . collect ( ) ;
494+ let all_keys: std:: collections:: HashSet < _ > = old_keys. union ( & new_keys) . collect ( ) ;
495+
496+ // determine if it should be added, updated, or removed.
497+ for pkg_name in all_keys {
498+ let old_payload = current. firmware . get ( * pkg_name) ;
499+ let new_payload_path = available_payloads. get ( * pkg_name) ;
500+
501+ let ( diff, src_dir, new_content) = match ( old_payload, new_payload_path) {
502+ // Payload exists in both old state and new source.
503+ ( Some ( old) , Some ( new_path) ) => {
504+ let new_ver_dir = rootcxt
505+ . sysroot
506+ . sub_dir ( firmware_base_dir_path) ?
507+ . sub_dir ( new_path. as_os_str ( ) ) ?;
508+ let new_payload_dir = new_ver_dir. sub_dir ( "EFI" ) ?;
509+ let new_ft = crate :: filetree:: FileTree :: new_from_dir ( & new_payload_dir) ?;
510+ let old_ft = old. filetree . as_ref ( ) . unwrap_or ( & new_ft) ;
511+ let diff = old_ft. diff ( & new_ft) ?;
512+
513+ let meta: ContentMetadata =
514+ serde_json:: from_reader ( new_ver_dir. open_file ( "EFI.json" ) ?) ?;
515+ let content = Box :: new ( InstalledContent {
516+ meta,
517+ filetree : Some ( new_ft) ,
518+ adopted_from : None ,
519+ firmware : BTreeMap :: new ( ) ,
520+ } ) ;
521+ ( diff, Some ( new_payload_dir) , Some ( content) )
522+ }
523+ // add as old payload is none
524+ ( None , Some ( new_path) ) => {
525+ let new_ver_dir = rootcxt
526+ . sysroot
527+ . sub_dir ( firmware_base_dir_path) ?
528+ . sub_dir ( new_path. as_os_str ( ) ) ?;
529+ let new_payload_dir = new_ver_dir. sub_dir ( "EFI" ) ?;
530+ let new_ft = crate :: filetree:: FileTree :: new_from_dir ( & new_payload_dir) ?;
531+ let empty_ft = crate :: filetree:: FileTree {
532+ children : BTreeMap :: new ( ) ,
533+ } ;
534+ let diff = empty_ft. diff ( & new_ft) ?;
535+
536+ let meta: ContentMetadata =
537+ serde_json:: from_reader ( new_ver_dir. open_file ( "EFI.json" ) ?) ?;
538+ let content = Box :: new ( InstalledContent {
539+ meta,
540+ filetree : Some ( new_ft) ,
541+ adopted_from : None ,
542+ firmware : BTreeMap :: new ( ) ,
543+ } ) ;
544+ ( diff, Some ( new_payload_dir) , Some ( content) )
545+ }
546+ // remove payload if not in new source
547+ ( Some ( old) , None ) => {
548+ let empty_ft = crate :: filetree:: FileTree {
549+ children : BTreeMap :: new ( ) ,
550+ } ;
551+ let old_ft = old. filetree . as_ref ( ) . unwrap_or ( & empty_ft) ;
552+ let diff = old_ft. diff ( & empty_ft) ?;
553+ ( diff, None , None )
554+ }
555+ // Should not happen.
556+ ( None , None ) => continue ,
557+ } ;
558+
559+ //apply the above diffs
560+ for esp in esp_devices. iter ( ) {
561+ let destpath = & self . ensure_mounted_esp ( rootcxt. path . as_ref ( ) , Path :: new ( esp) ) ?;
562+ let destdir = openat:: Dir :: open ( destpath) ?;
563+ let src_dir = src_dir. as_ref ( ) . unwrap_or ( & destdir) ;
564+ filetree:: apply_diff ( src_dir, & destdir, & diff, None )
565+ . context ( format ! ( "applying firmware diff for {}" , pkg_name) ) ?;
566+ }
567+
568+ if let Some ( content) = new_content {
569+ updated_firmware. insert ( pkg_name. to_string ( ) , content) ;
570+ }
571+ }
572+
573+ for esp in esp_devices. iter ( ) {
574+ let destpath = & self . ensure_mounted_esp ( rootcxt. path . as_ref ( ) , Path :: new ( esp) ) ?;
575+ let destdir = openat:: Dir :: open ( destpath) ?;
474576 destdir. syncfs ( ) ?;
475577 drop ( destdir) ;
476578 self . unmount ( ) . context ( "unmount after update" ) ?;
@@ -481,7 +583,7 @@ impl Component for Efi {
481583 meta : updatemeta,
482584 filetree : Some ( updatef) ,
483585 adopted_from,
484- firmware : BTreeMap :: new ( ) ,
586+ firmware : updated_firmware ,
485587 } )
486588 }
487589
0 commit comments