1use super::vfs::{
2 VfsError, VfsResult, VirtualDirEntry, VirtualFileSystem, VirtualStat, VirtualUtimeSpec, S_IFLNK,
3};
4
5#[derive(Debug, Clone)]
14pub struct SingleSymlinkFileSystem {
15 target: String,
16}
17
18impl SingleSymlinkFileSystem {
19 pub fn new(target: impl Into<String>) -> Self {
20 Self {
21 target: target.into(),
22 }
23 }
24
25 pub fn target(&self) -> &str {
26 &self.target
27 }
28
29 fn root_stat(&self) -> VirtualStat {
30 VirtualStat {
31 mode: S_IFLNK | 0o777,
32 size: self.target.len() as u64,
33 blocks: 0,
34 dev: 9101,
35 rdev: 0,
36 is_directory: false,
37 is_symbolic_link: true,
38 atime_ms: 0,
39 atime_nsec: 0,
40 mtime_ms: 0,
41 mtime_nsec: 0,
42 ctime_ms: 0,
43 ctime_nsec: 0,
44 birthtime_ms: 0,
45 ino: 1,
46 nlink: 1,
47 uid: 1000,
48 gid: 1000,
49 }
50 }
51
52 fn not_found(path: &str) -> VfsError {
53 VfsError::new("ENOENT", format!("no such file or directory, '{path}'"))
54 }
55
56 fn readonly(op: &str, path: &str) -> VfsError {
57 VfsError::new(
58 "EROFS",
59 format!("read-only single-symlink filesystem, {op} '{path}'"),
60 )
61 }
62}
63
64impl VirtualFileSystem for SingleSymlinkFileSystem {
65 fn read_file(&mut self, path: &str) -> VfsResult<Vec<u8>> {
66 Err(Self::not_found(path))
67 }
68
69 fn read_dir(&mut self, path: &str) -> VfsResult<Vec<String>> {
70 Err(VfsError::new(
71 "ENOTDIR",
72 format!("not a directory, readdir '{path}'"),
73 ))
74 }
75
76 fn read_dir_with_types(&mut self, path: &str) -> VfsResult<Vec<VirtualDirEntry>> {
77 Err(VfsError::new(
78 "ENOTDIR",
79 format!("not a directory, readdir '{path}'"),
80 ))
81 }
82
83 fn write_file(&mut self, path: &str, _content: impl Into<Vec<u8>>) -> VfsResult<()> {
84 Err(Self::readonly("write", path))
85 }
86
87 fn create_dir(&mut self, path: &str) -> VfsResult<()> {
88 Err(Self::readonly("mkdir", path))
89 }
90
91 fn mkdir(&mut self, path: &str, _recursive: bool) -> VfsResult<()> {
92 Err(Self::readonly("mkdir", path))
93 }
94
95 fn exists(&self, path: &str) -> bool {
96 path == "/"
97 }
98
99 fn stat(&mut self, path: &str) -> VfsResult<VirtualStat> {
100 self.lstat(path)
101 }
102
103 fn remove_file(&mut self, path: &str) -> VfsResult<()> {
104 Err(Self::readonly("unlink", path))
105 }
106
107 fn remove_dir(&mut self, path: &str) -> VfsResult<()> {
108 Err(Self::readonly("rmdir", path))
109 }
110
111 fn rename(&mut self, old_path: &str, new_path: &str) -> VfsResult<()> {
112 Err(VfsError::new(
113 "EROFS",
114 format!("read-only single-symlink filesystem, rename '{old_path}' to '{new_path}'"),
115 ))
116 }
117
118 fn realpath(&self, path: &str) -> VfsResult<String> {
119 if path == "/" {
120 return Err(VfsError::new(
121 "ELOOP",
122 "single-symlink root must be resolved by the mount table",
123 ));
124 }
125 Err(Self::not_found(path))
126 }
127
128 fn symlink(&mut self, target: &str, link_path: &str) -> VfsResult<()> {
129 Err(VfsError::new(
130 "EROFS",
131 format!("read-only single-symlink filesystem, symlink '{link_path}' -> '{target}'"),
132 ))
133 }
134
135 fn read_link(&self, path: &str) -> VfsResult<String> {
136 if path == "/" {
137 Ok(self.target.clone())
138 } else {
139 Err(Self::not_found(path))
140 }
141 }
142
143 fn lstat(&self, path: &str) -> VfsResult<VirtualStat> {
144 if path == "/" {
145 Ok(self.root_stat())
146 } else {
147 Err(Self::not_found(path))
148 }
149 }
150
151 fn link(&mut self, old_path: &str, new_path: &str) -> VfsResult<()> {
152 Err(VfsError::new(
153 "EROFS",
154 format!("read-only single-symlink filesystem, link '{old_path}' to '{new_path}'"),
155 ))
156 }
157
158 fn chmod(&mut self, path: &str, _mode: u32) -> VfsResult<()> {
159 Err(Self::readonly("chmod", path))
160 }
161
162 fn chown(&mut self, path: &str, _uid: u32, _gid: u32) -> VfsResult<()> {
163 Err(Self::readonly("chown", path))
164 }
165
166 fn utimes(&mut self, path: &str, _atime_ms: u64, _mtime_ms: u64) -> VfsResult<()> {
167 Err(Self::readonly("utimes", path))
168 }
169
170 fn utimes_spec(
171 &mut self,
172 path: &str,
173 _atime: VirtualUtimeSpec,
174 _mtime: VirtualUtimeSpec,
175 _follow_symlinks: bool,
176 ) -> VfsResult<()> {
177 Err(Self::readonly("utimes", path))
178 }
179
180 fn truncate(&mut self, path: &str, _length: u64) -> VfsResult<()> {
181 Err(Self::readonly("truncate", path))
182 }
183
184 fn pread(&mut self, path: &str, _offset: u64, _length: usize) -> VfsResult<Vec<u8>> {
185 Err(Self::not_found(path))
186 }
187}