evobench_tools/utillib/
linux_mounts.rs

1//! Get information on the filesystem mount points on Linux
2
3use std::{
4    collections::HashMap,
5    fs::Metadata,
6    path::{Path, PathBuf},
7    sync::Arc,
8};
9
10use anyhow::{Result, anyhow};
11use kstring::KString;
12
13use crate::{ctx, utillib::into_arc_path::IntoArcPath};
14
15pub struct MountPoint {
16    pub device_name: KString,
17    pub path: KString,
18    pub fstype: KString,
19    pub options: KString,
20    // Ignore the remaining (numeric) fields (for now)
21}
22
23impl MountPoint {
24    pub fn is_tmpfs(&self) -> bool {
25        &self.fstype == "tmpfs"
26    }
27
28    pub fn path(&self) -> Arc<Path> {
29        self.path.as_str().into_arc_path()
30    }
31
32    pub fn path_buf(&self) -> PathBuf {
33        self.path.as_str().into()
34    }
35
36    pub fn path_metadata(&self) -> Result<Metadata> {
37        let path: &Path = self.path.as_str().as_ref();
38        path.metadata()
39            .map_err(ctx!("getting metadata for {path:?}"))
40    }
41}
42
43pub struct MountPoints {
44    mount_points: Vec<MountPoint>,
45    by_path: HashMap<KString, usize>,
46}
47
48impl MountPoints {
49    pub fn read() -> Result<Self> {
50        let input =
51            std::fs::read_to_string("/proc/mounts").map_err(ctx!("reading /proc/mounts"))?;
52        let mount_points: Vec<MountPoint> = input
53            .trim_end()
54            .split("\n")
55            .map(|line| -> Result<MountPoint> {
56                // XX what is the precise syntax of this file?
57                let mut items = line.split(" ");
58                macro_rules! let_get {
59                    { $name:tt } =>  {
60                        let $name = KString::from_ref(
61                            items.next().ok_or_else(
62                                || anyhow!("missing value for {:?}", stringify!($name)))?
63                        );
64                    }
65                }
66                let_get!(device_name);
67                let_get!(path);
68                let_get!(fstype);
69                let_get!(options);
70                Ok(MountPoint {
71                    device_name,
72                    path,
73                    fstype,
74                    options,
75                })
76            })
77            .collect::<Result<_>>()?;
78
79        let by_path = mount_points
80            .iter()
81            .enumerate()
82            .map(|(i, p)| (p.path.clone(), i))
83            .collect();
84
85        Ok(MountPoints {
86            mount_points,
87            by_path,
88        })
89    }
90
91    pub fn get_by_path(&self, path: &str) -> Option<&MountPoint> {
92        self.by_path.get(path).map(|i| &self.mount_points[*i])
93    }
94}