1mod block_device;
2mod read_mountinfos;
3
4use {
5 crate::*,
6 block_device::*,
7 lazy_regex::*,
8 std::{
9 ffi::CString,
10 mem,
11 os::unix::ffi::OsStrExt,
12 path::Path,
13 },
14};
15
16pub use read_mountinfos::ParseMountInfoError;
17
18pub fn new_disk(name: String) -> Disk {
19 let rotational = sys::read_file_as_bool(format!("/sys/block/{name}/queue/rotational"));
20 let removable = sys::read_file_as_bool(format!("/sys/block/{name}/removable"));
21 let ram = regex_is_match!(r#"^zram\d*$"#, &name);
22 let dm_uuid = sys::read_file(format!("/sys/block/{name}/dm/uuid")).ok();
23 let crypted = dm_uuid
24 .as_ref()
25 .is_some_and(|uuid| uuid.starts_with("CRYPT-"));
26 let lvm = dm_uuid.is_some_and(|uuid| uuid.starts_with("LVM-"));
27 Disk {
28 name,
29 rotational,
30 removable,
31 image: false,
32 read_only: None,
33 ram,
34 lvm,
35 crypted,
36 }
37}
38
39pub fn read_mounts(options: &ReadOptions) -> Result<Vec<Mount>, Error> {
41 let by_label = read_by("label").ok();
42 let by_uuid = read_by("uuid").ok();
43 let by_partuuid = read_by("partuuid").ok();
44
45 let bd_list = BlockDeviceList::read()?;
49 read_mountinfos::read_all_mountinfos()?
50 .drain(..)
51 .map(|info| {
52 let top_bd = bd_list.find_top(info.dev, info.dm_name(), info.fs_name());
53 let fs_label = get_label(&info.fs, by_label.as_deref());
54 let uuid = get_label(&info.fs, by_uuid.as_deref());
55 let part_uuid = get_label(&info.fs, by_partuuid.as_deref());
56 let disk = top_bd.map(|bd| new_disk(bd.name.clone()));
57 let stats = if !options.remote_stats && info.is_remote() {
58 Err(StatsError::Excluded)
59 } else {
60 read_stats(&info.mount_point)
61 };
62 Ok(Mount {
63 info,
64 fs_label,
65 disk,
66 stats,
67 uuid,
68 part_uuid,
69 })
70 })
71 .collect()
72}
73
74pub fn read_stats(mount_point: &Path) -> Result<Stats, StatsError> {
75 let c_mount_point = CString::new(mount_point.as_os_str().as_bytes()).unwrap();
76 unsafe {
77 let mut statvfs = mem::MaybeUninit::<libc::statvfs>::uninit();
78 let code = libc::statvfs(c_mount_point.as_ptr(), statvfs.as_mut_ptr());
79 match code {
80 0 => {
81 let statvfs = statvfs.assume_init();
82
83 let bsize = statvfs.f_bsize;
85 let blocks = statvfs.f_blocks;
86 let bfree = statvfs.f_bfree;
87 let bavail = statvfs.f_bavail;
88 if bsize == 0 || blocks == 0 || bfree > blocks || bavail > blocks {
89 return Err(StatsError::Unconsistent);
91 }
92
93 let bused = blocks - bavail;
95
96 let files = statvfs.f_files;
98 let ffree = statvfs.f_ffree;
99 let favail = statvfs.f_favail;
100 #[allow(clippy::useless_conversion)]
101 let inodes = Inodes::new(files.into(), ffree.into(), favail.into());
102
103 #[allow(clippy::useless_conversion)]
104 Ok(Stats {
105 bsize: bsize.into(),
106 blocks: blocks.into(),
107 bused: bused.into(),
108 bfree: bfree.into(),
109 bavail: bavail.into(),
110 inodes: inodes.into(),
111 })
112 }
113 _ => {
114 Err(StatsError::Unreachable)
117 }
118 }
119 }
120}