nucleus/filesystem/
tmpfs.rs1use crate::error::{NucleusError, Result};
2use nix::mount::{mount, umount2, MntFlags, MsFlags};
3use std::path::{Path, PathBuf};
4use tracing::{debug, info};
5
6pub struct TmpfsMount {
8 path: PathBuf,
9 size_bytes: Option<u64>,
10 mounted: bool,
11}
12
13impl TmpfsMount {
14 pub fn new<P: AsRef<Path>>(path: P, size_bytes: Option<u64>) -> Self {
16 Self {
17 path: path.as_ref().to_path_buf(),
18 size_bytes,
19 mounted: false,
20 }
21 }
22
23 pub fn mount(&mut self) -> Result<()> {
27 if self.mounted {
28 debug!("tmpfs already mounted at {:?}, skipping", self.path);
29 return Ok(());
30 }
31
32 info!("Mounting tmpfs at {:?}", self.path);
33
34 if !self.path.exists() {
36 std::fs::create_dir_all(&self.path).map_err(|e| {
37 NucleusError::FilesystemError(format!(
38 "Failed to create mount point {:?}: {}",
39 self.path, e
40 ))
41 })?;
42 }
43
44 let mut options = String::new();
46 if let Some(size) = self.size_bytes {
47 options = format!("size={}", size);
48 }
49
50 let options_cstr = if options.is_empty() {
51 None
52 } else {
53 Some(options.as_str())
54 };
55
56 let flags = MsFlags::MS_NOSUID | MsFlags::MS_NODEV;
60
61 mount(
62 Some("tmpfs"),
63 &self.path,
64 Some("tmpfs"),
65 flags,
66 options_cstr,
67 )
68 .map_err(|e| {
69 NucleusError::FilesystemError(format!(
70 "Failed to mount tmpfs at {:?}: {}",
71 self.path, e
72 ))
73 })?;
74
75 self.mounted = true;
76 info!("Successfully mounted tmpfs at {:?}", self.path);
77
78 Ok(())
79 }
80
81 pub fn unmount(&mut self) -> Result<()> {
83 if !self.mounted {
84 debug!("tmpfs not mounted at {:?}, skipping", self.path);
85 return Ok(());
86 }
87
88 info!("Unmounting tmpfs at {:?}", self.path);
89
90 umount2(&self.path, MntFlags::MNT_DETACH).map_err(|e| {
91 NucleusError::FilesystemError(format!(
92 "Failed to unmount tmpfs at {:?}: {}",
93 self.path, e
94 ))
95 })?;
96
97 self.mounted = false;
98 info!("Successfully unmounted tmpfs at {:?}", self.path);
99
100 Ok(())
101 }
102
103 pub fn path(&self) -> &Path {
105 &self.path
106 }
107
108 pub fn is_mounted(&self) -> bool {
110 self.mounted
111 }
112}
113
114impl Drop for TmpfsMount {
115 fn drop(&mut self) {
116 if self.mounted {
117 let _ = self.unmount();
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_tmpfs_mount_new() {
128 let mount = TmpfsMount::new("/tmp/test", Some(1024 * 1024));
129 assert!(!mount.is_mounted());
130 assert_eq!(mount.path(), Path::new("/tmp/test"));
131 }
132
133 }