Skip to content

Commit

Permalink
efi: update the ESP by creating a tmpdir and RENAME_EXCHANGE
Browse files Browse the repository at this point in the history
Fixes coreos#454
See Timothée's comment coreos#454 (comment)
  • Loading branch information
HuijingHei committed Jun 19, 2024
1 parent f90b45e commit 5311abb
Showing 1 changed file with 92 additions and 5 deletions.
97 changes: 92 additions & 5 deletions src/efi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ impl Efi {
Ok(esp)
}

fn esp_path_tmp(&self) -> Result<PathBuf> {
self.ensure_mounted_esp(Path::new("/"))
.map(|v| v.join(".EFI.tmp"))
}

fn open_esp_tmp_optional(&self) -> Result<Option<openat::Dir>> {
let sysroot = openat::Dir::open("/")?;
let esp = sysroot.sub_dir_optional(&self.esp_path_tmp()?)?;
Ok(esp)
}

pub(crate) fn ensure_mounted_esp(&self, root: &Path) -> Result<PathBuf> {
let mut mountpoint = self.mountpoint.borrow_mut();
if let Some(mountpoint) = mountpoint.as_deref() {
Expand Down Expand Up @@ -348,12 +359,36 @@ impl Component for Efi {
.context("opening update dir")?;
let updatef = filetree::FileTree::new_from_dir(&updated).context("reading update dir")?;
let diff = currentf.diff(&updatef)?;
self.ensure_mounted_esp(Path::new("/"))?;
let destdir = self.open_esp().context("opening EFI dir")?;
validate_esp(&destdir)?;
let mountdir = self.ensure_mounted_esp(Path::new("/"))?;

// copy esp dir to temp to do apply diff
let esp = &self.esp_path()?;
let tmpesp = &self.esp_path_tmp()?;
copy_dir_all(esp, tmpesp).context("copying esp dir to temp dir")?;
assert!(tmpesp.exists());

let tmpdir = if let Some(p) = self.open_esp_tmp_optional()? {
p
} else {
bail!("Failed to open temp efi dir");
};
validate_esp(&tmpdir)?;
log::trace!("applying diff: {}", &diff);
filetree::apply_diff(&updated, &destdir, &diff, None)
.context("applying filesystem changes")?;
filetree::apply_diff(&updated, &tmpdir, &diff, None)
.context("applying filesystem changes to temp EFI")?;
{
// do local exchange of the temp dir and esp dir
let parentdir = if let Some(p) = sysroot.sub_dir_optional(&mountdir)? {
p
} else {
bail!("Failed to get parent dir");
};
parentdir
.local_exchange(tmpesp, esp)
.with_context(|| format!("local exchange for {} and {}", tmpesp.display(), esp.display()))?;
// finally remove the temp dir
std::fs::remove_dir_all(tmpesp)?;
}
let adopted_from = None;
Ok(InstalledContent {
meta: updatemeta,
Expand Down Expand Up @@ -580,6 +615,28 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
Ok(result)
}

fn copy_dir_all(src: &Path, dst: &Path) -> Result<()> {
// Create the destination directory if it doesn't exist
if !dst.exists() {
std::fs::create_dir_all(dst)?;
}

// Iterate over directory entries
for entry in std::fs::read_dir(src)? {
let entry = entry?;
let path = entry.path();
let relative_path = path.strip_prefix(src)?;
let destination = dst.join(relative_path);

if path.is_dir() {
copy_dir_all(&path, &destination)?;
} else {
std::fs::copy(&path, &destination)?;
}
}
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -655,4 +712,34 @@ Boot0003* test";
);
Ok(())
}

#[test]
fn test_copy_dir_all() -> Result<()> {
env_logger::init();
let td = tempfile::tempdir()?;
let tdp = td.path();
let src = tdp.join("efi");
std::fs::create_dir_all(&src)?;
std::fs::create_dir_all(tdp.join("efi/BOOT"))?;
std::fs::create_dir_all(tdp.join("efi/fedora"))?;
std::fs::write(
tdp.join("efi/BOOT").join("fbx64.efi"),
"fall back data",
)?;
std::fs::write(
tdp.join("efi/fedora").join(crate::efi::SHIM),
"shim data",
)?;
std::fs::write(
tdp.join("efi/fedora").join("grub.cfg"),
"grub config data",
)?;
let dest = tdp.join("tmpefi");
copy_dir_all(src.as_path(), dest.as_path())?;
assert_eq!(dest.join("BOOT/fbx64.efi").exists(), true);
assert_eq!(dest.join("fedora").join(crate::efi::SHIM).exists(), true);
assert_eq!(dest.join("fedora/grub.cfg").exists(), true);

Ok(())
}
}

0 comments on commit 5311abb

Please sign in to comment.