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