Skip to content

Commit 1d4112a

Browse files
committed
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 c3c1602 commit 1d4112a

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

crates/blockdev/src/blockdev.rs

Lines changed: 88 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,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

Comments
 (0)