@@ -14,6 +14,7 @@ use bootc_internal_utils::CommandRunExt;
1414use camino:: { Utf8Path , Utf8PathBuf } ;
1515use cap_std:: fs:: Dir ;
1616use cap_std_ext:: cap_std;
17+ use cap_std_ext:: dirext:: CapStdExtDirExt ;
1718use chrono:: prelude:: * ;
1819use fn_error_context:: context;
1920use openat_ext:: OpenatDirExt ;
@@ -461,18 +462,34 @@ impl Component for Efi {
461462 }
462463
463464 fn generate_update_metadata ( & self , sysroot : & str ) -> Result < Option < ContentMetadata > > {
464- let sysroot_path = Utf8Path :: new ( sysroot) ;
465+ let sysroot_path = Path :: new ( sysroot) ;
466+ let sysroot_dir = Dir :: open_ambient_dir ( sysroot_path, cap_std:: ambient_authority ( ) ) ?;
467+
468+ if let Some ( ostreeboot) = sysroot_dir
469+ . open_dir_optional ( ostreeutil:: BOOT_PREFIX )
470+ . context ( "Opening usr/lib/ostree-boot" ) ?
471+ {
472+ let cruft = [ "loader" , "grub2" ] ;
473+ for p in cruft. iter ( ) {
474+ ostreeboot. remove_all_optional ( p) ?;
475+ }
476+ // Transfer ostree-boot EFI files to usr/lib/efi
477+ transfer_ostree_boot_to_usr ( sysroot_path) ?;
478+
479+ // Remove usr/lib/ostree-boot/efi/EFI dir (after transfer) or if it is empty
480+ ostreeboot. remove_all_optional ( "efi/EFI" ) ?;
481+ }
482+
483+ // copy EFI files from usr/lib/efi to updates dir
484+ if let Some ( efi_components) =
485+ get_efi_component_from_usr ( Utf8Path :: from_path ( sysroot_path) . unwrap ( ) , EFILIB ) ?
486+ {
487+ // Confirm EFI has at least two components grub2 & shim
488+ // See https://github.com/coreos/bootupd/issues/994
489+ assert ! ( efi_components. len( ) > 1 ) ;
465490
466- // copy EFI files to updates dir from usr/lib/efi
467- let efilib_path = sysroot_path. join ( EFILIB ) ;
468- let meta = if efilib_path. exists ( ) {
469491 let mut packages = Vec :: new ( ) ;
470492 let mut modules_vec: Vec < Module > = vec ! [ ] ;
471- let sysroot_dir = Dir :: open_ambient_dir ( sysroot_path, cap_std:: ambient_authority ( ) ) ?;
472- let efi_components = get_efi_component_from_usr ( & sysroot_path, EFILIB ) ?;
473- if efi_components. len ( ) == 0 {
474- bail ! ( "Failed to find EFI components from {efilib_path}" ) ;
475- }
476493 for efi in efi_components {
477494 Command :: new ( "cp" )
478495 . args ( [ "-rp" , "--reflink=auto" ] )
@@ -490,50 +507,16 @@ impl Component for Efi {
490507
491508 // change to now to workaround https://github.com/coreos/bootupd/issues/933
492509 let timestamp = std:: time:: SystemTime :: now ( ) ;
493- ContentMetadata {
510+ let meta = ContentMetadata {
494511 timestamp : chrono:: DateTime :: < Utc > :: from ( timestamp) ,
495512 version : packages. join ( "," ) ,
496513 versions : Some ( modules_vec) ,
497- }
514+ } ;
515+ write_update_metadata ( sysroot, self , & meta) ?;
516+ Ok ( Some ( meta) )
498517 } else {
499- let ostreebootdir = sysroot_path. join ( ostreeutil:: BOOT_PREFIX ) ;
500-
501- // move EFI files to updates dir from /usr/lib/ostree-boot
502- if ostreebootdir. exists ( ) {
503- let cruft = [ "loader" , "grub2" ] ;
504- for p in cruft. iter ( ) {
505- let p = ostreebootdir. join ( p) ;
506- if p. exists ( ) {
507- std:: fs:: remove_dir_all ( & p) ?;
508- }
509- }
510-
511- let efisrc = ostreebootdir. join ( "efi/EFI" ) ;
512- if !efisrc. exists ( ) {
513- bail ! ( "Failed to find {:?}" , & efisrc) ;
514- }
515-
516- let dest_efidir = component_updatedir ( sysroot, self ) ;
517- let dest_efidir =
518- Utf8PathBuf :: from_path_buf ( dest_efidir) . expect ( "Path is invalid UTF-8" ) ;
519- // Fork off mv() because on overlayfs one can't rename() a lower level
520- // directory today, and this will handle the copy fallback.
521- Command :: new ( "mv" ) . args ( [ & efisrc, & dest_efidir] ) . run ( ) ?;
522-
523- let efidir = openat:: Dir :: open ( dest_efidir. as_std_path ( ) )
524- . with_context ( || format ! ( "Opening {}" , dest_efidir) ) ?;
525- let files = crate :: util:: filenames ( & efidir) ?. into_iter ( ) . map ( |mut f| {
526- f. insert_str ( 0 , "/boot/efi/EFI/" ) ;
527- f
528- } ) ;
529- query_files ( sysroot, files) ?
530- } else {
531- anyhow:: bail!( "Failed to find {ostreebootdir}" ) ;
532- }
533- } ;
534-
535- write_update_metadata ( sysroot, self , & meta) ?;
536- Ok ( Some ( meta) )
518+ anyhow:: bail!( "Failed to find EFI components" ) ;
519+ }
537520 }
538521
539522 fn query_update ( & self , sysroot : & openat:: Dir ) -> Result < Option < ContentMetadata > > {
@@ -730,7 +713,7 @@ pub struct EFIComponent {
730713fn get_efi_component_from_usr < ' a > (
731714 sysroot : & ' a Utf8Path ,
732715 usr_path : & ' a str ,
733- ) -> Result < Vec < EFIComponent > > {
716+ ) -> Result < Option < Vec < EFIComponent > > > {
734717 let efilib_path = sysroot. join ( usr_path) ;
735718 let skip_count = Utf8Path :: new ( usr_path) . components ( ) . count ( ) ;
736719
@@ -761,9 +744,65 @@ fn get_efi_component_from_usr<'a>(
761744 } )
762745 . collect ( ) ;
763746
747+ if components. len ( ) == 0 {
748+ return Ok ( None ) ;
749+ }
764750 components. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
765751
766- Ok ( components)
752+ Ok ( Some ( components) )
753+ }
754+
755+ /// Copy files from usr/lib/ostree-boot/efi/EFI to /usr/lib/efi/<component>/<evr>/
756+ fn transfer_ostree_boot_to_usr ( sysroot : & Path ) -> Result < ( ) > {
757+ let ostreeboot_efi = Path :: new ( ostreeutil:: BOOT_PREFIX ) . join ( "efi" ) ;
758+ let ostreeboot_efi_path = sysroot. join ( & ostreeboot_efi) ;
759+
760+ let efi = ostreeboot_efi_path. join ( "EFI" ) ;
761+ if !efi. exists ( ) {
762+ return Ok ( ( ) ) ;
763+ }
764+ for entry in WalkDir :: new ( & efi) {
765+ let entry = entry?;
766+
767+ if entry. file_type ( ) . is_file ( ) {
768+ let entry_path = entry. path ( ) ;
769+
770+ // get path EFI/{BOOT,<vendor>}/<file>
771+ let filepath = entry_path. strip_prefix ( & ostreeboot_efi_path) ?;
772+ // get path /boot/efi/EFI/{BOOT,<vendor>}/<file>
773+ let boot_filepath = Path :: new ( "/boot/efi" ) . join ( filepath) ;
774+
775+ // Run `rpm -qf <filepath>`
776+ let pkg = crate :: packagesystem:: query_file (
777+ sysroot. to_str ( ) . unwrap ( ) ,
778+ boot_filepath. to_str ( ) . unwrap ( ) ,
779+ ) ?;
780+
781+ let ( name, evr) = pkg. split_once ( ' ' ) . unwrap ( ) ;
782+ let component = name. split ( '-' ) . next ( ) . unwrap_or ( "" ) ;
783+ // get path usr/lib/efi/<component>/<evr>
784+ let efilib_path = Path :: new ( EFILIB ) . join ( component) . join ( evr) ;
785+
786+ let sysroot_dir = openat:: Dir :: open ( sysroot) ?;
787+ // Ensure dest parent directory exists
788+ if let Some ( parent) = efilib_path. join ( filepath) . parent ( ) {
789+ sysroot_dir. ensure_dir_all ( parent, 0o755 ) ?;
790+ }
791+
792+ // Source dir is usr/lib/ostree-boot/efi
793+ let src = sysroot_dir
794+ . sub_dir ( & ostreeboot_efi)
795+ . context ( "Opening ostree-boot dir" ) ?;
796+ // Dest dir is usr/lib/efi/<component>/<evr>
797+ let dest = sysroot_dir
798+ . sub_dir ( & efilib_path)
799+ . context ( "Opening usr/lib/efi dir" ) ?;
800+ // Copy file from ostree-boot to usr/lib/efi
801+ src. copy_file_at ( filepath, & dest, filepath)
802+ . context ( "Copying file to usr/lib/efi" ) ?;
803+ }
804+ }
805+ Ok ( ( ) )
767806}
768807
769808#[ cfg( test) ]
@@ -900,7 +939,7 @@ Boot0003* test";
900939 let efi_comps = get_efi_component_from_usr ( utf8_tpath, EFILIB ) ?;
901940 assert_eq ! (
902941 efi_comps,
903- vec![
942+ Some ( vec![
904943 EFIComponent {
905944 name: "BAR" . to_string( ) ,
906945 version: "1.1" . to_string( ) ,
@@ -911,12 +950,12 @@ Boot0003* test";
911950 version: "1.1" . to_string( ) ,
912951 path: Utf8PathBuf :: from( "usr/lib/efi/FOO/1.1/EFI" ) ,
913952 } ,
914- ]
953+ ] )
915954 ) ;
916955 std:: fs:: remove_dir_all ( efi_path. join ( "BAR/1.1/EFI" ) ) ?;
917956 std:: fs:: remove_dir_all ( efi_path. join ( "FOO/1.1/EFI" ) ) ?;
918957 let efi_comps = get_efi_component_from_usr ( utf8_tpath, EFILIB ) ?;
919- assert_eq ! ( efi_comps, [ ] ) ;
958+ assert_eq ! ( efi_comps, None ) ;
920959 Ok ( ( ) )
921960 }
922961}
0 commit comments