mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-27 02:41:53 +00:00
102 lines
3.0 KiB
Rust
102 lines
3.0 KiB
Rust
use std::path::Path;
|
|
|
|
use tracing::instrument;
|
|
|
|
use crate::Error;
|
|
use crate::prelude::*;
|
|
use crate::util::Invoke;
|
|
|
|
pub async fn is_mountpoint(path: impl AsRef<Path>) -> Result<bool, Error> {
|
|
let is_mountpoint = tokio::process::Command::new("mountpoint")
|
|
.arg(path.as_ref())
|
|
.stdout(std::process::Stdio::null())
|
|
.stderr(std::process::Stdio::null())
|
|
.status()
|
|
.await?;
|
|
Ok(is_mountpoint.success())
|
|
}
|
|
|
|
#[instrument(skip_all)]
|
|
pub async fn bind<P0: AsRef<Path>, P1: AsRef<Path>>(
|
|
src: P0,
|
|
dst: P1,
|
|
read_only: bool,
|
|
) -> Result<(), Error> {
|
|
tracing::info!(
|
|
"{}",
|
|
t!(
|
|
"disk.mount.binding",
|
|
src = src.as_ref().display(),
|
|
dst = dst.as_ref().display()
|
|
)
|
|
);
|
|
if is_mountpoint(&dst).await? {
|
|
unmount(dst.as_ref(), true).await?;
|
|
}
|
|
tokio::fs::create_dir_all(&src).await?;
|
|
tokio::fs::create_dir_all(&dst).await?;
|
|
let mut mount_cmd = tokio::process::Command::new("mount");
|
|
mount_cmd.arg("--bind");
|
|
if read_only {
|
|
mount_cmd.arg("-o").arg("ro");
|
|
}
|
|
mount_cmd
|
|
.arg(src.as_ref())
|
|
.arg(dst.as_ref())
|
|
.invoke(crate::ErrorKind::Filesystem)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[instrument(skip_all)]
|
|
pub async fn unmount<P: AsRef<Path>>(mountpoint: P, lazy: bool) -> Result<(), Error> {
|
|
tracing::debug!("Unmounting {}.", mountpoint.as_ref().display());
|
|
let mut cmd = tokio::process::Command::new("umount");
|
|
if lazy {
|
|
cmd.arg("-l");
|
|
}
|
|
cmd.arg(mountpoint.as_ref())
|
|
.invoke(crate::ErrorKind::Filesystem)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Unmounts all mountpoints under (and including) the given path, in reverse
|
|
/// depth order so that nested mounts are unmounted before their parents.
|
|
#[instrument(skip_all)]
|
|
pub async fn unmount_all_under<P: AsRef<Path>>(path: P, lazy: bool) -> Result<(), Error> {
|
|
let path = path.as_ref();
|
|
let canonical_path = tokio::fs::canonicalize(path)
|
|
.await
|
|
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("canonicalize {path:?}")))?;
|
|
|
|
let mounts_content = tokio::fs::read_to_string("/proc/mounts")
|
|
.await
|
|
.with_ctx(|_| (ErrorKind::Filesystem, "read /proc/mounts"))?;
|
|
|
|
// Collect all mountpoints under our path
|
|
let mut mountpoints: Vec<&str> = mounts_content
|
|
.lines()
|
|
.filter_map(|line| {
|
|
let mountpoint = line.split_whitespace().nth(1)?;
|
|
// Check if this mountpoint is under our target path
|
|
let mp_path = Path::new(mountpoint);
|
|
if mp_path.starts_with(&canonical_path) {
|
|
Some(mountpoint)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
// Sort by path length descending so we unmount deepest first
|
|
mountpoints.sort_by(|a, b| b.len().cmp(&a.len()));
|
|
|
|
for mountpoint in mountpoints {
|
|
tracing::debug!("Unmounting nested mountpoint: {}", mountpoint);
|
|
unmount(mountpoint, lazy).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|