disk_types/
device.rs

1use std::{
2    cell::RefCell,
3    fmt::Debug,
4    fs::File,
5    io::{self, Read},
6    path::{Path, PathBuf},
7    str::FromStr,
8};
9use sysfs_class::{Block, SysClass};
10
11/// Methods that all block devices share, whether they are partitions or disks.
12///
13/// This trait is required to implement other disk traits.
14pub trait BlockDeviceExt {
15    /// The sys path of the block device.
16    fn sys_block_path(&self) -> PathBuf { sys_block_path(self.get_device_name(), "") }
17
18    /// Checks if the device is a read-only device.
19    fn is_read_only(&self) -> bool {
20        Block::from_path(&self.sys_block_path())
21            .ok()
22            .map_or(false, |block| block.ro().ok() == Some(1))
23    }
24
25    /// Checks if the device is a removable device.
26    ///
27    /// # Notes
28    /// This is only applicable for disk devices.
29    fn is_removable(&self) -> bool {
30        Block::from_path(&self.sys_block_path())
31            .ok()
32            .map_or(false, |block| block.removable().ok() == Some(1))
33    }
34
35    /// Checks if the device is a rotational device.
36    ///
37    /// # Notes
38    /// This is only applicable for disk devices.
39    fn is_rotational(&self) -> bool {
40        Block::from_path(&self.sys_block_path())
41            .ok()
42            .map_or(false, |block| block.queue_rotational().ok() == Some(1))
43    }
44
45    /// The path to the block device, such as `/dev/sda1`, or `/dev/data/root`.
46    fn get_device_path(&self) -> &Path;
47
48    /// The mount point of this block device, if it is mounted.
49    fn get_mount_point(&self) -> Option<&Path> { None }
50
51    /// The name of the device, such as `sda1`.
52    fn get_device_name(&self) -> &str {
53        self.get_device_path()
54            .file_name()
55            .expect("BlockDeviceExt::get_device_path missing file_name")
56            .to_str()
57            .expect("BlockDeviceExt::get_device_path invalid file_name")
58    }
59
60    /// The combined total number of sectors on the disk.
61    fn get_sectors(&self) -> u64 {
62        let size_file = sys_block_path(self.get_device_name(), "/size");
63        read_file::<u64>(&size_file).expect("no sector count found")
64    }
65
66    /// The size of each logical sector, in bytes.
67    fn get_logical_block_size(&self) -> u64 {
68        let path = sys_block_path(self.get_device_name(), "/queue/logical_block_size");
69        read_file::<u64>(&path).expect("logical block size not found")
70    }
71
72    /// The size of each logical sector, in bytes.
73    fn get_physical_block_size(&self) -> u64 {
74        let path = sys_block_path(self.get_device_name(), "/queue/physical_block_size");
75        read_file::<u64>(&path).expect("physical block size not found")
76    }
77}
78
79fn sys_block_path(name: &str, ppath: &str) -> PathBuf {
80    PathBuf::from(["/sys/class/block/", name, ppath].concat())
81}
82
83thread_local! {
84    static BUFFER: RefCell<String> = String::with_capacity(256).into();
85}
86
87fn read_file<T: FromStr>(path: &Path) -> io::Result<T>
88where
89    <T as FromStr>::Err: Debug,
90{
91    BUFFER.with(|buffer| {
92        let mut buffer = buffer.borrow_mut();
93        File::open(path)?.read_to_string(&mut buffer)?;
94        let value = buffer.trim().parse::<T>();
95        buffer.clear();
96        value.map_err(|why| io::Error::new(io::ErrorKind::Other, format!("{:?}", why)))
97    })
98}