virtual_fs/mem_fs/
mod.rs

1mod file;
2mod file_opener;
3mod filesystem;
4mod offloaded_file;
5mod stdio;
6
7use file::{File, FileHandle, ReadOnlyFile};
8pub use filesystem::FileSystem;
9pub use offloaded_file::OffloadBackingStore;
10#[cfg(not(feature = "js"))]
11use std::time::{SystemTime, UNIX_EPOCH};
12pub use stdio::{Stderr, Stdin, Stdout};
13#[cfg(feature = "js")]
14pub use web_time::{SystemTime, UNIX_EPOCH};
15
16use crate::Metadata;
17use std::{
18    ffi::{OsStr, OsString},
19    path::PathBuf,
20    sync::{
21        Arc, Mutex,
22        atomic::{AtomicBool, AtomicUsize, Ordering},
23    },
24};
25
26use self::offloaded_file::OffloadedFile;
27
28type Inode = usize;
29const ROOT_INODE: Inode = 0;
30
31#[derive(Debug, Default)]
32struct FileLifecycle {
33    open_handles: AtomicUsize,
34    unlinked: AtomicBool,
35}
36
37impl FileLifecycle {
38    fn opened(&self) {
39        self.open_handles.fetch_add(1, Ordering::AcqRel);
40    }
41
42    fn closed(&self) -> usize {
43        let previous = self
44            .open_handles
45            .fetch_update(Ordering::AcqRel, Ordering::Acquire, |count| {
46                count.checked_sub(1)
47            })
48            .expect("FileLifecycle::closed called with zero open handles");
49        previous - 1
50    }
51
52    fn open_handle_count(&self) -> usize {
53        self.open_handles.load(Ordering::Acquire)
54    }
55
56    fn mark_unlinked(&self) {
57        self.unlinked.store(true, Ordering::Release);
58    }
59
60    fn is_unlinked(&self) -> bool {
61        self.unlinked.load(Ordering::Acquire)
62    }
63}
64
65#[derive(Debug)]
66struct FileNode {
67    inode: Inode,
68    name: OsString,
69    file: File,
70    metadata: Metadata,
71    lifecycle: Arc<FileLifecycle>,
72}
73
74#[derive(Debug)]
75struct ReadOnlyFileNode {
76    inode: Inode,
77    name: OsString,
78    file: ReadOnlyFile,
79    metadata: Metadata,
80    lifecycle: Arc<FileLifecycle>,
81}
82
83#[derive(Debug)]
84struct OffloadedFileNode {
85    inode: Inode,
86    name: OsString,
87    file: OffloadedFile,
88    metadata: Metadata,
89    lifecycle: Arc<FileLifecycle>,
90}
91
92#[derive(Debug)]
93struct ArcFileNode {
94    inode: Inode,
95    name: OsString,
96    fs: Arc<dyn crate::FileSystem + Send + Sync>,
97    path: PathBuf,
98    metadata: Metadata,
99    lifecycle: Arc<FileLifecycle>,
100}
101
102// FIXME: this is broken!!! A `VirtualFile` stores its own offset,
103// so a file stored this way can only be read once!
104#[derive(Debug)]
105struct CustomFileNode {
106    inode: Inode,
107    name: OsString,
108    file: Mutex<Box<dyn crate::VirtualFile + Send + Sync>>,
109    metadata: Metadata,
110    lifecycle: Arc<FileLifecycle>,
111}
112
113#[derive(Debug)]
114struct DirectoryNode {
115    inode: Inode,
116    name: OsString,
117    children: Vec<Inode>,
118    metadata: Metadata,
119}
120
121#[derive(Debug)]
122struct ArcDirectoryNode {
123    inode: Inode,
124    name: OsString,
125    fs: Arc<dyn crate::FileSystem + Send + Sync>,
126    path: PathBuf,
127    metadata: Metadata,
128}
129
130#[derive(Debug)]
131struct SymlinkNode {
132    inode: Inode,
133    name: OsString,
134    target: PathBuf,
135    metadata: Metadata,
136}
137
138#[derive(Debug)]
139enum Node {
140    File(FileNode),
141    OffloadedFile(OffloadedFileNode),
142    ReadOnlyFile(ReadOnlyFileNode),
143    ArcFile(ArcFileNode),
144    CustomFile(CustomFileNode),
145    Symlink(SymlinkNode),
146    Directory(DirectoryNode),
147    ArcDirectory(ArcDirectoryNode),
148}
149
150impl Node {
151    fn inode(&self) -> Inode {
152        *match self {
153            Self::File(FileNode { inode, .. }) => inode,
154            Self::OffloadedFile(OffloadedFileNode { inode, .. }) => inode,
155            Self::ReadOnlyFile(ReadOnlyFileNode { inode, .. }) => inode,
156            Self::ArcFile(ArcFileNode { inode, .. }) => inode,
157            Self::CustomFile(CustomFileNode { inode, .. }) => inode,
158            Self::Symlink(SymlinkNode { inode, .. }) => inode,
159            Self::Directory(DirectoryNode { inode, .. }) => inode,
160            Self::ArcDirectory(ArcDirectoryNode { inode, .. }) => inode,
161        }
162    }
163
164    fn name(&self) -> &OsStr {
165        match self {
166            Self::File(FileNode { name, .. }) => name.as_os_str(),
167            Self::OffloadedFile(OffloadedFileNode { name, .. }) => name.as_os_str(),
168            Self::ReadOnlyFile(ReadOnlyFileNode { name, .. }) => name.as_os_str(),
169            Self::ArcFile(ArcFileNode { name, .. }) => name.as_os_str(),
170            Self::CustomFile(CustomFileNode { name, .. }) => name.as_os_str(),
171            Self::Symlink(SymlinkNode { name, .. }) => name.as_os_str(),
172            Self::Directory(DirectoryNode { name, .. }) => name.as_os_str(),
173            Self::ArcDirectory(ArcDirectoryNode { name, .. }) => name.as_os_str(),
174        }
175    }
176
177    fn metadata(&self) -> &Metadata {
178        match self {
179            Self::File(FileNode { metadata, .. }) => metadata,
180            Self::OffloadedFile(OffloadedFileNode { metadata, .. }) => metadata,
181            Self::ReadOnlyFile(ReadOnlyFileNode { metadata, .. }) => metadata,
182            Self::ArcFile(ArcFileNode { metadata, .. }) => metadata,
183            Self::CustomFile(CustomFileNode { metadata, .. }) => metadata,
184            Self::Symlink(SymlinkNode { metadata, .. }) => metadata,
185            Self::Directory(DirectoryNode { metadata, .. }) => metadata,
186            Self::ArcDirectory(ArcDirectoryNode { metadata, .. }) => metadata,
187        }
188    }
189
190    fn file_lifecycle(&self) -> Option<&Arc<FileLifecycle>> {
191        match self {
192            Self::File(FileNode { lifecycle, .. })
193            | Self::OffloadedFile(OffloadedFileNode { lifecycle, .. })
194            | Self::ReadOnlyFile(ReadOnlyFileNode { lifecycle, .. })
195            | Self::ArcFile(ArcFileNode { lifecycle, .. })
196            | Self::CustomFile(CustomFileNode { lifecycle, .. }) => Some(lifecycle),
197            _ => None,
198        }
199    }
200
201    fn metadata_mut(&mut self) -> &mut Metadata {
202        match self {
203            Self::File(FileNode { metadata, .. }) => metadata,
204            Self::OffloadedFile(OffloadedFileNode { metadata, .. }) => metadata,
205            Self::ReadOnlyFile(ReadOnlyFileNode { metadata, .. }) => metadata,
206            Self::ArcFile(ArcFileNode { metadata, .. }) => metadata,
207            Self::CustomFile(CustomFileNode { metadata, .. }) => metadata,
208            Self::Symlink(SymlinkNode { metadata, .. }) => metadata,
209            Self::Directory(DirectoryNode { metadata, .. }) => metadata,
210            Self::ArcDirectory(ArcDirectoryNode { metadata, .. }) => metadata,
211        }
212    }
213
214    fn set_name(&mut self, new_name: OsString) {
215        match self {
216            Self::File(FileNode { name, .. }) => *name = new_name,
217            Self::OffloadedFile(OffloadedFileNode { name, .. }) => *name = new_name,
218            Self::ReadOnlyFile(ReadOnlyFileNode { name, .. }) => *name = new_name,
219            Self::ArcFile(ArcFileNode { name, .. }) => *name = new_name,
220            Self::CustomFile(CustomFileNode { name, .. }) => *name = new_name,
221            Self::Symlink(SymlinkNode { name, .. }) => *name = new_name,
222            Self::Directory(DirectoryNode { name, .. }) => *name = new_name,
223            Self::ArcDirectory(ArcDirectoryNode { name, .. }) => *name = new_name,
224        }
225    }
226}
227
228fn time() -> u64 {
229    // SAFETY: It's very unlikely that the system returns a time that
230    // is before `UNIX_EPOCH` :-).
231    SystemTime::now()
232        .duration_since(UNIX_EPOCH)
233        .unwrap()
234        .as_nanos() as u64
235}