Skip to content

Commit df3e863

Browse files
ckyrouaccgwalters
authored andcommitted
blockdev: Add find_all_roots and rename root_disk to find_single_root
This is prep for supporting multiple parent devices. Assisted-by: Claude Code (Opus 4) Signed-off-by: ckyrouac <ckyrouac@redhat.com>
1 parent c96f611 commit df3e863

5 files changed

Lines changed: 47 additions & 25 deletions

File tree

crates/blockdev/src/blockdev.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -279,34 +279,48 @@ impl Device {
279279
}
280280
}
281281

282-
/// Walk the parent chain to find the root (whole disk) device.
282+
/// Walk the parent chain to find all root (whole disk) devices,
283+
/// and fail if more than one root is found.
283284
///
284-
/// Returns the root device with its children (partitions) populated.
285-
/// If this device is already a root device, returns a clone of `self`.
286-
/// Fails if the device has multiple parents at any level.
287-
pub fn root_disk(&self) -> Result<Device> {
285+
/// This is a convenience wrapper around `find_all_roots` for callers
286+
/// that expect exactly one backing device (e.g. non-RAID setups).
287+
pub fn require_single_root(&self) -> Result<Device> {
288+
let mut roots = self.find_all_roots()?;
289+
match roots.len() {
290+
1 => Ok(roots.remove(0)),
291+
n => anyhow::bail!(
292+
"Expected a single root device for {}, but found {n}",
293+
self.path()
294+
),
295+
}
296+
}
297+
298+
/// Walk the parent chain to find all root (whole disk) devices.
299+
///
300+
/// Returns all root devices with their children (partitions) populated.
301+
/// This handles devices backed by multiple parents (e.g. RAID arrays)
302+
/// by following all branches of the parent tree.
303+
/// If this device is already a root device, returns a single-element list.
304+
pub fn find_all_roots(&self) -> Result<Vec<Device>> {
288305
let Some(parents) = self.list_parents()? else {
289306
// Already a root device; re-query to ensure children are populated
290-
return list_dev(Utf8Path::new(&self.path()));
307+
return Ok(vec![list_dev(Utf8Path::new(&self.path()))?]);
291308
};
292-
let mut current = parents;
293-
loop {
294-
anyhow::ensure!(
295-
current.len() == 1,
296-
"Device {} has multiple parents; cannot determine root disk",
297-
self.path()
298-
);
299-
let mut parent = current.into_iter().next().unwrap();
300-
match parent.children.take() {
309+
310+
let mut roots = Vec::new();
311+
let mut queue = parents;
312+
while let Some(mut device) = queue.pop() {
313+
match device.children.take() {
301314
Some(grandparents) if !grandparents.is_empty() => {
302-
current = grandparents;
315+
queue.extend(grandparents);
303316
}
304317
_ => {
305-
// Found the root; re-query to populate its actual children
306-
return list_dev(Utf8Path::new(&parent.path()));
318+
// Found a root; re-query to populate its actual children
319+
roots.push(list_dev(Utf8Path::new(&device.path()))?);
307320
}
308321
}
309322
}
323+
Ok(roots)
310324
}
311325
}
312326

crates/lib/src/bootc_composefs/boot.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,8 @@ pub(crate) fn setup_composefs_bls_boot(
545545
cmdline.add_or_modify(&param);
546546

547547
// Locate ESP partition device
548-
let root_dev = bootc_blockdev::list_dev_by_dir(&storage.physical_root)?.root_disk()?;
548+
let root_dev =
549+
bootc_blockdev::list_dev_by_dir(&storage.physical_root)?.require_single_root()?;
549550
let esp_dev = root_dev.find_partition_of_esp()?;
550551

551552
(
@@ -1083,7 +1084,8 @@ pub(crate) fn setup_composefs_uki_boot(
10831084
let bootloader = host.require_composefs_booted()?.bootloader.clone();
10841085

10851086
// Locate ESP partition device
1086-
let root_dev = bootc_blockdev::list_dev_by_dir(&storage.physical_root)?.root_disk()?;
1087+
let root_dev =
1088+
bootc_blockdev::list_dev_by_dir(&storage.physical_root)?.require_single_root()?;
10871089
let esp_dev = root_dev.find_partition_of_esp()?;
10881090

10891091
(
@@ -1255,7 +1257,10 @@ pub(crate) async fn setup_composefs_boot(
12551257

12561258
if cfg!(target_arch = "s390x") {
12571259
// TODO: Integrate s390x support into install_via_bootupd
1258-
crate::bootloader::install_via_zipl(&root_setup.device_info, boot_uuid)?;
1260+
crate::bootloader::install_via_zipl(
1261+
&root_setup.device_info.require_single_root()?,
1262+
boot_uuid,
1263+
)?;
12591264
} else if postfetch.detected_bootloader == Bootloader::Grub {
12601265
crate::bootloader::install_via_bootupd(
12611266
&root_setup.device_info,

crates/lib/src/bootloader.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub(crate) fn mount_esp_part(root: &Dir, root_path: &Utf8Path, is_ostree: bool)
4545
root
4646
};
4747

48-
let dev = bootc_blockdev::list_dev_by_dir(physical_root)?.root_disk()?;
48+
let dev = bootc_blockdev::list_dev_by_dir(physical_root)?.require_single_root()?;
4949
if let Some(esp_dev) = dev.find_partition_of_type(bootc_blockdev::ESP) {
5050
let esp_path = esp_dev.path();
5151
bootc_mount::mount(&esp_path, &root_path.join(&efi_path))?;

crates/lib/src/install.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,7 +1795,8 @@ async fn install_with_sysroot(
17951795

17961796
if cfg!(target_arch = "s390x") {
17971797
// TODO: Integrate s390x support into install_via_bootupd
1798-
crate::bootloader::install_via_zipl(&rootfs.device_info, boot_uuid)?;
1798+
// zipl only supports single device
1799+
crate::bootloader::install_via_zipl(&rootfs.device_info.require_single_root()?, boot_uuid)?;
17991800
} else {
18001801
match postfetch.detected_bootloader {
18011802
Bootloader::Grub => {
@@ -2514,7 +2515,8 @@ pub(crate) async fn install_to_filesystem(
25142515
// Find the real underlying backing device for the root. This is currently just required
25152516
// for GRUB (BIOS) and in the future zipl (I think).
25162517
let device_info = {
2517-
let dev = bootc_blockdev::list_dev(Utf8Path::new(&inspect.source))?.root_disk()?;
2518+
let dev =
2519+
bootc_blockdev::list_dev(Utf8Path::new(&inspect.source))?.require_single_root()?;
25182520
tracing::debug!("Backing device: {}", dev.path());
25192521
dev
25202522
};

crates/lib/src/store/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ impl BootedStorage {
196196
let composefs = Arc::new(composefs);
197197

198198
//TODO: this assumes a single ESP on the root device
199-
let root_dev = bootc_blockdev::list_dev_by_dir(&physical_root)?.root_disk()?;
199+
let root_dev =
200+
bootc_blockdev::list_dev_by_dir(&physical_root)?.require_single_root()?;
200201
let esp_dev = root_dev.find_partition_of_esp()?;
201202
let esp_mount = mount_esp(&esp_dev.path())?;
202203

0 commit comments

Comments
 (0)