Skip to content

Commit 47e2bcc

Browse files
ckyrouaccgwalters
authored andcommitted
blockdev: Backfill partition number from sysfs for older lsblk
The "partn" column was added in util-linux 2.39, which is newer than what CentOS 9 / RHEL 9 ship (2.37). Read the partition number from sysfs when lsblk doesn't provide it. This is done by refactoring the existing backfill function into a more generic one used to backfill both start and partn. Assisted-by: Claude Code (claude-opus-4-5-20251101) Signed-off-by: ckyrouac <[email protected]>
1 parent f7944e9 commit 47e2bcc

File tree

1 file changed

+31
-20
lines changed

1 file changed

+31
-20
lines changed

crates/blockdev/src/blockdev.rs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ pub struct Device {
3333
pub partlabel: Option<String>,
3434
pub parttype: Option<String>,
3535
pub partuuid: Option<String>,
36+
/// Partition number (1-indexed). None for whole disk devices.
37+
pub partn: Option<u32>,
3638
pub children: Option<Vec<Device>>,
3739
pub size: u64,
3840
#[serde(rename = "maj:min")]
@@ -60,33 +62,42 @@ impl Device {
6062
self.children.as_ref().is_some_and(|v| !v.is_empty())
6163
}
6264

63-
// The "start" parameter was only added in a version of util-linux that's only
64-
// in Fedora 40 as of this writing.
65-
fn backfill_start(&mut self) -> Result<()> {
65+
/// Read a sysfs property for this device and parse it as the target type.
66+
fn read_sysfs_property<T>(&self, property: &str) -> Result<Option<T>>
67+
where
68+
T: std::str::FromStr,
69+
T::Err: std::error::Error + Send + Sync + 'static,
70+
{
6671
let Some(majmin) = self.maj_min.as_deref() else {
67-
// This shouldn't happen
68-
return Ok(());
72+
return Ok(None);
6973
};
70-
let sysfs_start_path = format!("/sys/dev/block/{majmin}/start");
71-
if Utf8Path::new(&sysfs_start_path).try_exists()? {
72-
let start = std::fs::read_to_string(&sysfs_start_path)
73-
.with_context(|| format!("Reading {sysfs_start_path}"))?;
74-
tracing::debug!("backfilled start to {start}");
75-
self.start = Some(
76-
start
77-
.trim()
78-
.parse()
79-
.context("Parsing sysfs start property")?,
80-
);
74+
let sysfs_path = format!("/sys/dev/block/{majmin}/{property}");
75+
if !Utf8Path::new(&sysfs_path).try_exists()? {
76+
return Ok(None);
8177
}
82-
Ok(())
78+
let value = std::fs::read_to_string(&sysfs_path)
79+
.with_context(|| format!("Reading {sysfs_path}"))?;
80+
let parsed = value
81+
.trim()
82+
.parse()
83+
.with_context(|| format!("Parsing sysfs {property} property"))?;
84+
tracing::debug!("backfilled {property} to {value}");
85+
Ok(Some(parsed))
8386
}
8487

8588
/// Older versions of util-linux may be missing some properties. Backfill them if they're missing.
8689
pub fn backfill_missing(&mut self) -> Result<()> {
87-
// Add new properties to backfill here
88-
self.backfill_start()?;
89-
// And recurse to child devices
90+
// The "start" parameter was only added in a version of util-linux that's only
91+
// in Fedora 40 as of this writing.
92+
if self.start.is_none() {
93+
self.start = self.read_sysfs_property("start")?;
94+
}
95+
// The "partn" column was added in util-linux 2.39, which is newer than
96+
// what CentOS 9 / RHEL 9 ship (2.37). Note: sysfs uses "partition" not "partn".
97+
if self.partn.is_none() {
98+
self.partn = self.read_sysfs_property("partition")?;
99+
}
100+
// Recurse to child devices
90101
for child in self.children.iter_mut().flatten() {
91102
child.backfill_missing()?;
92103
}

0 commit comments

Comments
 (0)