Skip to content

Commit a53c172

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

File tree

1 file changed

+87
-5
lines changed

1 file changed

+87
-5
lines changed

src/efi.rs

+87-5
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,40 @@ 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+
let parentdir = if let Some(p) = sysroot.sub_dir_optional(&mountdir)? {
382+
p
383+
} else {
384+
bail!("Failed to get parent dir");
385+
};
386+
parentdir.local_exchange(tmpesp, esp).with_context(|| {
387+
format!(
388+
"local exchange for {} and {}",
389+
tmpesp.display(),
390+
esp.display()
391+
)
392+
})?;
393+
// finally remove the temp dir
394+
std::fs::remove_dir_all(tmpesp)?;
395+
}
357396
let adopted_from = None;
358397
Ok(InstalledContent {
359398
meta: updatemeta,
@@ -580,6 +619,28 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
580619
Ok(result)
581620
}
582621

622+
fn copy_dir_all(src: &Path, dst: &Path) -> Result<()> {
623+
// Create the destination directory if it doesn't exist
624+
if !dst.exists() {
625+
std::fs::create_dir_all(dst)?;
626+
}
627+
628+
// Iterate over directory entries
629+
for entry in std::fs::read_dir(src)? {
630+
let entry = entry?;
631+
let path = entry.path();
632+
let relative_path = path.strip_prefix(src)?;
633+
let destination = dst.join(relative_path);
634+
635+
if path.is_dir() {
636+
copy_dir_all(&path, &destination)?;
637+
} else {
638+
std::fs::copy(&path, &destination)?;
639+
}
640+
}
641+
Ok(())
642+
}
643+
583644
#[cfg(test)]
584645
mod tests {
585646
use super::*;
@@ -655,4 +716,25 @@ Boot0003* test";
655716
);
656717
Ok(())
657718
}
719+
720+
#[test]
721+
fn test_copy_dir_all() -> Result<()> {
722+
env_logger::init();
723+
let td = tempfile::tempdir()?;
724+
let tdp = td.path();
725+
let src = tdp.join("efi");
726+
std::fs::create_dir_all(&src)?;
727+
std::fs::create_dir_all(tdp.join("efi/BOOT"))?;
728+
std::fs::create_dir_all(tdp.join("efi/fedora"))?;
729+
std::fs::write(tdp.join("efi/BOOT").join("fbx64.efi"), "fall back data")?;
730+
std::fs::write(tdp.join("efi/fedora").join(crate::efi::SHIM), "shim data")?;
731+
std::fs::write(tdp.join("efi/fedora").join("grub.cfg"), "grub config data")?;
732+
let dest = tdp.join("tmpefi");
733+
copy_dir_all(src.as_path(), dest.as_path())?;
734+
assert_eq!(dest.join("BOOT/fbx64.efi").exists(), true);
735+
assert_eq!(dest.join("fedora").join(crate::efi::SHIM).exists(), true);
736+
assert_eq!(dest.join("fedora/grub.cfg").exists(), true);
737+
738+
Ok(())
739+
}
658740
}

0 commit comments

Comments
 (0)