Add mounting of system directories when chrooting
parent
8e2029cbb0
commit
c9e9049a3a
@ -1,4 +1,10 @@
|
|||||||
|
let REQUIRES_CHROOT = true;
|
||||||
# Applies all system changes of `install-base`
|
# Applies all system changes of `install-base`
|
||||||
def main [cfg] {
|
def main [cfg] {
|
||||||
echo "Executing up task `install-base` with config" $cfg
|
echo "Executing up task `install-base` with config" $cfg
|
||||||
|
ls /etc
|
||||||
|
cat /etc/mtab
|
||||||
|
pacman -S --noconfirm neofetch
|
||||||
|
neofetch
|
||||||
|
exit 1
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,176 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use sys_mount::{FilesystemType, Mount, MountBuilder, MountFlags, UnmountDrop, UnmountFlags};
|
||||||
|
use tokio::fs;
|
||||||
|
|
||||||
|
use crate::error::ChrootError;
|
||||||
|
|
||||||
|
pub enum Mapping {
|
||||||
|
Device(PathBuf),
|
||||||
|
Dir(PathBuf, DirOpts),
|
||||||
|
Special(PathBuf, PathBuf, DirOpts),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct DirOpts {
|
||||||
|
fs_type: Option<String>,
|
||||||
|
mount_flags: Option<MountFlags>,
|
||||||
|
required: bool,
|
||||||
|
unmount_flags: Option<UnmountFlags>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirOpts {
|
||||||
|
pub fn fs_type<S: Into<String>>(mut self, fs_type: S) -> Self {
|
||||||
|
self.fs_type = Some(fs_type.into());
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flags(mut self, flags: MountFlags) -> Self {
|
||||||
|
self.mount_flags = Some(flags);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optional(mut self) -> Self {
|
||||||
|
self.required = false;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmount_flags(mut self, flags: UnmountFlags) -> Self {
|
||||||
|
self.unmount_flags = Some(flags);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MappingHandle {
|
||||||
|
Mount(UnmountDrop<Mount>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mapping {
|
||||||
|
fn dev<P: Into<PathBuf>>(path: P) -> Self {
|
||||||
|
Self::Device(path.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dir<P: Into<PathBuf>>(path: P, opt: DirOpts) -> Self {
|
||||||
|
Self::Dir(path.into(), opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn special<P1: Into<PathBuf>, P2: Into<PathBuf>>(src: P1, dst: P2, opt: DirOpts) -> Self {
|
||||||
|
Self::Special(src.into(), dst.into(), opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_mapping(&self, root_path: &Path) -> Result<MappingHandle, ChrootError> {
|
||||||
|
match &self {
|
||||||
|
Mapping::Device(d) => self.map_dev(d, &root_path.join(d)).await,
|
||||||
|
Mapping::Dir(d, opt) => self.map_dir(d, &root_path.join(d), opt).await,
|
||||||
|
Mapping::Special(src, dst, opt) => self.map_dir(src, &root_path.join(dst), opt).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn map_dev(&self, src: &Path, dst: &Path) -> Result<MappingHandle, ChrootError> {
|
||||||
|
if !dst.exists() {
|
||||||
|
if let Some(parent) = dst.parent() {
|
||||||
|
fs::create_dir_all(&parent)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ChrootError::Mount(dst.to_owned(), e))?;
|
||||||
|
}
|
||||||
|
fs::write(&dst, "")
|
||||||
|
.await
|
||||||
|
.map_err(|e| ChrootError::Mount(dst.to_owned(), e))?;
|
||||||
|
}
|
||||||
|
let mount = MountBuilder::default()
|
||||||
|
.flags(MountFlags::BIND)
|
||||||
|
.mount_autodrop(src, dst, UnmountFlags::empty())
|
||||||
|
.map_err(|e| ChrootError::Mount(dst.to_owned(), e))?;
|
||||||
|
|
||||||
|
Ok(MappingHandle::Mount(mount))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn map_dir(
|
||||||
|
&self,
|
||||||
|
src: &Path,
|
||||||
|
dst: &Path,
|
||||||
|
opt: &DirOpts,
|
||||||
|
) -> Result<MappingHandle, ChrootError> {
|
||||||
|
if !dst.exists() {
|
||||||
|
fs::create_dir_all(&dst)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ChrootError::Mount(dst.to_owned(), e))?;
|
||||||
|
}
|
||||||
|
let fs_type =
|
||||||
|
FilesystemType::Manual(opt.fs_type.as_ref().map(String::as_str).unwrap_or(""));
|
||||||
|
let flags = opt.mount_flags.unwrap_or_else(|| MountFlags::empty());
|
||||||
|
|
||||||
|
let mount = MountBuilder::default()
|
||||||
|
.fstype(fs_type)
|
||||||
|
.flags(flags)
|
||||||
|
.mount_autodrop(src, dst, opt.unmount_flags.unwrap_or(UnmountFlags::empty()))
|
||||||
|
.map_err(|e| ChrootError::Mount(src.to_owned(), e))?;
|
||||||
|
|
||||||
|
Ok(MappingHandle::Mount(mount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_mappings() -> Vec<Mapping> {
|
||||||
|
static NOSUID: MountFlags = MountFlags::NOSUID;
|
||||||
|
static NOEXEC: MountFlags = MountFlags::NOEXEC;
|
||||||
|
static NODEV: MountFlags = MountFlags::NODEV;
|
||||||
|
static STRICTATIME: MountFlags = MountFlags::STRICTATIME;
|
||||||
|
|
||||||
|
vec![
|
||||||
|
Mapping::dir(
|
||||||
|
"proc",
|
||||||
|
DirOpts::default()
|
||||||
|
.fs_type("proc")
|
||||||
|
.flags(NOSUID | NOEXEC | NODEV),
|
||||||
|
),
|
||||||
|
Mapping::dir(
|
||||||
|
"sys",
|
||||||
|
DirOpts::default()
|
||||||
|
.fs_type("sysfs")
|
||||||
|
.flags(NOSUID | NOEXEC | NODEV)
|
||||||
|
.unmount_flags(UnmountFlags::DETACH),
|
||||||
|
),
|
||||||
|
Mapping::dir(
|
||||||
|
"sys/firmware/efi/efivars",
|
||||||
|
DirOpts::default()
|
||||||
|
.fs_type("efivarfs")
|
||||||
|
.flags(NOSUID | NOEXEC | NODEV)
|
||||||
|
.optional(),
|
||||||
|
),
|
||||||
|
Mapping::special(
|
||||||
|
"udev",
|
||||||
|
"dev",
|
||||||
|
DirOpts::default().fs_type("devtmpfs").flags(NOSUID),
|
||||||
|
),
|
||||||
|
Mapping::special(
|
||||||
|
"devpts",
|
||||||
|
"dev/pts",
|
||||||
|
DirOpts::default().fs_type("devpts").flags(NOSUID | NOEXEC),
|
||||||
|
),
|
||||||
|
Mapping::special(
|
||||||
|
"shm",
|
||||||
|
"dev/shm",
|
||||||
|
DirOpts::default().fs_type("tmpfs").flags(NOSUID | NODEV),
|
||||||
|
),
|
||||||
|
Mapping::dev("/dev/full"),
|
||||||
|
Mapping::dev("/dev/null"),
|
||||||
|
Mapping::dev("/dev/random"),
|
||||||
|
Mapping::dev("/dev/tty"),
|
||||||
|
Mapping::dev("/dev/urandom"),
|
||||||
|
Mapping::dev("/dev/zero"),
|
||||||
|
Mapping::dir(
|
||||||
|
"/run",
|
||||||
|
DirOpts::default().fs_type("tmpfs").flags(NOSUID | NODEV),
|
||||||
|
),
|
||||||
|
Mapping::dir(
|
||||||
|
"/tmp",
|
||||||
|
DirOpts::default()
|
||||||
|
.fs_type("tmpfs")
|
||||||
|
.flags(STRICTATIME | NODEV | NOSUID),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use tokio::fs;
|
||||||
|
|
||||||
|
use crate::error::ChrootError;
|
||||||
|
|
||||||
|
use super::{mapping::default_mappings, Chroot};
|
||||||
|
|
||||||
|
impl Chroot {
|
||||||
|
/// Creates a new chroot with the given path
|
||||||
|
pub async fn create<P: Into<PathBuf>>(root_path: P) -> Result<Self, ChrootError> {
|
||||||
|
let root_path = root_path.into();
|
||||||
|
if !root_path.exists() {
|
||||||
|
fs::create_dir_all(&root_path)
|
||||||
|
.await
|
||||||
|
.map_err(ChrootError::CreateChroot)?;
|
||||||
|
}
|
||||||
|
let default_mappings = default_mappings();
|
||||||
|
let mut handles = Vec::with_capacity(default_mappings.len());
|
||||||
|
|
||||||
|
for mapping in default_mappings {
|
||||||
|
let handle = mapping.create_mapping(&root_path).await?;
|
||||||
|
handles.push(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
root_path,
|
||||||
|
_mappings: handles,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue