1use crate::error::OciSpecError;
2use crate::runtime::LinuxIdMapping;
3use derive_builder::Builder;
4use getset::{CopyGetters, Getters, MutGetters, Setters};
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8#[derive(
9 Builder, Clone, CopyGetters, Debug, Deserialize, Eq, Getters, Setters, PartialEq, Serialize,
10)]
11#[builder(
12 default,
13 pattern = "owned",
14 setter(into, strip_option),
15 build_fn(error = "OciSpecError")
16)]
17pub struct Root {
20 #[serde(default)]
22 #[getset(get = "pub", set = "pub")]
23 path: PathBuf,
24
25 #[serde(default, skip_serializing_if = "Option::is_none")]
28 #[getset(get_copy = "pub", set = "pub")]
29 readonly: Option<bool>,
30}
31
32impl Default for Root {
35 fn default() -> Self {
36 Root {
37 path: PathBuf::from("rootfs"),
38 readonly: true.into(),
39 }
40 }
41}
42
43#[derive(
44 Builder,
45 Clone,
46 Debug,
47 Default,
48 Deserialize,
49 Eq,
50 Getters,
51 MutGetters,
52 Setters,
53 PartialEq,
54 Serialize,
55)]
56#[builder(
57 default,
58 pattern = "owned",
59 setter(into, strip_option),
60 build_fn(error = "OciSpecError", validate = "Self::validate")
61)]
62#[getset(get_mut = "pub", get = "pub", set = "pub")]
63pub struct Mount {
65 destination: PathBuf,
68
69 #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
70 typ: Option<String>,
72
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 source: Option<PathBuf>,
76
77 #[serde(default, skip_serializing_if = "Option::is_none")]
78 options: Option<Vec<String>>,
80
81 #[serde(
82 default,
83 skip_serializing_if = "Option::is_none",
84 rename = "uidMappings"
85 )]
86 uid_mappings: Option<Vec<LinuxIdMapping>>,
96
97 #[serde(
98 default,
99 skip_serializing_if = "Option::is_none",
100 rename = "gidMappings"
101 )]
102 gid_mappings: Option<Vec<LinuxIdMapping>>,
112}
113
114pub fn get_default_mounts() -> Vec<Mount> {
116 vec![
117 Mount {
118 destination: PathBuf::from("/proc"),
119 typ: "proc".to_string().into(),
120 source: PathBuf::from("proc").into(),
121 options: None,
122 uid_mappings: None,
123 gid_mappings: None,
124 },
125 Mount {
126 destination: PathBuf::from("/dev"),
127 typ: "tmpfs".to_string().into(),
128 source: PathBuf::from("tmpfs").into(),
129 options: vec![
130 "nosuid".into(),
131 "strictatime".into(),
132 "mode=755".into(),
133 "size=65536k".into(),
134 ]
135 .into(),
136 uid_mappings: None,
137 gid_mappings: None,
138 },
139 Mount {
140 destination: PathBuf::from("/dev/pts"),
141 typ: "devpts".to_string().into(),
142 source: PathBuf::from("devpts").into(),
143 options: vec![
144 "nosuid".into(),
145 "noexec".into(),
146 "newinstance".into(),
147 "ptmxmode=0666".into(),
148 "mode=0620".into(),
149 "gid=5".into(),
150 ]
151 .into(),
152 uid_mappings: None,
153 gid_mappings: None,
154 },
155 Mount {
156 destination: PathBuf::from("/dev/shm"),
157 typ: "tmpfs".to_string().into(),
158 source: PathBuf::from("shm").into(),
159 options: vec![
160 "nosuid".into(),
161 "noexec".into(),
162 "nodev".into(),
163 "mode=1777".into(),
164 "size=65536k".into(),
165 ]
166 .into(),
167 uid_mappings: None,
168 gid_mappings: None,
169 },
170 Mount {
171 destination: PathBuf::from("/dev/mqueue"),
172 typ: "mqueue".to_string().into(),
173 source: PathBuf::from("mqueue").into(),
174 options: vec!["nosuid".into(), "noexec".into(), "nodev".into()].into(),
175 uid_mappings: None,
176 gid_mappings: None,
177 },
178 Mount {
179 destination: PathBuf::from("/sys"),
180 typ: "sysfs".to_string().into(),
181 source: PathBuf::from("sysfs").into(),
182 options: vec![
183 "nosuid".into(),
184 "noexec".into(),
185 "nodev".into(),
186 "ro".into(),
187 ]
188 .into(),
189 uid_mappings: None,
190 gid_mappings: None,
191 },
192 Mount {
193 destination: PathBuf::from("/sys/fs/cgroup"),
194 typ: "cgroup".to_string().into(),
195 source: PathBuf::from("cgroup").into(),
196 options: vec![
197 "nosuid".into(),
198 "noexec".into(),
199 "nodev".into(),
200 "relatime".into(),
201 "ro".into(),
202 ]
203 .into(),
204 uid_mappings: None,
205 gid_mappings: None,
206 },
207 ]
208}
209
210impl MountBuilder {
211 fn validate(&self) -> Result<(), OciSpecError> {
212 let uid_specified = self
213 .uid_mappings
214 .as_ref()
215 .and_then(|v| v.as_ref())
216 .map(|v| !v.is_empty())
217 .unwrap_or(false);
218
219 let gid_specified = self
220 .gid_mappings
221 .as_ref()
222 .and_then(|v| v.as_ref())
223 .map(|v| !v.is_empty())
224 .unwrap_or(false);
225
226 if uid_specified ^ gid_specified {
227 return Err(OciSpecError::Other(
228 "Mount.uidMappings and Mount.gidMappings must be specified together".to_string(),
229 ));
230 }
231
232 Ok(())
233 }
234}
235
236#[allow(clippy::manual_inspect)]
241pub fn get_rootless_mounts() -> Vec<Mount> {
242 let mut mounts = get_default_mounts();
243 mounts
244 .iter_mut()
245 .find(|m| m.destination.to_string_lossy() == "/dev/pts")
246 .map(|m| {
247 if let Some(opts) = &mut m.options {
248 opts.retain(|o| o != "gid=5")
249 }
250 m
251 });
252 mounts
253 .iter_mut()
254 .find(|m| m.destination.to_string_lossy() == "/sys")
255 .map(|m| {
256 m.typ = Some("none".to_string());
257 m.source = Some("/sys".into());
258 if let Some(o) = m.options.as_mut() {
259 o.push("rbind".to_string())
260 }
261 m
262 });
263 mounts
264}