Skip to content

Commit 9c3e439

Browse files
committed
blockdev: Restore multipath partition number fallback from device path
When the sysfs `partition` attribute is missing on multipath devices and lsblk's `partn` field is also unavailable, fall back to extracting trailing digits from the ESP device path (e.g. `/dev/mapper/mpatha2` → `"2"`). This restores the behavior originally added in bootupd PR [#1006](coreos/bootupd#1006) that was lost during the migration to the bootc-internal-blockdev crate. Assisted-by: Claude Code Signed-off-by: ckyrouac <ckyrouac@redhat.com>
1 parent 14b8d6a commit 9c3e439

1 file changed

Lines changed: 74 additions & 1 deletion

File tree

crates/blockdev/src/blockdev.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ impl Device {
154154
/// We read `/sys/class/block/<name>/partition` rather than parsing device
155155
/// names because naming conventions vary across disk types (sd, nvme, dm, etc.).
156156
/// On multipath devices the sysfs `partition` attribute doesn't exist, so we
157-
/// fall back to the `partn` field reported by lsblk.
157+
/// fall back to the `partn` field reported by lsblk, then to parsing the
158+
/// partition suffix from the ESP device path relative to the parent device
159+
/// path (e.g. parent `/dev/mapper/mpatha`, ESP `/dev/mapper/mpatha2` → `"2"`).
158160
pub fn get_esp_partition_number(&self) -> Result<String> {
159161
let esp_device = self.find_partition_of_esp()?;
160162
let devname = &esp_device.name;
@@ -170,6 +172,15 @@ impl Device {
170172
if let Some(partn) = esp_device.partn {
171173
return Ok(partn.to_string());
172174
}
175+
// Last resort: strip the parent device path from the ESP device path,
176+
// then skip any non-digit separator (e.g. "p") to get the partition number.
177+
// For example: parent "/dev/mapper/mpatha", ESP "/dev/mapper/mpatha2" → "2"
178+
// parent "/dev/mapper/mpatha", ESP "/dev/mapper/mpathap2" → "2"
179+
let parent_path = self.path();
180+
let esp_path = esp_device.path();
181+
if let Some(n) = parse_partition_number_from_suffix(&parent_path, &esp_path) {
182+
return Ok(n);
183+
}
173184
}
174185
anyhow::bail!("Not supported for {devname}")
175186
}
@@ -695,6 +706,26 @@ pub fn parse_size_mib(mut s: &str) -> Result<u64> {
695706
Ok(v * mul)
696707
}
697708

709+
/// Extract a partition number by stripping the parent device path from the
710+
/// ESP partition device path, then skipping any non-digit separator characters.
711+
///
712+
/// Multipath partition devices are named by appending a partition suffix to
713+
/// the parent device path. The suffix may include a separator like "p" before
714+
/// the digits:
715+
/// - `/dev/mapper/mpatha` + `2` → `/dev/mapper/mpatha2`
716+
/// - `/dev/mapper/mpatha` + `p2` → `/dev/mapper/mpathap2`
717+
///
718+
/// This function returns `None` if the ESP path doesn't start with the parent
719+
/// path or if no trailing digits are found in the suffix.
720+
fn parse_partition_number_from_suffix(parent_path: &str, esp_path: &str) -> Option<String> {
721+
let suffix = esp_path.strip_prefix(parent_path)?;
722+
let digits = suffix.trim_start_matches(|c: char| !c.is_ascii_digit());
723+
if digits.is_empty() {
724+
return None;
725+
}
726+
Some(digits.to_string())
727+
}
728+
698729
#[cfg(test)]
699730
mod test {
700731
use super::*;
@@ -927,4 +958,46 @@ mod test {
927958
let dev = make_mbr_disk(&["0x83", "0x82"]);
928959
assert!(dev.find_partition_of_esp().is_err());
929960
}
961+
962+
#[test]
963+
fn test_parse_partition_number_from_suffix() {
964+
// Short alias like /dev/mapper/mpatha → /dev/mapper/mpatha2
965+
assert_eq!(
966+
parse_partition_number_from_suffix("/dev/mapper/mpatha", "/dev/mapper/mpatha2"),
967+
Some("2".into())
968+
);
969+
// With a "p" separator: /dev/mapper/mpatha → /dev/mapper/mpathap2
970+
assert_eq!(
971+
parse_partition_number_from_suffix("/dev/mapper/mpatha", "/dev/mapper/mpathap2"),
972+
Some("2".into())
973+
);
974+
// WWID-style name with "part" separator
975+
assert_eq!(
976+
parse_partition_number_from_suffix(
977+
"/dev/mapper/3600508b4001",
978+
"/dev/mapper/3600508b4001-part1"
979+
),
980+
Some("1".into())
981+
);
982+
// Multi-digit partition number
983+
assert_eq!(
984+
parse_partition_number_from_suffix("/dev/mapper/mpatha", "/dev/mapper/mpatha12"),
985+
Some("12".into())
986+
);
987+
// ESP path doesn't share the parent prefix → None
988+
assert_eq!(
989+
parse_partition_number_from_suffix("/dev/mapper/mpatha", "/dev/sda1"),
990+
None
991+
);
992+
// No digits in suffix → None
993+
assert_eq!(
994+
parse_partition_number_from_suffix("/dev/mapper/mpatha", "/dev/mapper/mpathap"),
995+
None
996+
);
997+
// Identical paths (no suffix at all) → None
998+
assert_eq!(
999+
parse_partition_number_from_suffix("/dev/mapper/mpatha", "/dev/mapper/mpatha"),
1000+
None
1001+
);
1002+
}
9301003
}

0 commit comments

Comments
 (0)