From 37d33898a03b09be383f665e26819c2afaacb64d Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 14 Apr 2026 18:44:39 +0000 Subject: [PATCH 1/5] Update cap-std-ext to 5.1.2, use new CmdFds API This is a safer API, but this is specifically prep for using varlink. Assisted-by: OpenCode (Claude Opus 4) Signed-off-by: Colin Walters --- Cargo.lock | 28 +++++++------- Cargo.toml | 2 +- crates/lib/src/podstorage.rs | 46 ++++++++++++++++------- crates/ostree-ext/src/container/skopeo.rs | 6 ++- crates/ostree-ext/src/tar/write.rs | 6 ++- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f74813b23..2f1340ffa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,7 +173,7 @@ version = "0.1.0" source = "git+https://github.com/bootc-dev/bcvk?rev=f4dd05a74ccb54084093c9167317a14356ca53e4#f4dd05a74ccb54084093c9167317a14356ca53e4" dependencies = [ "camino", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "color-eyre", "data-encoding", "libc", @@ -224,7 +224,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bootc-kernel-cmdline", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "cfsctl", "clap", "fn-error-context", @@ -242,7 +242,7 @@ dependencies = [ "bootc-internal-mount", "bootc-internal-utils", "camino", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "fn-error-context", "indoc", "libc", @@ -261,7 +261,7 @@ dependencies = [ "anyhow", "bootc-internal-utils", "camino", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "fn-error-context", "indoc", "libc", @@ -277,7 +277,7 @@ version = "1.15.1" dependencies = [ "anstream 1.0.0", "anyhow", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "chrono", "owo-colors", "rustix", @@ -319,7 +319,7 @@ dependencies = [ "bootc-tmpfiles", "camino", "canon-json", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "cfg-if", "cfsctl", "chrono", @@ -370,7 +370,7 @@ dependencies = [ "anyhow", "bootc-internal-utils", "camino", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "fn-error-context", "hex", "indoc", @@ -388,7 +388,7 @@ dependencies = [ "anyhow", "bootc-internal-utils", "camino", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "fn-error-context", "indoc", "rustix", @@ -523,9 +523,9 @@ dependencies = [ [[package]] name = "cap-std-ext" -version = "5.1.1" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f9d7cf114dea582f663f03f4c563d0fc5ca2c8fa4c496eb538d8f01981ea51" +checksum = "6c6c5ac70c4465b47c3a322330f87c2df31e42ac527497d94dc1f1d1cb8989f9" dependencies = [ "cap-primitives 4.0.2", "cap-tempfile 4.0.2", @@ -1216,7 +1216,7 @@ version = "0.1.0" dependencies = [ "anstream 1.0.0", "anyhow", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "cfsctl", "fn-error-context", "hex", @@ -2079,7 +2079,7 @@ checksum = "784ff94e3f5a9b89659b8a4442104df6b5e7974c9b355c4f4ae6e9794af1ad2b" dependencies = [ "camino", "canon-json", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "chrono", "flate2", "hex", @@ -2179,7 +2179,7 @@ dependencies = [ "bootc-internal-utils", "camino", "canon-json", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "cfsctl", "chrono", "clap", @@ -3004,7 +3004,7 @@ dependencies = [ "bcvk-qemu", "bootc-kernel-cmdline", "camino", - "cap-std-ext 5.1.1", + "cap-std-ext 5.1.2", "clap", "data-encoding", "fn-error-context", diff --git a/Cargo.toml b/Cargo.toml index 217789e1b..0b4b16281 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ anstream = "1.0" anyhow = "1.0.82" camino = "1.1.6" canon-json = "0.2.1" -cap-std-ext = "5.1.1" +cap-std-ext = "5.1.2" cfg-if = "1.0" chrono = { version = "0.4.38", default-features = false } clap = "4.5.4" diff --git a/crates/lib/src/podstorage.rs b/crates/lib/src/podstorage.rs index 7d826b2ce..b41584d08 100644 --- a/crates/lib/src/podstorage.rs +++ b/crates/lib/src/podstorage.rs @@ -19,7 +19,7 @@ use bootc_utils::{AsyncCommandRunExt, CommandRunExt, ExitStatusExt}; use camino::{Utf8Path, Utf8PathBuf}; use cap_std_ext::cap_std::fs::Dir; use cap_std_ext::cap_tempfile::TempDir; -use cap_std_ext::cmdext::CapStdExtCommandExt; +use cap_std_ext::cmdext::{CapStdExtCommandExt, CmdFds}; use cap_std_ext::dirext::CapStdExtDirExt; use cap_std_ext::{cap_std, cap_tempfile}; use fn_error_context::context; @@ -80,7 +80,12 @@ pub(crate) enum PullMode { #[allow(unsafe_code)] #[context("Binding storage roots")] -fn bind_storage_roots(cmd: &mut Command, storage_root: &Dir, run_root: &Dir) -> Result<()> { +pub(crate) fn bind_storage_roots( + cmd: &mut Command, + fds: &mut CmdFds, + storage_root: &Dir, + run_root: &Dir, +) -> Result<()> { // podman requires an absolute path, for two reasons right now: // - It writes the file paths into `db.sql`, a sqlite database for unknown reasons // - It forks helper binaries, so just giving it /proc/self/fd won't work as @@ -121,19 +126,16 @@ fn bind_storage_roots(cmd: &mut Command, storage_root: &Dir, run_root: &Dir) -> Ok(()) }) }; - cmd.take_fd_n(run_root, STORAGE_RUN_FD); + fds.take_fd_n(run_root, STORAGE_RUN_FD); Ok(()) } -// Initialize a `podman` subprocess with: -// - storage overridden to point to to storage_root -// - Authentication (auth.json) using the bootc/ostree owned auth -fn new_podman_cmd_in(sysroot: &Dir, storage_root: &Dir, run_root: &Dir) -> Result { - let mut cmd = Command::new("podman"); - bind_storage_roots(&mut cmd, storage_root, run_root)?; - let run_root = format!("/proc/self/fd/{STORAGE_RUN_FD}"); - cmd.args(["--root", STORAGE_ALIAS_DIR, "--runroot", run_root.as_str()]); - +/// Set up `REGISTRY_AUTH_FILE` on a command, passing the bootc/ostree +/// auth file via an anonymous tmpfile fd. +/// +/// If no bootc-owned auth is configured, an empty `{}` is passed to +/// prevent podman from falling back to user-owned auth paths. +pub(crate) fn setup_auth(cmd: &mut Command, fds: &mut CmdFds, sysroot: &Dir) -> Result<()> { let tmpd = &cap_std::fs::Dir::open_ambient_dir("/tmp", cap_std::ambient_authority())?; let mut tempfile = cap_tempfile::TempFile::new_anonymous(tmpd).map(std::io::BufWriter::new)?; @@ -154,9 +156,23 @@ fn new_podman_cmd_in(sysroot: &Dir, storage_root: &Dir, run_root: &Dir) -> Resul .into_std(); let fd: Arc = std::sync::Arc::new(tempfile.into()); let target_fd = fd.as_fd().as_raw_fd(); - cmd.take_fd_n(fd, target_fd); + fds.take_fd_n(fd, target_fd); cmd.env("REGISTRY_AUTH_FILE", format!("/proc/self/fd/{target_fd}")); + Ok(()) +} + +// Initialize a `podman` subprocess with: +// - storage overridden to point to to storage_root +// - Authentication (auth.json) using the bootc/ostree owned auth +fn new_podman_cmd_in(sysroot: &Dir, storage_root: &Dir, run_root: &Dir) -> Result { + let mut cmd = Command::new("podman"); + let mut fds = CmdFds::new(); + bind_storage_roots(&mut cmd, &mut fds, storage_root, run_root)?; + let run_root = format!("/proc/self/fd/{STORAGE_RUN_FD}"); + cmd.args(["--root", STORAGE_ALIAS_DIR, "--runroot", run_root.as_str()]); + setup_auth(&mut cmd, &mut fds, sysroot)?; + cmd.take_fds(fds); Ok(cmd) } @@ -435,7 +451,9 @@ impl CStorage { cmd.stdout(Stdio::null()); // An ephemeral place for the transient state; let temp_runroot = TempDir::new(cap_std::ambient_authority())?; - bind_storage_roots(&mut cmd, &self.storage_root, &temp_runroot)?; + let mut fds = CmdFds::new(); + bind_storage_roots(&mut cmd, &mut fds, &self.storage_root, &temp_runroot)?; + cmd.take_fds(fds); // The destination (target stateroot) + container storage dest let storage_dest = &format!( diff --git a/crates/ostree-ext/src/container/skopeo.rs b/crates/ostree-ext/src/container/skopeo.rs index eceaf0935..f70ae4920 100644 --- a/crates/ostree-ext/src/container/skopeo.rs +++ b/crates/ostree-ext/src/container/skopeo.rs @@ -2,7 +2,7 @@ use super::ImageReference; use anyhow::{Context, Result}; -use cap_std_ext::cmdext::CapStdExtCommandExt; +use cap_std_ext::cmdext::{CapStdExtCommandExt, CmdFds}; use containers_image_proxy::oci_spec::image as oci_image; use fn_error_context::context; use io_lifetimes::OwnedFd; @@ -80,7 +80,9 @@ pub async fn copy( cmd.arg("--digestfile"); cmd.arg(digestfile.path()); if let Some((add_fd, n)) = add_fd { - cmd.take_fd_n(add_fd, n); + let mut fds = CmdFds::new(); + fds.take_fd_n(add_fd, n); + cmd.take_fds(fds); } if let Some(authfile) = authfile { cmd.arg("--authfile"); diff --git a/crates/ostree-ext/src/tar/write.rs b/crates/ostree-ext/src/tar/write.rs index ec2e1b41f..7b4a9b2d8 100644 --- a/crates/ostree-ext/src/tar/write.rs +++ b/crates/ostree-ext/src/tar/write.rs @@ -14,7 +14,7 @@ use camino::{Utf8Component, Utf8Path, Utf8PathBuf}; use cap_std::io_lifetimes; use cap_std_ext::cap_std::fs::Dir; -use cap_std_ext::cmdext::CapStdExtCommandExt; +use cap_std_ext::cmdext::{CapStdExtCommandExt, CmdFds}; use cap_std_ext::{cap_std, cap_tempfile}; use containers_image_proxy::oci_spec::image as oci_image; use fn_error_context::context; @@ -423,7 +423,9 @@ pub async fn write_tar( .stdout(Stdio::piped()) .stderr(Stdio::piped()) .args(["commit"]); - c.take_fd_n(repofd.clone(), 3); + let mut fds = CmdFds::new(); + fds.take_fd_n(repofd.clone(), 3); + c.take_fds(fds); c.arg("--repo=/proc/self/fd/3"); if let Some(sepolicy) = sepolicy.as_ref() { c.arg("--selinux-policy"); From f61730b1509102eefc63f008cd4c2d09912d9dfb Mon Sep 17 00:00:00 2001 From: James Reilly Date: Sat, 11 Apr 2026 16:39:53 +0530 Subject: [PATCH 2/5] blockdev: handle ZFS dataset sources in list_dev_by_dir ZFS filesystems report their mount source as a dataset name (e.g. 'rpool/root') rather than a block device path. Passing this to lsblk causes it to fail with 'not a block device', breaking bootc status and bootc upgrade on ZFS-rooted systems. Add list_dev_for_zfs_dataset() which extracts the pool name and queries 'zpool list -H -v -P ' to find the underlying block device path, then delegates to list_dev() as normal. Detect ZFS sources in list_dev_by_dir() via fstype=='zfs' or by a non-absolute path containing '/' (dataset name form). Fixes: #1240 Signed-off-by: James Reilly Assisted-by: GitHub Copilot (claude-sonnet-4-6) --- crates/blockdev/src/blockdev.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/crates/blockdev/src/blockdev.rs b/crates/blockdev/src/blockdev.rs index 666faf1d2..8db1cc56d 100644 --- a/crates/blockdev/src/blockdev.rs +++ b/crates/blockdev/src/blockdev.rs @@ -382,10 +382,39 @@ pub fn list_dev(dev: &Utf8Path) -> Result { .ok_or_else(|| anyhow!("no device output from lsblk for {dev}")) } +#[context("Finding block device for ZFS dataset {dataset}")] +fn list_dev_for_zfs_dataset(dataset: &str) -> Result { + let dataset = dataset.strip_prefix("ZFS=").unwrap_or(dataset); + let pool = dataset + .split('/') + .next() + .ok_or_else(|| anyhow!("Invalid ZFS dataset: {dataset}"))?; + + let output = Command::new("zpool") + .args(["list", "-H", "-v", "-P", pool]) + .run_get_string() + .with_context(|| format!("Querying ZFS pool {pool}"))?; + + for line in output.lines() { + if line.starts_with('\t') || line.starts_with(' ') { + let dev_path = line.trim_start().split('\t').next().unwrap_or("").trim(); + if dev_path.starts_with('/') { + return list_dev(Utf8Path::new(dev_path)); + } + } + } + + anyhow::bail!("Could not find a block device backing ZFS pool {pool}") +} + /// List the device containing the filesystem mounted at the given directory. pub fn list_dev_by_dir(dir: &Dir) -> Result { let fsinfo = bootc_mount::inspect_filesystem_of_dir(dir)?; - list_dev(&Utf8PathBuf::from(&fsinfo.source)) + let source = &fsinfo.source; + if fsinfo.fstype == "zfs" || (!source.starts_with('/') && source.contains('/')) { + return list_dev_for_zfs_dataset(source); + } + list_dev(&Utf8PathBuf::from(source)) } pub struct LoopbackDevice { From 245050f78362514ac7e4301b306299465e22e6e2 Mon Sep 17 00:00:00 2001 From: James Reilly Date: Tue, 14 Apr 2026 23:01:46 +0530 Subject: [PATCH 3/5] Update crates/blockdev/src/blockdev.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: James Reilly --- crates/blockdev/src/blockdev.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/blockdev/src/blockdev.rs b/crates/blockdev/src/blockdev.rs index 8db1cc56d..44c23d34e 100644 --- a/crates/blockdev/src/blockdev.rs +++ b/crates/blockdev/src/blockdev.rs @@ -411,7 +411,7 @@ fn list_dev_for_zfs_dataset(dataset: &str) -> Result { pub fn list_dev_by_dir(dir: &Dir) -> Result { let fsinfo = bootc_mount::inspect_filesystem_of_dir(dir)?; let source = &fsinfo.source; - if fsinfo.fstype == "zfs" || (!source.starts_with('/') && source.contains('/')) { + if fsinfo.fstype == "zfs" || source.starts_with("ZFS=") { return list_dev_for_zfs_dataset(source); } list_dev(&Utf8PathBuf::from(source)) From 47c362089bfeb300442932f70e4f6d19c33753b8 Mon Sep 17 00:00:00 2001 From: Wolter Eldering Date: Fri, 3 Apr 2026 21:16:35 +0200 Subject: [PATCH 4/5] fix: add riscv64 to this_arch_root Signed-off-by: Wolter Eldering --- crates/lib/src/discoverable_partition_specification.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/lib/src/discoverable_partition_specification.rs b/crates/lib/src/discoverable_partition_specification.rs index b4ef6cb6b..a743d17cb 100644 --- a/crates/lib/src/discoverable_partition_specification.rs +++ b/crates/lib/src/discoverable_partition_specification.rs @@ -521,6 +521,8 @@ pub const fn this_arch_root() -> &'static str { ROOT_PPC64 } else if #[cfg(all(target_arch = "powerpc64", target_endian = "little"))] { ROOT_PPC64_LE + } else if #[cfg(target_arch = "riscv64")] { + ROOT_RISCV64 } else { compile_error!("Unsupported architecture") } From d69621dd4db0db43999aa9cdcfe4a3f2b77189e3 Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Wed, 15 Apr 2026 16:18:23 -0400 Subject: [PATCH 5/5] provision-derived: Don't hardcode bootupd version This got bumped recently so the old version is no longer available. Signed-off-by: John Eckersberg --- hack/provision-derived.sh | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hack/provision-derived.sh b/hack/provision-derived.sh index 4b4b342d6..9d5910aff 100755 --- a/hack/provision-derived.sh +++ b/hack/provision-derived.sh @@ -107,7 +107,23 @@ repo_gpgcheck=0 enabled=1 enabled_metadata=1 REPOEOF -dnf -y install bootupd-0.2.32.43.g38208d3 + +# This unfortunately has "older" versions with higher NEVRA: +# +# # dnf --disablerepo=* --enablerepo=copr:copr.fedorainfracloud.org:group_CoreOS:continuous repoquery bootupd 2> /dev/null +# bootupd-0:0.2.32.45.gb483a63-1.fc45.x86_64 +# bootupd-0:202501200321.0.2.25.65.ge296f82-1.fc42.src +# bootupd-0:202501200321.0.2.25.65.ge296f82-1.fc42.x86_64 +# bootupd-0:202501210627.0.2.25.67.gefe41b6-1.fc42.src +# +# So we need to be more selective, but also be dynamic to grab newer +# versions +# +# The subscription-manager plugin needs to be disabled because it +# likes to write warnings to stdout which corrupts the NEVRA output +# we're going for here... +bootupd_nevra=$(dnf --disableplugin=subscription-manager --disablerepo=* --enablerepo=copr:copr.fedorainfracloud.org:group_CoreOS:continuous repoquery --latest-limit 1 --arch "$(uname -m)" "bootupd-0.2.*") +dnf -y install ${bootupd_nevra} rm -f /etc/yum.repos.d/coreos-continuous.repo dnf clean all