@@ -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,36 @@ 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!( "doing local exchange for {} and {}" , tmpesp. display( ) , esp. display( ) ) ;
383+ p. local_exchange ( tmpesp, esp) ?;
384+ } else {
385+ bail ! ( "Failed to get mount dir" ) ;
386+ } ;
387+ // finally remove the temp dir
388+ let tmpesp = & self . esp_path_tmp ( ) ?;
389+ std:: fs:: remove_dir_all ( tmpesp) ?;
390+ assert_eq ! ( tmpesp. exists( ) , false ) ;
391+ }
357392 let adopted_from = None ;
358393 Ok ( InstalledContent {
359394 meta : updatemeta,
@@ -580,6 +615,28 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
580615 Ok ( result)
581616}
582617
618+ fn copy_dir_all ( src : & Path , dst : & Path ) -> Result < ( ) > {
619+ // Create the destination directory if it doesn't exist
620+ if !dst. exists ( ) {
621+ std:: fs:: create_dir_all ( dst) ?;
622+ }
623+
624+ // Iterate over directory entries
625+ for entry in std:: fs:: read_dir ( src) ? {
626+ let entry = entry?;
627+ let path = entry. path ( ) ;
628+ let relative_path = path. strip_prefix ( src) ?;
629+ let destination = dst. join ( relative_path) ;
630+
631+ if path. is_dir ( ) {
632+ copy_dir_all ( & path, & destination) ?;
633+ } else {
634+ std:: fs:: copy ( & path, & destination) ?;
635+ }
636+ }
637+ Ok ( ( ) )
638+ }
639+
583640#[ cfg( test) ]
584641mod tests {
585642 use super :: * ;
@@ -655,4 +712,25 @@ Boot0003* test";
655712 ) ;
656713 Ok ( ( ) )
657714 }
715+
716+ #[ test]
717+ fn test_copy_dir_all ( ) -> Result < ( ) > {
718+ env_logger:: init ( ) ;
719+ let td = tempfile:: tempdir ( ) ?;
720+ let tdp = td. path ( ) ;
721+ let src = tdp. join ( "efi" ) ;
722+ std:: fs:: create_dir_all ( & src) ?;
723+ std:: fs:: create_dir_all ( tdp. join ( "efi/BOOT" ) ) ?;
724+ std:: fs:: create_dir_all ( tdp. join ( "efi/fedora" ) ) ?;
725+ std:: fs:: write ( tdp. join ( "efi/BOOT" ) . join ( "fbx64.efi" ) , "fall back data" ) ?;
726+ std:: fs:: write ( tdp. join ( "efi/fedora" ) . join ( crate :: efi:: SHIM ) , "shim data" ) ?;
727+ std:: fs:: write ( tdp. join ( "efi/fedora" ) . join ( "grub.cfg" ) , "grub config data" ) ?;
728+ let dest = tdp. join ( "tmpefi" ) ;
729+ copy_dir_all ( src. as_path ( ) , dest. as_path ( ) ) ?;
730+ assert_eq ! ( dest. join( "BOOT/fbx64.efi" ) . exists( ) , true ) ;
731+ assert_eq ! ( dest. join( "fedora" ) . join( crate :: efi:: SHIM ) . exists( ) , true ) ;
732+ assert_eq ! ( dest. join( "fedora/grub.cfg" ) . exists( ) , true ) ;
733+
734+ Ok ( ( ) )
735+ }
658736}
0 commit comments