Skip to content

Commit 79cb3ad

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 8a40a4e commit 79cb3ad

File tree

1 file changed

+54
-16
lines changed

1 file changed

+54
-16
lines changed

src/efi.rs

+54-16
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use anyhow::{bail, Context, Result};
1313
use fn_error_context::context;
1414
use openat_ext::OpenatDirExt;
1515
use os_release::OsRelease;
16-
use rustix::fd::BorrowedFd;
1716
use walkdir::WalkDir;
1817
use widestring::U16CString;
1918

@@ -26,6 +25,9 @@ use crate::{component::*, packagesystem};
2625
/// Well-known paths to the ESP that may have been mounted external to us.
2726
pub(crate) const ESP_MOUNTS: &[&str] = &["boot/efi", "efi", "boot"];
2827

28+
/// Backup EFI dir
29+
const TEMP_EFI_DIR: &str = ".EFI.tmp";
30+
2931
/// The binary to change EFI boot ordering
3032
const EFIBOOTMGR: &str = "efibootmgr";
3133
#[cfg(target_arch = "aarch64")]
@@ -83,9 +85,9 @@ impl Efi {
8385
if !mnt.exists() {
8486
continue;
8587
}
86-
let st =
87-
rustix::fs::statfs(&mnt).with_context(|| format!("statfs failed for {mnt:?}"))?;
88-
if st.f_type != libc::MSDOS_SUPER_MAGIC {
88+
let st = nix::sys::statfs::statfs(&mnt)
89+
.with_context(|| format!("statfs failed for {mnt:?}"))?;
90+
if st.filesystem_type() != nix::sys::statfs::MSDOS_SUPER_MAGIC {
8991
continue;
9092
}
9193
log::debug!("Reusing existing {mnt:?}");
@@ -349,12 +351,39 @@ impl Component for Efi {
349351
.context("opening update dir")?;
350352
let updatef = filetree::FileTree::new_from_dir(&updated).context("reading update dir")?;
351353
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)?;
354+
let mountdir = self.ensure_mounted_esp(Path::new("/"))?;
355+
356+
/* copy "lowest" directory that we need to make the change
357+
* e.g. we only affect EFI/fedora for example and not all of EFI
358+
*/
359+
360+
let efipath = self.esp_path()?;
361+
let tmp_efipath = mountdir.join(TEMP_EFI_DIR);
362+
// remove a previous directory if it exists in order to handle being interrupted in the middle.
363+
if tmp_efipath.exists() {
364+
std::fs::remove_dir_all(&tmp_efipath)?;
365+
}
366+
copy_dir(&efipath, &tmp_efipath).with_context(|| "copying existing files to temp dir")?;
367+
368+
let tmpdir = sysroot.sub_dir(&tmp_efipath)?;
369+
validate_esp(&tmpdir)?;
355370
log::trace!("applying diff: {}", &diff);
356-
filetree::apply_diff(&updated, &destdir, &diff, None)
357-
.context("applying filesystem changes")?;
371+
filetree::apply_diff(&updated, &tmpdir, &diff, None)
372+
.context("applying filesystem changes to temp EFI")?;
373+
{
374+
let esp = sysroot.sub_dir(&mountdir)?;
375+
log::trace!(
376+
"doing local exchange for {} and {}",
377+
tmp_efipath.display(),
378+
efipath.display()
379+
);
380+
381+
esp.local_exchange(&tmp_efipath, &efipath)
382+
.with_context(|| format!("exchange for {:?} and {:?}", tmp_efipath, efipath))?;
383+
// finally remove the temp dir
384+
log::trace!("cleanup: {}", tmp_efipath.display());
385+
std::fs::remove_dir_all(&tmp_efipath).context("clean up temp")?;
386+
}
358387
let adopted_from = None;
359388
Ok(InstalledContent {
360389
meta: updatemeta,
@@ -455,13 +484,10 @@ impl Drop for Efi {
455484
}
456485

457486
fn validate_esp(dir: &openat::Dir) -> Result<()> {
458-
let dir = unsafe { BorrowedFd::borrow_raw(dir.as_raw_fd()) };
459-
let stat = rustix::fs::fstatfs(&dir)?;
460-
if stat.f_type != libc::MSDOS_SUPER_MAGIC {
461-
bail!(
462-
"EFI mount is not a msdos filesystem, but is {:?}",
463-
stat.f_type
464-
);
487+
let stat = nix::sys::statfs::fstatfs(dir)?;
488+
let fstype = stat.filesystem_type();
489+
if fstype != nix::sys::statfs::MSDOS_SUPER_MAGIC {
490+
bail!("EFI mount is not a msdos filesystem, but is {:?}", fstype);
465491
};
466492
Ok(())
467493
}
@@ -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)]
588626
mod tests {
589627
use super::*;

0 commit comments

Comments
 (0)