use crate::error::{NucleusError, Result};
use nix::mount::{mount, umount2, MntFlags, MsFlags};
use std::path::{Path, PathBuf};
use tracing::{debug, info};
pub struct TmpfsMount {
path: PathBuf,
size_bytes: Option<u64>,
mounted: bool,
}
impl TmpfsMount {
pub fn new<P: AsRef<Path>>(path: P, size_bytes: Option<u64>) -> Self {
Self {
path: path.as_ref().to_path_buf(),
size_bytes,
mounted: false,
}
}
pub fn mount(&mut self) -> Result<()> {
if self.mounted {
debug!("tmpfs already mounted at {:?}, skipping", self.path);
return Ok(());
}
info!("Mounting tmpfs at {:?}", self.path);
if !self.path.exists() {
std::fs::create_dir_all(&self.path).map_err(|e| {
NucleusError::FilesystemError(format!(
"Failed to create mount point {:?}: {}",
self.path, e
))
})?;
}
let mut options = String::new();
if let Some(size) = self.size_bytes {
options = format!("size={}", size);
}
let options_cstr = if options.is_empty() {
None
} else {
Some(options.as_str())
};
let flags = MsFlags::MS_NOSUID | MsFlags::MS_NODEV;
mount(
Some("tmpfs"),
&self.path,
Some("tmpfs"),
flags,
options_cstr,
)
.map_err(|e| {
NucleusError::FilesystemError(format!(
"Failed to mount tmpfs at {:?}: {}",
self.path, e
))
})?;
self.mounted = true;
info!("Successfully mounted tmpfs at {:?}", self.path);
Ok(())
}
pub fn unmount(&mut self) -> Result<()> {
if !self.mounted {
debug!("tmpfs not mounted at {:?}, skipping", self.path);
return Ok(());
}
info!("Unmounting tmpfs at {:?}", self.path);
umount2(&self.path, MntFlags::MNT_DETACH).map_err(|e| {
NucleusError::FilesystemError(format!(
"Failed to unmount tmpfs at {:?}: {}",
self.path, e
))
})?;
self.mounted = false;
info!("Successfully unmounted tmpfs at {:?}", self.path);
Ok(())
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn is_mounted(&self) -> bool {
self.mounted
}
}
impl Drop for TmpfsMount {
fn drop(&mut self) {
if self.mounted {
let _ = self.unmount();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tmpfs_mount_new() {
let mount = TmpfsMount::new("/tmp/test", Some(1024 * 1024));
assert!(!mount.is_mounted());
assert_eq!(mount.path(), Path::new("/tmp/test"));
}
}