Skip to main content

libcontainer/rootfs/
rootfs.rs

1use 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
16/// Holds information about rootfs
17pub 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    /// Change propagation type of rootfs as specified in spec.
129    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}