use std::{
ffi::{CStr, CString},
os::unix::prelude::OsStrExt,
path::{Path, PathBuf},
str::FromStr,
};
use anyhow::bail;
use super::bindings;
use crate::collection::disks::unix::{FileSystem, Usage};
pub(crate) struct Partition {
device: String,
mount_point: PathBuf,
fs_type: FileSystem,
}
impl Partition {
#[inline]
pub fn mount_point(&self) -> &Path {
self.mount_point.as_path()
}
#[inline]
pub fn fs_type(&self) -> &FileSystem {
&self.fs_type
}
pub fn usage(&self) -> anyhow::Result<Usage> {
let path = CString::new(self.mount_point().as_os_str().as_bytes())?;
let mut vfs = std::mem::MaybeUninit::<libc::statvfs>::uninit();
let result = unsafe { libc::statvfs(path.as_ptr(), vfs.as_mut_ptr()) };
if result == 0 {
Ok(Usage::new(unsafe { vfs.assume_init() }))
} else {
bail!("statvfs failed to get the disk usage for disk {path:?}")
}
}
#[inline]
pub fn get_device_name(&self) -> String {
self.device.clone()
}
}
fn partitions_iter() -> anyhow::Result<impl Iterator<Item = Partition>> {
let mounts = bindings::mounts()?;
unsafe fn ptr_to_cow<'a>(ptr: *const i8) -> std::borrow::Cow<'a, str> {
unsafe { CStr::from_ptr(ptr).to_string_lossy() }
}
Ok(mounts.into_iter().map(|stat| {
let device = unsafe { ptr_to_cow(stat.f_mntfromname.as_ptr()).to_string() };
let fs_type = {
let fs_type_str = unsafe { ptr_to_cow(stat.f_fstypename.as_ptr()) };
FileSystem::from_str(&fs_type_str).unwrap_or(FileSystem::Other(fs_type_str.to_string()))
};
let mount_point = {
let path_str = unsafe { ptr_to_cow(stat.f_mntonname.as_ptr()).to_string() };
PathBuf::from(path_str)
};
Partition {
device,
mount_point,
fs_type,
}
}))
}
#[expect(dead_code)]
pub(crate) fn partitions() -> anyhow::Result<Vec<Partition>> {
partitions_iter().map(|iter| iter.collect())
}
pub(crate) fn physical_partitions() -> anyhow::Result<Vec<Partition>> {
partitions_iter().map(|iter| {
iter.filter(|partition| partition.fs_type().is_physical())
.collect()
})
}