1use crate::mem_fs::FileSystem as MemFileSystem;
2use crate::{
3 FileDescriptor, FileOpener, FileSystem, FsError, Metadata, OpenOptions, OpenOptionsConfig,
4 ReadDir, VirtualFile,
5};
6use anyhow::anyhow;
7use std::convert::TryInto;
8use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read, Seek, SeekFrom, Write};
9use std::ops::Deref;
10use std::path::Path;
11use std::path::PathBuf;
12use std::sync::Arc;
13use webc::{FsEntry, FsEntryType, OwnedFsEntryFile, WebC};
14
15#[derive(Debug)]
17pub struct WebcFileSystem<T>
18where
19 T: std::fmt::Debug + Send + Sync + 'static,
20{
21 pub package: String,
22 pub webc: Arc<T>,
23 pub memory: Arc<MemFileSystem>,
24}
25
26impl<T> WebcFileSystem<T>
27where
28 T: std::fmt::Debug + Send + Sync + 'static,
29 T: Deref<Target = WebC<'static>>,
30{
31 pub fn init(webc: Arc<T>, package: &str) -> Self {
32 let fs = Self {
33 package: package.to_string(),
34 webc: webc.clone(),
35 memory: Arc::new(MemFileSystem::default()),
36 };
37 for volume in webc.get_volumes_for_package(package) {
38 for directory in webc.list_directories(&volume) {
39 let _ = fs.create_dir(Path::new(&directory));
40 }
41 }
42 fs
43 }
44}
45
46#[derive(Debug)]
48struct WebCFileOpener<T>
49where
50 T: std::fmt::Debug + Send + Sync + 'static,
51{
52 pub package: String,
53 pub webc: Arc<T>,
54 pub memory: Arc<MemFileSystem>,
55}
56
57impl<T> FileOpener for WebCFileOpener<T>
58where
59 T: std::fmt::Debug + Send + Sync + 'static,
60 T: Deref<Target = WebC<'static>>,
61{
62 fn open(
63 &mut self,
64 path: &Path,
65 _conf: &OpenOptionsConfig,
66 ) -> Result<Box<dyn VirtualFile + Send + Sync>, FsError> {
67 match get_volume_name_opt(path) {
68 Some(volume) => {
69 let file = (*self.webc)
70 .volumes
71 .get(&volume)
72 .ok_or(FsError::EntityNotFound)?
73 .get_file_entry(&format!("{}", path.display()))
74 .map_err(|_e| FsError::EntityNotFound)?;
75
76 Ok(Box::new(WebCFile {
77 package: self.package.clone(),
78 volume,
79 webc: self.webc.clone(),
80 path: path.to_path_buf(),
81 entry: file,
82 cursor: 0,
83 }))
84 }
85 None => {
86 for volume in self.webc.get_volumes_for_package(&self.package) {
87 let v = match self.webc.volumes.get(&volume) {
88 Some(s) => s,
89 None => continue, };
91
92 let entry = match v.get_file_entry(&format!("{}", path.display())) {
93 Ok(s) => s,
94 Err(_) => continue, };
96
97 return Ok(Box::new(WebCFile {
98 package: self.package.clone(),
99 volume: volume.clone(),
100 webc: self.webc.clone(),
101 path: path.to_path_buf(),
102 entry,
103 cursor: 0,
104 }));
105 }
106 self.memory.new_open_options().open(path)
107 }
108 }
109 }
110}
111
112#[derive(Debug)]
113struct WebCFile<T>
114where
115 T: std::fmt::Debug + Send + Sync + 'static,
116{
117 pub webc: Arc<T>,
118 #[allow(dead_code)]
119 pub package: String,
120 pub volume: String,
121 #[allow(dead_code)]
122 pub path: PathBuf,
123 pub entry: OwnedFsEntryFile,
124 pub cursor: u64,
125}
126
127impl<T> VirtualFile for WebCFile<T>
128where
129 T: std::fmt::Debug + Send + Sync + 'static,
130 T: Deref<Target = WebC<'static>>,
131{
132 fn last_accessed(&self) -> u64 {
133 0
134 }
135 fn last_modified(&self) -> u64 {
136 0
137 }
138 fn created_time(&self) -> u64 {
139 0
140 }
141 fn size(&self) -> u64 {
142 self.entry.get_len()
143 }
144 fn set_len(&mut self, _new_size: u64) -> Result<(), FsError> {
145 Ok(())
146 }
147 fn unlink(&mut self) -> Result<(), FsError> {
148 Ok(())
149 }
150 fn bytes_available(&self) -> Result<usize, FsError> {
151 Ok(self.size().try_into().unwrap_or(u32::MAX as usize))
152 }
153 fn sync_to_disk(&self) -> Result<(), FsError> {
154 Ok(())
155 }
156 fn get_fd(&self) -> Option<FileDescriptor> {
157 None
158 }
159}
160
161impl<T> Read for WebCFile<T>
162where
163 T: std::fmt::Debug + Send + Sync + 'static,
164 T: Deref<Target = WebC<'static>>,
165{
166 fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
167 let bytes = self
168 .webc
169 .volumes
170 .get(&self.volume)
171 .ok_or_else(|| {
172 IoError::new(
173 IoErrorKind::NotFound,
174 anyhow!("Unknown volume {:?}", self.volume),
175 )
176 })?
177 .get_file_bytes(&self.entry)
178 .map_err(|e| IoError::new(IoErrorKind::NotFound, e))?;
179
180 let cursor: usize = self.cursor.try_into().unwrap_or(u32::MAX as usize);
181 let _start = cursor.min(bytes.len());
182 let bytes = &bytes[cursor..];
183
184 let mut len = 0;
185 for (source, target) in bytes.iter().zip(buf.iter_mut()) {
186 *target = *source;
187 len += 1;
188 }
189
190 Ok(len)
191 }
192}
193
194impl<T> Write for WebCFile<T>
197where
198 T: std::fmt::Debug + Send + Sync + 'static,
199{
200 fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
201 Ok(buf.len())
202 }
203 fn flush(&mut self) -> Result<(), IoError> {
204 Ok(())
205 }
206}
207
208impl<T> Seek for WebCFile<T>
209where
210 T: std::fmt::Debug + Send + Sync + 'static,
211 T: Deref<Target = WebC<'static>>,
212{
213 fn seek(&mut self, pos: SeekFrom) -> Result<u64, IoError> {
214 let self_size = self.size();
215 match pos {
216 SeekFrom::Start(s) => {
217 self.cursor = s.min(self_size);
218 }
219 SeekFrom::End(e) => {
220 let self_size_i64 = self_size.try_into().unwrap_or(i64::MAX);
221 self.cursor = ((self_size_i64).saturating_add(e))
222 .min(self_size_i64)
223 .try_into()
224 .unwrap_or(i64::MAX as u64);
225 }
226 SeekFrom::Current(c) => {
227 self.cursor = (self
228 .cursor
229 .saturating_add(c.try_into().unwrap_or(i64::MAX as u64)))
230 .min(self_size);
231 }
232 }
233 Ok(self.cursor)
234 }
235}
236
237fn get_volume_name_opt<P: AsRef<Path>>(path: P) -> Option<String> {
238 use std::path::Component::Normal;
239 if let Some(Normal(n)) = path.as_ref().components().next() {
240 if let Some(s) = n.to_str() {
241 if s.ends_with(':') {
242 return Some(s.replace(':', ""));
243 }
244 }
245 }
246 None
247}
248
249#[allow(dead_code)]
250fn get_volume_name<P: AsRef<Path>>(path: P) -> String {
251 get_volume_name_opt(path).unwrap_or_else(|| "atom".to_string())
252}
253
254fn transform_into_read_dir<'a>(path: &Path, fs_entries: &[FsEntry<'a>]) -> crate::ReadDir {
255 let entries = fs_entries
256 .iter()
257 .map(|e| crate::DirEntry {
258 path: path.join(&*e.text),
259 metadata: Ok(crate::Metadata {
260 ft: translate_file_type(e.fs_type),
261 accessed: 0,
262 created: 0,
263 modified: 0,
264 len: e.get_len(),
265 }),
266 })
267 .collect();
268
269 crate::ReadDir::new(entries)
270}
271
272impl<T> FileSystem for WebcFileSystem<T>
273where
274 T: std::fmt::Debug + Send + Sync + 'static,
275 T: Deref<Target = WebC<'static>>,
276{
277 fn read_dir(&self, path: &Path) -> Result<ReadDir, FsError> {
278 let path = normalizes_path(path);
279 let read_dir_result = self
280 .webc
281 .read_dir(&self.package, &path)
282 .map(|o| transform_into_read_dir(Path::new(&path), o.as_ref()))
283 .map_err(|_| FsError::EntityNotFound);
284
285 match read_dir_result {
286 Ok(o) => Ok(o),
287 Err(_) => self.memory.read_dir(Path::new(&path)),
288 }
289 }
290 fn create_dir(&self, path: &Path) -> Result<(), FsError> {
291 let path = normalizes_path(path);
292 let result = self.memory.create_dir(Path::new(&path));
293 result
294 }
295 fn remove_dir(&self, path: &Path) -> Result<(), FsError> {
296 let path = normalizes_path(path);
297 let result = self.memory.remove_dir(Path::new(&path));
298 if self.webc.get_file_entry(&self.package, &path).is_some() {
299 Ok(())
300 } else {
301 result
302 }
303 }
304 fn rename(&self, from: &Path, to: &Path) -> Result<(), FsError> {
305 let from = normalizes_path(from);
306 let to = normalizes_path(to);
307 let result = self.memory.rename(Path::new(&from), Path::new(&to));
308 if self.webc.get_file_entry(&self.package, &from).is_some() {
309 Ok(())
310 } else {
311 result
312 }
313 }
314 fn metadata(&self, path: &Path) -> Result<Metadata, FsError> {
315 let path = normalizes_path(path);
316 if let Some(fs_entry) = self.webc.get_file_entry(&self.package, &path) {
317 Ok(Metadata {
318 ft: translate_file_type(FsEntryType::File),
319 accessed: 0,
320 created: 0,
321 modified: 0,
322 len: fs_entry.1.get_len(),
323 })
324 } else if self.webc.read_dir(&self.package, &path).is_ok() {
325 Ok(Metadata {
326 ft: translate_file_type(FsEntryType::Dir),
327 accessed: 0,
328 created: 0,
329 modified: 0,
330 len: 0,
331 })
332 } else {
333 self.memory.metadata(Path::new(&path))
334 }
335 }
336 fn remove_file(&self, path: &Path) -> Result<(), FsError> {
337 let path = normalizes_path(path);
338 let result = self.memory.remove_file(Path::new(&path));
339 if self.webc.get_file_entry(&self.package, &path).is_some() {
340 Ok(())
341 } else {
342 result
343 }
344 }
345 fn new_open_options(&self) -> OpenOptions {
346 OpenOptions::new(Box::new(WebCFileOpener {
347 package: self.package.clone(),
348 webc: self.webc.clone(),
349 memory: self.memory.clone(),
350 }))
351 }
352 fn symlink_metadata(&self, path: &Path) -> Result<Metadata, FsError> {
353 let path = normalizes_path(path);
354 if let Some(fs_entry) = self.webc.get_file_entry(&self.package, &path) {
355 Ok(Metadata {
356 ft: translate_file_type(FsEntryType::File),
357 accessed: 0,
358 created: 0,
359 modified: 0,
360 len: fs_entry.1.get_len(),
361 })
362 } else if self.webc.read_dir(&self.package, &path).is_ok() {
363 Ok(Metadata {
364 ft: translate_file_type(FsEntryType::Dir),
365 accessed: 0,
366 created: 0,
367 modified: 0,
368 len: 0,
369 })
370 } else {
371 self.memory.symlink_metadata(Path::new(&path))
372 }
373 }
374}
375
376fn normalizes_path(path: &Path) -> String {
377 let path = format!("{}", path.display());
378 if !path.starts_with('/') {
379 format!("/{path}")
380 } else {
381 path
382 }
383}
384
385fn translate_file_type(f: FsEntryType) -> crate::FileType {
386 crate::FileType {
387 dir: f == FsEntryType::Dir,
388 file: f == FsEntryType::File,
389 symlink: false,
390 char_device: false,
391 block_device: false,
392 socket: false,
393 fifo: false,
394 }
395}