From 71fcbe5de06eabb7ebf90643442ec704976de06f Mon Sep 17 00:00:00 2001 From: James Reilly Date: Sat, 11 Apr 2026 16:39:53 +0530 Subject: [PATCH 1/2] 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 d45a0d3cee3350646b9660773429b337140b3fde Mon Sep 17 00:00:00 2001 From: James Reilly Date: Tue, 14 Apr 2026 23:01:46 +0530 Subject: [PATCH 2/2] 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))