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