libcontainer/rootfs/
rootfs.rs1use std::collections::HashSet;
2use std::path::Path;
3
4use nix::mount::MsFlags;
5use oci_spec::runtime::{Linux, Spec};
6
7use super::device::Device;
8use super::mount::{Mount, MountOptions};
9use super::symlink::Symlink;
10use super::utils::default_devices;
11use super::{Result, RootfsError};
12use crate::error::MissingSpecError;
13use crate::syscall::Syscall;
14use crate::syscall::syscall::create_syscall;
15
16pub struct RootFS {
18 syscall: Box<dyn Syscall>,
19}
20
21impl Default for RootFS {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl RootFS {
28 pub fn new() -> RootFS {
29 RootFS {
30 syscall: create_syscall(),
31 }
32 }
33
34 pub fn mount_to_rootfs(
35 &self,
36 linux: &Linux,
37 spec: &Spec,
38 rootfs: &Path,
39 cgroup_ns: bool,
40 ) -> Result<()> {
41 let mut flags = MsFlags::MS_REC;
42 match linux.rootfs_propagation().as_deref() {
43 Some("shared") => flags |= MsFlags::MS_SHARED,
44 Some("private") => flags |= MsFlags::MS_PRIVATE,
45 Some("slave" | "unbindable") | None => flags |= MsFlags::MS_SLAVE,
46 Some(unknown) => {
47 return Err(RootfsError::UnknownRootfsPropagation(unknown.to_string()));
48 }
49 }
50
51 self.syscall
52 .mount(None, Path::new("/"), None, flags, None)
53 .map_err(|err| {
54 tracing::error!(
55 ?err,
56 ?flags,
57 "failed to change the mount propagation type of the root"
58 );
59
60 err
61 })?;
62
63 let mounter = Mount::new();
64
65 mounter.make_parent_mount_private(rootfs)?;
66
67 tracing::debug!("mount root fs {:?}", rootfs);
68 self.syscall
69 .mount(
70 Some(rootfs),
71 rootfs,
72 None,
73 MsFlags::MS_BIND | MsFlags::MS_REC,
74 None,
75 )
76 .map_err(|err| {
77 tracing::error!(?rootfs, ?err, "failed to bind mount rootfs");
78 err
79 })?;
80
81 let global_options = MountOptions {
82 root: rootfs,
83 label: linux.mount_label().as_deref(),
84 cgroup_ns,
85 };
86
87 if let Some(mounts) = spec.mounts() {
88 for mount in mounts {
89 mounter.setup_mount(mount, &global_options)?;
90 }
91 }
92 Ok(())
93 }
94
95 pub fn prepare_rootfs(
96 &self,
97 spec: &Spec,
98 rootfs: &Path,
99 bind_devices: bool,
100 cgroup_ns: bool,
101 ) -> Result<()> {
102 tracing::debug!(?rootfs, "prepare rootfs");
103 let linux = spec.linux().as_ref().ok_or(MissingSpecError::Linux)?;
104
105 self.mount_to_rootfs(linux, spec, rootfs, cgroup_ns)?;
106
107 let symlinker = Symlink::new();
108 symlinker.setup_kcore_symlink(rootfs)?;
109 symlinker.setup_default_symlinks(rootfs)?;
110
111 let devicer = Device::new();
112 if let Some(added_devices) = linux.devices() {
113 let mut path_set = HashSet::new();
114 let devices = default_devices();
115 added_devices.iter().for_each(|d| {
116 path_set.insert(d.path());
117 });
118 let default = devices.iter().filter(|d| !path_set.contains(d.path()));
119 devicer.create_devices(rootfs, added_devices.iter().chain(default), bind_devices)
120 } else {
121 devicer.create_devices(rootfs, &default_devices(), bind_devices)
122 }?;
123
124 symlinker.setup_ptmx(rootfs)?;
125 Ok(())
126 }
127
128 pub fn adjust_root_mount_propagation(&self, linux: &Linux) -> Result<()> {
130 let rootfs_propagation = linux.rootfs_propagation().as_deref();
131 let flags = match rootfs_propagation {
132 Some("shared") => Some(MsFlags::MS_SHARED),
133 Some("unbindable") => Some(MsFlags::MS_UNBINDABLE),
134 _ => None,
135 };
136
137 if let Some(flags) = flags {
138 self.syscall
139 .mount(None, Path::new("/"), None, flags, None)
140 .map_err(|err| {
141 tracing::error!(
142 ?err,
143 ?flags,
144 "failed to adjust the mount propagation type of the root"
145 );
146 err
147 })?;
148 }
149
150 Ok(())
151 }
152}