@@ -632,11 +632,11 @@ impl Component for Efi {
632632 for p in cruft. iter ( ) {
633633 ostreeboot. remove_all_optional ( p) ?;
634634 }
635- // Transfer ostree-boot EFI files to usr/lib/efi
635+ // Transfer ostree-boot efi/ files to usr/lib/efi
636636 transfer_ostree_boot_to_usr ( sysroot_path) ?;
637637
638- // Remove usr/lib/ostree-boot/ efi/EFI dir ( after transfer) or if it is empty
639- ostreeboot. remove_all_optional ( "efi/EFI " ) ?;
638+ // Remove the entire efi/ tree after transfer, or if it is empty
639+ ostreeboot. remove_all_optional ( "efi" ) ?;
640640 }
641641
642642 if let Some ( efi_components) =
@@ -958,55 +958,73 @@ fn latest_versions(components: &[EFIComponent]) -> Vec<&EFIComponent> {
958958 result
959959}
960960
961- /// Copy files from usr/lib/ostree-boot/efi/EFI to /usr/lib/efi/<component>/<evr>/
961+ /// Copy files from usr/lib/ostree-boot/efi/ to usr/lib/efi/<component>/<evr>/
962+ ///
963+ /// Walks the entire `efi/` directory (both `EFI/` subdirectories and
964+ /// root-level firmware files) and uses `rpm -qf` to determine which
965+ /// package owns each file so it can be placed in the right component
966+ /// directory under `usr/lib/efi/`.
962967fn transfer_ostree_boot_to_usr ( sysroot : & Path ) -> Result < ( ) > {
968+ transfer_ostree_boot_to_usr_impl ( sysroot, |sysroot_path, filepath| {
969+ let boot_filepath = Path :: new ( "/boot/efi" ) . join ( filepath) ;
970+ crate :: packagesystem:: query_file (
971+ sysroot_path. to_str ( ) . unwrap ( ) ,
972+ boot_filepath. to_str ( ) . unwrap ( ) ,
973+ )
974+ } )
975+ }
976+
977+ /// Inner implementation that accepts a package-resolver callback so it
978+ /// can be unit-tested without a real RPM database.
979+ ///
980+ /// `resolve_pkg(sysroot, filepath)` must return `"<name> <evr>"`.
981+ fn transfer_ostree_boot_to_usr_impl < F > ( sysroot : & Path , resolve_pkg : F ) -> Result < ( ) >
982+ where
983+ F : Fn ( & Path , & Path ) -> Result < String > ,
984+ {
963985 let ostreeboot_efi = Path :: new ( ostreeutil:: BOOT_PREFIX ) . join ( "efi" ) ;
964986 let ostreeboot_efi_path = sysroot. join ( & ostreeboot_efi) ;
965987
966- let efi = ostreeboot_efi_path. join ( "EFI" ) ;
967- if !efi. exists ( ) {
988+ if !ostreeboot_efi_path. exists ( ) {
968989 return Ok ( ( ) ) ;
969990 }
970- for entry in WalkDir :: new ( & efi) {
971- let entry = entry?;
972991
973- if entry. file_type ( ) . is_file ( ) {
974- let entry_path = entry. path ( ) ;
992+ let sysroot_dir = openat:: Dir :: open ( sysroot) ?;
993+ // Source dir is usr/lib/ostree-boot/efi
994+ let src = sysroot_dir
995+ . sub_dir ( & ostreeboot_efi)
996+ . context ( "Opening ostree-boot/efi dir" ) ?;
975997
976- // get path EFI/{BOOT,<vendor>}/<file>
977- let filepath = entry_path. strip_prefix ( & ostreeboot_efi_path) ?;
978- // get path /boot/efi/EFI/{BOOT,<vendor>}/<file>
979- let boot_filepath = Path :: new ( "/boot/efi" ) . join ( filepath) ;
998+ for entry in WalkDir :: new ( & ostreeboot_efi_path) {
999+ let entry = entry?;
1000+ if !entry. file_type ( ) . is_file ( ) {
1001+ continue ;
1002+ }
9801003
981- // Run `rpm -qf <filepath>`
982- let pkg = crate :: packagesystem:: query_file (
983- sysroot. to_str ( ) . unwrap ( ) ,
984- boot_filepath. to_str ( ) . unwrap ( ) ,
985- ) ?;
1004+ // get path relative to the efi/ root (e.g. EFI/BOOT/shim.efi or start4.elf)
1005+ let filepath = entry. path ( ) . strip_prefix ( & ostreeboot_efi_path) ?;
9861006
987- let ( name, evr) = pkg. split_once ( ' ' ) . unwrap ( ) ;
988- let component = name. split ( '-' ) . next ( ) . unwrap_or ( "" ) ;
989- // get path usr/lib/efi/<component>/<evr>
990- let efilib_path = Path :: new ( EFILIB ) . join ( component) . join ( evr) ;
1007+ // Run `rpm -qf /boot/efi/<filepath>` to find the owning package
1008+ let pkg = resolve_pkg ( sysroot, filepath) ?;
9911009
992- let sysroot_dir = openat :: Dir :: open ( sysroot ) ? ;
993- // Ensure dest parent directory exists
994- if let Some ( parent ) = efilib_path . join ( filepath ) . parent ( ) {
995- sysroot_dir . ensure_dir_all ( parent , 0o755 ) ? ;
996- }
1010+ let ( name , evr ) = pkg
1011+ . split_once ( ' ' )
1012+ . with_context ( || format ! ( "parsing rpm output: {}" , pkg ) ) ? ;
1013+ // get path usr/lib/efi/<component>/<evr>
1014+ let efilib_path = Path :: new ( EFILIB ) . join ( name ) . join ( evr ) ;
9971015
998- // Source dir is usr/lib/ostree-boot/efi
999- let src = sysroot_dir
1000- . sub_dir ( & ostreeboot_efi)
1001- . context ( "Opening ostree-boot dir" ) ?;
1002- // Dest dir is usr/lib/efi/<component>/<evr>
1003- let dest = sysroot_dir
1004- . sub_dir ( & efilib_path)
1005- . context ( "Opening usr/lib/efi dir" ) ?;
1006- // Copy file from ostree-boot to usr/lib/efi
1007- src. copy_file_at ( filepath, & dest, filepath)
1008- . context ( "Copying file to usr/lib/efi" ) ?;
1016+ // Ensure dest parent directory exists
1017+ if let Some ( parent) = efilib_path. join ( filepath) . parent ( ) {
1018+ sysroot_dir. ensure_dir_all ( parent, 0o755 ) ?;
10091019 }
1020+
1021+ // Dest dir is usr/lib/efi/<component>/<evr>
1022+ let dest = sysroot_dir
1023+ . sub_dir ( & efilib_path)
1024+ . context ( "Opening usr/lib/efi dir" ) ?;
1025+ // Copy file from ostree-boot to usr/lib/efi
1026+ src. copy_file_at ( filepath, & dest, filepath)
1027+ . context ( "Copying file to usr/lib/efi" ) ?;
10101028 }
10111029 Ok ( ( ) )
10121030}
@@ -1316,4 +1334,66 @@ Boot0003* test";
13161334
13171335 Ok ( ( ) )
13181336 }
1337+
1338+ #[ test]
1339+ fn test_transfer_ostree_boot_to_usr ( ) -> Result < ( ) > {
1340+ let tmpdir = tempfile:: tempdir ( ) ?;
1341+ let sysroot = tmpdir. path ( ) ;
1342+
1343+ // Simulate usr/lib/ostree-boot/efi/ with both EFI/ and root-level files
1344+ let efi_dir = sysroot. join ( "usr/lib/ostree-boot/efi" ) ;
1345+ std:: fs:: create_dir_all ( efi_dir. join ( "EFI/vendor" ) ) ?;
1346+ std:: fs:: write ( efi_dir. join ( "EFI/vendor/foo.efi" ) , "foo data" ) ?;
1347+ std:: fs:: create_dir_all ( efi_dir. join ( "EFI/BOOT" ) ) ?;
1348+ std:: fs:: write ( efi_dir. join ( "EFI/BOOT/BOOTAA64.EFI" ) , "boot data" ) ?;
1349+ // Root-level files
1350+ std:: fs:: write ( efi_dir. join ( "bar.dtb" ) , "bar data" ) ?;
1351+ std:: fs:: write ( efi_dir. join ( "baz.bin" ) , "baz data" ) ?;
1352+ std:: fs:: create_dir_all ( efi_dir. join ( "sub" ) ) ?;
1353+ std:: fs:: write ( efi_dir. join ( "sub/quux.dat" ) , "quux data" ) ?;
1354+
1355+ // Ensure the destination base directory exists
1356+ std:: fs:: create_dir_all ( sysroot. join ( EFILIB ) ) ?;
1357+
1358+ // Fake resolver: EFI files belong to "FOO 1.0", root-level
1359+ // files to "BAR 2.0"
1360+ let resolve = |_sysroot : & Path , filepath : & Path | -> Result < String > {
1361+ let s = filepath. to_str ( ) . unwrap ( ) ;
1362+ if s. starts_with ( "EFI" ) {
1363+ Ok ( "FOO 1.0" . to_string ( ) )
1364+ } else {
1365+ Ok ( "BAR 2.0" . to_string ( ) )
1366+ }
1367+ } ;
1368+
1369+ transfer_ostree_boot_to_usr_impl ( sysroot, resolve) ?;
1370+
1371+ // EFI files should be under EFILIB/FOO/1.0/EFI/...
1372+ let foo_base = sysroot. join ( "usr/lib/efi/FOO/1.0" ) ;
1373+ assert_eq ! (
1374+ std:: fs:: read_to_string( foo_base. join( "EFI/vendor/foo.efi" ) ) ?,
1375+ "foo data"
1376+ ) ;
1377+ assert_eq ! (
1378+ std:: fs:: read_to_string( foo_base. join( "EFI/BOOT/BOOTAA64.EFI" ) ) ?,
1379+ "boot data"
1380+ ) ;
1381+
1382+ // Root-level files should be under EFILIB/BAR/2.0/
1383+ let bar_base = sysroot. join ( "usr/lib/efi/BAR/2.0" ) ;
1384+ assert_eq ! (
1385+ std:: fs:: read_to_string( bar_base. join( "bar.dtb" ) ) ?,
1386+ "bar data"
1387+ ) ;
1388+ assert_eq ! (
1389+ std:: fs:: read_to_string( bar_base. join( "baz.bin" ) ) ?,
1390+ "baz data"
1391+ ) ;
1392+ assert_eq ! (
1393+ std:: fs:: read_to_string( bar_base. join( "sub/quux.dat" ) ) ?,
1394+ "quux data"
1395+ ) ;
1396+
1397+ Ok ( ( ) )
1398+ }
13191399}
0 commit comments