From 7f46422223bad1b95d9f100576d335a8aabcb9bf Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 20 Jan 2025 20:55:08 -0500 Subject: [PATCH] Use bootc-utils for command helpers This enables more code sharing. I didn't do a full port here, but this starts the ball rolling. In particular I think `run_and_parse_json()` is nice and elegant. --- Cargo.lock | 144 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/efi.rs | 36 ++++-------- src/filesystem.rs | 15 ++--- src/filetree.rs | 9 ++- 5 files changed, 165 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6225afa..8c7dd3ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "1.1.2" @@ -93,6 +108,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + [[package]] name = "bincode" version = "1.3.3" @@ -123,12 +153,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bootc-utils" +version = "0.0.0" +source = "git+https://github.com/containers/bootc#e8bb9f748fdfeb5164667046b3c6678c619ad46c" +dependencies = [ + "anyhow", + "rustix", + "serde", + "serde_json", + "tempfile", + "tokio", + "tracing", +] + [[package]] name = "bootupd" version = "0.2.26" dependencies = [ "anyhow", "bincode", + "bootc-utils", "camino", "cap-std-ext", "chrono", @@ -161,6 +206,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + [[package]] name = "camino" version = "1.1.9" @@ -449,6 +500,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "heck" version = "0.5.0" @@ -620,6 +677,26 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "nix" version = "0.23.2" @@ -664,6 +741,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -738,6 +824,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "pkg-config" version = "0.3.27" @@ -827,6 +919,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "rustix" version = "0.38.43" @@ -965,6 +1063,52 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "windows-sys 0.52.0", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index 596bed8e..2ceb6a70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ path = "src/main.rs" [dependencies] anyhow = "1.0" bincode = "1.3.2" +bootc-utils = { git = "https://github.com/containers/bootc", commit = "e8bb9f748fdfeb5164667046b3c6678c619ad46c" } cap-std-ext = "4.0.4" camino = "1.1.9" chrono = { version = "0.4.39", features = ["serde"] } diff --git a/src/efi.rs b/src/efi.rs index 29de6fb6..f7a0daa4 100644 --- a/src/efi.rs +++ b/src/efi.rs @@ -121,13 +121,11 @@ impl Efi { if !mnt.exists() { continue; } - let status = std::process::Command::new("mount") + std::process::Command::new("mount") .arg(&esp_device) .arg(&mnt) - .status()?; - if !status.success() { - anyhow::bail!("Failed to mount {:?}", esp_device); - } + .run() + .with_context(|| format!("Failed to mount {:?}", esp_device))?; log::debug!("Mounted at {mnt:?}"); *mountpoint = Some(mnt); break; @@ -137,10 +135,10 @@ impl Efi { fn unmount(&self) -> Result<()> { if let Some(mount) = self.mountpoint.borrow_mut().take() { - let status = Command::new("umount").arg(&mount).status()?; - if !status.success() { - anyhow::bail!("Failed to unmount {mount:?}: {status:?}"); - } + Command::new("umount") + .arg(&mount) + .run() + .with_context(|| format!("Failed to unmount {mount:?}"))?; log::trace!("Unmounted"); } Ok(()) @@ -308,15 +306,12 @@ impl Component for Efi { // TODO - add some sort of API that allows directly setting the working // directory to a file descriptor. - let r = std::process::Command::new("cp") + std::process::Command::new("cp") .args(["-rp", "--reflink=auto"]) .arg(&srcdir_name) .arg(destdir) .current_dir(format!("/proc/self/fd/{}", src_root.as_raw_fd())) - .status()?; - if !r.success() { - anyhow::bail!("Failed to copy"); - } + .run()?; if update_firmware { if let Some(vendordir) = self.get_efi_vendor(&src_root)? { self.update_firmware(device, destd, &vendordir)? @@ -505,17 +500,10 @@ pub(crate) fn clear_efi_target(target: &str) -> Result<()> { for entry in boot_entries { if entry.name.to_lowercase() == target { log::debug!("Deleting matched target {:?}", entry); - let output = Command::new(EFIBOOTMGR) + Command::new(EFIBOOTMGR) .args(["-b", entry.id.as_str(), "-B"]) - .output()?; - let st = output.status; - if !st.success() { - std::io::copy( - &mut std::io::Cursor::new(output.stderr), - &mut std::io::stderr().lock(), - )?; - anyhow::bail!("Failed to invoke {EFIBOOTMGR}: {st:?}"); - } + .run() + .with_context(|| format!("Failed to invoke {EFIBOOTMGR}"))?; } } diff --git a/src/filesystem.rs b/src/filesystem.rs index 80b942b3..aebf519b 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,9 +1,9 @@ -use std::io::Write; use std::os::fd::AsRawFd; use std::os::unix::process::CommandExt; use std::process::Command; -use anyhow::{Context, Result}; +use anyhow::Result; +use bootc_utils::CommandRunExt; use fn_error_context::context; use rustix::fd::BorrowedFd; use serde::Deserialize; @@ -27,19 +27,12 @@ pub(crate) struct Findmnt { pub(crate) fn inspect_filesystem(root: &openat::Dir, path: &str) -> Result { let rootfd = unsafe { BorrowedFd::borrow_raw(root.as_raw_fd()) }; // SAFETY: This is unsafe just for the pre_exec, when we port to cap-std we can use cap-std-ext - let o = unsafe { + let o: Findmnt = unsafe { Command::new("findmnt") .args(["-J", "-v", "--output=SOURCE,FSTYPE,OPTIONS,UUID", path]) .pre_exec(move || rustix::process::fchdir(rootfd).map_err(Into::into)) - .output()? + .run_and_parse_json()? }; - let st = o.status; - if !st.success() { - let _ = std::io::stderr().write_all(&o.stderr)?; - anyhow::bail!("findmnt failed: {st:?}"); - } - let o: Findmnt = serde_json::from_reader(std::io::Cursor::new(&o.stdout)) - .context("Parsing findmnt output")?; o.filesystems .into_iter() .next() diff --git a/src/filetree.rs b/src/filetree.rs index b8648dd6..44439058 100644 --- a/src/filetree.rs +++ b/src/filetree.rs @@ -286,18 +286,17 @@ pub(crate) fn syncfs(d: &openat::Dir) -> Result<()> { /// Copy from src to dst at root dir #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] fn copy_dir(root: &openat::Dir, src: &str, dst: &str) -> Result<()> { + use bootc_utils::CommandRunExt; + let rootfd = unsafe { BorrowedFd::borrow_raw(root.as_raw_fd()) }; - let r = unsafe { + unsafe { Command::new("cp") .args(["-a"]) .arg(src) .arg(dst) .pre_exec(move || rustix::process::fchdir(rootfd).map_err(Into::into)) - .status()? + .run()? }; - if !r.success() { - anyhow::bail!("Failed to copy {src} to {dst}"); - } log::debug!("Copy {src} to {dst}"); Ok(()) }