From 56eb7d60c677d222902fa7579d65e525a661234b Mon Sep 17 00:00:00 2001 From: Huijing Hei Date: Thu, 4 Sep 2025 17:09:25 +0800 Subject: [PATCH 1/2] efi: update `get_efi_component_from_usr()` to return `Option` --- src/efi.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/efi.rs b/src/efi.rs index c446edb5..f6b00be8 100644 --- a/src/efi.rs +++ b/src/efi.rs @@ -453,16 +453,18 @@ impl Component for Efi { fn generate_update_metadata(&self, sysroot: &str) -> Result { let sysroot_path = Utf8Path::new(sysroot); - // copy EFI files to updates dir from usr/lib/efi let efilib_path = sysroot_path.join(EFILIB); - let meta = if efilib_path.exists() { + let efi_comps = if efilib_path.exists() { + get_efi_component_from_usr(&sysroot_path, EFILIB)? + } else { + None + }; + + // copy EFI files to updates dir from usr/lib/efi + let meta = if let Some(efi_components) = efi_comps { let mut packages = Vec::new(); let mut modules_vec: Vec = vec![]; let sysroot_dir = Dir::open_ambient_dir(sysroot_path, cap_std::ambient_authority())?; - let efi_components = get_efi_component_from_usr(&sysroot_path, EFILIB)?; - if efi_components.len() == 0 { - bail!("Failed to find EFI components from {efilib_path}"); - } for efi in efi_components { Command::new("cp") .args(["-rp", "--reflink=auto"]) @@ -734,7 +736,7 @@ pub struct EFIComponent { fn get_efi_component_from_usr<'a>( sysroot: &'a Utf8Path, usr_path: &'a str, -) -> Result> { +) -> Result>> { let efilib_path = sysroot.join(usr_path); let skip_count = Utf8Path::new(usr_path).components().count(); @@ -765,9 +767,12 @@ fn get_efi_component_from_usr<'a>( }) .collect(); + if components.len() == 0 { + return Ok(None); + } components.sort_by(|a, b| a.name.cmp(&b.name)); - Ok(components) + Ok(Some(components)) } #[cfg(test)] @@ -904,7 +909,7 @@ Boot0003* test"; let efi_comps = get_efi_component_from_usr(utf8_tpath, EFILIB)?; assert_eq!( efi_comps, - vec![ + Some(vec![ EFIComponent { name: "BAR".to_string(), version: "1.1".to_string(), @@ -915,12 +920,12 @@ Boot0003* test"; version: "1.1".to_string(), path: Utf8PathBuf::from("usr/lib/efi/FOO/1.1/EFI"), }, - ] + ]) ); std::fs::remove_dir_all(efi_path.join("BAR/1.1/EFI"))?; std::fs::remove_dir_all(efi_path.join("FOO/1.1/EFI"))?; let efi_comps = get_efi_component_from_usr(utf8_tpath, EFILIB)?; - assert_eq!(efi_comps, []); + assert_eq!(efi_comps, None); Ok(()) } } From 798f2a2351bda329a003255cf28afa8f7ebc7127 Mon Sep 17 00:00:00 2001 From: Huijing Hei Date: Thu, 4 Sep 2025 18:18:47 +0800 Subject: [PATCH 2/2] efi: Extend `install()` to support `usr/lib/efi` --- src/efi.rs | 41 ++++++++++++++++++++++++----------------- src/util.rs | 16 ++++++++++++++++ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/efi.rs b/src/efi.rs index f6b00be8..d3a49ecb 100644 --- a/src/efi.rs +++ b/src/efi.rs @@ -365,8 +365,6 @@ impl Component for Efi { anyhow::bail!("No update metadata for component {} found", self.name()); }; log::debug!("Found metadata {}", meta.version); - let srcdir_name = component_updatedirname(self); - let ft = crate::filetree::FileTree::new_from_dir(&src_dir.sub_dir(&srcdir_name)?)?; // Let's attempt to use an already mounted ESP at the target // dest_root if one is already mounted there in a known ESP location. @@ -387,16 +385,30 @@ impl Component for Efi { .with_context(|| format!("opening dest dir {}", destpath.display()))?; validate_esp_fstype(destd)?; - // TODO - add some sort of API that allows directly setting the working - // directory to a file descriptor. - std::process::Command::new("cp") - .args(["-rp", "--reflink=auto"]) - .arg(&srcdir_name) - .arg(destpath) - .current_dir(format!("/proc/self/fd/{}", src_dir.as_raw_fd())) - .run()?; + let src_path = Path::new(src_root); + let efilib_path = src_path.join(EFILIB); + let efi_comps = if efilib_path.exists() { + get_efi_component_from_usr(&Utf8Path::new(src_root), EFILIB)? + } else { + None + }; + + let efi_path = if let Some(efi_components) = efi_comps { + for efi in efi_components { + log::trace!("Copy {} to {}", efi.path, destpath.display()); + util::copy_in_fd(&src_dir, &efi.path, &destpath)?; + } + PathBuf::from(EFILIB) + } else { + let updates = component_updatedirname(self); + util::copy_in_fd(&src_dir, &updates, &destpath)?; + updates + }; + + let ft = crate::filetree::FileTree::new_from_dir(&src_dir.sub_dir(&efi_path)?)?; + if update_firmware { - if let Some(vendordir) = self.get_efi_vendor(&Path::new(src_root))? { + if let Some(vendordir) = self.get_efi_vendor(&src_path.join(efi_path))? { self.update_firmware(device, destd, &vendordir)? } } @@ -466,12 +478,7 @@ impl Component for Efi { let mut modules_vec: Vec = vec![]; let sysroot_dir = Dir::open_ambient_dir(sysroot_path, cap_std::ambient_authority())?; for efi in efi_components { - Command::new("cp") - .args(["-rp", "--reflink=auto"]) - .arg(&efi.path) - .arg(crate::model::BOOTUPD_UPDATES_DIR) - .current_dir(format!("/proc/self/fd/{}", sysroot_dir.as_raw_fd())) - .run()?; + util::copy_in_fd(&sysroot_dir, &efi.path, crate::model::BOOTUPD_UPDATES_DIR)?; packages.push(format!("{}-{}", efi.name, efi.version)); modules_vec.push(Module { name: efi.name, diff --git a/src/util.rs b/src/util.rs index 3d32bc5c..01c130ac 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,7 +3,9 @@ use std::path::Path; use std::process::Command; use anyhow::{bail, Context, Result}; +use bootc_internal_utils::CommandRunExt; use openat_ext::OpenatDirExt; +use rustix::fd::AsRawFd; /// Parse an environment variable as UTF-8 #[allow(dead_code)] @@ -120,3 +122,17 @@ impl Drop for SignalTerminationGuard { signal_hook_registry::unregister(self.0); } } + +pub(crate) fn copy_in_fd, P2: AsRef>( + fd: &F, + src: P1, + dest: P2, +) -> Result<()> { + let cwd = format!("/proc/self/fd/{}", fd.as_raw_fd()); + Command::new("cp") + .args(["-rp", "--reflink=auto"]) + .arg(src.as_ref()) + .arg(dest.as_ref()) + .current_dir(cwd) + .run() +}