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#[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 SystemTime::now()
232 .duration_since(UNIX_EPOCH)
233 .unwrap()
234 .as_nanos() as u64
235}