@@ -26,6 +26,9 @@ use crate::{component::*, packagesystem};
2626/// Well-known paths to the ESP that may have been mounted external to us.
2727pub ( crate ) const ESP_MOUNTS : & [ & str ] = & [ "boot/efi" , "efi" , "boot" ] ;
2828
29+ /// Backup EFI dir
30+ const TEMP_EFI_DIR : & str = ".EFI.tmp" ;
31+
2932/// The binary to change EFI boot ordering
3033const EFIBOOTMGR : & str = "efibootmgr" ;
3134#[ cfg( target_arch = "aarch64" ) ]
@@ -349,12 +352,39 @@ impl Component for Efi {
349352 . context ( "opening update dir" ) ?;
350353 let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
351354 let diff = currentf. diff ( & updatef) ?;
352- self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
353- let destdir = self . open_esp ( ) . context ( "opening EFI dir" ) ?;
354- validate_esp ( & destdir) ?;
355+ let mountdir = self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
356+
357+ /* copy "lowest" directory that we need to make the change
358+ * e.g. we only affect EFI/fedora for example and not all of EFI
359+ */
360+
361+ let efipath = self . esp_path ( ) ?;
362+ let tmp_efipath = mountdir. join ( TEMP_EFI_DIR ) ;
363+ // remove a previous directory if it exists in order to handle being interrupted in the middle.
364+ if tmp_efipath. exists ( ) {
365+ std:: fs:: remove_dir_all ( & tmp_efipath) ?;
366+ }
367+ copy_dir ( & efipath, & tmp_efipath) . with_context ( || "copying existing files to temp dir" ) ?;
368+
369+ let tmpdir = sysroot. sub_dir ( & tmp_efipath) ?;
370+ validate_esp ( & tmpdir) ?;
355371 log:: trace!( "applying diff: {}" , & diff) ;
356- filetree:: apply_diff ( & updated, & destdir, & diff, None )
357- . context ( "applying filesystem changes" ) ?;
372+ filetree:: apply_diff ( & updated, & tmpdir, & diff, None )
373+ . context ( "applying filesystem changes to temp EFI" ) ?;
374+ {
375+ let esp = sysroot. sub_dir ( & mountdir) ?;
376+ log:: trace!(
377+ "doing local exchange for {} and {}" ,
378+ tmp_efipath. display( ) ,
379+ efipath. display( )
380+ ) ;
381+
382+ esp. local_exchange ( & tmp_efipath, & efipath)
383+ . with_context ( || format ! ( "exchange for {:?} and {:?}" , tmp_efipath, efipath) ) ?;
384+ // finally remove the temp dir
385+ log:: trace!( "cleanup: {}" , tmp_efipath. display( ) ) ;
386+ std:: fs:: remove_dir_all ( & tmp_efipath) . context ( "clean up temp" ) ?;
387+ }
358388 let adopted_from = None ;
359389 Ok ( InstalledContent {
360390 meta : updatemeta,
@@ -584,6 +614,18 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
584614 Ok ( result)
585615}
586616
617+ fn copy_dir ( src : & Path , dst : & Path ) -> Result < ( ) > {
618+ let r = std:: process:: Command :: new ( "cp" )
619+ . args ( [ "-a" ] )
620+ . arg ( src)
621+ . arg ( dst)
622+ . status ( ) ?;
623+ if !r. success ( ) {
624+ anyhow:: bail!( "Failed to copy" ) ;
625+ }
626+ Ok ( ( ) )
627+ }
628+
587629#[ cfg( test) ]
588630mod tests {
589631 use super :: * ;
0 commit comments