1use std::any::Any;
2use std::ffi::OsString;
3use std::fmt;
4use std::io::{self, Read, Seek, Write};
5use std::path::{Path, PathBuf};
6use thiserror::Error;
7
8#[cfg(all(not(feature = "host-fs"), not(feature = "mem-fs")))]
9compile_error!("At least the `host-fs` or the `mem-fs` feature must be enabled. Please, pick one.");
10
11#[cfg(feature = "host-fs")]
15pub mod host_fs;
16#[cfg(feature = "mem-fs")]
17pub mod mem_fs;
18#[cfg(feature = "static-fs")]
19pub mod static_fs;
20#[cfg(feature = "webc-fs")]
21pub mod webc_fs;
22
23pub type Result<T> = std::result::Result<T, FsError>;
24
25#[derive(Debug)]
26#[repr(transparent)]
27pub struct FileDescriptor(usize);
28
29impl From<u32> for FileDescriptor {
30    fn from(a: u32) -> Self {
31        Self(a as usize)
32    }
33}
34
35impl From<FileDescriptor> for u32 {
36    fn from(a: FileDescriptor) -> u32 {
37        a.0 as u32
38    }
39}
40
41pub trait FileSystem: fmt::Debug + Send + Sync + 'static + Upcastable {
42    fn read_dir(&self, path: &Path) -> Result<ReadDir>;
43    fn create_dir(&self, path: &Path) -> Result<()>;
44    fn remove_dir(&self, path: &Path) -> Result<()>;
45    fn rename(&self, from: &Path, to: &Path) -> Result<()>;
46    fn metadata(&self, path: &Path) -> Result<Metadata>;
47    fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
51        self.metadata(path)
52    }
53    fn remove_file(&self, path: &Path) -> Result<()>;
54
55    fn new_open_options(&self) -> OpenOptions;
56}
57
58impl dyn FileSystem + 'static {
59    #[inline]
60    pub fn downcast_ref<T: 'static>(&'_ self) -> Option<&'_ T> {
61        self.upcast_any_ref().downcast_ref::<T>()
62    }
63    #[inline]
64    pub fn downcast_mut<T: 'static>(&'_ mut self) -> Option<&'_ mut T> {
65        self.upcast_any_mut().downcast_mut::<T>()
66    }
67}
68
69pub trait FileOpener {
70    fn open(
71        &mut self,
72        path: &Path,
73        conf: &OpenOptionsConfig,
74    ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>>;
75}
76
77#[derive(Debug, Clone)]
78pub struct OpenOptionsConfig {
79    pub read: bool,
80    pub write: bool,
81    pub create_new: bool,
82    pub create: bool,
83    pub append: bool,
84    pub truncate: bool,
85}
86
87impl OpenOptionsConfig {
88    pub fn minimum_rights(&self, parent_rights: &Self) -> Self {
90        Self {
91            read: parent_rights.read && self.read,
92            write: parent_rights.write && self.write,
93            create_new: parent_rights.create_new && self.create_new,
94            create: parent_rights.create && self.create,
95            append: parent_rights.append && self.append,
96            truncate: parent_rights.truncate && self.truncate,
97        }
98    }
99
100    pub const fn read(&self) -> bool {
101        self.read
102    }
103
104    pub const fn write(&self) -> bool {
105        self.write
106    }
107
108    pub const fn create_new(&self) -> bool {
109        self.create_new
110    }
111
112    pub const fn create(&self) -> bool {
113        self.create
114    }
115
116    pub const fn append(&self) -> bool {
117        self.append
118    }
119
120    pub const fn truncate(&self) -> bool {
121        self.truncate
122    }
123}
124
125impl fmt::Debug for OpenOptions {
126    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127        self.conf.fmt(f)
128    }
129}
130
131pub struct OpenOptions {
132    opener: Box<dyn FileOpener>,
133    conf: OpenOptionsConfig,
134}
135
136impl OpenOptions {
137    pub fn new(opener: Box<dyn FileOpener>) -> Self {
138        Self {
139            opener,
140            conf: OpenOptionsConfig {
141                read: false,
142                write: false,
143                create_new: false,
144                create: false,
145                append: false,
146                truncate: false,
147            },
148        }
149    }
150
151    pub fn get_config(&self) -> OpenOptionsConfig {
152        self.conf.clone()
153    }
154
155    pub fn options(&mut self, options: OpenOptionsConfig) -> &mut Self {
156        self.conf = options;
157        self
158    }
159
160    pub fn read(&mut self, read: bool) -> &mut Self {
161        self.conf.read = read;
162        self
163    }
164
165    pub fn write(&mut self, write: bool) -> &mut Self {
166        self.conf.write = write;
167        self
168    }
169
170    pub fn append(&mut self, append: bool) -> &mut Self {
171        self.conf.append = append;
172        self
173    }
174
175    pub fn truncate(&mut self, truncate: bool) -> &mut Self {
176        self.conf.truncate = truncate;
177        self
178    }
179
180    pub fn create(&mut self, create: bool) -> &mut Self {
181        self.conf.create = create;
182        self
183    }
184
185    pub fn create_new(&mut self, create_new: bool) -> &mut Self {
186        self.conf.create_new = create_new;
187        self
188    }
189
190    pub fn open<P: AsRef<Path>>(
191        &mut self,
192        path: P,
193    ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
194        self.opener.open(path.as_ref(), &self.conf)
195    }
196}
197
198pub trait VirtualFile: fmt::Debug + Write + Read + Seek + Upcastable {
201    fn last_accessed(&self) -> u64;
203
204    fn last_modified(&self) -> u64;
206
207    fn created_time(&self) -> u64;
209
210    fn size(&self) -> u64;
212
213    fn set_len(&mut self, new_size: u64) -> Result<()>;
216
217    fn unlink(&mut self) -> Result<()>;
219
220    fn sync_to_disk(&self) -> Result<()> {
224        Ok(())
225    }
226
227    fn bytes_available(&self) -> Result<usize> {
229        Ok(self.bytes_available_read()?.unwrap_or(0usize)
230            + self.bytes_available_write()?.unwrap_or(0usize))
231    }
232
233    fn bytes_available_read(&self) -> Result<Option<usize>> {
236        Ok(None)
237    }
238
239    fn bytes_available_write(&self) -> Result<Option<usize>> {
242        Ok(None)
243    }
244
245    fn is_open(&self) -> bool {
248        true
249    }
250
251    fn get_fd(&self) -> Option<FileDescriptor> {
254        None
255    }
256}
257
258pub trait Upcastable {
261    fn upcast_any_ref(&'_ self) -> &'_ dyn Any;
262    fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any;
263    fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
264}
265
266impl<T: Any + fmt::Debug + 'static> Upcastable for T {
267    #[inline]
268    fn upcast_any_ref(&'_ self) -> &'_ dyn Any {
269        self
270    }
271    #[inline]
272    fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any {
273        self
274    }
275    #[inline]
276    fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
277        self
278    }
279}
280
281#[derive(Debug, Copy, Clone, PartialEq, Eq)]
283pub enum StdioMode {
284    Piped,
286    Inherit,
288    Null,
290    Log,
292}
293
294#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)]
296pub enum FsError {
297    #[error("fd not a directory")]
299    BaseNotDirectory,
300    #[error("fd not a file")]
302    NotAFile,
303    #[error("invalid fd")]
305    InvalidFd,
306    #[error("file exists")]
308    AlreadyExists,
309    #[error("lock error")]
311    Lock,
312    #[error("io error")]
315    IOError,
316    #[error("address is in use")]
318    AddressInUse,
319    #[error("address could not be found")]
321    AddressNotAvailable,
322    #[error("broken pipe (was closed)")]
324    BrokenPipe,
325    #[error("connection aborted")]
327    ConnectionAborted,
328    #[error("connection refused")]
330    ConnectionRefused,
331    #[error("connection reset")]
333    ConnectionReset,
334    #[error("operation interrupted")]
336    Interrupted,
337    #[error("invalid internal data")]
339    InvalidData,
340    #[error("invalid input")]
342    InvalidInput,
343    #[error("connection is not open")]
345    NotConnected,
346    #[error("entity not found")]
348    EntityNotFound,
349    #[error("can't access device")]
351    NoDevice,
352    #[error("permission denied")]
354    PermissionDenied,
355    #[error("time out")]
357    TimedOut,
358    #[error("unexpected eof")]
360    UnexpectedEof,
361    #[error("blocking operation. try again")]
363    WouldBlock,
364    #[error("write returned 0")]
366    WriteZero,
367    #[error("directory not empty")]
369    DirectoryNotEmpty,
370    #[error("unknown error found")]
372    UnknownError,
373}
374
375impl From<io::Error> for FsError {
376    fn from(io_error: io::Error) -> Self {
377        match io_error.kind() {
378            io::ErrorKind::AddrInUse => FsError::AddressInUse,
379            io::ErrorKind::AddrNotAvailable => FsError::AddressNotAvailable,
380            io::ErrorKind::AlreadyExists => FsError::AlreadyExists,
381            io::ErrorKind::BrokenPipe => FsError::BrokenPipe,
382            io::ErrorKind::ConnectionAborted => FsError::ConnectionAborted,
383            io::ErrorKind::ConnectionRefused => FsError::ConnectionRefused,
384            io::ErrorKind::ConnectionReset => FsError::ConnectionReset,
385            io::ErrorKind::Interrupted => FsError::Interrupted,
386            io::ErrorKind::InvalidData => FsError::InvalidData,
387            io::ErrorKind::InvalidInput => FsError::InvalidInput,
388            io::ErrorKind::NotConnected => FsError::NotConnected,
389            io::ErrorKind::NotFound => FsError::EntityNotFound,
390            io::ErrorKind::PermissionDenied => FsError::PermissionDenied,
391            io::ErrorKind::TimedOut => FsError::TimedOut,
392            io::ErrorKind::UnexpectedEof => FsError::UnexpectedEof,
393            io::ErrorKind::WouldBlock => FsError::WouldBlock,
394            io::ErrorKind::WriteZero => FsError::WriteZero,
395            io::ErrorKind::Other => FsError::IOError,
396            _ => FsError::UnknownError,
398        }
399    }
400}
401
402#[derive(Debug)]
403pub struct ReadDir {
404    data: Vec<DirEntry>,
406    index: usize,
407}
408
409impl ReadDir {
410    pub fn new(data: Vec<DirEntry>) -> Self {
411        Self { data, index: 0 }
412    }
413}
414
415#[derive(Debug, Clone)]
416pub struct DirEntry {
417    pub path: PathBuf,
418    pub metadata: Result<Metadata>,
420}
421
422impl DirEntry {
423    pub fn path(&self) -> PathBuf {
424        self.path.clone()
425    }
426
427    pub fn metadata(&self) -> Result<Metadata> {
428        self.metadata.clone()
429    }
430
431    pub fn file_type(&self) -> Result<FileType> {
432        let metadata = self.metadata.clone()?;
433        Ok(metadata.file_type())
434    }
435
436    pub fn file_name(&self) -> OsString {
437        self.path
438            .file_name()
439            .unwrap_or(self.path.as_os_str())
440            .to_owned()
441    }
442}
443
444#[allow(clippy::len_without_is_empty)] #[derive(Clone, Debug, Default)]
446pub struct Metadata {
448    pub ft: FileType,
449    pub accessed: u64,
450    pub created: u64,
451    pub modified: u64,
452    pub len: u64,
453}
454
455impl Metadata {
456    pub fn is_file(&self) -> bool {
457        self.ft.is_file()
458    }
459
460    pub fn is_dir(&self) -> bool {
461        self.ft.is_dir()
462    }
463
464    pub fn accessed(&self) -> u64 {
465        self.accessed
466    }
467
468    pub fn created(&self) -> u64 {
469        self.created
470    }
471
472    pub fn modified(&self) -> u64 {
473        self.modified
474    }
475
476    pub fn file_type(&self) -> FileType {
477        self.ft.clone()
478    }
479
480    pub fn len(&self) -> u64 {
481        self.len
482    }
483}
484
485#[derive(Clone, Debug, Default)]
486pub struct FileType {
488    pub dir: bool,
489    pub file: bool,
490    pub symlink: bool,
491    pub char_device: bool,
494    pub block_device: bool,
495    pub socket: bool,
496    pub fifo: bool,
497}
498
499impl FileType {
500    pub fn is_dir(&self) -> bool {
501        self.dir
502    }
503    pub fn is_file(&self) -> bool {
504        self.file
505    }
506    pub fn is_symlink(&self) -> bool {
507        self.symlink
508    }
509    pub fn is_char_device(&self) -> bool {
510        self.char_device
511    }
512    pub fn is_block_device(&self) -> bool {
513        self.block_device
514    }
515    pub fn is_socket(&self) -> bool {
516        self.socket
517    }
518    pub fn is_fifo(&self) -> bool {
519        self.fifo
520    }
521}
522
523impl Iterator for ReadDir {
524    type Item = Result<DirEntry>;
525
526    fn next(&mut self) -> Option<Result<DirEntry>> {
527        if let Some(v) = self.data.get(self.index).cloned() {
528            self.index += 1;
529            return Some(Ok(v));
530        }
531        None
532    }
533}