use anyhow::{Context, Result};
use nix::mount::{self, MntFlags, MsFlags};
use nix::unistd;
use oci_spec::runtime::Mount;
use std::ffi::OsString;
use std::fs;
use std::path::Path;
pub fn mount_rootfs(rootfs: &Path) -> Result<()> {
mount::mount(
None::<&str>,
"/",
None::<&str>,
MsFlags::MS_PRIVATE | MsFlags::MS_REC,
None::<&str>,
)
.context("failed to change the propagation type of the root mount to private")?;
mount::mount(
Some(rootfs),
rootfs,
None::<&str>,
MsFlags::MS_BIND | MsFlags::MS_REC,
None::<&str>,
)
.context("failed to remount the root mount")?;
Ok(())
}
pub fn pivot_rootfs(rootfs: &Path) -> Result<()> {
unistd::chdir(rootfs).context("failed to invoke chdir")?;
fs::create_dir_all(rootfs.join("root_archive")).context("failed to create ./root_archive")?;
unistd::pivot_root(rootfs.as_os_str(), rootfs.join("root_archive").as_os_str())
.context("failed to invoke pivot_root")?;
mount::umount2("./root_archive", MntFlags::MNT_DETACH)
.context("failed to umount ./root_archive")?;
fs::remove_dir_all("./root_archive").context("failed to remove ./root_archive")?;
unistd::chdir("/").context("failed to invoke chdir")?;
Ok(())
}
fn mount_to_msflags(mount: &Mount) -> (MsFlags, OsString) {
let mut mount_flags = MsFlags::empty();
let mut mount_data = Vec::new();
if let Some(options) = &mount.options() {
for option in options {
if let Some((is_clear, flag)) = match option.as_str() {
"defaults" => Some((false, MsFlags::empty())),
"ro" => Some((false, MsFlags::MS_RDONLY)),
"rw" => Some((true, MsFlags::MS_RDONLY)),
"suid" => Some((true, MsFlags::MS_NOSUID)),
"nosuid" => Some((false, MsFlags::MS_NOSUID)),
"dev" => Some((true, MsFlags::MS_NODEV)),
"nodev" => Some((false, MsFlags::MS_NODEV)),
"exec" => Some((true, MsFlags::MS_NOEXEC)),
"noexec" => Some((false, MsFlags::MS_NOEXEC)),
"sync" => Some((false, MsFlags::MS_SYNCHRONOUS)),
"async" => Some((true, MsFlags::MS_SYNCHRONOUS)),
"dirsync" => Some((false, MsFlags::MS_DIRSYNC)),
"remount" => Some((false, MsFlags::MS_REMOUNT)),
"mand" => Some((false, MsFlags::MS_MANDLOCK)),
"nomand" => Some((true, MsFlags::MS_MANDLOCK)),
"atime" => Some((true, MsFlags::MS_NOATIME)),
"noatime" => Some((false, MsFlags::MS_NOATIME)),
"diratime" => Some((true, MsFlags::MS_NODIRATIME)),
"nodiratime" => Some((false, MsFlags::MS_NODIRATIME)),
"bind" => Some((false, MsFlags::MS_BIND)),
"rbind" => Some((false, MsFlags::MS_BIND | MsFlags::MS_REC)),
"unbindable" => Some((false, MsFlags::MS_UNBINDABLE)),
"runbindable" => Some((false, MsFlags::MS_UNBINDABLE | MsFlags::MS_REC)),
"private" => Some((true, MsFlags::MS_PRIVATE)),
"rprivate" => Some((true, MsFlags::MS_PRIVATE | MsFlags::MS_REC)),
"shared" => Some((true, MsFlags::MS_SHARED)),
"rshared" => Some((true, MsFlags::MS_SHARED | MsFlags::MS_REC)),
"slave" => Some((true, MsFlags::MS_SLAVE)),
"rslave" => Some((true, MsFlags::MS_SLAVE | MsFlags::MS_REC)),
"relatime" => Some((true, MsFlags::MS_RELATIME)),
"norelatime" => Some((true, MsFlags::MS_RELATIME)),
"strictatime" => Some((true, MsFlags::MS_STRICTATIME)),
"nostrictatime" => Some((true, MsFlags::MS_STRICTATIME)),
_ => None,
} {
if is_clear {
mount_flags &= !flag;
} else {
mount_flags |= flag;
}
} else {
mount_data.push(option.as_str());
}
}
}
(mount_flags, mount_data.join(",").into())
}
pub fn custom_mount(rootfs: &Path, mount: &Mount) -> Result<()> {
let destination = rootfs.join(
mount
.destination()
.display()
.to_string()
.trim_start_matches('/'),
);
if !destination.exists() {
fs::create_dir_all(&destination)?;
}
let (mount_flags, mount_data) = mount_to_msflags(mount);
mount::mount(
mount.source().as_ref(),
&destination,
mount.typ().as_deref(),
mount_flags,
Some(mount_data).as_deref(),
)?;
Ok(())
}