@@ -19,6 +19,9 @@ pub const ESP_ID_MBR: &[u8] = &[0x06, 0xEF];
1919/// EFI System Partition (ESP) for UEFI boot on GPT
2020pub const ESP : & str = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" ;
2121
22+ /// BIOS boot partition type GUID for GPT
23+ pub const BIOS_BOOT : & str = "21686148-6449-6e6f-744e-656564454649" ;
24+
2225#[ derive( Debug , Deserialize ) ]
2326struct DevicesOutput {
2427 blockdevices : Vec < Device > ,
@@ -69,6 +72,87 @@ impl Device {
6972 self . children . as_ref ( ) . is_some_and ( |v| !v. is_empty ( ) )
7073 }
7174
75+ // Check if the device is mpath
76+ pub fn is_mpath ( & self ) -> Result < bool > {
77+ let dm_path = Utf8PathBuf :: from_path_buf ( std:: fs:: canonicalize ( self . path ( ) ) ?)
78+ . map_err ( |_| anyhow:: anyhow!( "Non-UTF8 path" ) ) ?;
79+ let dm_name = dm_path. file_name ( ) . unwrap_or ( "" ) ;
80+ let uuid_path = Utf8PathBuf :: from ( format ! ( "/sys/class/block/{dm_name}/dm/uuid" ) ) ;
81+
82+ if uuid_path. exists ( ) {
83+ let uuid = std:: fs:: read_to_string ( & uuid_path)
84+ . with_context ( || format ! ( "Failed to read {uuid_path}" ) ) ?;
85+ if uuid. trim_start ( ) . starts_with ( "mpath-" ) {
86+ return Ok ( true ) ;
87+ }
88+ }
89+ Ok ( false )
90+ }
91+
92+ /// Get the numeric partition index of the ESP (e.g. "1", "2").
93+ ///
94+ /// We read `/sys/class/block/<name>/partition` rather than parsing device
95+ /// names because naming conventions vary across disk types (sd, nvme, dm, etc.).
96+ /// On multipath devices the sysfs `partition` attribute doesn't exist, so we
97+ /// fall back to the `partn` field reported by lsblk.
98+ pub fn get_esp_partition_number ( & self ) -> Result < String > {
99+ let esp_device = self . find_partition_of_esp ( ) ?;
100+ let devname = & esp_device. name ;
101+
102+ let partition_path = Utf8PathBuf :: from ( format ! ( "/sys/class/block/{devname}/partition" ) ) ;
103+ if partition_path. exists ( ) {
104+ return std:: fs:: read_to_string ( & partition_path)
105+ . with_context ( || format ! ( "Failed to read {partition_path}" ) ) ;
106+ }
107+
108+ // On multipath the partition attribute is not existing
109+ if self . is_mpath ( ) ? {
110+ if let Some ( partn) = esp_device. partn {
111+ return Ok ( partn. to_string ( ) ) ;
112+ }
113+ }
114+ anyhow:: bail!( "Not supported for {devname}" )
115+ }
116+
117+ /// Find BIOS boot partition among children.
118+ pub fn find_partition_of_bios_boot ( & self ) -> Option < & Device > {
119+ self . find_partition_of_type ( BIOS_BOOT )
120+ }
121+
122+ /// Find all ESP partitions across all root devices backing this device.
123+ /// Calls find_all_roots() to discover physical disks, then searches each for an ESP.
124+ /// Returns None if no ESPs are found.
125+ pub fn find_colocated_esps ( & self ) -> Result < Option < Vec < Device > > > {
126+ let roots = self . find_all_roots ( ) ?;
127+ let mut esps = Vec :: new ( ) ;
128+ for root in & roots {
129+ if let Ok ( esp) = root. find_partition_of_esp ( ) {
130+ esps. push ( esp. clone ( ) ) ;
131+ }
132+ }
133+ if esps. is_empty ( ) {
134+ return Ok ( None ) ;
135+ }
136+ Ok ( Some ( esps) )
137+ }
138+
139+ /// Find all BIOS boot partitions across all root devices backing this device.
140+ /// Calls find_all_roots() to discover physical disks, then searches each for a BIOS boot partition.
141+ /// Returns None if no BIOS boot partitions are found.
142+ pub fn find_colocated_bios_boot ( & self ) -> Result < Option < Vec < Device > > > {
143+ let roots = self . find_all_roots ( ) ?;
144+ let mut bios_boots = Vec :: new ( ) ;
145+ for root in & roots {
146+ if let Some ( bios) = root. find_partition_of_bios_boot ( ) {
147+ bios_boots. push ( bios. clone ( ) ) ;
148+ }
149+ }
150+ if bios_boots. is_empty ( ) {
151+ return Ok ( None ) ;
152+ }
153+ Ok ( Some ( bios_boots) )
154+ }
155+
72156 /// Find a child partition by partition type (case-insensitive).
73157 pub fn find_partition_of_type ( & self , parttype : & str ) -> Option < & Device > {
74158 self . children . as_ref ( ) ?. iter ( ) . find ( |child| {
@@ -506,6 +590,10 @@ mod test {
506590 // Verify find_partition_of_esp works
507591 let esp = dev. find_partition_of_esp ( ) . unwrap ( ) ;
508592 assert_eq ! ( esp. partn, Some ( 2 ) ) ;
593+ // Verify find_partition_of_bios_boot works (vda1 is BIOS-BOOT)
594+ let bios = dev. find_partition_of_bios_boot ( ) . unwrap ( ) ;
595+ assert_eq ! ( bios. partn, Some ( 1 ) ) ;
596+ assert_eq ! ( bios. parttype. as_deref( ) . unwrap( ) , BIOS_BOOT ) ;
509597 }
510598
511599 #[ test]
0 commit comments