@@ -72,6 +72,17 @@ impl Efi {
7272 Ok ( esp)
7373 }
7474
75+ fn esp_path_tmp ( & self ) -> Result < PathBuf > {
76+ self . ensure_mounted_esp ( Path :: new ( "/" ) )
77+ . map ( |v| v. join ( ".EFI.tmp" ) )
78+ }
79+
80+ fn open_esp_tmp_optional ( & self ) -> Result < Option < openat:: Dir > > {
81+ let sysroot = openat:: Dir :: open ( "/" ) ?;
82+ let esp = sysroot. sub_dir_optional ( & self . esp_path_tmp ( ) ?) ?;
83+ Ok ( esp)
84+ }
85+
7586 pub ( crate ) fn ensure_mounted_esp ( & self , root : & Path ) -> Result < PathBuf > {
7687 let mut mountpoint = self . mountpoint . borrow_mut ( ) ;
7788 if let Some ( mountpoint) = mountpoint. as_deref ( ) {
@@ -348,12 +359,40 @@ impl Component for Efi {
348359 . context ( "opening update dir" ) ?;
349360 let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
350361 let diff = currentf. diff ( & updatef) ?;
351- self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
352- let destdir = self . open_esp ( ) . context ( "opening EFI dir" ) ?;
353- validate_esp ( & destdir) ?;
362+ let mountdir = self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
363+
364+ // copy esp dir to temp to do apply diff
365+ let esp = & self . esp_path ( ) ?;
366+ let tmpesp = & self . esp_path_tmp ( ) ?;
367+ copy_dir_all ( esp, tmpesp) . context ( "copying esp dir to temp dir" ) ?;
368+ assert ! ( tmpesp. exists( ) ) ;
369+
370+ let tmpdir = if let Some ( p) = self . open_esp_tmp_optional ( ) ? {
371+ p
372+ } else {
373+ bail ! ( "Failed to open temp efi dir" ) ;
374+ } ;
375+ validate_esp ( & tmpdir) ?;
354376 log:: trace!( "applying diff: {}" , & diff) ;
355- filetree:: apply_diff ( & updated, & destdir, & diff, None )
356- . context ( "applying filesystem changes" ) ?;
377+ filetree:: apply_diff ( & updated, & tmpdir, & diff, None )
378+ . context ( "applying filesystem changes to temp EFI" ) ?;
379+ {
380+ // do local exchange of the temp dir and esp dir
381+ if let Some ( p) = sysroot. sub_dir_optional ( & mountdir) ? {
382+ log:: trace!(
383+ "doing local exchange for {} and {}" ,
384+ tmpesp. display( ) ,
385+ esp. display( )
386+ ) ;
387+ p. local_exchange ( tmpesp, esp) ?;
388+ } else {
389+ bail ! ( "Failed to get mount dir" ) ;
390+ } ;
391+ // finally remove the temp dir
392+ let tmpesp = & self . esp_path_tmp ( ) ?;
393+ std:: fs:: remove_dir_all ( tmpesp) ?;
394+ assert_eq ! ( tmpesp. exists( ) , false ) ;
395+ }
357396 let adopted_from = None ;
358397 Ok ( InstalledContent {
359398 meta : updatemeta,
@@ -580,6 +619,28 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
580619 Ok ( result)
581620}
582621
622+ fn copy_dir_all ( src : & Path , dst : & Path ) -> Result < ( ) > {
623+ // Create the destination directory if it doesn't exist
624+ if !dst. exists ( ) {
625+ std:: fs:: create_dir_all ( dst) ?;
626+ }
627+
628+ // Iterate over directory entries
629+ for entry in std:: fs:: read_dir ( src) ? {
630+ let entry = entry?;
631+ let path = entry. path ( ) ;
632+ let relative_path = path. strip_prefix ( src) ?;
633+ let destination = dst. join ( relative_path) ;
634+
635+ if path. is_dir ( ) {
636+ copy_dir_all ( & path, & destination) ?;
637+ } else {
638+ std:: fs:: copy ( & path, & destination) ?;
639+ }
640+ }
641+ Ok ( ( ) )
642+ }
643+
583644#[ cfg( test) ]
584645mod tests {
585646 use super :: * ;
@@ -655,4 +716,25 @@ Boot0003* test";
655716 ) ;
656717 Ok ( ( ) )
657718 }
719+
720+ #[ test]
721+ fn test_copy_dir_all ( ) -> Result < ( ) > {
722+ env_logger:: init ( ) ;
723+ let td = tempfile:: tempdir ( ) ?;
724+ let tdp = td. path ( ) ;
725+ let src = tdp. join ( "efi" ) ;
726+ std:: fs:: create_dir_all ( & src) ?;
727+ std:: fs:: create_dir_all ( tdp. join ( "efi/BOOT" ) ) ?;
728+ std:: fs:: create_dir_all ( tdp. join ( "efi/fedora" ) ) ?;
729+ std:: fs:: write ( tdp. join ( "efi/BOOT" ) . join ( "fbx64.efi" ) , "fall back data" ) ?;
730+ std:: fs:: write ( tdp. join ( "efi/fedora" ) . join ( crate :: efi:: SHIM ) , "shim data" ) ?;
731+ std:: fs:: write ( tdp. join ( "efi/fedora" ) . join ( "grub.cfg" ) , "grub config data" ) ?;
732+ let dest = tdp. join ( "tmpefi" ) ;
733+ copy_dir_all ( src. as_path ( ) , dest. as_path ( ) ) ?;
734+ assert_eq ! ( dest. join( "BOOT/fbx64.efi" ) . exists( ) , true ) ;
735+ assert_eq ! ( dest. join( "fedora" ) . join( crate :: efi:: SHIM ) . exists( ) , true ) ;
736+ assert_eq ! ( dest. join( "fedora/grub.cfg" ) . exists( ) , true ) ;
737+
738+ Ok ( ( ) )
739+ }
658740}
0 commit comments