1use {
2 crate::Inodes,
3 std::{
4 ffi::CString,
5 mem,
6 os::unix::ffi::OsStrExt,
7 path::Path,
8 },
9};
10
11#[derive(Debug, Clone)]
13pub struct Stats {
14 pub bsize: u64,
16 pub blocks: u64,
18 pub bfree: u64,
20 pub bavail: u64,
22 pub inodes: Option<Inodes>,
24}
25
26#[derive(Debug, snafu::Snafu, Clone, Copy, PartialEq, Eq)]
27#[snafu(visibility(pub(crate)))]
28pub enum StatsError {
29
30 #[snafu(display("Could not stat mount point"))]
31 Unreachable,
32
33 #[snafu(display("Unconsistent stats"))]
34 Unconsistent,
35
36 #[snafu(display("Excluded"))]
38 Excluded,
39}
40
41impl Stats {
42 pub fn from(mount_point: &Path) -> Result<Self, StatsError> {
43 let c_mount_point = CString::new(mount_point.as_os_str().as_bytes()).unwrap();
44 unsafe {
45 let mut statvfs = mem::MaybeUninit::<libc::statvfs>::uninit();
46 let code = libc::statvfs(c_mount_point.as_ptr(), statvfs.as_mut_ptr());
47 match code {
48 0 => {
49 let statvfs = statvfs.assume_init();
50
51 let bsize = statvfs.f_bsize as u64;
53 let blocks = statvfs.f_blocks as u64;
54 let bfree = statvfs.f_bfree as u64;
55 let bavail = statvfs.f_bavail as u64;
56 if bsize == 0 || blocks == 0 || bfree > blocks || bavail > blocks {
57 return Err(StatsError::Unconsistent);
59 }
60
61 let files = statvfs.f_files as u64;
63 let ffree = statvfs.f_ffree as u64;
64 let favail = statvfs.f_favail as u64;
65 let inodes = Inodes::new(files, ffree, favail);
66
67 Ok(Stats {
68 bsize,
69 blocks,
70 bfree,
71 bavail,
72 inodes,
73 })
74 }
75 _ => {
76 Err(StatsError::Unreachable)
79 }
80 }
81 }
82 }
83 pub fn size(&self) -> u64 {
84 self.bsize * self.blocks
85 }
86 pub fn available(&self) -> u64 {
87 self.bsize * self.bavail
88 }
89 pub fn used(&self) -> u64 {
90 self.size() - self.available()
91 }
92 pub fn use_share(&self) -> f64 {
93 if self.size() == 0 {
94 0.0
95 } else {
96 self.used() as f64 / (self.size() as f64)
97 }
98 }
99}