secure_exec_vfs_core/adapter/
mounted_fs.rs1use crate::posix::{
2 MountedFileSystem, VfsError as PosixVfsError, VfsResult as PosixVfsResult, VirtualDirEntry,
3 VirtualStat,
4};
5use std::any::Any;
6use std::sync::atomic::{AtomicU64, Ordering};
7use tokio::runtime::{Builder, Runtime};
8
9static NEXT_ENGINE_DEVICE_ID: AtomicU64 = AtomicU64::new(4096);
10
11pub struct MountedEngineFileSystem<F> {
12 inner: F,
13 runtime: Runtime,
14 device_id: u64,
15}
16
17impl<F> MountedEngineFileSystem<F> {
18 pub fn new(inner: F) -> PosixVfsResult<Self> {
19 Ok(Self {
20 inner,
21 runtime: Builder::new_current_thread()
22 .enable_all()
23 .build()
24 .map_err(|error| {
25 PosixVfsError::io(format!("create vfs engine runtime: {error}"))
26 })?,
27 device_id: NEXT_ENGINE_DEVICE_ID.fetch_add(1, Ordering::Relaxed),
28 })
29 }
30
31 fn block_on<T>(
32 &self,
33 future: impl std::future::Future<Output = crate::engine::VfsResult<T>>,
34 ) -> PosixVfsResult<T> {
35 self.runtime.block_on(future).map_err(convert_error)
36 }
37}
38
39impl<F> MountedFileSystem for MountedEngineFileSystem<F>
40where
41 F: crate::engine::VirtualFileSystem + 'static,
42{
43 fn as_any(&self) -> &dyn Any {
44 self
45 }
46
47 fn as_any_mut(&mut self) -> &mut dyn Any {
48 self
49 }
50
51 fn read_file(&mut self, path: &str) -> PosixVfsResult<Vec<u8>> {
52 self.block_on(self.inner.read_file(path))
53 }
54
55 fn read_dir(&mut self, path: &str) -> PosixVfsResult<Vec<String>> {
56 self.block_on(self.inner.read_dir(path))
57 }
58
59 fn read_dir_with_types(&mut self, path: &str) -> PosixVfsResult<Vec<VirtualDirEntry>> {
60 self.block_on(self.inner.read_dir_with_types(path))
61 .map(|entries| {
62 entries
63 .into_iter()
64 .map(|entry| VirtualDirEntry {
65 name: entry.name,
66 is_directory: entry.kind == crate::engine::InodeType::Directory,
67 is_symbolic_link: entry.kind == crate::engine::InodeType::Symlink,
68 })
69 .collect()
70 })
71 }
72
73 fn write_file(&mut self, path: &str, content: Vec<u8>) -> PosixVfsResult<()> {
74 self.block_on(self.inner.write_file(path, &content))
75 }
76
77 fn write_file_with_mode(
78 &mut self,
79 path: &str,
80 content: Vec<u8>,
81 mode: Option<u32>,
82 ) -> PosixVfsResult<()> {
83 self.write_file(path, content)?;
84 if let Some(mode) = mode {
85 self.chmod(path, mode)?;
86 }
87 Ok(())
88 }
89
90 fn create_file_exclusive(&mut self, path: &str, content: Vec<u8>) -> PosixVfsResult<()> {
91 if self.exists(path) {
92 return Err(PosixVfsError::new(
93 "EEXIST",
94 format!("file already exists, open '{path}'"),
95 ));
96 }
97 self.write_file(path, content)
98 }
99
100 fn create_file_exclusive_with_mode(
101 &mut self,
102 path: &str,
103 content: Vec<u8>,
104 mode: Option<u32>,
105 ) -> PosixVfsResult<()> {
106 self.create_file_exclusive(path, content)?;
107 if let Some(mode) = mode {
108 self.chmod(path, mode)?;
109 }
110 Ok(())
111 }
112
113 fn append_file(&mut self, path: &str, content: Vec<u8>) -> PosixVfsResult<u64> {
114 self.block_on(self.inner.append(path, &content))
115 }
116
117 fn create_dir(&mut self, path: &str) -> PosixVfsResult<()> {
118 self.block_on(self.inner.create_dir(path))
119 }
120
121 fn create_dir_with_mode(&mut self, path: &str, mode: Option<u32>) -> PosixVfsResult<()> {
122 self.create_dir(path)?;
123 if let Some(mode) = mode {
124 self.chmod(path, mode)?;
125 }
126 Ok(())
127 }
128
129 fn mkdir(&mut self, path: &str, recursive: bool) -> PosixVfsResult<()> {
130 self.block_on(self.inner.mkdir(path, recursive))
131 }
132
133 fn mkdir_with_mode(
134 &mut self,
135 path: &str,
136 recursive: bool,
137 mode: Option<u32>,
138 ) -> PosixVfsResult<()> {
139 self.mkdir(path, recursive)?;
140 if let Some(mode) = mode {
141 self.chmod(path, mode)?;
142 }
143 Ok(())
144 }
145
146 fn exists(&self, path: &str) -> bool {
147 self.runtime.block_on(self.inner.exists(path))
148 }
149
150 fn stat(&mut self, path: &str) -> PosixVfsResult<VirtualStat> {
151 let stat = self.block_on(self.inner.stat(path))?;
152 Ok(convert_stat(stat, self.device_id))
153 }
154
155 fn remove_file(&mut self, path: &str) -> PosixVfsResult<()> {
156 self.block_on(self.inner.remove_file(path))
157 }
158
159 fn remove_dir(&mut self, path: &str) -> PosixVfsResult<()> {
160 self.block_on(self.inner.remove_dir(path))
161 }
162
163 fn rename(&mut self, old_path: &str, new_path: &str) -> PosixVfsResult<()> {
164 self.block_on(self.inner.rename(old_path, new_path))
165 }
166
167 fn realpath(&self, path: &str) -> PosixVfsResult<String> {
168 self.block_on(self.inner.realpath(path))
169 }
170
171 fn symlink(&mut self, target: &str, link_path: &str) -> PosixVfsResult<()> {
172 self.block_on(self.inner.symlink(target, link_path))
173 }
174
175 fn read_link(&self, path: &str) -> PosixVfsResult<String> {
176 self.block_on(self.inner.readlink(path))
177 }
178
179 fn lstat(&self, path: &str) -> PosixVfsResult<VirtualStat> {
180 let stat = self.block_on(self.inner.lstat(path))?;
181 Ok(convert_stat(stat, self.device_id))
182 }
183
184 fn link(&mut self, old_path: &str, new_path: &str) -> PosixVfsResult<()> {
185 self.block_on(self.inner.link(old_path, new_path))
186 }
187
188 fn chmod(&mut self, path: &str, mode: u32) -> PosixVfsResult<()> {
189 self.block_on(self.inner.chmod(path, mode))
190 }
191
192 fn chown(&mut self, path: &str, uid: u32, gid: u32) -> PosixVfsResult<()> {
193 self.block_on(self.inner.chown(path, uid, gid))
194 }
195
196 fn utimes(&mut self, path: &str, atime_ms: u64, mtime_ms: u64) -> PosixVfsResult<()> {
197 self.block_on(self.inner.utimes(path, atime_ms, mtime_ms))
198 }
199
200 fn truncate(&mut self, path: &str, length: u64) -> PosixVfsResult<()> {
201 self.block_on(self.inner.truncate(path, length))
202 }
203
204 fn pread(&mut self, path: &str, offset: u64, length: usize) -> PosixVfsResult<Vec<u8>> {
205 self.block_on(self.inner.pread(path, offset, length))
206 }
207}
208
209fn convert_error(error: crate::engine::VfsError) -> PosixVfsError {
210 PosixVfsError::new(error.code(), error.message().to_owned())
211}
212
213fn convert_stat(stat: crate::engine::VirtualStat, device_id: u64) -> VirtualStat {
214 VirtualStat {
215 mode: stat.mode,
216 size: stat.size,
217 blocks: stat.blocks,
218 dev: device_id,
219 rdev: 0,
220 is_directory: stat.is_directory,
221 is_symbolic_link: stat.is_symbolic_link,
222 atime_ms: timespec_ms(stat.atime),
223 atime_nsec: stat.atime.nsec,
224 mtime_ms: timespec_ms(stat.mtime),
225 mtime_nsec: stat.mtime.nsec,
226 ctime_ms: timespec_ms(stat.ctime),
227 ctime_nsec: stat.ctime.nsec,
228 birthtime_ms: timespec_ms(stat.birthtime),
229 ino: stat.ino,
230 nlink: stat.nlink,
231 uid: stat.uid,
232 gid: stat.gid,
233 }
234}
235
236fn timespec_ms(time: crate::engine::Timespec) -> u64 {
237 if time.sec < 0 {
238 return 0;
239 }
240 (time.sec as u64).saturating_mul(1_000) + u64::from(time.nsec / 1_000_000)
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246 use crate::engine::engines::{ChunkedFs, ChunkedFsOptions};
247 use crate::engine::mem::{InMemoryMetadataStore, MemoryBlockStore};
248 use crate::posix::S_IFREG;
249
250 #[test]
251 fn mounted_engine_filesystem_bridges_sync_posix_calls() {
252 let fs = ChunkedFs::with_options(
253 InMemoryMetadataStore::new(),
254 MemoryBlockStore::new(),
255 ChunkedFsOptions {
256 inline_threshold: 2,
257 chunk_size: 4,
258 ..ChunkedFsOptions::default()
259 },
260 );
261 let mut mounted = MountedEngineFileSystem::new(fs).expect("create mounted engine fs");
262
263 mounted
264 .mkdir("/work/nested", true)
265 .expect("create nested dir");
266 mounted
267 .write_file_with_mode("/work/nested/file.txt", b"hello".to_vec(), Some(0o600))
268 .expect("write file");
269 assert_eq!(
270 mounted
271 .pread("/work/nested/file.txt", 1, 3)
272 .expect("pread file"),
273 b"ell"
274 );
275 let entries = mounted
276 .read_dir_with_types("/work/nested")
277 .expect("read typed dir");
278 assert_eq!(entries.len(), 1);
279 assert_eq!(entries[0].name, "file.txt");
280 assert!(!entries[0].is_directory);
281
282 let stat = mounted.stat("/work/nested/file.txt").expect("stat file");
283 assert_eq!(stat.mode & 0o777, 0o600);
284 assert_eq!(stat.mode & S_IFREG, S_IFREG);
285 assert_eq!(stat.size, 5);
286 }
287}