@@ -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) ]
699730mod 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