mod block_device;
mod read_mountinfos;
use {
crate::*,
block_device::*,
lazy_regex::*,
std::{
ffi::CString,
mem,
os::unix::ffi::OsStrExt,
path::Path,
sync::mpsc,
thread,
time::Duration,
},
};
pub use read_mountinfos::ParseMountInfoError;
pub fn new_disk(name: String) -> Disk {
let rotational = sys::read_file_as_bool(format!("/sys/block/{name}/queue/rotational"));
let removable = sys::read_file_as_bool(format!("/sys/block/{name}/removable"));
let ram = regex_is_match!(r#"^zram\d*$"#, &name);
let dm_uuid = sys::read_file(format!("/sys/block/{name}/dm/uuid")).ok();
let crypted = dm_uuid
.as_ref()
.is_some_and(|uuid| uuid.starts_with("CRYPT-"));
let lvm = dm_uuid.is_some_and(|uuid| uuid.starts_with("LVM-"));
Disk {
name,
rotational,
removable,
image: false,
read_only: None,
ram,
lvm,
crypted,
}
}
pub fn read_mounts(options: &ReadOptions) -> Result<Vec<Mount>, Error> {
let by_label = read_by("label").ok();
let by_uuid = read_by("uuid").ok();
let by_partuuid = read_by("partuuid").ok();
let bd_list = BlockDeviceList::read()?;
read_mountinfos::read_all_mountinfos()?
.drain(..)
.map(|info| {
let top_bd = bd_list.find_top(info.dev, info.dm_name(), info.fs_name());
let fs_label = get_label(&info.fs, by_label.as_deref());
let uuid = get_label(&info.fs, by_uuid.as_deref());
let part_uuid = get_label(&info.fs, by_partuuid.as_deref());
let disk = top_bd.map(|bd| new_disk(bd.name.clone()));
let stats = if info.is_remote() && !options.remote_stats {
Err(StatsError::Excluded)
} else if let Some(timeout) = options.stats_timeout {
read_stats_with_timeout(&info.mount_point, timeout)
} else {
read_stats(&info.mount_point)
};
Ok(Mount {
info,
fs_label,
disk,
stats,
uuid,
part_uuid,
})
})
.collect()
}
pub fn read_stats_with_timeout(
mount_point: &Path,
timeout: Duration,
) -> Result<Stats, StatsError> {
let mount_point = mount_point.to_path_buf();
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let stats = read_stats(&mount_point);
let _ = tx.send(stats);
});
rx.recv_timeout(timeout).map_err(|_| StatsError::Timeout)?
}
pub fn read_stats(mount_point: &Path) -> Result<Stats, StatsError> {
let c_mount_point = CString::new(mount_point.as_os_str().as_bytes()).unwrap();
unsafe {
let mut statvfs = mem::MaybeUninit::<libc::statvfs>::uninit();
let code = libc::statvfs(c_mount_point.as_ptr(), statvfs.as_mut_ptr());
match code {
0 => {
let statvfs = statvfs.assume_init();
let bsize = statvfs.f_bsize;
let blocks = statvfs.f_blocks;
let bfree = statvfs.f_bfree;
let bavail = statvfs.f_bavail;
if bsize == 0 || blocks == 0 || bfree > blocks || bavail > blocks {
return Err(StatsError::Unconsistent);
}
let bused = blocks - bavail;
let files = statvfs.f_files;
let ffree = statvfs.f_ffree;
let favail = statvfs.f_favail;
#[allow(clippy::useless_conversion)]
let inodes = Inodes::new(files.into(), ffree.into(), favail.into());
#[allow(clippy::useless_conversion)]
Ok(Stats {
bsize: bsize.into(),
blocks: blocks.into(),
bused: bused.into(),
bfree: bfree.into(),
bavail: bavail.into(),
inodes: inodes.into(),
})
}
_ => {
Err(StatsError::Unreachable)
}
}
}
}