@@ -1141,7 +1141,10 @@ pub(crate) fn exec_in_host_mountns(args: &[std::ffi::OsString]) -> Result<()> {
11411141pub ( crate ) struct RootSetup {
11421142 #[ cfg( feature = "install-to-disk" ) ]
11431143 luks_device : Option < String > ,
1144- pub ( crate ) device_info : bootc_blockdev:: PartitionTable ,
1144+ /// Information about the backing block device partition tables.
1145+ /// Contains all devices that have an ESP partition when the root filesystem
1146+ /// spans multiple backing devices (e.g., LVM across multiple disks).
1147+ pub ( crate ) device_info : Vec < bootc_blockdev:: PartitionTable > ,
11451148 /// Absolute path to the location where we've mounted the physical
11461149 /// root filesystem for the system we're installing.
11471150 pub ( crate ) physical_root_path : Utf8PathBuf ,
@@ -1602,7 +1605,9 @@ async fn install_with_sysroot(
16021605
16031606 if cfg ! ( target_arch = "s390x" ) {
16041607 // TODO: Integrate s390x support into install_via_bootupd
1605- crate :: bootloader:: install_via_zipl ( & rootfs. device_info , boot_uuid) ?;
1608+ // zipl only supports single device
1609+ let device = rootfs. device_info . first ( ) ;
1610+ crate :: bootloader:: install_via_zipl ( device, boot_uuid) ?;
16061611 } else {
16071612 match postfetch. detected_bootloader {
16081613 Bootloader :: Grub => {
@@ -1733,15 +1738,21 @@ async fn install_to_filesystem_impl(
17331738 // Drop exclusive ownership since we're done with mutation
17341739 let rootfs = & * rootfs;
17351740
1736- match & rootfs. device_info . label {
1737- bootc_blockdev:: PartitionType :: Dos => crate :: utils:: medium_visibility_warning (
1738- "Installing to `dos` format partitions is not recommended" ,
1739- ) ,
1740- bootc_blockdev:: PartitionType :: Gpt => {
1741- // The only thing we should be using in general
1742- }
1743- bootc_blockdev:: PartitionType :: Unknown ( o) => {
1744- crate :: utils:: medium_visibility_warning ( & format ! ( "Unknown partition label {o}" ) )
1741+ // Check partition type of all backing devices
1742+ for device_info in & rootfs. device_info {
1743+ match & device_info. label {
1744+ bootc_blockdev:: PartitionType :: Dos => {
1745+ crate :: utils:: medium_visibility_warning ( & format ! (
1746+ "Installing to `dos` format partitions is not recommended: {}" ,
1747+ device_info. path( )
1748+ ) )
1749+ }
1750+ bootc_blockdev:: PartitionType :: Gpt => {
1751+ // The only thing we should be using in general
1752+ }
1753+ bootc_blockdev:: PartitionType :: Unknown ( o) => crate :: utils:: medium_visibility_warning (
1754+ & format ! ( "Unknown partition label {o}: {}" , device_info. path( ) ) ,
1755+ ) ,
17451756 }
17461757 }
17471758
@@ -2291,27 +2302,77 @@ pub(crate) async fn install_to_filesystem(
22912302 } ;
22922303 tracing:: debug!( "boot UUID: {boot_uuid:?}" ) ;
22932304
2294- // Find the real underlying backing device for the root. This is currently just required
2295- // for GRUB (BIOS) and in the future zipl (I think).
2296- let backing_device = {
2305+ // Walk up the block device hierarchy to find physical backing device(s).
2306+ // Examples:
2307+ // /dev/sda3 -> /dev/sda (single disk)
2308+ // /dev/mapper/vg-lv -> /dev/sda2, /dev/sdb2 (LVM across two disks)
2309+ let backing_devices: Vec < String > = {
22972310 let mut dev = inspect. source ;
22982311 loop {
22992312 tracing:: debug!( "Finding parents for {dev}" ) ;
2300- let mut parents = bootc_blockdev:: find_parent_devices ( & dev) ?. into_iter ( ) ;
2301- let Some ( parent) = parents. next ( ) else {
2302- break ;
2303- } ;
2304- if let Some ( next) = parents. next ( ) {
2305- anyhow:: bail!(
2306- "Found multiple parent devices {parent} and {next}; not currently supported"
2313+ let parents = bootc_blockdev:: find_parent_devices ( & dev) ?;
2314+ if parents. is_empty ( ) {
2315+ // Reached a physical disk
2316+ break vec ! [ dev] ;
2317+ }
2318+ if parents. len ( ) > 1 {
2319+ // Multi-device (e.g., LVM across disks) - return all
2320+ tracing:: debug!(
2321+ "Found multiple parent devices: {:?}; will search for ESP" ,
2322+ parents
23072323 ) ;
2324+ break parents;
2325+ }
2326+ // Single parent (e.g. LVM LV -> VG -> PV) - keep walking up
2327+ dev = parents. into_iter ( ) . next ( ) . unwrap ( ) ;
2328+ }
2329+ } ;
2330+ tracing:: debug!( "Backing devices: {backing_devices:?}" ) ;
2331+
2332+ // Determine the device and partition info to use for bootloader installation.
2333+ // If there are multiple backing devices, we search for all that contain an ESP.
2334+ let device_info: Vec < bootc_blockdev:: PartitionTable > = if backing_devices. len ( ) == 1 {
2335+ // Single backing device - use it directly
2336+ let dev = & backing_devices[ 0 ] ;
2337+ vec ! [ bootc_blockdev:: partitions_of( Utf8Path :: new( dev) ) ?]
2338+ } else {
2339+ // Multiple backing devices - find all with ESP
2340+ let mut esp_devices = Vec :: new ( ) ;
2341+ for dev in & backing_devices {
2342+ match bootc_blockdev:: partitions_of ( Utf8Path :: new ( dev) ) {
2343+ Ok ( table) => {
2344+ match table. find_partition_of_esp ( ) {
2345+ Ok ( Some ( _) ) => {
2346+ tracing:: info!( "Found ESP on device {dev}" ) ;
2347+ esp_devices. push ( table) ;
2348+ }
2349+ Ok ( None ) => ( ) ,
2350+ Err ( e) => {
2351+ // Some partition table types may not be supported for ESP detection.
2352+ // Log and continue checking other devices.
2353+ tracing:: debug!( "Could not check for ESP on {dev}: {e}" ) ;
2354+ }
2355+ }
2356+ }
2357+ Err ( e) => {
2358+ // Some backing devices may not have partition tables (e.g., raw LVM PVs
2359+ // or whole-disk filesystems). These can't have an ESP, so skip them.
2360+ tracing:: debug!( "Failed to read partition table from {dev}: {e}" ) ;
2361+ }
23082362 }
2309- dev = parent;
23102363 }
2311- dev
2364+ if esp_devices. is_empty ( ) {
2365+ // No ESP found on any backing device. This is not fatal because:
2366+ // - BIOS boot uses MBR, not ESP
2367+ // - bootupd may auto-detect ESP via mounted /boot/efi
2368+ // However, UEFI boot without a detectable ESP will fail.
2369+ tracing:: warn!(
2370+ "No ESP found on any backing device ({:?}); UEFI boot may fail" ,
2371+ backing_devices
2372+ ) ;
2373+ }
2374+ esp_devices
23122375 } ;
2313- tracing:: debug!( "Backing device: {backing_device}" ) ;
2314- let device_info = bootc_blockdev:: partitions_of ( Utf8Path :: new ( & backing_device) ) ?;
23152376
23162377 let rootarg = format ! ( "root={}" , root_info. mount_spec) ;
23172378 let mut boot = if let Some ( spec) = fsopts. boot_mount_spec {
0 commit comments