Skip to main content

rigtop_core/linux/
mounts.rs

1//! Mount table from `/proc/mounts`.
2
3use std::fs;
4
5use super::SysRoots;
6
7/// One line of the mount table.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct MountEntry {
10    /// Source device (e.g. `/dev/nvme1n1p1`).
11    pub device: String,
12    /// Mount point.
13    pub mount_point: String,
14    /// Filesystem type.
15    pub fs_type: String,
16}
17
18/// Reads `/proc/mounts`. Returns an empty list if it cannot be read.
19pub 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
25/// Parses mount table text.
26pub(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
39/// Decodes the octal escapes (`\040` for space, etc.) used in the mount table.
40fn 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        // An escape is a backslash followed by exactly three octal digits.
46        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}