libcontainer/container/
init_builder.rs1use std::fs;
2use std::path::{Path, PathBuf};
3use std::rc::Rc;
4
5use oci_spec::runtime::Spec;
6use user_ns::UserNamespaceConfig;
7
8use super::builder::ContainerBuilder;
9use super::builder_impl::ContainerBuilderImpl;
10use super::{Container, ContainerStatus};
11use crate::config::YoukiConfig;
12use crate::error::{ErrInvalidSpec, LibcontainerError, MissingSpecError};
13use crate::notify_socket::NOTIFY_FILE;
14use crate::process::args::ContainerType;
15use crate::syscall::syscall::create_syscall;
16use crate::{apparmor, tty, user_ns, utils};
17
18pub struct InitContainerBuilder {
20 base: ContainerBuilder,
21 bundle: PathBuf,
22 use_systemd: bool,
23 detached: bool,
24 no_pivot: bool,
25 as_sibling: bool,
26}
27
28impl InitContainerBuilder {
29 pub(super) fn new(builder: ContainerBuilder, bundle: PathBuf) -> Self {
32 Self {
33 base: builder,
34 bundle,
35 use_systemd: true,
36 detached: true,
37 no_pivot: false,
38 as_sibling: false,
39 }
40 }
41
42 pub fn with_systemd(mut self, should_use: bool) -> Self {
44 self.use_systemd = should_use;
45 self
46 }
47
48 pub fn as_sibling(mut self, as_sibling: bool) -> Self {
51 self.as_sibling = as_sibling;
52 self
53 }
54
55 pub fn with_detach(mut self, detached: bool) -> Self {
56 self.detached = detached;
57 self
58 }
59
60 pub fn with_no_pivot(mut self, no_pivot: bool) -> Self {
61 self.no_pivot = no_pivot;
62 self
63 }
64
65 pub fn build(self) -> Result<Container, LibcontainerError> {
67 let spec = self.load_spec()?;
68 let container_dir = self.create_container_dir()?;
69
70 let mut container = self.create_container_state(&container_dir)?;
71 container
72 .set_systemd(self.use_systemd)
73 .set_annotations(spec.annotations().clone());
74
75 let notify_path = container_dir.join(NOTIFY_FILE);
76 let rootfs = fs::canonicalize(spec.root().as_ref().ok_or(MissingSpecError::Root)?.path())
78 .map_err(LibcontainerError::OtherIO)?;
79
80 let csocketfd = if let Some(console_socket) = &self.base.console_socket {
83 Some(tty::setup_console_socket(
84 &container_dir,
85 console_socket,
86 "console-socket",
87 )?)
88 } else {
89 None
90 };
91
92 let user_ns_config = UserNamespaceConfig::new(&spec)?;
93
94 let config = YoukiConfig::from_spec(&spec, container.id())?;
95 config.save(&container_dir).map_err(|err| {
96 tracing::error!(?container_dir, "failed to save config: {}", err);
97 err
98 })?;
99
100 let mut builder_impl = ContainerBuilderImpl {
101 container_type: ContainerType::InitContainer,
102 syscall: self.base.syscall,
103 container_id: self.base.container_id,
104 pid_file: self.base.pid_file,
105 console_socket: csocketfd,
106 use_systemd: self.use_systemd,
107 spec: Rc::new(spec),
108 rootfs,
109 user_ns_config,
110 notify_path,
111 container: Some(container.clone()),
112 preserve_fds: self.base.preserve_fds,
113 detached: self.detached,
114 executor: self.base.executor,
115 no_pivot: self.no_pivot,
116 stdin: self.base.stdin,
117 stdout: self.base.stdout,
118 stderr: self.base.stderr,
119 as_sibling: self.as_sibling,
120 sub_cgroup_path: None,
121 process_label: None,
122 };
123
124 builder_impl.create()?;
125
126 container.refresh_state()?;
127
128 Ok(container)
129 }
130
131 fn create_container_dir(&self) -> Result<PathBuf, LibcontainerError> {
132 let container_dir = self.base.root_path.join(&self.base.container_id);
133 tracing::debug!("container directory will be {:?}", container_dir);
134
135 if container_dir.exists() {
136 tracing::error!(id = self.base.container_id, dir = ?container_dir, "container already exists");
137 return Err(LibcontainerError::Exist);
138 }
139
140 std::fs::create_dir_all(&container_dir).map_err(|err| {
141 tracing::error!(
142 ?container_dir,
143 "failed to create container directory: {}",
144 err
145 );
146 LibcontainerError::OtherIO(err)
147 })?;
148
149 Ok(container_dir)
150 }
151
152 fn load_spec(&self) -> Result<Spec, LibcontainerError> {
153 let source_spec_path = self.bundle.join("config.json");
154 let mut spec = Spec::load(source_spec_path)?;
155 Self::validate_spec(&spec)?;
156
157 spec.canonicalize_rootfs(&self.bundle).map_err(|err| {
158 tracing::error!(bundle = ?self.bundle, "failed to canonicalize rootfs: {}", err);
159 err
160 })?;
161
162 Ok(spec)
163 }
164
165 fn validate_spec(spec: &Spec) -> Result<(), LibcontainerError> {
166 let version = spec.version();
167 if !version.starts_with("1.") {
168 tracing::error!(
169 "runtime spec has incompatible version '{}'. Only 1.X.Y is supported",
170 spec.version()
171 );
172 Err(ErrInvalidSpec::UnsupportedVersion)?;
173 }
174
175 if let Some(process) = spec.process() {
176 if let Some(profile) = process.apparmor_profile() {
177 let apparmor_is_enabled = apparmor::is_enabled().map_err(|err| {
178 tracing::error!(?err, "failed to check if apparmor is enabled");
179 LibcontainerError::OtherIO(err)
180 })?;
181 if !apparmor_is_enabled {
182 tracing::error!(
183 ?profile,
184 "apparmor profile exists in the spec, but apparmor is not activated on this system"
185 );
186 Err(ErrInvalidSpec::AppArmorNotEnabled)?;
187 }
188 }
189
190 if let Some(io_priority) = process.io_priority() {
191 let priority = io_priority.priority();
192 let iop_class_res = serde_json::to_string(&io_priority.class());
193 match iop_class_res {
194 Ok(iop_class) => {
195 if !(0..=7).contains(&priority) {
196 tracing::error!(
197 ?priority,
198 "io priority '{}' not between 0 and 7 (inclusive), class '{}' not in (IO_PRIO_CLASS_RT,IO_PRIO_CLASS_BE,IO_PRIO_CLASS_IDLE)",
199 priority,
200 iop_class
201 );
202 Err(ErrInvalidSpec::IoPriority)?;
203 }
204 }
205 Err(e) => {
206 tracing::error!(?priority, ?e, "failed to parse io priority class");
207 Err(ErrInvalidSpec::IoPriority)?;
208 }
209 }
210 }
211 }
212
213 if let Some(mounts) = spec.mounts() {
214 utils::validate_mount_options(mounts)?;
215 }
216
217 let syscall = create_syscall();
218 utils::validate_spec_for_new_user_ns(spec, &*syscall)?;
219 utils::validate_spec_for_net_devices(spec, &*syscall)
220 .map_err(LibcontainerError::NetDevicesError)?;
221
222 Ok(())
223 }
224
225 fn create_container_state(&self, container_dir: &Path) -> Result<Container, LibcontainerError> {
226 let container = Container::new(
227 &self.base.container_id,
228 ContainerStatus::Creating,
229 None,
230 &self.bundle,
231 container_dir,
232 )?;
233 container.save()?;
234 Ok(container)
235 }
236}