use std::path::{Path, PathBuf};
use std::time::Duration;
use anyhow::{Context, Result, bail};
pub use arcbox_core::host_mount::MountInfo;
use arcbox_core::host_mount::MountTable;
pub const MOUNT_ATTEMPT_TIMEOUT: Duration = Duration::from_secs(20);
pub const UNMOUNT_TIMEOUT: Duration = Duration::from_secs(5);
pub fn current_mount_info(path: &Path) -> Option<MountInfo> {
MountTable::read()?.at(path)
}
pub fn mounts_under(root: &Path) -> Vec<(PathBuf, MountInfo)> {
MountTable::read().map_or_else(Vec::new, |table| table.under(root))
}
pub async fn mount_nfs(opts: &str, source: &str, mount_path: &Path) -> Result<()> {
let mut command = tokio::process::Command::new("/sbin/mount_nfs");
command
.arg("-o")
.arg(opts)
.arg(source)
.arg(mount_path)
.kill_on_drop(true);
let output = tokio::time::timeout(MOUNT_ATTEMPT_TIMEOUT, command.output())
.await
.map_err(|_| anyhow::anyhow!("mount_nfs did not return within {MOUNT_ATTEMPT_TIMEOUT:?}"))?
.context("failed to execute mount_nfs")?;
if output.status.success() {
Ok(())
} else {
bail!(
"mount_nfs exited with {}: {}",
output.status.code().unwrap_or(-1),
String::from_utf8_lossy(&output.stderr).trim()
)
}
}
pub async fn unmount(path: &Path) -> Result<()> {
run_umount(path, &[]).await
}
pub async fn unmount_force(path: &Path) -> Result<()> {
run_umount(path, &["-f"]).await
}
async fn run_umount(path: &Path, flags: &[&str]) -> Result<()> {
let mut command = tokio::process::Command::new("/sbin/umount");
command.args(flags).arg(path).kill_on_drop(true);
let output = tokio::time::timeout(UNMOUNT_TIMEOUT, command.output())
.await
.context("umount timed out")??;
if output.status.success() {
Ok(())
} else {
bail!(
"umount exited with {}: {}",
output.status.code().unwrap_or(-1),
String::from_utf8_lossy(&output.stderr).trim()
)
}
}