Skip to content

Commit c96f611

Browse files
ckyrouaccgwalters
authored andcommitted
blockdev: Add blockdev functions from bootupd
Migrating all the blockdev code that is in bootupd into the blockdev crate to simplify its usage and keep it colocated. Signed-off-by: ckyrouac <ckyrouac@redhat.com>
1 parent 0940424 commit c96f611

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

crates/blockdev/src/blockdev.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ pub const ESP_ID_MBR: &[u8] = &[0x06, 0xEF];
1919
/// EFI System Partition (ESP) for UEFI boot on GPT
2020
pub 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)]
2326
struct DevicesOutput {
2427
blockdevices: Vec<Device>,
@@ -69,6 +72,79 @@ 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 esps: Vec<_> = self
127+
.find_all_roots()?
128+
.iter()
129+
.flat_map(|root| root.find_partition_of_esp().ok())
130+
.cloned()
131+
.collect();
132+
Ok((!esps.is_empty()).then_some(esps))
133+
}
134+
135+
/// Find all BIOS boot partitions across all root devices backing this device.
136+
/// Calls find_all_roots() to discover physical disks, then searches each for a BIOS boot partition.
137+
/// Returns None if no BIOS boot partitions are found.
138+
pub fn find_colocated_bios_boot(&self) -> Result<Option<Vec<Device>>> {
139+
let bios_boots: Vec<_> = self
140+
.find_all_roots()?
141+
.iter()
142+
.filter_map(|root| root.find_partition_of_bios_boot())
143+
.cloned()
144+
.collect();
145+
Ok((!bios_boots.is_empty()).then_some(bios_boots))
146+
}
147+
72148
/// Find a child partition by partition type (case-insensitive).
73149
pub fn find_partition_of_type(&self, parttype: &str) -> Option<&Device> {
74150
self.children.as_ref()?.iter().find(|child| {
@@ -506,6 +582,10 @@ mod test {
506582
// Verify find_partition_of_esp works
507583
let esp = dev.find_partition_of_esp().unwrap();
508584
assert_eq!(esp.partn, Some(2));
585+
// Verify find_partition_of_bios_boot works (vda1 is BIOS-BOOT)
586+
let bios = dev.find_partition_of_bios_boot().unwrap();
587+
assert_eq!(bios.partn, Some(1));
588+
assert_eq!(bios.parttype.as_deref().unwrap(), BIOS_BOOT);
509589
}
510590

511591
#[test]

0 commit comments

Comments
 (0)