@@ -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+ /// Temp 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,35 @@ 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+ let efipath = self . esp_path ( ) ?;
358+ let tmp_efipath = mountdir. join ( TEMP_EFI_DIR ) ;
359+ // remove a previous directory if it exists in order to handle being interrupted in the middle.
360+ if tmp_efipath. exists ( ) {
361+ std:: fs:: remove_dir_all ( & tmp_efipath) ?;
362+ }
363+ copy_dir ( & efipath, & tmp_efipath) . with_context ( || "copying existing files to temp dir" ) ?;
364+
365+ let tmpdir = sysroot. sub_dir ( & tmp_efipath) ?;
366+ validate_esp ( & tmpdir) ?;
355367 log:: trace!( "applying diff: {}" , & diff) ;
356- filetree:: apply_diff ( & updated, & destdir, & diff, None )
357- . context ( "applying filesystem changes" ) ?;
368+ filetree:: apply_diff ( & updated, & tmpdir, & diff, None )
369+ . context ( "applying filesystem changes to temp EFI" ) ?;
370+ {
371+ let esp = sysroot. sub_dir ( & mountdir) ?;
372+ log:: trace!(
373+ "doing local exchange for {} and {}" ,
374+ tmp_efipath. display( ) ,
375+ efipath. display( )
376+ ) ;
377+
378+ esp. local_exchange ( & tmp_efipath, & efipath)
379+ . with_context ( || format ! ( "exchange for {:?} and {:?}" , tmp_efipath, efipath) ) ?;
380+ // finally remove the temp dir
381+ log:: trace!( "cleanup: {}" , tmp_efipath. display( ) ) ;
382+ std:: fs:: remove_dir_all ( & tmp_efipath) . context ( "clean up temp" ) ?;
383+ }
358384 let adopted_from = None ;
359385 Ok ( InstalledContent {
360386 meta : updatemeta,
@@ -584,6 +610,18 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
584610 Ok ( result)
585611}
586612
613+ fn copy_dir ( src : & Path , dst : & Path ) -> Result < ( ) > {
614+ let r = std:: process:: Command :: new ( "cp" )
615+ . args ( [ "-a" ] )
616+ . arg ( src)
617+ . arg ( dst)
618+ . status ( ) ?;
619+ if !r. success ( ) {
620+ anyhow:: bail!( "Failed to copy" ) ;
621+ }
622+ Ok ( ( ) )
623+ }
624+
587625#[ cfg( test) ]
588626mod tests {
589627 use super :: * ;
0 commit comments