Skip to main content

secure_exec_vfs_core/adapter/
mounted_fs.rs

1use 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}