Skip to content

Commit 1022d5b

Browse files
committed
disks: Add some logging, fix sysfs resolution, type expansion
We need to use generic BlockDevice from the partitions crate rather than a specific disk implementation, in order to allow not only mocking but loopback device use. We've also fixed up the sysfs vs system_root resolution to ensure we're doing proper path discovery. As a bonus one can now initialise a loop device disk from a given `/dev/loopX` path. Signed-off-by: Ikey Doherty <[email protected]>
1 parent 632ee5c commit 1022d5b

File tree

7 files changed

+103
-35
lines changed

7 files changed

+103
-35
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/disks/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ description = "A library for working with disks and partitions"
66

77
[dependencies]
88
regex = "1"
9+
log.workspace = true

crates/disks/src/disk.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
// SPDX-License-Identifier: MPL-2.0
44

55
use core::fmt;
6+
use std::fs;
67
use std::{
7-
fs,
88
ops::Deref,
99
path::{Path, PathBuf},
1010
};
@@ -146,6 +146,8 @@ impl DiskInit for BasicDisk {
146146
fn from_sysfs_path(sysroot: &Path, name: &str) -> Option<Self> {
147147
let node = sysroot.join(name);
148148

149+
log::debug!("Initializing disk at sysfs path: {:?}", node);
150+
149151
// Read the partitions of the disk if any
150152
let mut partitions: Vec<_> = fs::read_dir(&node)
151153
.ok()?
@@ -157,12 +159,24 @@ impl DiskInit for BasicDisk {
157159
.collect();
158160
partitions.sort_by_key(|p| p.number);
159161

162+
let sectors = sysfs::read(&node, "size").unwrap_or(0);
163+
log::debug!("Read {} sectors for disk {}", sectors, name);
164+
165+
let device = PathBuf::from(DEVFS_DIR).join(name);
166+
log::debug!("Device path: {:?}", device);
167+
168+
let model = sysfs::read(&node, "device/model");
169+
log::debug!("Model: {:?}", model);
170+
171+
let vendor = sysfs::read(&node, "device/vendor");
172+
log::debug!("Vendor: {:?}", vendor);
173+
160174
Some(Self {
161175
name: name.to_owned(),
162-
sectors: sysfs::read(sysroot, &node, "size").unwrap_or(0),
163-
device: PathBuf::from(DEVFS_DIR).join(name),
164-
model: sysfs::read(sysroot, &node, "device/model"),
165-
vendor: sysfs::read(sysroot, &node, "device/vendor"),
176+
sectors,
177+
device,
178+
model,
179+
vendor,
166180
partitions,
167181
})
168182
}

crates/disks/src/lib.rs

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::{
99
};
1010

1111
pub use disk::*;
12+
use partition::Partition;
1213
pub mod loopback;
1314
pub mod mmc;
1415
pub mod mock;
@@ -18,8 +19,8 @@ pub mod scsi;
1819
mod sysfs;
1920
pub mod virt;
2021

21-
const SYSFS_DIR: &str = "/sys/class/block";
22-
const DEVFS_DIR: &str = "/dev";
22+
const SYSFS_DIR: &str = "sys/class/block";
23+
const DEVFS_DIR: &str = "dev";
2324

2425
/// A block device on the system which can be either a physical disk or a partition.
2526
#[derive(Debug)]
@@ -39,6 +40,65 @@ impl BlockDevice {
3940
Self::discover_in_sysroot("/")
4041
}
4142

43+
/// Returns the total number of sectors on the block device.
44+
pub fn sectors(&self) -> u64 {
45+
match self {
46+
BlockDevice::Disk(disk) => disk.sectors(),
47+
BlockDevice::Loopback(device) => device.disk().map_or(0, |d| d.sectors()),
48+
}
49+
}
50+
51+
/// Returns the total size of the block device in bytes.
52+
pub fn size(&self) -> u64 {
53+
self.sectors() * 512
54+
}
55+
56+
/// Returns the partitions on the block device.
57+
pub fn partitions(&self) -> &[Partition] {
58+
match self {
59+
BlockDevice::Disk(disk) => disk.partitions(),
60+
BlockDevice::Loopback(device) => device.disk().map_or(&[], |d| d.partitions()),
61+
}
62+
}
63+
64+
/// Creates a mock block device with a specified number of sectors.
65+
pub fn mock_device(disk: mock::MockDisk) -> Self {
66+
BlockDevice::Disk(Box::new(Disk::Mock(disk)))
67+
}
68+
69+
/// Creates a loopback block device from a file path.
70+
pub fn loopback_device(device: loopback::Device) -> Self {
71+
BlockDevice::Loopback(Box::new(device))
72+
}
73+
74+
/// Creates a BlockDevice from a specific device path
75+
///
76+
/// # Arguments
77+
///
78+
/// * `device_path` - Path to the block device (e.g. "/dev/sda")
79+
///
80+
/// # Returns
81+
///
82+
/// The block device or an IO error if creation fails.
83+
pub fn from_sysfs_path(sysfs_root: impl AsRef<Path>, name: impl AsRef<str>) -> io::Result<BlockDevice> {
84+
let name = name.as_ref();
85+
let sysfs_dir = sysfs_root.as_ref();
86+
87+
if let Some(disk) = scsi::Disk::from_sysfs_path(sysfs_dir, name) {
88+
return Ok(BlockDevice::Disk(Box::new(Disk::Scsi(disk))));
89+
} else if let Some(disk) = nvme::Disk::from_sysfs_path(sysfs_dir, name) {
90+
return Ok(BlockDevice::Disk(Box::new(Disk::Nvme(disk))));
91+
} else if let Some(disk) = mmc::Disk::from_sysfs_path(sysfs_dir, name) {
92+
return Ok(BlockDevice::Disk(Box::new(Disk::Mmc(disk))));
93+
} else if let Some(device) = virt::Disk::from_sysfs_path(sysfs_dir, name) {
94+
return Ok(BlockDevice::Disk(Box::new(Disk::Virtual(device))));
95+
} else if let Some(device) = loopback::Device::from_sysfs_path(sysfs_dir, name) {
96+
return Ok(BlockDevice::Loopback(Box::new(device)));
97+
}
98+
99+
Err(io::Error::new(io::ErrorKind::NotFound, "Device not found"))
100+
}
101+
42102
/// Returns the name of the block device.
43103
pub fn name(&self) -> &str {
44104
match self {
@@ -79,21 +139,9 @@ impl BlockDevice {
79139
// For all the discovered block devices, try to create a Disk instance
80140
// At this point we completely ignore partitions. They come later.
81141
for entry in entries {
82-
let device = if let Some(disk) = scsi::Disk::from_sysfs_path(&sysfs_dir, &entry) {
83-
BlockDevice::Disk(Box::new(Disk::Scsi(disk)))
84-
} else if let Some(disk) = nvme::Disk::from_sysfs_path(&sysfs_dir, &entry) {
85-
BlockDevice::Disk(Box::new(Disk::Nvme(disk)))
86-
} else if let Some(disk) = mmc::Disk::from_sysfs_path(&sysfs_dir, &entry) {
87-
BlockDevice::Disk(Box::new(Disk::Mmc(disk)))
88-
} else if let Some(device) = virt::Disk::from_sysfs_path(&sysfs_dir, &entry) {
89-
BlockDevice::Disk(Box::new(Disk::Virtual(device)))
90-
} else if let Some(device) = loopback::Device::from_sysfs_path(&sysfs_dir, &entry) {
91-
BlockDevice::Loopback(Box::new(device))
92-
} else {
93-
continue;
94-
};
95-
96-
devices.push(device);
142+
if let Ok(device) = BlockDevice::from_sysfs_path(&sysfs_dir, &entry) {
143+
devices.push(device);
144+
}
97145
}
98146

99147
Ok(devices)

crates/disks/src/loopback.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ impl Device {
4040
///
4141
/// * `Some(Device)` if the name matches loop pattern (starts with "loop" followed by numbers)
4242
/// * `None` if the name doesn't match or the device can't be initialized
43-
pub(crate) fn from_sysfs_path(sysroot: &Path, name: &str) -> Option<Self> {
43+
pub fn from_sysfs_path(sysroot: &Path, name: &str) -> Option<Self> {
4444
let matching = name.starts_with("loop") && name[4..].chars().all(char::is_numeric);
45-
let node = sysroot.join(SYSFS_DIR).join(name);
46-
let file = sysfs::read::<PathBuf>(sysroot, &node, "loop/backing_file");
45+
let node = sysroot.join(name);
46+
let file = sysfs::read::<PathBuf>(&node, "loop/backing_file");
4747
let disk = file.as_ref().and_then(|_| BasicDisk::from_sysfs_path(sysroot, name));
4848
if matching {
4949
Some(Self {
@@ -57,6 +57,12 @@ impl Device {
5757
}
5858
}
5959

60+
/// Creates a new Device instance from a device path.
61+
pub fn from_device_path(device: &Path) -> Option<Self> {
62+
let name = device.file_name()?.to_string_lossy().to_string();
63+
Self::from_sysfs_path(&PathBuf::from("/").join(SYSFS_DIR), &name)
64+
}
65+
6066
/// Returns the device name.
6167
pub fn name(&self) -> &str {
6268
&self.name

crates/disks/src/partition.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use std::fmt;
66
use std::path::{Path, PathBuf};
77

8-
use crate::{sysfs, DEVFS_DIR, SYSFS_DIR};
8+
use crate::{sysfs, DEVFS_DIR};
99

1010
/// Represents a partition on a disk device
1111
/// - Size in sectors
@@ -49,10 +49,10 @@ impl Partition {
4949
/// * `Some(Partition)` if partition exists and is valid
5050
/// * `None` if partition doesn't exist or is invalid
5151
pub fn from_sysfs_path(sysroot: &Path, name: &str) -> Option<Self> {
52-
let node = sysroot.join(SYSFS_DIR).join(name);
53-
let partition_no: u32 = sysfs::read(sysroot, &node, "partition")?;
54-
let start = sysfs::read(sysroot, &node, "start")?;
55-
let size = sysfs::read(sysroot, &node, "size")?;
52+
let node = sysroot.join(name);
53+
let partition_no: u32 = sysfs::read(&node, "partition")?;
54+
let start = sysfs::read(&node, "start")?;
55+
let size = sysfs::read(&node, "size")?;
5656
Some(Self {
5757
name: name.to_owned(),
5858
number: partition_no,

crates/disks/src/sysfs.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ use std::{fs, path::Path, str::FromStr};
1010
///
1111
/// # Arguments
1212
///
13-
/// * `sysroot` - Base path of the sysfs mount point
14-
/// * `node` - Path to specific sysfs node relative to sysroot
13+
/// * `node` - Fully qualified path to specific sysfs node
1514
/// * `key` - Name of the sysfs attribute to read
1615
///
1716
/// # Returns
@@ -22,10 +21,9 @@ use std::{fs, path::Path, str::FromStr};
2221
/// # Type Parameters
2322
///
2423
/// * `T` - Target type that implements FromStr for parsing the raw value
25-
pub(crate) fn read<T>(sysroot: &Path, node: &Path, key: &str) -> Option<T>
24+
pub(crate) fn read<T>(node: &Path, key: &str) -> Option<T>
2625
where
2726
T: FromStr,
2827
{
29-
let path = sysroot.join(node).join(key);
30-
fs::read_to_string(&path).ok()?.trim().parse().ok()
28+
fs::read_to_string(node.join(key)).ok()?.trim().parse().ok()
3129
}

0 commit comments

Comments
 (0)