1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use {
crate::*,
lazy_regex::*,
snafu::prelude::*,
std::{
path::PathBuf,
str::FromStr,
},
};
static REMOTE_ONLY_FS_TYPES: &[&str] = &["afs", "coda", "auristorfs", "fhgfs", "gpfs", "ibrix", "ocfs2", "vxfs"];
pub type MountId = u32;
#[derive(Debug, Clone)]
pub struct MountInfo {
pub id: MountId,
pub parent: MountId,
pub dev: DeviceId,
pub root: PathBuf,
pub mount_point: PathBuf,
pub fs: String,
pub fs_type: String,
pub bound: bool,
}
impl MountInfo {
pub fn dm_name(&self) -> Option<&str> {
regex_captures!(r#"^/dev/mapper/([^/]+)$"#, &self.fs)
.map(|(_, dm_name)| dm_name)
}
pub fn fs_name(&self) -> Option<&str> {
regex_find!(r#"[^\\/]+$"#, &self.fs)
}
pub fn is_remote(&self) -> bool {
self.fs.contains(':')
|| (
self.fs.starts_with("//")
&& ["cifs", "smb3", "smbfs"].contains(&self.fs_type.as_ref())
)
|| REMOTE_ONLY_FS_TYPES.contains(&self.fs_type.as_ref())
|| self.fs == "-hosts"
}
}
#[derive(Debug, Snafu)]
#[snafu(display("Could not parse {line} as mount info"))]
pub struct ParseMountInfoError {
line: String,
}
impl FromStr for MountInfo {
type Err = ParseMountInfoError;
fn from_str(line: &str) -> Result<Self, Self::Err> {
(|| {
let mut tokens = line.split_whitespace();
let id = tokens.next()?.parse().ok()?;
let parent = tokens.next()?.parse().ok()?;
let dev = tokens.next()?.parse().ok()?;
let root = str_to_pathbuf(tokens.next()?);
let mount_point = str_to_pathbuf(tokens.next()?);
loop {
let token = tokens.next()?;
if token == "-" {
break;
}
};
let fs_type = tokens.next()?.to_string();
let fs = tokens.next()?.to_string();
Some(Self {
id,
parent,
dev,
root,
mount_point,
fs,
fs_type,
bound: false, })
})().with_context(|| ParseMountInfoSnafu { line })
}
}
fn str_to_pathbuf(s: &str) -> PathBuf {
PathBuf::from(sys::decode_string(s))
}
pub fn read_mountinfo() -> Result<Vec<MountInfo>, Error> {
let mut mounts: Vec<MountInfo> = Vec::new();
let path = "/proc/self/mountinfo";
let file_content = sys::read_file(path)
.context(CantReadDirSnafu { path })?;
for line in file_content.trim().split('\n') {
let mut mount: MountInfo = line.parse()
.map_err(|source| Error::ParseMountInfo { source })?;
mount.bound = mounts.iter().any(|m| m.dev == mount.dev);
mounts.push(mount);
}
Ok(mounts)
}
#[test]
fn test_from_str() {
let mi = MountInfo::from_str(
"47 21 0:41 / /dev/hugepages rw,relatime shared:27 - hugetlbfs hugetlbfs rw,pagesize=2M"
).unwrap();
assert_eq!(mi.id, 47);
assert_eq!(mi.dev, DeviceId::new(0, 41));
assert_eq!(mi.root, PathBuf::from("/"));
assert_eq!(mi.mount_point, PathBuf::from("/dev/hugepages"));
let mi = MountInfo::from_str(
"106 26 8:17 / /home/dys/dev rw,relatime shared:57 - xfs /dev/sdb1 rw,attr2,inode64,noquota"
).unwrap();
assert_eq!(mi.id, 106);
assert_eq!(mi.dev, DeviceId::new(8, 17));
assert_eq!(&mi.fs, "/dev/sdb1");
assert_eq!(&mi.fs_type, "xfs");
}