Skip to content

Commit 3a7cd83

Browse files
committed
efi: update the ESP by creating a tmpdir and RENAME_EXCHANGE
See Timothée's comment #454 (comment) Fixes #454
1 parent f90b45e commit 3a7cd83

File tree

1 file changed

+83
-5
lines changed

1 file changed

+83
-5
lines changed

src/efi.rs

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
584641
mod 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

Comments
 (0)