nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1use std::fs;
2
3use anyhow::{Context, Result};
4
5pub struct Mounts {
6 inner: Vec<Mount>,
7}
8
9#[derive(Debug)]
10pub struct Mount {
11 _spec: String,
12 file: String,
13 _vfstype: String,
14 pub mntopts: MntOpts,
15}
16
17#[derive(Debug)]
18pub struct MntOpts {
19 inner: Vec<String>,
20}
21
22impl Mounts {
23 pub fn parse_from_proc_mounts() -> Result<Self> {
24 let proc_mounts =
25 fs::read_to_string("/proc/mounts").context("Failed to read /proc/mounts")?;
26 Self::parse(&proc_mounts)
27 }
28
29 fn parse(s: &str) -> Result<Self> {
30 let mut inner = Vec::new();
31 for line in s.lines() {
32 let mut split = line.split_whitespace();
33 let mount = Mount {
34 _spec: split.next().context("Failed to parse spec")?.to_string(),
35 file: split.next().context("Failed to parse file")?.to_string(),
36 _vfstype: split.next().context("Failed to parse vfstype")?.to_string(),
37 mntopts: MntOpts::parse(split.next().context("Failed to parse mntopts")?),
38 };
39 inner.push(mount);
40 }
41 Ok(Self { inner })
42 }
43
44 pub fn find_mountpoint(&self, mountpoint: &str) -> Option<&Mount> {
45 self.inner.iter().rev().find(|m| m.file == mountpoint)
46 }
47}
48
49impl MntOpts {
50 fn parse(s: &str) -> Self {
51 let mut vec = Vec::new();
52 for sp in s.split(',') {
53 vec.push(sp.to_string());
54 }
55
56 Self { inner: vec }
57 }
58
59 pub fn contains(&self, s: &str) -> bool {
60 self.inner.contains(&s.to_string())
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 use indoc::indoc;
69
70 #[test]
71 fn test_proc_mounts_parsing() -> Result<()> {
72 let s = indoc! {r"
73 /dev/mapper/root / btrfs rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/ 0 0
74 tmpfs /run tmpfs rw,nosuid,nodev,size=15350916k,nr_inodes=819200,mode=755 0 0
75 devtmpfs /dev devtmpfs rw,nosuid,size=3070184k,nr_inodes=7671201,mode=755 0 0
76 devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=3,mode=620,ptmxmode=666 0 0
77 tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0
78 proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
79 ramfs /run/keys ramfs rw,nosuid,nodev,relatime,mode=750 0 0
80 sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
81 /dev/mapper/root /nix/store btrfs ro,nosuid,nodev,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/ 0 0
82 securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
83 cgroup2 /sys/fs/cgroup cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot 0 0
84 none /sys/fs/pstore pstore rw,nosuid,nodev,noexec,relatime 0 0
85 efivarfs /sys/firmware/efi/efivars efivarfs rw,nosuid,nodev,noexec,relatime 0 0
86 bpf /sys/fs/bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700 0 0
87 hugetlbfs /dev/hugepages hugetlbfs rw,nosuid,nodev,relatime,pagesize=2M 0 0
88 mqueue /dev/mqueue mqueue rw,nosuid,nodev,noexec,relatime 0 0
89 debugfs /sys/kernel/debug debugfs rw,nosuid,nodev,noexec,relatime 0 0
90 tracefs /sys/kernel/tracing tracefs rw,nosuid,nodev,noexec,relatime 0 0
91 "};
92
93 let mounts = Mounts::parse(s)?;
94 if let Some(nix_store_mount) = mounts.find_mountpoint("/nix/store") {
95 println!("{nix_store_mount:?}");
96 println!("{:?}", nix_store_mount.mntopts);
97 assert!(nix_store_mount.mntopts.contains("ro"));
98 assert!(!nix_store_mount.mntopts.contains("no"));
99 }
100
101 Ok(())
102 }
103}