rigtop_core/linux/
mounts.rs1use std::fs;
4
5use super::SysRoots;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct MountEntry {
10 pub device: String,
12 pub mount_point: String,
14 pub fs_type: String,
16}
17
18pub fn read_mounts(roots: &SysRoots) -> Vec<MountEntry> {
20 fs::read_to_string(roots.proc.join("mounts"))
21 .map(|text| parse(&text))
22 .unwrap_or_default()
23}
24
25pub(crate) fn parse(text: &str) -> Vec<MountEntry> {
27 text.lines()
28 .filter_map(|line| {
29 let mut fields = line.split_whitespace();
30 Some(MountEntry {
31 device: unescape(fields.next()?),
32 mount_point: unescape(fields.next()?),
33 fs_type: fields.next()?.to_owned(),
34 })
35 })
36 .collect()
37}
38
39fn unescape(field: &str) -> String {
41 let bytes = field.as_bytes();
42 let mut out = Vec::with_capacity(bytes.len());
43 let mut i = 0;
44 while i < bytes.len() {
45 if bytes[i] == b'\\' && i + 4 <= bytes.len() {
47 let octal = std::str::from_utf8(&bytes[i + 1..i + 4]).ok();
48 if let Some(value) = octal.and_then(|o| u8::from_str_radix(o, 8).ok()) {
49 out.push(value);
50 i += 4;
51 continue;
52 }
53 }
54 out.push(bytes[i]);
55 i += 1;
56 }
57 String::from_utf8_lossy(&out).into_owned()
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn parses_fields_and_escapes() {
66 let entries = parse(
67 "/dev/nvme1n1p1 / btrfs rw,relatime 0 0\n/dev/sda1 /mnt/my\\040disk ext4 rw 0 0\n",
68 );
69 assert_eq!(entries.len(), 2);
70 assert_eq!(entries[0].fs_type, "btrfs");
71 assert_eq!(entries[1].mount_point, "/mnt/my disk");
72 }
73}