@@ -26,6 +26,9 @@ use crate::{component::*, packagesystem};
26
26
/// Well-known paths to the ESP that may have been mounted external to us.
27
27
pub ( crate ) const ESP_MOUNTS : & [ & str ] = & [ "boot/efi" , "efi" , "boot" ] ;
28
28
29
+ /// Backup EFI dir
30
+ const TEMP_EFI_DIR : & str = ".EFI.tmp" ;
31
+
29
32
/// The binary to change EFI boot ordering
30
33
const EFIBOOTMGR : & str = "efibootmgr" ;
31
34
#[ cfg( target_arch = "aarch64" ) ]
@@ -349,12 +352,39 @@ impl Component for Efi {
349
352
. context ( "opening update dir" ) ?;
350
353
let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
351
354
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) ?;
355
371
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
+ }
358
388
let adopted_from = None ;
359
389
Ok ( InstalledContent {
360
390
meta : updatemeta,
@@ -584,6 +614,18 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
584
614
Ok ( result)
585
615
}
586
616
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
+
587
629
#[ cfg( test) ]
588
630
mod tests {
589
631
use super :: * ;
0 commit comments