lfs_core/
stats.rs

1use {
2    crate::Inodes,
3    std::{
4        ffi::CString,
5        mem,
6        os::unix::ffi::OsStrExt,
7        path::Path,
8    },
9};
10
11/// inode & blocs information given by statvfs
12#[derive(Debug, Clone)]
13pub struct Stats {
14    /// block size
15    pub bsize: u64,
16    /// number of blocks
17    pub blocks: u64,
18    /// number of free blocks
19    pub bfree: u64,
20    /// number of free blocks for underprivileged users
21    pub bavail: u64,
22    /// information relative to inodes, if available
23    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    /// Options made us not even try
37    #[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                    // blocks info
52                    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                        // unconsistent or void data
58                        return Err(StatsError::Unconsistent);
59                    }
60
61                    // inodes info, will be checked in Inodes::new
62                    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                    // the filesystem wasn't found, it's a strange one, for example a
77                    // docker one, or a disconnected remote one
78                    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}