linux_mount_options/
lib.rs1#[macro_use]
2extern crate log;
3
4use std::path::{Path, PathBuf};
5
6#[derive(Debug, PartialEq, Eq)]
7pub struct MountInfo {
8 mount_point: PathBuf,
9 filesystem: FileSystems,
10 options: MountOptions,
11}
12
13impl MountInfo {
14 pub fn is_local(&self) -> bool {
15 use FileSystems::*;
16 match self.filesystem {
17 Cifs | Nfs => false,
18 _ => true,
19 }
20 }
21
22 pub fn is_case_sensitive(&self) -> bool {
23 use FileSystems::*;
24 match self.filesystem {
25 Cifs => false,
26 _ => true,
27 }
28 }
29}
30
31#[derive(Debug, PartialEq, Eq)]
32pub enum MountOptions {
33 RelATime,
34 NoATime,
35 Unknown,
36}
37
38#[derive(Debug, PartialEq, Eq)]
39pub enum FileSystems {
40 Btrfs,
41 Cifs,
42 Ext2,
43 Ext3,
44 Ext4,
45 Nfs,
46 Sysfs,
47 Sshfs,
48 Tmpfs,
49 Fat,
50 Unknown(String),
51}
52
53fn parse_fs(fs: &str) -> FileSystems {
54 use FileSystems::*;
55 match fs.trim() {
56 "btrfs" => Btrfs,
57 "cifs" => Cifs,
58 "ext2" => Ext2,
59 "ext3" => Ext3,
60 "ext4" => Ext4,
61 "nfs" | "nfs4" => Nfs,
62 "sshfs" | "fuse.sshfs" => Sshfs,
63 "sysfs" => Sysfs,
64 "tmpfs" => Tmpfs,
65 "vfat" => Fat,
66 u => {
67 warn!("unknown filesystem {:?}, please sumbmit a PR/MR", u);
68 Unknown(fs.to_owned())
69 }
70 }
71}
72
73pub fn detect<P: AsRef<Path>>(target_dir: P) -> std::io::Result<MountInfo> {
77 let target_dir = target_dir.as_ref();
78 let target_dir = target_dir.canonicalize()?;
79 let target_dir = target_dir
80 .components()
81 .map(|c| Some(c))
82 .chain(std::iter::once(None));
83
84 let mounts = &std::fs::read("/proc/mounts")?;
85 let mounts = String::from_utf8_lossy(mounts);
86 let root = Path::new("/");
87
88 let mut mounted_filesystem = None;
89 let mut longest_match = 0;
90
91 for line in mounts.lines() {
92 let mut parts = line.trim().split(" ");
94
95 parts.next();
97
98 let mut mount_point = parts.collect::<Vec<_>>();
100 let filesystem = mount_point[1];
101 let mount_options = mount_point[mount_point.len() - 3].split(',');
102 mount_point.truncate(mount_point.len() - 4);
103
104 let mount_point = mount_point.join(" ");
105 let mount_point = Path::new(&mount_point);
106
107 let mut matching_parts = 0;
108 for (o, mp) in target_dir.clone().zip(mount_point.components()) {
109 if let Some(o) = o {
110 if o == mp {
111 matching_parts += 1;
112 } else {
113 matching_parts = 0;
114 break;
115 }
116 } else {
117 matching_parts = 0;
118 break;
119 }
120 }
121 if (mount_point == root && longest_match <= 1)
122 || (mount_point != root && matching_parts > longest_match)
123 {
124 longest_match = matching_parts;
125 mounted_filesystem =
126 Some((mount_point.to_owned(), parse_fs(filesystem), mount_options));
127 }
128 }
129
130 let (mount_point, filesystem, options) = if let Some((mount_point, filesystem, mount_options)) =
131 mounted_filesystem
132 {
133 if mount_options.clone().any(|a| a == "relatime") {
134 warn!("the target_dir is mounted on a filesystem \"{}\" with the \"relatime\" attribute! This can lead to problems with fsfreeze. Consider mounting it with \"noatime\"", mount_point.display());
135 (mount_point, filesystem, MountOptions::RelATime)
136 } else if mount_options.clone().any(|a| a == "noatime") {
137 (mount_point, filesystem, MountOptions::NoATime)
138 } else {
139 warn!(
140 "neither relatime nor noatime found: {}",
141 mount_options.collect::<Vec<_>>().join(",")
142 );
143 (mount_point, filesystem, MountOptions::Unknown)
144 }
145 } else {
146 warn!("unable to find mounted filesystem");
147 (
148 PathBuf::from("/"),
149 FileSystems::Unknown("<not found>".to_string()),
150 MountOptions::Unknown,
151 )
152 };
153
154 Ok(MountInfo {
155 mount_point,
156 filesystem,
157 options,
158 })
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn root() {
167 let info = detect("/").unwrap();
168 assert_eq!(PathBuf::from("/"), info.mount_point);
169 }
170
171 #[test]
172 fn path_ref() {
173 let _ = detect(&Path::new("/"));
174 }
175
176 #[test]
177 fn path_buf_ref() {
178 use std::path::PathBuf;
179
180 let _ = detect(&PathBuf::from("/"));
181 }
182
183 #[test]
184 fn path() {
185 let _ = detect(Path::new("/"));
186 }
187
188 #[test]
189 fn path_buf() {
190 use std::path::PathBuf;
191
192 let _ = detect(PathBuf::from("/"));
193 }
194
195 #[test]
196 fn str() {
197 let _ = detect("/");
198 }
199
200 #[test]
201 fn string() {
202 let _ = detect("/".to_string());
203 }
204}