use std::{
cell::RefCell,
fmt::Debug,
fs::File,
io::{self, Read},
path::{Path, PathBuf},
str::FromStr,
};
use sysfs_class::{Block, SysClass};
pub trait BlockDeviceExt {
fn sys_block_path(&self) -> PathBuf { sys_block_path(self.get_device_name(), "") }
fn is_read_only(&self) -> bool {
Block::from_path(&self.sys_block_path())
.ok()
.map_or(false, |block| block.ro().ok() == Some(1))
}
fn is_removable(&self) -> bool {
Block::from_path(&self.sys_block_path())
.ok()
.map_or(false, |block| block.removable().ok() == Some(1))
}
fn is_rotational(&self) -> bool {
Block::from_path(&self.sys_block_path())
.ok()
.map_or(false, |block| block.queue_rotational().ok() == Some(1))
}
fn get_device_path(&self) -> &Path;
fn get_mount_point(&self) -> Option<&Path> { None }
fn get_device_name(&self) -> &str {
self.get_device_path()
.file_name()
.expect("BlockDeviceExt::get_device_path missing file_name")
.to_str()
.expect("BlockDeviceExt::get_device_path invalid file_name")
}
fn get_sectors(&self) -> u64 {
let size_file = sys_block_path(self.get_device_name(), "/size");
read_file::<u64>(&size_file).expect("no sector count found")
}
fn get_logical_block_size(&self) -> u64 {
let path = sys_block_path(self.get_device_name(), "/queue/logical_block_size");
read_file::<u64>(&path).expect("logical block size not found")
}
fn get_physical_block_size(&self) -> u64 {
let path = sys_block_path(self.get_device_name(), "/queue/physical_block_size");
read_file::<u64>(&path).expect("physical block size not found")
}
}
fn sys_block_path(name: &str, ppath: &str) -> PathBuf {
PathBuf::from(["/sys/class/block/", name, ppath].concat())
}
thread_local! {
static BUFFER: RefCell<String> = String::with_capacity(256).into();
}
fn read_file<T: FromStr>(path: &Path) -> io::Result<T>
where
<T as FromStr>::Err: Debug,
{
BUFFER.with(|buffer| {
let mut buffer = buffer.borrow_mut();
File::open(path)?.read_to_string(&mut buffer)?;
let value = buffer.trim().parse::<T>();
buffer.clear();
value.map_err(|why| io::Error::new(io::ErrorKind::Other, format!("{:?}", why)))
})
}