@@ -11,8 +11,10 @@ use std::process::Command;
1111
1212use anyhow:: { bail, Context , Result } ;
1313use bootc_internal_utils:: CommandRunExt ;
14+ use camino:: { Utf8Path , Utf8PathBuf } ;
1415use cap_std:: fs:: Dir ;
1516use cap_std_ext:: cap_std;
17+ use chrono:: prelude:: * ;
1618use fn_error_context:: context;
1719use openat_ext:: OpenatDirExt ;
1820use os_release:: OsRelease ;
@@ -31,6 +33,9 @@ use crate::{component::*, packagesystem};
3133/// Well-known paths to the ESP that may have been mounted external to us.
3234pub ( crate ) const ESP_MOUNTS : & [ & str ] = & [ "boot/efi" , "efi" , "boot" ] ;
3335
36+ /// New efi dir under usr/lib
37+ const EFILIB : & str = "usr/lib/efi" ;
38+
3439/// The binary to change EFI boot ordering
3540const EFIBOOTMGR : & str = "efibootmgr" ;
3641#[ cfg( target_arch = "aarch64" ) ]
@@ -427,38 +432,77 @@ impl Component for Efi {
427432 } )
428433 }
429434
430- fn generate_update_metadata ( & self , sysroot_path : & str ) -> Result < ContentMetadata > {
431- let ostreebootdir = Path :: new ( sysroot_path) . join ( ostreeutil:: BOOT_PREFIX ) ;
432- let dest_efidir = component_updatedir ( sysroot_path, self ) ;
433-
434- if ostreebootdir. exists ( ) {
435- let cruft = [ "loader" , "grub2" ] ;
436- for p in cruft. iter ( ) {
437- let p = ostreebootdir. join ( p) ;
438- if p. exists ( ) {
439- std:: fs:: remove_dir_all ( & p) ?;
435+ fn generate_update_metadata ( & self , sysroot : & str ) -> Result < ContentMetadata > {
436+ let sysroot_path = Utf8Path :: new ( sysroot) ;
437+
438+ // copy EFI files to updates dir from usr/lib/efi
439+ let meta = if sysroot_path. join ( EFILIB ) . exists ( ) {
440+ let mut packages = Vec :: new ( ) ;
441+ for efi_dir in get_efi_path_from_usr ( & sysroot_path) ? {
442+ let efi_dir = efi_dir. context ( "Invalid EFI directory path" ) ?;
443+
444+ let sysroot_dir =
445+ Dir :: open_ambient_dir ( sysroot_path, cap_std:: ambient_authority ( ) ) ?;
446+ Command :: new ( "cp" )
447+ . args ( [ "-rp" , "--reflink=auto" ] )
448+ . arg ( & efi_dir)
449+ . arg ( crate :: model:: BOOTUPD_UPDATES_DIR )
450+ . current_dir ( format ! ( "/proc/self/fd/{}" , sysroot_dir. as_raw_fd( ) ) )
451+ . run ( ) ?;
452+
453+ if let Ok ( relative) = efi_dir. strip_prefix ( EFILIB ) {
454+ let mut components = relative. components ( ) ;
455+
456+ if let ( Some ( pkg) , Some ( ver) ) = ( components. next ( ) , components. next ( ) ) {
457+ packages. push ( format ! ( "{}-{}" , pkg. as_str( ) , ver. as_str( ) ) ) ;
458+ }
440459 }
441460 }
442461
443- let efisrc = ostreebootdir. join ( "efi/EFI" ) ;
444- if !efisrc. exists ( ) {
445- bail ! ( "Failed to find {:?}" , & efisrc) ;
462+ // change to now to workaround https://github.com/coreos/bootupd/issues/933
463+ let timestamp = std:: time:: SystemTime :: now ( ) ;
464+ ContentMetadata {
465+ timestamp : chrono:: DateTime :: < Utc > :: from ( timestamp) ,
466+ version : packages. join ( "," ) ,
446467 }
468+ } else {
469+ let ostreebootdir = sysroot_path. join ( ostreeutil:: BOOT_PREFIX ) ;
470+
471+ // move EFI files to updates dir from /usr/lib/ostree-boot
472+ if ostreebootdir. exists ( ) {
473+ let cruft = [ "loader" , "grub2" ] ;
474+ for p in cruft. iter ( ) {
475+ let p = ostreebootdir. join ( p) ;
476+ if p. exists ( ) {
477+ std:: fs:: remove_dir_all ( & p) ?;
478+ }
479+ }
447480
448- // Fork off mv() because on overlayfs one can't rename() a lower level
449- // directory today, and this will handle the copy fallback.
450- Command :: new ( "mv" ) . args ( [ & efisrc , & dest_efidir ] ) . run ( ) ? ;
451- }
481+ let efisrc = ostreebootdir . join ( "efi/EFI" ) ;
482+ if !efisrc . exists ( ) {
483+ bail ! ( "Failed to find {:?}" , & efisrc ) ;
484+ }
452485
453- let efidir = openat:: Dir :: open ( & dest_efidir)
454- . with_context ( || format ! ( "Opening {}" , dest_efidir. display( ) ) ) ?;
455- let files = crate :: util:: filenames ( & efidir) ?. into_iter ( ) . map ( |mut f| {
456- f. insert_str ( 0 , "/boot/efi/EFI/" ) ;
457- f
458- } ) ;
486+ let dest_efidir = component_updatedir ( sysroot, self ) ;
487+ let dest_efidir =
488+ Utf8PathBuf :: from_path_buf ( dest_efidir) . expect ( "Path is invalid UTF-8" ) ;
489+ // Fork off mv() because on overlayfs one can't rename() a lower level
490+ // directory today, and this will handle the copy fallback.
491+ Command :: new ( "mv" ) . args ( [ & efisrc, & dest_efidir] ) . run ( ) ?;
492+
493+ let efidir = openat:: Dir :: open ( dest_efidir. as_std_path ( ) )
494+ . with_context ( || format ! ( "Opening {}" , dest_efidir) ) ?;
495+ let files = crate :: util:: filenames ( & efidir) ?. into_iter ( ) . map ( |mut f| {
496+ f. insert_str ( 0 , "/boot/efi/EFI/" ) ;
497+ f
498+ } ) ;
499+ packagesystem:: query_files ( sysroot, files) ?
500+ } else {
501+ anyhow:: bail!( "Failed to find {ostreebootdir}" ) ;
502+ }
503+ } ;
459504
460- let meta = packagesystem:: query_files ( sysroot_path, files) ?;
461- write_update_metadata ( sysroot_path, self , & meta) ?;
505+ write_update_metadata ( sysroot, self , & meta) ?;
462506 Ok ( meta)
463507 }
464508
@@ -654,6 +698,29 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
654698 Ok ( result)
655699}
656700
701+ /// Get EFI path under usr/lib/efi, eg. usr/lib/efi/<package>/<version>/EFI
702+ fn get_efi_path_from_usr (
703+ sysroot : & Utf8Path ,
704+ ) -> Result < impl Iterator < Item = Result < Utf8PathBuf > > + use < ' _ > > {
705+ let efilib_path = sysroot. join ( EFILIB ) ;
706+
707+ Ok ( WalkDir :: new ( efilib_path)
708+ . min_depth ( 3 )
709+ . max_depth ( 3 )
710+ . into_iter ( )
711+ . filter_entry ( |e| e. file_type ( ) . is_dir ( ) && e. file_name ( ) == "EFI" )
712+ . map ( move |entry| {
713+ let abs_path = entry. context ( "Failed to read directory entry" ) ?. into_path ( ) ;
714+
715+ let rel_path = abs_path
716+ . strip_prefix ( sysroot)
717+ . context ( "Failed to strip sysroot prefix" ) ?;
718+
719+ Utf8PathBuf :: from_path_buf ( rel_path. to_path_buf ( ) )
720+ . map_err ( |e| anyhow:: anyhow!( "Invalid UTF-8 path : {}" , e. display( ) ) )
721+ } ) )
722+ }
723+
657724#[ cfg( test) ]
658725mod tests {
659726 use cap_std_ext:: dirext:: CapStdExtDirExt ;
@@ -774,4 +841,23 @@ Boot0003* test";
774841 }
775842 Ok ( ( ) )
776843 }
844+
845+ #[ test]
846+ fn test_get_efi_path_from_usr ( ) -> Result < ( ) > {
847+ let tmpdir: & tempfile:: TempDir = & tempfile:: tempdir ( ) ?;
848+ let tpath = tmpdir. path ( ) ;
849+ let efi_path = tpath. join ( "usr/lib/efi" ) ;
850+ std:: fs:: create_dir_all ( efi_path. join ( "FOO/1.1/EFI" ) ) ?;
851+ std:: fs:: create_dir_all ( efi_path. join ( "BAR/1.1/EFI" ) ) ?;
852+ std:: fs:: create_dir_all ( efi_path. join ( "FOOBAR/1.1/test" ) ) ?;
853+ let utf8_tpath =
854+ Utf8Path :: from_path ( tpath) . ok_or_else ( || anyhow:: anyhow!( "Path is not valid UTF-8" ) ) ?;
855+ let efi_path_iter = get_efi_path_from_usr ( utf8_tpath) ?;
856+ let paths: Vec < Utf8PathBuf > = efi_path_iter. filter_map ( Result :: ok) . collect ( ) ;
857+ assert_eq ! (
858+ paths,
859+ [ "usr/lib/efi/FOO/1.1/EFI" , "usr/lib/efi/BAR/1.1/EFI" ]
860+ ) ;
861+ Ok ( ( ) )
862+ }
777863}
0 commit comments