1use std::path::PathBuf;
2use std::str::FromStr;
3
4use nix::mount::MsFlags;
5use nix::sys::stat::SFlag;
6use oci_spec::runtime::{LinuxDevice, LinuxDeviceBuilder, LinuxDeviceType, Mount};
7
8use super::mount::MountError;
9use crate::syscall::linux::{self, MountOption, MountRecursive};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct MountOptionConfig {
13 pub flags: MsFlags,
15
16 pub data: Vec<String>,
18
19 pub rec_attr: Option<linux::MountAttr>,
21}
22
23pub fn default_devices() -> Vec<LinuxDevice> {
24 vec![
25 LinuxDeviceBuilder::default()
26 .path(PathBuf::from("/dev/null"))
27 .typ(LinuxDeviceType::C)
28 .major(1)
29 .minor(3)
30 .file_mode(0o0666u32)
31 .build()
32 .unwrap(),
33 LinuxDeviceBuilder::default()
34 .path(PathBuf::from("/dev/zero"))
35 .typ(LinuxDeviceType::C)
36 .major(1)
37 .minor(5)
38 .file_mode(0o0666u32)
39 .build()
40 .unwrap(),
41 LinuxDeviceBuilder::default()
42 .path(PathBuf::from("/dev/full"))
43 .typ(LinuxDeviceType::C)
44 .major(1)
45 .minor(7)
46 .file_mode(0o0666u32)
47 .build()
48 .unwrap(),
49 LinuxDeviceBuilder::default()
50 .path(PathBuf::from("/dev/tty"))
51 .typ(LinuxDeviceType::C)
52 .major(5)
53 .minor(0)
54 .file_mode(0o0666u32)
55 .build()
56 .unwrap(),
57 LinuxDeviceBuilder::default()
58 .path(PathBuf::from("/dev/urandom"))
59 .typ(LinuxDeviceType::C)
60 .major(1)
61 .minor(9)
62 .file_mode(0o0666u32)
63 .build()
64 .unwrap(),
65 LinuxDeviceBuilder::default()
66 .path(PathBuf::from("/dev/random"))
67 .typ(LinuxDeviceType::C)
68 .major(1)
69 .minor(8)
70 .file_mode(0o0666u32)
71 .build()
72 .unwrap(),
73 ]
74}
75
76pub fn to_sflag(dev_type: LinuxDeviceType) -> SFlag {
77 match dev_type {
78 LinuxDeviceType::A => SFlag::S_IFBLK | SFlag::S_IFCHR | SFlag::S_IFIFO,
79 LinuxDeviceType::B => SFlag::S_IFBLK,
80 LinuxDeviceType::C | LinuxDeviceType::U => SFlag::S_IFCHR,
81 LinuxDeviceType::P => SFlag::S_IFIFO,
82 }
83}
84
85pub fn parse_mount(m: &Mount) -> std::result::Result<MountOptionConfig, MountError> {
86 let mut flags = MsFlags::empty();
87 let mut data = Vec::new();
88 let mut mount_attr: Option<linux::MountAttr> = None;
89
90 if let Some(options) = &m.options() {
91 for option in options {
92 if let Ok(mount_attr_option) = linux::MountRecursive::from_str(option.as_str()) {
93 let (is_clear, flag) = match mount_attr_option {
97 MountRecursive::Rdonly(is_clear, flag) => (is_clear, flag),
98 MountRecursive::Nosuid(is_clear, flag) => (is_clear, flag),
99 MountRecursive::Nodev(is_clear, flag) => (is_clear, flag),
100 MountRecursive::Noexec(is_clear, flag) => (is_clear, flag),
101 MountRecursive::Atime(is_clear, flag) => (is_clear, flag),
102 MountRecursive::Relatime(is_clear, flag) => (is_clear, flag),
103 MountRecursive::Noatime(is_clear, flag) => (is_clear, flag),
104 MountRecursive::StrictAtime(is_clear, flag) => (is_clear, flag),
105 MountRecursive::NoDiratime(is_clear, flag) => (is_clear, flag),
106 MountRecursive::Nosymfollow(is_clear, flag) => (is_clear, flag),
107 };
108
109 if mount_attr.is_none() {
110 mount_attr = Some(linux::MountAttr {
111 attr_set: 0,
112 attr_clr: 0,
113 propagation: 0,
114 userns_fd: 0,
115 });
116 }
117
118 if let Some(mount_attr) = &mut mount_attr {
119 if is_clear {
120 mount_attr.attr_clr |= flag;
121 } else {
122 mount_attr.attr_set |= flag;
123 if flag & linux::MOUNT_ATTR__ATIME == flag {
124 mount_attr.attr_clr |= linux::MOUNT_ATTR__ATIME;
128 }
129 }
130 }
131 continue;
132 }
133
134 if let Some((is_clear, flag)) = match MountOption::from_str(option.as_ref()) {
135 Ok(v) => match v {
136 MountOption::Defaults(is_clear, flag) => Some((is_clear, flag)),
137 MountOption::Ro(is_clear, flag) => Some((is_clear, flag)),
138 MountOption::Rw(is_clear, flag) => Some((is_clear, flag)),
139 MountOption::Suid(is_clear, flag) => Some((is_clear, flag)),
140 MountOption::Nosuid(is_clear, flag) => Some((is_clear, flag)),
141 MountOption::Dev(is_clear, flag) => Some((is_clear, flag)),
142 MountOption::Nodev(is_clear, flag) => Some((is_clear, flag)),
143 MountOption::Exec(is_clear, flag) => Some((is_clear, flag)),
144 MountOption::Noexec(is_clear, flag) => Some((is_clear, flag)),
145 MountOption::Sync(is_clear, flag) => Some((is_clear, flag)),
146 MountOption::Async(is_clear, flag) => Some((is_clear, flag)),
147 MountOption::Dirsync(is_clear, flag) => Some((is_clear, flag)),
148 MountOption::Remount(is_clear, flag) => Some((is_clear, flag)),
149 MountOption::Mand(is_clear, flag) => Some((is_clear, flag)),
150 MountOption::Nomand(is_clear, flag) => Some((is_clear, flag)),
151 MountOption::Atime(is_clear, flag) => Some((is_clear, flag)),
152 MountOption::Noatime(is_clear, flag) => Some((is_clear, flag)),
153 MountOption::Diratime(is_clear, flag) => Some((is_clear, flag)),
154 MountOption::Nodiratime(is_clear, flag) => Some((is_clear, flag)),
155 MountOption::Bind(is_clear, flag) => Some((is_clear, flag)),
156 MountOption::Rbind(is_clear, flag) => Some((is_clear, flag)),
157 MountOption::Unbindable(is_clear, flag) => Some((is_clear, flag)),
158 MountOption::Runbindable(is_clear, flag) => Some((is_clear, flag)),
159 MountOption::Private(is_clear, flag) => Some((is_clear, flag)),
160 MountOption::Rprivate(is_clear, flag) => Some((is_clear, flag)),
161 MountOption::Shared(is_clear, flag) => Some((is_clear, flag)),
162 MountOption::Rshared(is_clear, flag) => Some((is_clear, flag)),
163 MountOption::Slave(is_clear, flag) => Some((is_clear, flag)),
164 MountOption::Rslave(is_clear, flag) => Some((is_clear, flag)),
165 MountOption::Relatime(is_clear, flag) => Some((is_clear, flag)),
166 MountOption::Norelatime(is_clear, flag) => Some((is_clear, flag)),
167 MountOption::Strictatime(is_clear, flag) => Some((is_clear, flag)),
168 MountOption::Nostrictatime(is_clear, flag) => Some((is_clear, flag)),
169 },
170 Err(unknown) => {
171 if unknown == "idmap" || unknown == "ridmap" {
172 return Err(MountError::UnsupportedMountOption(unknown));
173 }
174 None
175 }
176 } {
177 if is_clear {
178 flags &= !flag;
179 } else {
180 flags |= flag;
181 }
182 continue;
183 }
184
185 data.push(option.as_str());
186 }
187 }
188 Ok(MountOptionConfig {
189 flags,
190 data: data.into_iter().map(|s| s.to_string()).collect(),
191 rec_attr: mount_attr,
192 })
193}
194
195#[cfg(test)]
196mod tests {
197 use anyhow::Result;
198 use oci_spec::runtime::MountBuilder;
199
200 use super::*;
201 use crate::syscall::linux::MountAttr;
202
203 #[test]
204 fn test_to_sflag() {
205 assert_eq!(
206 SFlag::S_IFBLK | SFlag::S_IFCHR | SFlag::S_IFIFO,
207 to_sflag(LinuxDeviceType::A)
208 );
209 assert_eq!(SFlag::S_IFBLK, to_sflag(LinuxDeviceType::B));
210 assert_eq!(SFlag::S_IFCHR, to_sflag(LinuxDeviceType::C));
211 assert_eq!(SFlag::S_IFCHR, to_sflag(LinuxDeviceType::U));
212 assert_eq!(SFlag::S_IFIFO, to_sflag(LinuxDeviceType::P));
213 }
214
215 #[test]
216 fn test_parse_mount() -> Result<()> {
217 let mount_option_config = parse_mount(
218 &MountBuilder::default()
219 .destination(PathBuf::from("/proc"))
220 .typ("proc")
221 .source(PathBuf::from("proc"))
222 .build()?,
223 )?;
224 assert_eq!(
225 MountOptionConfig {
226 flags: MsFlags::empty(),
227 data: vec![],
228 rec_attr: None,
229 },
230 mount_option_config
231 );
232
233 let mount_option_config = parse_mount(
234 &MountBuilder::default()
235 .destination(PathBuf::from("/dev"))
236 .typ("tmpfs")
237 .source(PathBuf::from("tmpfs"))
238 .options(vec![
239 "nosuid".to_string(),
240 "strictatime".to_string(),
241 "mode=755".to_string(),
242 "size=65536k".to_string(),
243 ])
244 .build()?,
245 )?;
246 assert_eq!(
247 MountOptionConfig {
248 flags: MsFlags::MS_NOSUID | MsFlags::MS_STRICTATIME,
249 data: vec!["mode=755".to_string(), "size=65536k".to_string()],
250 rec_attr: None,
251 },
252 mount_option_config
253 );
254
255 let mount_option_config = parse_mount(
256 &MountBuilder::default()
257 .destination(PathBuf::from("/dev/pts"))
258 .typ("devpts")
259 .source(PathBuf::from("devpts"))
260 .options(vec![
261 "nosuid".to_string(),
262 "noexec".to_string(),
263 "newinstance".to_string(),
264 "ptmxmode=0666".to_string(),
265 "mode=0620".to_string(),
266 "gid=5".to_string(),
267 ])
268 .build()
269 .unwrap(),
270 )?;
271 assert_eq!(
272 MountOptionConfig {
273 flags: MsFlags::MS_NOSUID | MsFlags::MS_NOEXEC,
274 data: vec![
275 "newinstance".to_string(),
276 "ptmxmode=0666".to_string(),
277 "mode=0620".to_string(),
278 "gid=5".to_string()
279 ],
280 rec_attr: None
281 },
282 mount_option_config
283 );
284
285 let mount_option_config = parse_mount(
286 &MountBuilder::default()
287 .destination(PathBuf::from("/dev/shm"))
288 .typ("tmpfs")
289 .source(PathBuf::from("shm"))
290 .options(vec![
291 "nosuid".to_string(),
292 "noexec".to_string(),
293 "nodev".to_string(),
294 "mode=1777".to_string(),
295 "size=65536k".to_string(),
296 ])
297 .build()?,
298 )?;
299 assert_eq!(
300 MountOptionConfig {
301 flags: MsFlags::MS_NOSUID | MsFlags::MS_NOEXEC | MsFlags::MS_NODEV,
302 data: vec!["mode=1777".to_string(), "size=65536k".to_string()],
303 rec_attr: None
304 },
305 mount_option_config
306 );
307
308 let mount_option_config = parse_mount(
309 &MountBuilder::default()
310 .destination(PathBuf::from("/dev/mqueue"))
311 .typ("mqueue")
312 .source(PathBuf::from("mqueue"))
313 .options(vec![
314 "nosuid".to_string(),
315 "noexec".to_string(),
316 "nodev".to_string(),
317 ])
318 .build()
319 .unwrap(),
320 )?;
321 assert_eq!(
322 MountOptionConfig {
323 flags: MsFlags::MS_NOSUID | MsFlags::MS_NOEXEC | MsFlags::MS_NODEV,
324 data: vec![],
325 rec_attr: None
326 },
327 mount_option_config
328 );
329
330 let mount_option_config = parse_mount(
331 &MountBuilder::default()
332 .destination(PathBuf::from("/sys"))
333 .typ("sysfs")
334 .source(PathBuf::from("sysfs"))
335 .options(vec![
336 "nosuid".to_string(),
337 "noexec".to_string(),
338 "nodev".to_string(),
339 "ro".to_string(),
340 ])
341 .build()?,
342 )?;
343 assert_eq!(
344 MountOptionConfig {
345 flags: MsFlags::MS_NOSUID
346 | MsFlags::MS_NOEXEC
347 | MsFlags::MS_NODEV
348 | MsFlags::MS_RDONLY,
349 data: vec![],
350 rec_attr: None,
351 },
352 mount_option_config
353 );
354
355 let mount_option_config = parse_mount(
356 &MountBuilder::default()
357 .destination(PathBuf::from("/sys/fs/cgroup"))
358 .typ("cgroup")
359 .source(PathBuf::from("cgroup"))
360 .options(vec![
361 "nosuid".to_string(),
362 "noexec".to_string(),
363 "nodev".to_string(),
364 "relatime".to_string(),
365 "ro".to_string(),
366 ])
367 .build()?,
368 )?;
369 assert_eq!(
370 MountOptionConfig {
371 flags: MsFlags::MS_NOSUID
372 | MsFlags::MS_NOEXEC
373 | MsFlags::MS_NODEV
374 | MsFlags::MS_RDONLY
375 | MsFlags::MS_RELATIME,
376 data: vec![],
377 rec_attr: None
378 },
379 mount_option_config,
380 );
381
382 let mount_option_config = parse_mount(
384 &MountBuilder::default()
385 .options(vec![
386 "defaults".to_string(),
387 "ro".to_string(),
388 "rw".to_string(),
389 "suid".to_string(),
390 "nosuid".to_string(),
391 "dev".to_string(),
392 "nodev".to_string(),
393 "exec".to_string(),
394 "noexec".to_string(),
395 "sync".to_string(),
396 "async".to_string(),
397 "dirsync".to_string(),
398 "remount".to_string(),
399 "mand".to_string(),
400 "nomand".to_string(),
401 "atime".to_string(),
402 "noatime".to_string(),
403 "diratime".to_string(),
404 "nodiratime".to_string(),
405 "bind".to_string(),
406 "rbind".to_string(),
407 "unbindable".to_string(),
408 "runbindable".to_string(),
409 "private".to_string(),
410 "rprivate".to_string(),
411 "shared".to_string(),
412 "rshared".to_string(),
413 "slave".to_string(),
414 "rslave".to_string(),
415 "relatime".to_string(),
416 "norelatime".to_string(),
417 "strictatime".to_string(),
418 "nostrictatime".to_string(),
419 ])
420 .build()?,
421 )?;
422 assert_eq!(
423 MountOptionConfig {
424 flags: MsFlags::MS_NOSUID
425 | MsFlags::MS_NODEV
426 | MsFlags::MS_NOEXEC
427 | MsFlags::MS_REMOUNT
428 | MsFlags::MS_DIRSYNC
429 | MsFlags::MS_NOATIME
430 | MsFlags::MS_NODIRATIME
431 | MsFlags::MS_BIND
432 | MsFlags::MS_UNBINDABLE,
433 data: vec![],
434 rec_attr: None,
435 },
436 mount_option_config
437 );
438
439 let mount_option_config = parse_mount(
441 &MountBuilder::default()
442 .options(vec![
443 "rro".to_string(),
444 "rrw".to_string(),
445 "rnosuid".to_string(),
446 "rsuid".to_string(),
447 "rnodev".to_string(),
448 "rdev".to_string(),
449 "rnoexec".to_string(),
450 "rexec".to_string(),
451 "rnodiratime".to_string(),
452 "rdiratime".to_string(),
453 "rrelatime".to_string(),
454 "rnorelatime".to_string(),
455 "rnoatime".to_string(),
456 "ratime".to_string(),
457 "rstrictatime".to_string(),
458 "rnostrictatime".to_string(),
459 "rnosymfollow".to_string(),
460 "rsymfollow".to_string(),
461 ])
462 .build()?,
463 )?;
464 assert_eq!(
465 MountOptionConfig {
466 flags: MsFlags::empty(),
467 data: vec![],
468 rec_attr: Some(MountAttr::all())
469 },
470 mount_option_config
471 );
472
473 Ok(())
474 }
475}