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`
|
||||
def main [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