use crate::devices::DeviceInfo;
use crate::platform::traits::DeviceInventory;
use anyhow::{Context, Result};
use log::{debug, warn};
use std::fs;
use std::path::{Path, PathBuf};
use super::LinuxPlatform;
impl DeviceInventory for LinuxPlatform {
fn list_storage_devices() -> Result<Vec<DeviceInfo>> {
let paths = fs::read_dir("/sys/block/").context("Failed to read /sys/block/ directory")?;
let mut devices: Vec<DeviceInfo> = Vec::new();
for entry in paths {
let entry = entry.context("Failed to read directory entry")?;
let sys_path = entry.path();
let Some(device_end_name) = entry.file_name().to_str().map(str::to_string) else {
warn!("Could not convert device name to string");
continue;
};
if !sys_path.join("device").exists() {
continue;
}
let vendor_name = read_sysfs_trimmed(&sys_path.join("device/vendor"));
let model_name = read_sysfs_trimmed(&sys_path.join("device/model"));
let removable_raw = read_sysfs_trimmed(&sys_path.join("removable"));
let size = read_sysfs_trimmed(&sys_path.join("size"))
.parse::<u64>()
.unwrap_or(0);
let removable = match removable_raw.as_str() {
"1" => 1u8,
"0" => 0u8,
_ => continue,
};
let dev_path = PathBuf::from("/dev/").join(&device_end_name);
if !dev_path.exists() {
continue;
}
devices.push(DeviceInfo {
device_name: dev_path.display().to_string(),
vendor_name,
model_name,
removable,
size,
});
}
debug!("Found {} devices", devices.len());
Ok(devices)
}
fn device_size_bytes(path: &str) -> Option<u64> {
Self::device_size_sectors(path).map(|sectors| sectors.saturating_mul(512))
}
fn device_size_sectors(path: &str) -> Option<u64> {
let block_name = Path::new(path).file_name().and_then(|n| n.to_str())?;
let size_path = format!("/sys/block/{block_name}/size");
fs::read_to_string(size_path).ok()?.trim().parse().ok()
}
fn is_removable(device_path: &str) -> Result<bool> {
let device_name = Path::new(device_path)
.file_name()
.context("Invalid device path")?
.to_str()
.context("Non UTF-8 device name")?;
let removable_path = PathBuf::from(format!("/sys/block/{device_name}/removable"));
debug!("Checking removable path: {}", removable_path.display());
let contents = fs::read_to_string(&removable_path)
.with_context(|| format!("Failed to open {}", removable_path.display()))?;
Ok(contents.trim() == "1")
}
}
fn read_sysfs_trimmed(path: &Path) -> String {
fs::read_to_string(path)
.map(|s| s.trim().to_string())
.unwrap_or_default()
}