Skip to content

Commit

Permalink
disks: Begin skeletal new "disks" crate
Browse files Browse the repository at this point in the history
The disks crate will support enumeration of various disks in time,
but for now we're solely focusing on NVME and SCSI (really all libata
managed devices)

By keeping these distinct for enumeration we'll be able to tacke on
composite and multipath devices without causing ourselves confusion
down the road.

Note, child partitions are deliberately ignored.

Signed-off-by: Ikey Doherty <[email protected]>
  • Loading branch information
ikeycode committed Jan 20, 2025
1 parent d1a7103 commit 4a17907
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions crates/disks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "disks"
version = "0.1.0"
edition = "2021"
description = "A library for working with disks and partitions"

[dependencies]
regex = "1"
41 changes: 41 additions & 0 deletions crates/disks/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright © 2025 Serpent OS Developers
//
// SPDX-License-Identifier: MPL-2.0

use std::{fs, path::PathBuf};

pub mod nvme;
pub mod scsi;

const SYSFS_DIR: &str = "/sys/class/block";

#[derive(Debug)]
pub struct Disk {
/// Partial-name, ie "sda"
pub name: String,

// Number of sectors (* 512 sector size for data size)
pub sectors: u64,
}

impl Disk {
fn from_sysfs_block_name(name: impl AsRef<str>) -> Self {
let name = name.as_ref().to_owned();
let entry = PathBuf::from(SYSFS_DIR).join(&name);

// Determine number of blocks
let block_file = entry.join("size");
let sectors = fs::read_to_string(block_file)
.ok()
.and_then(|s| s.trim().parse::<u64>().ok())
.unwrap_or(0);

Self { name, sectors }
}

/// Return usable size
/// TODO: Grab the block size from the system. We know Linux is built on 512s though.
pub fn size_in_bytes(&self) -> u64 {
self.sectors * 512
}
}
50 changes: 50 additions & 0 deletions crates/disks/src/nvme.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright © 2025 Serpent OS Developers
//
// SPDX-License-Identifier: MPL-2.0

//! NVME device enumeration and handling
//!
//! This module provides functionality to enumerate and handle NVME devices.
use std::{fs, io};

use regex::Regex;

use crate::{Disk, SYSFS_DIR};

pub fn enumerate() -> io::Result<Vec<Disk>> {
// Filter for NVME block devices in format nvmeXnY where X and Y are digits
// Exclude partitions (nvmeXnYpZ) and character devices
let nvme_pattern = Regex::new(r"^nvme\d+n\d+$").unwrap();

let items = fs::read_dir(SYSFS_DIR)?
.filter_map(Result::ok)
.filter_map(|e| Some(e.file_name().to_str()?.to_owned()))
.filter(|name| nvme_pattern.is_match(name))
.map(Disk::from_sysfs_block_name)
.collect();
Ok(items)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_enumerate() {
let devices = enumerate().expect("failed to collect nvme disks");
eprintln!("nvme devices: {devices:?}");
for device in devices.iter() {
let mut size = device.size_in_bytes() as f64;
size /= 1024.0 * 1024.0 * 1024.0;
// Cheeky emulation of `fdisk -l` output
eprintln!(
"Disk /dev/{}: {:.2} GiB, {} bytes, {} sectors",
device.name,
size,
device.size_in_bytes(),
device.sectors
);
}
}
}
34 changes: 34 additions & 0 deletions crates/disks/src/scsi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright © 2025 Serpent OS Developers
//
// SPDX-License-Identifier: MPL-2.0

//! SCSI device enumeration and handling
//!
//! OK. Not quite true. Per modern conventions, all libata devices are also considered SCSI devices.
//! This means all `/dev/sd*` devices.
use std::{fs, io};

use crate::{Disk, SYSFS_DIR};

pub fn enumerate() -> io::Result<Vec<Disk>> {
// Filtered list of SCSI devices whose paths begin with "sd" but not ending with a digit
let items = fs::read_dir(SYSFS_DIR)?
.filter_map(Result::ok)
.filter_map(|e| Some(e.file_name().to_str()?.to_owned()))
.filter(|e| e.starts_with("sd") && e[2..].chars().all(char::is_alphabetic))
.map(Disk::from_sysfs_block_name)
.collect();
Ok(items)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_enumerate() {
let devices = enumerate().expect("Failed to enumerate SCSI devices");
eprintln!("scsi devices: {devices:?}");
}
}

0 comments on commit 4a17907

Please sign in to comment.