1
0
Fork 0
soft/src/mount.rs

120 lines
3.6 KiB
Rust

use anyhow::{anyhow, Context, Result};
use base64::{engine::general_purpose as b64, Engine as _};
use duct::cmd;
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct TempDir {
path: PathBuf,
removed: bool,
}
impl TempDir {
pub fn new() -> Result<TempDir> {
let mut rbuf = [0u8; 10];
getrandom::getrandom(&mut rbuf).map_err(|_| anyhow!("Failed to get randomness"))?;
let path = PathBuf::from(format!("/var/run/soft/{}", b64::URL_SAFE_NO_PAD.encode(rbuf)));
log::trace!("Creating temporary directory '{}'", path.display());
std::fs::create_dir(&path).with_context(|| format!("Failed to create temporary directory '{}'", path.display()))?;
Ok(TempDir { path, removed: false })
}
pub fn cleanup(&mut self) -> Result<()> {
log::trace!("Removing temporary directory '{}'", self.path.display());
std::fs::remove_dir(&self.path)
.with_context(|| format!("Failed to remove temporary directory '{}'", self.path.display()))?;
self.removed = true;
Ok(())
}
pub fn path(&self) -> &Path {
&self.path
}
}
impl Drop for TempDir {
fn drop(&mut self) {
if !self.removed {
if let Err(e) = self.cleanup() {
log::error!("While cleaning up temporary directory '{}': {:?}", self.path.display(), e);
}
}
}
}
#[derive(Debug)]
enum MountPoint {
Existing(PathBuf),
Temp(TempDir),
}
#[derive(Debug)]
pub struct Mount {
point: MountPoint,
was: (String, PathBuf),
unmounted: bool,
}
impl Mount {
pub fn path(&self) -> &Path {
match &self.point {
MountPoint::Existing(p) => &p,
MountPoint::Temp(td) => &td.path,
}
}
pub fn mount_tmpfs(to: PathBuf) -> Result<Mount> {
Self::mount(Path::new("tmpfs"), to, "tmpfs", "rw")
}
pub fn mount_bind(from: &Path, to: PathBuf, rw: bool) -> Result<Mount> {
Self::mount(from, to, "nullfs", if rw { "rw" } else { "ro" })
}
pub fn mount_safe_dev(to: PathBuf) -> Result<Mount> {
Self::mount(Path::new("devfs"), to, "devfs", "ruleset=4")
}
pub fn mount_temp(from: &Path, fs: &str, opts: &str) -> Result<Mount> {
let td = TempDir::new()?;
let mut m = Self::mount(from, td.path().to_owned(), fs, opts)?;
m.point = MountPoint::Temp(td);
Ok(m)
}
pub fn mount(from: &Path, to: PathBuf, fs: &str, opts: &str) -> Result<Mount> {
log::trace!("Mounting {}: '{}' to '{}'", fs, from.display(), to.display());
cmd!("mount", "-t", fs, "-o", opts, from, &to)
.stdout_null()
.run()
.with_context(|| format!("Failed to mount {}: '{}' to '{}'", fs, from.display(), to.display()))?;
Ok(Mount {
point: MountPoint::Existing(to),
was: (fs.to_owned(), from.to_owned()),
unmounted: false,
})
}
pub fn unmount(&mut self) -> Result<()> {
log::trace!("Unmounting '{}' (was {} '{}')", self.path().display(), self.was.0, self.was.1.display());
cmd!("umount", "-f", self.path())
.run()
.with_context(|| format!("Failed to unmount '{}'", self.path().display()))?;
if let MountPoint::Temp(td) = &mut self.point {
td.cleanup()?;
}
self.unmounted = true;
Ok(())
}
}
impl Drop for Mount {
fn drop(&mut self) {
if !self.unmounted {
if let Err(e) = self.unmount() {
log::error!("While cleaning up mount '{}': {:?}", self.path().display(), e);
}
}
}
}