From 79cb3adf012a716430c8cc744d9103b16ba64081 Mon Sep 17 00:00:00 2001 From: HuijingHei Date: Wed, 19 Jun 2024 18:51:27 +0800 Subject: [PATCH] efi: update the ESP by creating a tmpdir and RENAME_EXCHANGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See Timothée's comment https://github.com/coreos/bootupd/issues/454#issuecomment-2178227050 Fixes https://github.com/coreos/bootupd/issues/454 --- src/efi.rs | 70 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/efi.rs b/src/efi.rs index 060eedd9..a09e28d9 100644 --- a/src/efi.rs +++ b/src/efi.rs @@ -13,7 +13,6 @@ use anyhow::{bail, Context, Result}; use fn_error_context::context; use openat_ext::OpenatDirExt; use os_release::OsRelease; -use rustix::fd::BorrowedFd; use walkdir::WalkDir; use widestring::U16CString; @@ -26,6 +25,9 @@ use crate::{component::*, packagesystem}; /// Well-known paths to the ESP that may have been mounted external to us. pub(crate) const ESP_MOUNTS: &[&str] = &["boot/efi", "efi", "boot"]; +/// Backup EFI dir +const TEMP_EFI_DIR: &str = ".EFI.tmp"; + /// The binary to change EFI boot ordering const EFIBOOTMGR: &str = "efibootmgr"; #[cfg(target_arch = "aarch64")] @@ -83,9 +85,9 @@ impl Efi { if !mnt.exists() { continue; } - let st = - rustix::fs::statfs(&mnt).with_context(|| format!("statfs failed for {mnt:?}"))?; - if st.f_type != libc::MSDOS_SUPER_MAGIC { + let st = nix::sys::statfs::statfs(&mnt) + .with_context(|| format!("statfs failed for {mnt:?}"))?; + if st.filesystem_type() != nix::sys::statfs::MSDOS_SUPER_MAGIC { continue; } log::debug!("Reusing existing {mnt:?}"); @@ -349,12 +351,39 @@ 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 "lowest" directory that we need to make the change + * e.g. we only affect EFI/fedora for example and not all of EFI + */ + + let efipath = self.esp_path()?; + let tmp_efipath = mountdir.join(TEMP_EFI_DIR); + // remove a previous directory if it exists in order to handle being interrupted in the middle. + if tmp_efipath.exists() { + std::fs::remove_dir_all(&tmp_efipath)?; + } + copy_dir(&efipath, &tmp_efipath).with_context(|| "copying existing files to temp dir")?; + + let tmpdir = sysroot.sub_dir(&tmp_efipath)?; + 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")?; + { + let esp = sysroot.sub_dir(&mountdir)?; + log::trace!( + "doing local exchange for {} and {}", + tmp_efipath.display(), + efipath.display() + ); + + esp.local_exchange(&tmp_efipath, &efipath) + .with_context(|| format!("exchange for {:?} and {:?}", tmp_efipath, efipath))?; + // finally remove the temp dir + log::trace!("cleanup: {}", tmp_efipath.display()); + std::fs::remove_dir_all(&tmp_efipath).context("clean up temp")?; + } let adopted_from = None; Ok(InstalledContent { meta: updatemeta, @@ -455,13 +484,10 @@ impl Drop for Efi { } fn validate_esp(dir: &openat::Dir) -> Result<()> { - let dir = unsafe { BorrowedFd::borrow_raw(dir.as_raw_fd()) }; - let stat = rustix::fs::fstatfs(&dir)?; - if stat.f_type != libc::MSDOS_SUPER_MAGIC { - bail!( - "EFI mount is not a msdos filesystem, but is {:?}", - stat.f_type - ); + let stat = nix::sys::statfs::fstatfs(dir)?; + let fstype = stat.filesystem_type(); + if fstype != nix::sys::statfs::MSDOS_SUPER_MAGIC { + bail!("EFI mount is not a msdos filesystem, but is {:?}", fstype); }; Ok(()) } @@ -584,6 +610,18 @@ fn find_file_recursive>(dir: P, target_file: &str) -> Result Result<()> { + let r = std::process::Command::new("cp") + .args(["-a"]) + .arg(src) + .arg(dst) + .status()?; + if !r.success() { + anyhow::bail!("Failed to copy"); + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*;