lfs_core/linux/
read_mountinfos.rs1use {
2 crate::*,
3 lazy_regex::*,
4 snafu::prelude::*,
5 std::path::PathBuf,
6};
7
8#[derive(Debug, Snafu)]
9#[snafu(display("Could not parse {line} as mount info"))]
10pub struct ParseMountInfoError {
11 line: String,
12}
13
14#[cfg(target_os = "linux")]
15impl std::str::FromStr for MountInfo {
16 type Err = ParseMountInfoError;
17 fn from_str(line: &str) -> Result<Self, Self::Err> {
18 (|| {
19 let mut tokens = line.split_whitespace();
23
24 let id = tokens.next()?.parse().ok()?;
25 let parent = tokens.next()?.parse().ok()?;
26
27 let id = Some(id);
30 let parent = Some(parent);
31
32 let dev = tokens.next()?.parse().ok()?;
33 let root = str_to_pathbuf(tokens.next()?);
34 let mount_point = str_to_pathbuf(tokens.next()?);
35
36 let direct_options =
37 regex_captures_iter!("(?:^|,)([^=,]+)(?:=([^=,]*))?", tokens.next()?,);
38
39 let mut options: Vec<MountOption> = direct_options
40 .map(|c| {
41 let name = c.get(1).unwrap().as_str().to_string();
42 let value = c.get(2).map(|v| v.as_str().to_string());
43 MountOption { name, value }
44 })
45 .collect();
46
47 loop {
50 let token = tokens.next()?;
51 if token == "-" {
52 break;
53 }
54 }
55
56 let fs_type = tokens.next()?.to_string();
57 let fs = tokens.next()?.to_string();
58
59 if let Some(super_options) = tokens.next() {
60 for c in regex_captures_iter!("(?:^|,)([^=,]+)(?:=([^=,]*))?", super_options) {
61 let name = c.get(1).unwrap().as_str().to_string();
62 if name == "rw" {
63 continue; }
65 if options.iter().any(|o| o.name == name) {
66 continue;
67 }
68 let value = c.get(2).map(|v| v.as_str().to_string());
69 options.push(MountOption { name, value });
70 }
71 }
72
73 Some(Self {
74 id,
75 parent,
76 dev,
77 root,
78 mount_point,
79 options,
80 fs,
81 fs_type,
82 bound: false, })
84 })()
85 .with_context(|| ParseMountInfoSnafu { line })
86 }
87}
88
89#[cfg(target_os = "linux")]
94fn str_to_pathbuf(s: &str) -> PathBuf {
95 PathBuf::from(sys::decode_string(s))
96}
97
98#[cfg(target_os = "linux")]
100pub fn read_all_mountinfos() -> Result<Vec<MountInfo>, Error> {
101 let mut mounts: Vec<MountInfo> = Vec::new();
102 let path = "/proc/self/mountinfo";
103 let file_content = sys::read_file(path).context(CantReadDirSnafu { path })?;
104 for line in file_content.trim().split('\n') {
105 let mut mount: MountInfo = line
106 .parse()
107 .map_err(|source| Error::ParseMountInfo { source })?;
108 mount.bound = mounts.iter().any(|m| m.dev == mount.dev);
109 mounts.push(mount);
110 }
111 Ok(mounts)
112}
113
114#[cfg(target_os = "linux")]
115#[allow(clippy::bool_assert_comparison)]
116#[test]
117fn test_from_str() {
118 use std::str::FromStr;
119
120 let mi = MountInfo::from_str(
121 "47 21 0:41 / /dev/hugepages rw,relatime shared:27 - hugetlbfs hugetlbfs rw,pagesize=2M",
122 )
123 .unwrap();
124 assert_eq!(mi.id, Some(47));
125 assert_eq!(mi.dev, DeviceId::new(0, 41));
126 assert_eq!(mi.root, PathBuf::from("/"));
127 assert_eq!(mi.mount_point, PathBuf::from("/dev/hugepages"));
128 assert_eq!(mi.options_string(), "rw,relatime,pagesize=2M".to_string());
129
130 let mi = MountInfo::from_str(
131 "106 26 8:17 / /home/dys/dev rw,noatime,compress=zstd:3 shared:57 - btrfs /dev/sdb1 rw,attr2,inode64,noquota"
132 ).unwrap();
133 assert_eq!(mi.id, Some(106));
134 assert_eq!(mi.dev, DeviceId::new(8, 17));
135 assert_eq!(&mi.fs, "/dev/sdb1");
136 assert_eq!(&mi.fs_type, "btrfs");
137 let mut options = mi.options.clone().into_iter();
138 assert_eq!(options.next(), Some(MountOption::new("rw", None)),);
139 assert_eq!(options.next(), Some(MountOption::new("noatime", None)));
140 assert_eq!(
141 options.next(),
142 Some(MountOption::new("compress", Some("zstd:3")))
143 );
144 assert_eq!(mi.has_option("noatime"), true);
145 assert_eq!(mi.has_option("relatime"), false);
146 assert_eq!(mi.option_value("thing"), None);
147 assert_eq!(mi.option_value("compress"), Some("zstd:3"));
148 assert_eq!(
149 mi.options_string(),
150 "rw,noatime,compress=zstd:3,attr2,inode64,noquota".to_string()
151 );
152
153 let mi = MountInfo::from_str(
154 "73 2 0:33 /root / rw,relatime shared:1 - btrfs /dev/vda3 rw,seclabel,compress=zstd:1,ssd,space_cache=v2,subvolid=256,subvol=/root"
155 ).unwrap();
156 assert_eq!(mi.option_value("compress"), Some("zstd:1"));
157 assert_eq!(
158 mi.options_string(),
159 "rw,relatime,seclabel,compress=zstd:1,ssd,space_cache=v2,subvolid=256,subvol=/root"
160 .to_string()
161 );
162}