Skip to main content

bashkit/fs/
readonly.rs

1//! Read-only filesystem wrapper.
2//!
3//! This wrapper is intentionally applied as the outer filesystem layer when an
4//! embedder wants a session that can inspect data but cannot persist or stage
5//! any filesystem changes, including copies into the in-memory VFS.
6
7use crate::time_compat::SystemTime;
8use async_trait::async_trait;
9use std::io::{Error as IoError, ErrorKind};
10use std::path::{Path, PathBuf};
11use std::sync::Arc;
12
13use super::limits::{FsLimits, FsUsage};
14use super::traits::{DirEntry, FileSystem, FileSystemExt, Metadata};
15use crate::error::Result;
16
17/// Denies all filesystem mutations while delegating read operations.
18pub struct ReadOnlyFs {
19    inner: Arc<dyn FileSystem>,
20}
21
22impl ReadOnlyFs {
23    pub fn new(inner: Arc<dyn FileSystem>) -> Self {
24        Self { inner }
25    }
26
27    fn readonly_error() -> crate::Error {
28        IoError::new(ErrorKind::PermissionDenied, "filesystem is read-only").into()
29    }
30}
31
32#[async_trait]
33impl FileSystem for ReadOnlyFs {
34    async fn read_file(&self, path: &Path) -> Result<Vec<u8>> {
35        self.inner.read_file(path).await
36    }
37
38    async fn write_file(&self, _path: &Path, _content: &[u8]) -> Result<()> {
39        Err(Self::readonly_error())
40    }
41
42    async fn append_file(&self, _path: &Path, _content: &[u8]) -> Result<()> {
43        Err(Self::readonly_error())
44    }
45
46    async fn mkdir(&self, _path: &Path, _recursive: bool) -> Result<()> {
47        Err(Self::readonly_error())
48    }
49
50    async fn remove(&self, _path: &Path, _recursive: bool) -> Result<()> {
51        Err(Self::readonly_error())
52    }
53
54    async fn stat(&self, path: &Path) -> Result<Metadata> {
55        self.inner.stat(path).await
56    }
57
58    async fn read_dir(&self, path: &Path) -> Result<Vec<DirEntry>> {
59        self.inner.read_dir(path).await
60    }
61
62    async fn exists(&self, path: &Path) -> Result<bool> {
63        self.inner.exists(path).await
64    }
65
66    async fn rename(&self, _from: &Path, _to: &Path) -> Result<()> {
67        Err(Self::readonly_error())
68    }
69
70    async fn copy(&self, _from: &Path, _to: &Path) -> Result<()> {
71        Err(Self::readonly_error())
72    }
73
74    async fn symlink(&self, _target: &Path, _link: &Path) -> Result<()> {
75        Err(Self::readonly_error())
76    }
77
78    async fn read_link(&self, path: &Path) -> Result<PathBuf> {
79        self.inner.read_link(path).await
80    }
81
82    async fn chmod(&self, _path: &Path, _mode: u32) -> Result<()> {
83        Err(Self::readonly_error())
84    }
85
86    async fn set_modified_time(&self, _path: &Path, _time: SystemTime) -> Result<()> {
87        Err(Self::readonly_error())
88    }
89
90    fn as_search_capable(&self) -> Option<&dyn super::SearchCapable> {
91        self.inner.as_search_capable()
92    }
93}
94
95#[async_trait]
96impl FileSystemExt for ReadOnlyFs {
97    fn usage(&self) -> FsUsage {
98        self.inner.usage()
99    }
100
101    fn limits(&self) -> FsLimits {
102        self.inner.limits()
103    }
104
105    async fn mkfifo(&self, _path: &Path, _mode: u32) -> Result<()> {
106        Err(Self::readonly_error())
107    }
108
109    fn vfs_snapshot(&self) -> Option<super::VfsSnapshot> {
110        self.inner.vfs_snapshot()
111    }
112
113    fn vfs_restore(&self, _snapshot: &super::VfsSnapshot) -> Result<()> {
114        Err(Self::readonly_error())
115    }
116}