@wasm-oj/server 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/THIRD_PARTY_NOTICES.md +302 -0
  4. package/crates/runtime-core/Cargo.lock +5099 -0
  5. package/crates/runtime-core/Cargo.toml +66 -0
  6. package/crates/runtime-core/README.md +47 -0
  7. package/crates/runtime-core/src/bin/wasm-oj-compiler.rs +418 -0
  8. package/crates/runtime-core/src/bin/wasm-oj-runner.rs +294 -0
  9. package/crates/runtime-core/src/capabilities.rs +118 -0
  10. package/crates/runtime-core/src/compiler.rs +658 -0
  11. package/crates/runtime-core/src/contract.rs +5 -0
  12. package/crates/runtime-core/src/deterministic.rs +1051 -0
  13. package/crates/runtime-core/src/error.rs +58 -0
  14. package/crates/runtime-core/src/filesystem.rs +547 -0
  15. package/crates/runtime-core/src/filesystem_quota.rs +167 -0
  16. package/crates/runtime-core/src/go_compiler_session.rs +297 -0
  17. package/crates/runtime-core/src/interactive.rs +1019 -0
  18. package/crates/runtime-core/src/judge_package.rs +1539 -0
  19. package/crates/runtime-core/src/lib.rs +98 -0
  20. package/crates/runtime-core/src/memory.rs +84 -0
  21. package/crates/runtime-core/src/meter.rs +549 -0
  22. package/crates/runtime-core/src/module_imports.rs +149 -0
  23. package/crates/runtime-core/src/module_policy.rs +714 -0
  24. package/crates/runtime-core/src/output.rs +204 -0
  25. package/crates/runtime-core/src/run/mod.rs +208 -0
  26. package/crates/runtime-core/src/run/native.rs +260 -0
  27. package/crates/runtime-core/src/run/web.rs +229 -0
  28. package/crates/runtime-core/src/run/web_runtime.rs +109 -0
  29. package/crates/runtime-core/src/types.rs +268 -0
  30. package/crates/runtime-core/src/web.rs +83 -0
  31. package/dist/chunks/go-toolchain-Dbt-lp2L.js +426 -0
  32. package/dist/chunks/java-toolchain-DajoRCHu.js +44 -0
  33. package/dist/chunks/python-toolchain-Dx834o2A.js +4 -0
  34. package/dist/chunks/rust-toolchain-CJ3sMxPE.js +252 -0
  35. package/dist/chunks/toolchains-C6KuA1yM.js +224 -0
  36. package/dist/go-stage.mjs +193 -0
  37. package/dist/index.d.ts +221 -0
  38. package/dist/index.js +4393 -0
  39. package/dist/java-stage.mjs +111 -0
  40. package/dist/python-stage.mjs +90 -0
  41. package/dist/rustc-stage.mjs +301 -0
  42. package/dist/server-build-stage.mjs +2564 -0
  43. package/dist/server-runner-stage.mjs +155 -0
  44. package/licenses/fflate-MIT.txt +21 -0
  45. package/licenses/runtime-core-dependencies.html +6253 -0
  46. package/licenses/runtime-core-dependencies.json +3041 -0
  47. package/licenses/wasmer-sdk-MIT.txt +21 -0
  48. package/licenses/wasmer-sdk-dependencies.html +6901 -0
  49. package/licenses/wasmer-sdk-dependencies.json +3013 -0
  50. package/package.json +70 -0
  51. package/rust-toolchain.toml +5 -0
  52. package/testdata/wojjdg02-v2-text.hex +1 -0
  53. package/vendor/shared-buffer/Cargo.toml +22 -0
  54. package/vendor/shared-buffer/LICENSE_APACHE.md +176 -0
  55. package/vendor/shared-buffer/LICENSE_MIT.md +25 -0
  56. package/vendor/shared-buffer/README.md +34 -0
  57. package/vendor/shared-buffer/src/lib.rs +58 -0
  58. package/vendor/shared-buffer/src/mmap.rs +250 -0
  59. package/vendor/shared-buffer/src/owned.rs +389 -0
  60. package/vendor/virtual-fs/Cargo.toml +181 -0
  61. package/vendor/virtual-fs/LICENSE +25 -0
  62. package/vendor/virtual-fs/src/arc_box_file.rs +142 -0
  63. package/vendor/virtual-fs/src/arc_file.rs +182 -0
  64. package/vendor/virtual-fs/src/arc_fs.rs +68 -0
  65. package/vendor/virtual-fs/src/buffer_file.rs +103 -0
  66. package/vendor/virtual-fs/src/builder.rs +232 -0
  67. package/vendor/virtual-fs/src/combine_file.rs +101 -0
  68. package/vendor/virtual-fs/src/cow_file.rs +345 -0
  69. package/vendor/virtual-fs/src/dual_write_file.rs +113 -0
  70. package/vendor/virtual-fs/src/empty_fs.rs +81 -0
  71. package/vendor/virtual-fs/src/filesystems.rs +108 -0
  72. package/vendor/virtual-fs/src/host_fs.rs +1390 -0
  73. package/vendor/virtual-fs/src/lib.rs +782 -0
  74. package/vendor/virtual-fs/src/limiter.rs +252 -0
  75. package/vendor/virtual-fs/src/mem_fs/file.rs +1799 -0
  76. package/vendor/virtual-fs/src/mem_fs/file_opener.rs +941 -0
  77. package/vendor/virtual-fs/src/mem_fs/filesystem.rs +2134 -0
  78. package/vendor/virtual-fs/src/mem_fs/mod.rs +245 -0
  79. package/vendor/virtual-fs/src/mem_fs/offloaded_file.rs +474 -0
  80. package/vendor/virtual-fs/src/mem_fs/stdio.rs +318 -0
  81. package/vendor/virtual-fs/src/mount_fs.rs +2225 -0
  82. package/vendor/virtual-fs/src/null_file.rs +87 -0
  83. package/vendor/virtual-fs/src/ops.rs +364 -0
  84. package/vendor/virtual-fs/src/overlay_fs.rs +2216 -0
  85. package/vendor/virtual-fs/src/passthru_fs.rs +119 -0
  86. package/vendor/virtual-fs/src/pipe.rs +603 -0
  87. package/vendor/virtual-fs/src/random_file.rs +88 -0
  88. package/vendor/virtual-fs/src/special_file.rs +108 -0
  89. package/vendor/virtual-fs/src/static_file.rs +133 -0
  90. package/vendor/virtual-fs/src/static_fs.rs +460 -0
  91. package/vendor/virtual-fs/src/tmp_fs.rs +95 -0
  92. package/vendor/virtual-fs/src/trace_fs.rs +258 -0
  93. package/vendor/virtual-fs/src/webc_volume_fs.rs +829 -0
  94. package/vendor/virtual-fs/src/zero_file.rs +90 -0
@@ -0,0 +1,2134 @@
1
+ //! This module contains the [`FileSystem`] type itself.
2
+
3
+ use self::offloaded_file::OffloadBackingStore;
4
+
5
+ use super::*;
6
+ use crate::{DirEntry, FileType, FsError, Metadata, OpenOptions, ReadDir, Result};
7
+ use futures::future::{BoxFuture, Either};
8
+ use slab::Slab;
9
+ use std::collections::VecDeque;
10
+ use std::convert::identity;
11
+ use std::ffi::OsString;
12
+ use std::fmt;
13
+ use std::path::{Component, Path, PathBuf};
14
+ use std::sync::{Arc, RwLock};
15
+
16
+ /// The in-memory file system!
17
+ ///
18
+ /// This `FileSystem` type can be cloned, it's a light copy of the
19
+ /// `FileSystemInner` (which is behind a `Arc` + `RwLock`).
20
+ #[derive(Clone, Default)]
21
+ pub struct FileSystem {
22
+ pub(super) inner: Arc<RwLock<FileSystemInner>>,
23
+ }
24
+
25
+ impl FileSystem {
26
+ /// Creates an in-memory filesystem whose implicit metadata updates use a
27
+ /// fixed nanosecond Unix timestamp.
28
+ pub fn with_fixed_timestamp(timestamp: u64) -> Self {
29
+ Self {
30
+ inner: Arc::new(RwLock::new(FileSystemInner::new(MetadataClock::Fixed(
31
+ timestamp,
32
+ )))),
33
+ }
34
+ }
35
+
36
+ pub fn set_memory_limiter(&self, limiter: crate::limiter::DynFsMemoryLimiter) {
37
+ self.inner.write().unwrap().limiter = Some(limiter);
38
+ }
39
+
40
+ pub fn new_open_options_ext(&self) -> &FileSystem {
41
+ self
42
+ }
43
+
44
+ /// Uses a mmap'ed file as a cache for file data thus removing the
45
+ /// need to copy the data into memory.
46
+ ///
47
+ /// This is especially important for journals as it means that the
48
+ /// data stored within the journals does not need to be copied
49
+ /// into memory, for very large journals this would otherwise be
50
+ /// a problem.
51
+ pub fn with_backing_offload(self, buffer: OffloadBackingStore) -> Result<Self> {
52
+ let mut lock = self.inner.write().map_err(|_| FsError::Lock)?;
53
+ lock.backing_offload.replace(buffer);
54
+ drop(lock);
55
+ Ok(self)
56
+ }
57
+
58
+ /// Canonicalize a path without validating that it actually exists.
59
+ pub fn canonicalize_unchecked(&self, path: &Path) -> Result<PathBuf> {
60
+ let lock = self.inner.read().map_err(|_| FsError::Lock)?;
61
+ lock.canonicalize_without_inode(path)
62
+ }
63
+
64
+ /// Merge all items from a given source path (directory) of a different file
65
+ /// system into this file system.
66
+ ///
67
+ /// Individual files and directories of the given path are mounted.
68
+ ///
69
+ /// This function is not recursive, only the items in the source_path are
70
+ /// mounted.
71
+ ///
72
+ /// See [`Self::union`] for mounting all inodes recursively.
73
+ pub fn mount_directory_entries(
74
+ &self,
75
+ target_path: &Path,
76
+ other: &Arc<dyn crate::FileSystem + Send + Sync>,
77
+ mut source_path: &Path,
78
+ ) -> Result<()> {
79
+ let fs_lock = self.inner.read().map_err(|_| FsError::Lock)?;
80
+
81
+ if cfg!(windows) {
82
+ // We need to take some care here because
83
+ // canonicalize_without_inode() doesn't accept Windows paths that
84
+ // start with a prefix (drive letters, UNC paths, etc.). If we
85
+ // somehow get one of those paths, we'll automatically trim it away.
86
+ let mut components = source_path.components();
87
+
88
+ if let Some(Component::Prefix(_)) = components.next() {
89
+ source_path = components.as_path();
90
+ }
91
+ }
92
+
93
+ let (_target_path, root_inode) = match fs_lock.canonicalize(target_path) {
94
+ Ok((p, InodeResolution::Found(inode))) => (p, inode),
95
+ Ok((_p, InodeResolution::Redirect(..))) => {
96
+ return Err(FsError::AlreadyExists);
97
+ }
98
+ Err(_) => {
99
+ // Root directory does not exist, so we can just mount.
100
+ return self.mount(target_path.to_path_buf(), other, source_path.to_path_buf());
101
+ }
102
+ };
103
+
104
+ let _root_node = match fs_lock.storage.get(root_inode).unwrap() {
105
+ Node::Directory(dir) => dir,
106
+ _ => {
107
+ return Err(FsError::AlreadyExists);
108
+ }
109
+ };
110
+
111
+ let source_path = fs_lock.canonicalize_without_inode(source_path)?;
112
+
113
+ std::mem::drop(fs_lock);
114
+
115
+ let source = other.read_dir(&source_path)?;
116
+ for entry in source.data {
117
+ let meta = entry.metadata?;
118
+
119
+ let entry_target_path = target_path.join(entry.path.file_name().unwrap());
120
+
121
+ if meta.is_file() {
122
+ self.insert_arc_file_at(entry_target_path, other.clone(), entry.path)?;
123
+ } else if meta.is_dir() {
124
+ self.insert_arc_directory_at(entry_target_path, other.clone(), entry.path)?;
125
+ }
126
+ }
127
+
128
+ Ok(())
129
+ }
130
+
131
+ pub fn union(&self, other: &Arc<dyn crate::FileSystem + Send + Sync>) {
132
+ // Iterate all the directories and files in the other filesystem
133
+ // and create references back to them in this filesystem
134
+ let mut remaining = VecDeque::new();
135
+ remaining.push_back(PathBuf::from("/"));
136
+ while let Some(next) = remaining.pop_back() {
137
+ if next
138
+ .file_name()
139
+ .map(|n| n.to_string_lossy().starts_with(".wh."))
140
+ .unwrap_or(false)
141
+ {
142
+ let rm = next.to_string_lossy();
143
+ let rm = &rm[".wh.".len()..];
144
+ let rm = PathBuf::from(rm);
145
+ let _ = crate::FileSystem::remove_dir(self, rm.as_path());
146
+ let _ = crate::FileSystem::remove_file(self, rm.as_path());
147
+ continue;
148
+ }
149
+ let _ = crate::FileSystem::create_dir(self, next.as_path());
150
+
151
+ let dir = match other.read_dir(next.as_path()) {
152
+ Ok(dir) => dir,
153
+ Err(_) => {
154
+ // TODO: propagate errors (except NotFound)
155
+ continue;
156
+ }
157
+ };
158
+
159
+ for sub_dir_res in dir {
160
+ let sub_dir = match sub_dir_res {
161
+ Ok(sub_dir) => sub_dir,
162
+ Err(_) => {
163
+ // TODO: propagate errors (except NotFound)
164
+ continue;
165
+ }
166
+ };
167
+
168
+ match sub_dir.file_type() {
169
+ Ok(t) if t.is_dir() => {
170
+ remaining.push_back(sub_dir.path());
171
+ }
172
+ Ok(t) if t.is_file() => {
173
+ if sub_dir.file_name().to_string_lossy().starts_with(".wh.") {
174
+ let rm = next.to_string_lossy();
175
+ let rm = &rm[".wh.".len()..];
176
+ let rm = PathBuf::from(rm);
177
+ let _ = crate::FileSystem::remove_dir(self, rm.as_path());
178
+ let _ = crate::FileSystem::remove_file(self, rm.as_path());
179
+ continue;
180
+ }
181
+ let _ = self
182
+ .new_open_options_ext()
183
+ .insert_arc_file(sub_dir.path(), other.clone());
184
+ }
185
+ _ => {}
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ pub fn mount(
192
+ &self,
193
+ target_path: PathBuf,
194
+ other: &Arc<dyn crate::FileSystem + Send + Sync>,
195
+ source_path: PathBuf,
196
+ ) -> Result<()> {
197
+ if crate::FileSystem::read_dir(self, target_path.as_path()).is_ok() {
198
+ return Err(FsError::AlreadyExists);
199
+ }
200
+
201
+ let (inode_of_parent, name_of_directory) = {
202
+ // Read lock.
203
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
204
+
205
+ // Canonicalize the path without checking the path exists,
206
+ // because it's about to be created.
207
+ let path = guard.canonicalize_without_inode(target_path.as_path())?;
208
+
209
+ // Check the path has a parent.
210
+ let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
211
+
212
+ // Check the directory name.
213
+ let name_of_directory = path
214
+ .file_name()
215
+ .ok_or(FsError::InvalidInput)?
216
+ .to_os_string();
217
+
218
+ // Find the parent inode.
219
+ let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
220
+ InodeResolution::Found(a) => a,
221
+ InodeResolution::Redirect(..) => {
222
+ return Err(FsError::AlreadyExists);
223
+ }
224
+ };
225
+
226
+ (inode_of_parent, name_of_directory)
227
+ };
228
+
229
+ {
230
+ // Write lock.
231
+ let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
232
+
233
+ // Creating the directory in the storage.
234
+ let time = fs.now();
235
+ let inode_of_directory = fs.storage.vacant_entry().key();
236
+ let real_inode_of_directory = fs.insert_node(Node::ArcDirectory(ArcDirectoryNode {
237
+ inode: inode_of_directory,
238
+ name: name_of_directory,
239
+ fs: other.clone(),
240
+ path: source_path,
241
+ metadata: {
242
+ Metadata {
243
+ ft: FileType {
244
+ dir: true,
245
+ ..Default::default()
246
+ },
247
+ accessed: time,
248
+ created: time,
249
+ modified: time,
250
+ len: 0,
251
+ }
252
+ },
253
+ }))?;
254
+
255
+ assert_eq!(
256
+ inode_of_directory, real_inode_of_directory,
257
+ "new directory inode should have been correctly calculated",
258
+ );
259
+
260
+ // Adding the new directory to its parent.
261
+ fs.add_child_to_node(inode_of_parent, inode_of_directory)?;
262
+ }
263
+
264
+ Ok(())
265
+ }
266
+
267
+ pub fn create_symlink(&self, source: &Path, target: &Path) -> Result<()> {
268
+ let (inode_of_parent, name_of_symlink) = {
269
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
270
+ let path = guard.canonicalize_without_inode(target)?;
271
+ let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
272
+ let name_of_symlink = path
273
+ .file_name()
274
+ .ok_or(FsError::InvalidInput)?
275
+ .to_os_string();
276
+ let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
277
+ InodeResolution::Found(a) => a,
278
+ InodeResolution::Redirect(fs, mut redirected_path) => {
279
+ redirected_path.push(&name_of_symlink);
280
+ drop(guard);
281
+ let fs_ref: &dyn crate::FileSystem = fs.as_ref();
282
+ if let Some(mem_fs) = fs_ref.downcast_ref::<Self>() {
283
+ return mem_fs.create_symlink(source, redirected_path.as_path());
284
+ }
285
+ return Err(FsError::Unsupported);
286
+ }
287
+ };
288
+ (inode_of_parent, name_of_symlink)
289
+ };
290
+
291
+ let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
292
+ if fs.canonicalize(target).is_ok() {
293
+ return Err(FsError::AlreadyExists);
294
+ }
295
+
296
+ let time = fs.now();
297
+ let inode_of_symlink = fs.storage.vacant_entry().key();
298
+ let real_inode_of_symlink = fs.insert_node(Node::Symlink(SymlinkNode {
299
+ inode: inode_of_symlink,
300
+ name: name_of_symlink,
301
+ target: source.to_path_buf(),
302
+ metadata: {
303
+ Metadata {
304
+ ft: FileType {
305
+ symlink: true,
306
+ ..Default::default()
307
+ },
308
+ accessed: time,
309
+ created: time,
310
+ modified: time,
311
+ len: source.as_os_str().len() as u64,
312
+ }
313
+ },
314
+ }))?;
315
+
316
+ assert_eq!(
317
+ inode_of_symlink, real_inode_of_symlink,
318
+ "new symlink inode should have been correctly calculated",
319
+ );
320
+
321
+ fs.add_child_to_node(inode_of_parent, inode_of_symlink)?;
322
+ Ok(())
323
+ }
324
+ }
325
+
326
+ impl crate::FileSystem for FileSystem {
327
+ fn readlink(&self, path: &Path) -> Result<PathBuf> {
328
+ // Read lock.
329
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
330
+
331
+ // Canonicalize the path.
332
+ let (_, inode_of_directory) = guard.canonicalize(path)?;
333
+ match inode_of_directory {
334
+ InodeResolution::Found(inode) => match guard.storage.get(inode) {
335
+ Some(Node::Symlink(SymlinkNode { target, .. })) => Ok(target.clone()),
336
+ _ => Err(FsError::InvalidInput),
337
+ },
338
+ InodeResolution::Redirect(fs, path) => fs.readlink(path.as_path()),
339
+ }
340
+ }
341
+
342
+ fn read_dir(&self, path: &Path) -> Result<ReadDir> {
343
+ // Read lock.
344
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
345
+
346
+ fn rebase_entries(entries: &mut ReadDir, base: &Path) {
347
+ for entry in &mut entries.data {
348
+ let name = entry.file_name();
349
+ entry.path = base.join(name);
350
+ }
351
+ }
352
+
353
+ // Canonicalize the path.
354
+ let (guest_path, inode_of_directory) = guard.canonicalize(path)?;
355
+ let inode_of_directory = match inode_of_directory {
356
+ InodeResolution::Found(a) => a,
357
+ InodeResolution::Redirect(fs, redirect_path) => {
358
+ let mut entries = fs.read_dir(redirect_path.as_path())?;
359
+ rebase_entries(&mut entries, &guest_path);
360
+ return Ok(entries);
361
+ }
362
+ };
363
+
364
+ // Check it's a directory and fetch the immediate children as `DirEntry`.
365
+ let inode = guard.storage.get(inode_of_directory);
366
+ let children = match inode {
367
+ Some(Node::Directory(DirectoryNode { children, .. })) => children
368
+ .iter()
369
+ .filter_map(|inode| guard.storage.get(*inode))
370
+ .map(|node| DirEntry {
371
+ path: {
372
+ let mut entry_path = path.to_path_buf();
373
+ entry_path.push(node.name());
374
+
375
+ entry_path
376
+ },
377
+ metadata: Ok(node.metadata().clone()),
378
+ })
379
+ .collect(),
380
+
381
+ Some(Node::ArcDirectory(ArcDirectoryNode {
382
+ fs, path: fs_path, ..
383
+ })) => {
384
+ let mut entries = fs.read_dir(fs_path.as_path())?;
385
+ rebase_entries(&mut entries, &guest_path);
386
+ return Ok(entries);
387
+ }
388
+
389
+ _ => return Err(FsError::InvalidInput),
390
+ };
391
+
392
+ Ok(ReadDir::new(children))
393
+ }
394
+
395
+ fn create_dir(&self, path: &Path) -> Result<()> {
396
+ if self.read_dir(path).is_ok() {
397
+ return Err(FsError::AlreadyExists);
398
+ }
399
+
400
+ let (inode_of_parent, name_of_directory) = {
401
+ // Read lock.
402
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
403
+
404
+ // Canonicalize the path without checking the path exists,
405
+ // because it's about to be created.
406
+ let path = guard.canonicalize_without_inode(path)?;
407
+
408
+ // Check the path has a parent.
409
+ let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
410
+
411
+ // Check the directory name.
412
+ let name_of_directory = path
413
+ .file_name()
414
+ .ok_or(FsError::InvalidInput)?
415
+ .to_os_string();
416
+
417
+ // Find the parent inode.
418
+ let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
419
+ InodeResolution::Found(a) => a,
420
+ InodeResolution::Redirect(fs, mut path) => {
421
+ drop(guard);
422
+ path.push(name_of_directory);
423
+ return fs.create_dir(path.as_path());
424
+ }
425
+ };
426
+
427
+ (inode_of_parent, name_of_directory)
428
+ };
429
+
430
+ if self.read_dir(path).is_ok() {
431
+ return Err(FsError::AlreadyExists);
432
+ }
433
+
434
+ {
435
+ // Write lock.
436
+ let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
437
+
438
+ // Creating the directory in the storage.
439
+ let time = fs.now();
440
+ let inode_of_directory = fs.storage.vacant_entry().key();
441
+ let real_inode_of_directory = fs.insert_node(Node::Directory(DirectoryNode {
442
+ inode: inode_of_directory,
443
+ name: name_of_directory,
444
+ children: Vec::new(),
445
+ metadata: {
446
+ Metadata {
447
+ ft: FileType {
448
+ dir: true,
449
+ ..Default::default()
450
+ },
451
+ accessed: time,
452
+ created: time,
453
+ modified: time,
454
+ len: 0,
455
+ }
456
+ },
457
+ }))?;
458
+
459
+ assert_eq!(
460
+ inode_of_directory, real_inode_of_directory,
461
+ "new directory inode should have been correctly calculated",
462
+ );
463
+
464
+ // Adding the new directory to its parent.
465
+ fs.add_child_to_node(inode_of_parent, inode_of_directory)?;
466
+ }
467
+
468
+ Ok(())
469
+ }
470
+
471
+ fn create_symlink(&self, source: &Path, target: &Path) -> Result<()> {
472
+ self.create_symlink(source, target)
473
+ }
474
+
475
+ fn remove_dir(&self, path: &Path) -> Result<()> {
476
+ let (inode_of_parent, position, inode_of_directory) = {
477
+ // Read lock.
478
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
479
+
480
+ // Canonicalize the path.
481
+ let (path, _) = guard.canonicalize(path)?;
482
+
483
+ // Check the path has a parent.
484
+ let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
485
+
486
+ // Check the directory name.
487
+ let name_of_directory = path
488
+ .file_name()
489
+ .ok_or(FsError::InvalidInput)?
490
+ .to_os_string();
491
+
492
+ // Find the parent inode.
493
+ let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
494
+ InodeResolution::Found(a) => a,
495
+ InodeResolution::Redirect(fs, mut parent_path) => {
496
+ drop(guard);
497
+ parent_path.push(name_of_directory);
498
+ return fs.remove_dir(parent_path.as_path());
499
+ }
500
+ };
501
+
502
+ // Get the child index to remove in the parent node, in
503
+ // addition to the inode of the directory to remove.
504
+ let (position, inode_of_directory) = guard
505
+ .as_parent_get_position_and_inode_of_directory(
506
+ inode_of_parent,
507
+ &name_of_directory,
508
+ DirectoryMustBeEmpty::Yes,
509
+ )?;
510
+
511
+ (inode_of_parent, position, inode_of_directory)
512
+ };
513
+
514
+ let inode_of_directory = match inode_of_directory {
515
+ InodeResolution::Found(a) => a,
516
+ InodeResolution::Redirect(fs, path) => {
517
+ return fs.remove_dir(path.as_path());
518
+ }
519
+ };
520
+
521
+ {
522
+ // Write lock.
523
+ let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
524
+
525
+ // Remove the directory from the storage.
526
+ fs.remove_node(inode_of_directory);
527
+
528
+ // Remove the child from the parent directory.
529
+ fs.remove_child_from_node(inode_of_parent, position)?;
530
+ }
531
+
532
+ Ok(())
533
+ }
534
+
535
+ fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>> {
536
+ Box::pin(async move {
537
+ let name_of_to;
538
+
539
+ // Read lock.
540
+ let (name_of_from, inode_of_from_parent, name_of_to, inode_of_to_parent) = {
541
+ let fs = self.inner.read().map_err(|_| FsError::Lock)?;
542
+
543
+ let from = fs.canonicalize_without_inode(from)?;
544
+ let to = fs.canonicalize_without_inode(to)?;
545
+
546
+ // Check the paths have parents.
547
+ let parent_of_from = from.parent().ok_or(FsError::BaseNotDirectory)?;
548
+ let parent_of_to = to.parent().ok_or(FsError::BaseNotDirectory)?;
549
+
550
+ // Check the names.
551
+ let name_of_from = from
552
+ .file_name()
553
+ .ok_or(FsError::InvalidInput)?
554
+ .to_os_string();
555
+ name_of_to = to.file_name().ok_or(FsError::InvalidInput)?.to_os_string();
556
+
557
+ // Find the parent inodes.
558
+ let inode_of_from_parent = match fs.inode_of_parent(parent_of_from)? {
559
+ InodeResolution::Found(a) => Either::Left(a),
560
+ InodeResolution::Redirect(fs, mut path) => {
561
+ path.push(&name_of_from);
562
+ Either::Right((fs, path))
563
+ }
564
+ };
565
+ let inode_of_to_parent = match fs.inode_of_parent(parent_of_to)? {
566
+ InodeResolution::Found(a) => Either::Left(a),
567
+ InodeResolution::Redirect(fs, mut path) => {
568
+ path.push(&name_of_to);
569
+ Either::Right((fs, path))
570
+ }
571
+ };
572
+
573
+ (
574
+ name_of_from,
575
+ inode_of_from_parent,
576
+ name_of_to,
577
+ inode_of_to_parent,
578
+ )
579
+ };
580
+
581
+ match (inode_of_from_parent, inode_of_to_parent) {
582
+ // Rename within this MemFS instance
583
+ (Either::Left(inode_of_from_parent), Either::Left(inode_of_to_parent)) => {
584
+ let fs = self.inner.read().map_err(|_| FsError::Lock)?;
585
+
586
+ // Find the inode of the dest file if it exists
587
+ let maybe_position_and_inode_of_file = fs
588
+ .as_parent_get_position_and_inode_of_file(
589
+ inode_of_to_parent,
590
+ &name_of_to,
591
+ )?;
592
+
593
+ // Get the child indexes to update in the parent nodes, in
594
+ // addition to the inode of the directory to update.
595
+ let (position_of_from, inode) = fs
596
+ .as_parent_get_position_and_inode(inode_of_from_parent, &name_of_from)?
597
+ .ok_or(FsError::EntryNotFound)?;
598
+
599
+ let (
600
+ (position_of_from, inode, inode_of_from_parent),
601
+ (inode_of_to_parent, name_of_to),
602
+ inode_dest,
603
+ ) = (
604
+ (position_of_from, inode, inode_of_from_parent),
605
+ (inode_of_to_parent, name_of_to),
606
+ maybe_position_and_inode_of_file,
607
+ );
608
+
609
+ let inode = match inode {
610
+ InodeResolution::Found(a) => a,
611
+ InodeResolution::Redirect(..) => {
612
+ return Err(FsError::InvalidInput);
613
+ }
614
+ };
615
+
616
+ drop(fs);
617
+
618
+ {
619
+ // Write lock.
620
+ let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
621
+
622
+ if let Some((position, inode_of_file)) = inode_dest {
623
+ // Remove the file from the storage.
624
+ match inode_of_file {
625
+ InodeResolution::Found(inode_of_file) => {
626
+ fs.remove_node(inode_of_file);
627
+ }
628
+ InodeResolution::Redirect(..) => {
629
+ return Err(FsError::InvalidInput);
630
+ }
631
+ }
632
+
633
+ fs.remove_child_from_node(inode_of_to_parent, position)?;
634
+ }
635
+
636
+ // Update the file name, and update the modified time.
637
+ fs.update_node_name(inode, name_of_to)?;
638
+
639
+ // The parents are different. Let's update them.
640
+ if inode_of_from_parent != inode_of_to_parent {
641
+ // Remove the file from its parent, and update the
642
+ // modified time.
643
+ fs.remove_child_from_node(inode_of_from_parent, position_of_from)?;
644
+
645
+ // Add the file to its new parent, and update the modified
646
+ // time.
647
+ fs.add_child_to_node(inode_of_to_parent, inode)?;
648
+ }
649
+ // Otherwise, we need to at least update the modified time of the parent.
650
+ else {
651
+ let time = fs.now();
652
+ let mut inode = fs.storage.get_mut(inode_of_from_parent);
653
+ match inode.as_mut() {
654
+ Some(Node::Directory(node)) => node.metadata.modified = time,
655
+ Some(Node::ArcDirectory(node)) => node.metadata.modified = time,
656
+ _ => return Err(FsError::UnknownError),
657
+ }
658
+ }
659
+ }
660
+
661
+ Ok(())
662
+ }
663
+
664
+ // Rename within the same mounted FS instance
665
+ (Either::Right((from_fs, from_path)), Either::Right((to_fs, to_path)))
666
+ if Arc::ptr_eq(&from_fs, &to_fs) =>
667
+ {
668
+ let same_fs = from_fs;
669
+ same_fs.rename(&from_path, &to_path).await
670
+ }
671
+
672
+ // Rename across file systems; we need to do a create and a delete
673
+ _ => crate::ops::move_across_filesystems(self, self, from, to).await,
674
+ }
675
+ })
676
+ }
677
+
678
+ fn metadata(&self, path: &Path) -> Result<Metadata> {
679
+ // Read lock.
680
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
681
+ match guard.inode_of(path)? {
682
+ InodeResolution::Found(inode) => Ok(guard
683
+ .storage
684
+ .get(inode)
685
+ .ok_or(FsError::UnknownError)?
686
+ .metadata()
687
+ .clone()),
688
+ InodeResolution::Redirect(fs, path) => {
689
+ drop(guard);
690
+ fs.metadata(path.as_path())
691
+ }
692
+ }
693
+ }
694
+
695
+ fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
696
+ // Read lock.
697
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
698
+ match guard.inode_of(path)? {
699
+ InodeResolution::Found(inode) => Ok(guard
700
+ .storage
701
+ .get(inode)
702
+ .ok_or(FsError::UnknownError)?
703
+ .metadata()
704
+ .clone()),
705
+ InodeResolution::Redirect(fs, path) => {
706
+ drop(guard);
707
+ fs.symlink_metadata(path.as_path())
708
+ }
709
+ }
710
+ }
711
+
712
+ fn remove_file(&self, path: &Path) -> Result<()> {
713
+ let (inode_of_parent, position, inode_of_file) = {
714
+ // Read lock.
715
+ let guard = self.inner.read().map_err(|_| FsError::Lock)?;
716
+
717
+ // Canonicalize the path.
718
+ let path = guard.canonicalize_without_inode(path)?;
719
+
720
+ // Check the path has a parent.
721
+ let parent_of_path = path.parent().ok_or(FsError::BaseNotDirectory)?;
722
+
723
+ // Check the file name.
724
+ let name_of_file = path
725
+ .file_name()
726
+ .ok_or(FsError::InvalidInput)?
727
+ .to_os_string();
728
+
729
+ // Find the parent inode.
730
+ let inode_of_parent = match guard.inode_of_parent(parent_of_path)? {
731
+ InodeResolution::Found(a) => a,
732
+ InodeResolution::Redirect(fs, mut parent_path) => {
733
+ parent_path.push(name_of_file);
734
+ return fs.remove_file(parent_path.as_path());
735
+ }
736
+ };
737
+
738
+ // Find the inode of the file if it exists, along with its position.
739
+ let maybe_position_and_inode_of_file =
740
+ guard.as_parent_get_position_and_inode_of_file(inode_of_parent, &name_of_file)?;
741
+
742
+ match maybe_position_and_inode_of_file {
743
+ Some((position, inode_of_file)) => (inode_of_parent, position, inode_of_file),
744
+ None => return Err(FsError::EntryNotFound),
745
+ }
746
+ };
747
+
748
+ let inode_of_file = match inode_of_file {
749
+ InodeResolution::Found(a) => a,
750
+ InodeResolution::Redirect(fs, path) => {
751
+ return fs.remove_file(path.as_path());
752
+ }
753
+ };
754
+
755
+ {
756
+ // Write lock.
757
+ let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
758
+ fs.unlink_file_inode(inode_of_parent, position, inode_of_file)?;
759
+ }
760
+
761
+ Ok(())
762
+ }
763
+
764
+ fn new_open_options(&self) -> OpenOptions<'_> {
765
+ OpenOptions::new(self)
766
+ }
767
+ }
768
+
769
+ impl fmt::Debug for FileSystem {
770
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
771
+ let fs: &FileSystemInner = &self.inner.read().unwrap();
772
+
773
+ fs.fmt(formatter)
774
+ }
775
+ }
776
+
777
+ /// The core of the file system. It contains a collection of `Node`s,
778
+ /// indexed by their respective `Inode` in a slab.
779
+ pub(super) struct FileSystemInner {
780
+ pub(super) storage: Slab<Node>,
781
+ pub(super) backing_offload: Option<OffloadBackingStore>,
782
+ pub(super) limiter: Option<crate::limiter::DynFsMemoryLimiter>,
783
+ clock: MetadataClock,
784
+ }
785
+
786
+ #[derive(Debug)]
787
+ pub(super) enum InodeResolution {
788
+ Found(Inode),
789
+ Redirect(Arc<dyn crate::FileSystem + Send + Sync + 'static>, PathBuf),
790
+ }
791
+
792
+ impl InodeResolution {
793
+ #[allow(dead_code)]
794
+ pub fn unwrap(&self) -> Inode {
795
+ match self {
796
+ Self::Found(a) => *a,
797
+ Self::Redirect(..) => {
798
+ panic!("failed to unwrap the inode as the resolution is a redirect");
799
+ }
800
+ }
801
+ }
802
+ }
803
+
804
+ impl FileSystemInner {
805
+ fn new(clock: MetadataClock) -> Self {
806
+ let time = clock.now();
807
+ let mut storage = Slab::new();
808
+ storage.insert(Node::Directory(DirectoryNode {
809
+ inode: ROOT_INODE,
810
+ name: OsString::from("/"),
811
+ children: Vec::new(),
812
+ metadata: Metadata {
813
+ ft: FileType {
814
+ dir: true,
815
+ ..Default::default()
816
+ },
817
+ accessed: time,
818
+ created: time,
819
+ modified: time,
820
+ len: 0,
821
+ },
822
+ }));
823
+ Self {
824
+ storage,
825
+ backing_offload: None,
826
+ limiter: None,
827
+ clock,
828
+ }
829
+ }
830
+
831
+ pub(super) fn now(&self) -> u64 {
832
+ self.clock.now()
833
+ }
834
+
835
+ pub(super) fn insert_node(&mut self, node: Node) -> Result<Inode> {
836
+ if let Some(limiter) = &self.limiter {
837
+ limiter.on_create_entry()?;
838
+ }
839
+ Ok(self.storage.insert(node))
840
+ }
841
+
842
+ pub(super) fn remove_node(&mut self, inode: Inode) -> Node {
843
+ let node = self.storage.remove(inode);
844
+ if let Some(limiter) = &self.limiter {
845
+ limiter.on_remove_entry();
846
+ }
847
+ node
848
+ }
849
+
850
+ pub(super) fn unlink_file_inode(
851
+ &mut self,
852
+ inode_of_parent: Inode,
853
+ position: usize,
854
+ inode_of_file: Inode,
855
+ ) -> Result<()> {
856
+ let remove_storage = match self.storage.get(inode_of_file) {
857
+ Some(node) => {
858
+ if let Some(lifecycle) = node.file_lifecycle() {
859
+ lifecycle.mark_unlinked();
860
+ lifecycle.open_handle_count() == 0
861
+ } else {
862
+ true
863
+ }
864
+ }
865
+ None => return Err(FsError::EntryNotFound),
866
+ };
867
+
868
+ if remove_storage {
869
+ self.remove_node(inode_of_file);
870
+ }
871
+
872
+ self.remove_child_from_node(inode_of_parent, position)
873
+ }
874
+
875
+ /// Get the inode associated to a path if it exists.
876
+ pub(super) fn inode_of(&self, path: &Path) -> Result<InodeResolution> {
877
+ // SAFETY: The root node always exists, so it's safe to unwrap here.
878
+ let mut node = self.storage.get(ROOT_INODE).unwrap();
879
+ let mut components = path.components();
880
+
881
+ match components.next() {
882
+ Some(Component::RootDir) => {}
883
+ _ => return Err(FsError::BaseNotDirectory),
884
+ }
885
+
886
+ while let Some(component) = components.next() {
887
+ node = match node {
888
+ Node::Directory(DirectoryNode { children, .. }) => children
889
+ .iter()
890
+ .filter_map(|inode| self.storage.get(*inode))
891
+ .find(|node| node.name() == component.as_os_str())
892
+ .ok_or(FsError::EntryNotFound)?,
893
+ Node::ArcDirectory(ArcDirectoryNode {
894
+ fs, path: fs_path, ..
895
+ }) => {
896
+ let mut path = fs_path.clone();
897
+ path.push(PathBuf::from(component.as_os_str()));
898
+ for component in components.by_ref() {
899
+ path.push(PathBuf::from(component.as_os_str()));
900
+ }
901
+ return Ok(InodeResolution::Redirect(fs.clone(), path));
902
+ }
903
+ _ => return Err(FsError::BaseNotDirectory),
904
+ };
905
+ }
906
+
907
+ Ok(InodeResolution::Found(node.inode()))
908
+ }
909
+
910
+ /// Get the inode associated to a “parent path”. The returned
911
+ /// inode necessarily represents a directory.
912
+ pub(super) fn inode_of_parent(&self, parent_path: &Path) -> Result<InodeResolution> {
913
+ match self.inode_of(parent_path)? {
914
+ InodeResolution::Found(inode_of_parent) => {
915
+ // Ensure it is a directory.
916
+ match self.storage.get(inode_of_parent) {
917
+ Some(Node::Directory(DirectoryNode { .. })) => {
918
+ Ok(InodeResolution::Found(inode_of_parent))
919
+ }
920
+ Some(Node::ArcDirectory(ArcDirectoryNode { fs, path, .. })) => {
921
+ Ok(InodeResolution::Redirect(fs.clone(), path.clone()))
922
+ }
923
+ _ => Err(FsError::BaseNotDirectory),
924
+ }
925
+ }
926
+ InodeResolution::Redirect(fs, path) => Ok(InodeResolution::Redirect(fs, path)),
927
+ }
928
+ }
929
+
930
+ /// From the inode of a parent node (so, a directory), returns the
931
+ /// child index of `name_of_directory` along with its inode.
932
+ pub(super) fn as_parent_get_position_and_inode_of_directory(
933
+ &self,
934
+ inode_of_parent: Inode,
935
+ name_of_directory: &OsString,
936
+ directory_must_be_empty: DirectoryMustBeEmpty,
937
+ ) -> Result<(usize, InodeResolution)> {
938
+ match self.storage.get(inode_of_parent) {
939
+ Some(Node::Directory(DirectoryNode { children, .. })) => children
940
+ .iter()
941
+ .enumerate()
942
+ .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
943
+ .find_map(|(nth, node)| match node {
944
+ Node::Directory(DirectoryNode {
945
+ inode,
946
+ name,
947
+ children,
948
+ ..
949
+ }) if name.as_os_str() == name_of_directory => {
950
+ if directory_must_be_empty.no() || children.is_empty() {
951
+ Some(Ok((nth, InodeResolution::Found(*inode))))
952
+ } else {
953
+ Some(Err(FsError::DirectoryNotEmpty))
954
+ }
955
+ }
956
+ Node::ArcDirectory(ArcDirectoryNode { name, fs, path, .. })
957
+ if name.as_os_str() == name_of_directory =>
958
+ {
959
+ Some(Ok((0, InodeResolution::Redirect(fs.clone(), path.clone()))))
960
+ }
961
+ _ => None,
962
+ })
963
+ .ok_or(FsError::InvalidInput)
964
+ .and_then(identity), // flatten
965
+
966
+ Some(Node::ArcDirectory(ArcDirectoryNode {
967
+ fs, path: fs_path, ..
968
+ })) => {
969
+ let mut path = fs_path.clone();
970
+ path.push(name_of_directory);
971
+ Ok((0, InodeResolution::Redirect(fs.clone(), path)))
972
+ }
973
+
974
+ _ => Err(FsError::BaseNotDirectory),
975
+ }
976
+ }
977
+
978
+ /// From the inode of a parent node (so, a directory), returns the
979
+ /// child index of `name_of_file` along with its inode.
980
+ pub(super) fn as_parent_get_position_and_inode_of_file(
981
+ &self,
982
+ inode_of_parent: Inode,
983
+ name_of_file: &OsString,
984
+ ) -> Result<Option<(usize, InodeResolution)>> {
985
+ match self.storage.get(inode_of_parent) {
986
+ Some(Node::Directory(DirectoryNode { children, .. })) => children
987
+ .iter()
988
+ .enumerate()
989
+ .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
990
+ .find_map(|(nth, node)| match node {
991
+ Node::File(FileNode { inode, name, .. })
992
+ | Node::OffloadedFile(OffloadedFileNode { inode, name, .. })
993
+ | Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. })
994
+ | Node::CustomFile(CustomFileNode { inode, name, .. })
995
+ | Node::ArcFile(ArcFileNode { inode, name, .. })
996
+ | Node::Symlink(SymlinkNode { inode, name, .. })
997
+ if name.as_os_str() == name_of_file =>
998
+ {
999
+ Some(Some((nth, InodeResolution::Found(*inode))))
1000
+ }
1001
+ _ => None,
1002
+ })
1003
+ .or(Some(None))
1004
+ .ok_or(FsError::InvalidInput),
1005
+
1006
+ Some(Node::ArcDirectory(ArcDirectoryNode {
1007
+ fs, path: fs_path, ..
1008
+ })) => {
1009
+ let mut path = fs_path.clone();
1010
+ path.push(name_of_file);
1011
+ Ok(Some((0, InodeResolution::Redirect(fs.clone(), path))))
1012
+ }
1013
+
1014
+ _ => Err(FsError::BaseNotDirectory),
1015
+ }
1016
+ }
1017
+
1018
+ /// From the inode of a parent node (so, a directory), returns the
1019
+ /// child index of `name_of` along with its inode, whatever the
1020
+ /// type of inode is (directory or file).
1021
+ fn as_parent_get_position_and_inode(
1022
+ &self,
1023
+ inode_of_parent: Inode,
1024
+ name_of: &OsString,
1025
+ ) -> Result<Option<(usize, InodeResolution)>> {
1026
+ match self.storage.get(inode_of_parent) {
1027
+ Some(Node::Directory(DirectoryNode { children, .. })) => children
1028
+ .iter()
1029
+ .enumerate()
1030
+ .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
1031
+ .find_map(|(nth, node)| match node {
1032
+ Node::File(FileNode { inode, name, .. })
1033
+ | Node::OffloadedFile(OffloadedFileNode { inode, name, .. })
1034
+ | Node::Directory(DirectoryNode { inode, name, .. })
1035
+ | Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. })
1036
+ | Node::CustomFile(CustomFileNode { inode, name, .. })
1037
+ | Node::ArcFile(ArcFileNode { inode, name, .. })
1038
+ if name.as_os_str() == name_of =>
1039
+ {
1040
+ Some(Some((nth, InodeResolution::Found(*inode))))
1041
+ }
1042
+ _ => None,
1043
+ })
1044
+ .or(Some(None))
1045
+ .ok_or(FsError::InvalidInput),
1046
+
1047
+ Some(Node::ArcDirectory(ArcDirectoryNode {
1048
+ fs, path: fs_path, ..
1049
+ })) => {
1050
+ let mut path = fs_path.clone();
1051
+ path.push(name_of);
1052
+ Ok(Some((0, InodeResolution::Redirect(fs.clone(), path))))
1053
+ }
1054
+
1055
+ _ => Err(FsError::BaseNotDirectory),
1056
+ }
1057
+ }
1058
+
1059
+ /// Set a new name for the node represented by `inode`.
1060
+ pub(super) fn update_node_name(&mut self, inode: Inode, new_name: OsString) -> Result<()> {
1061
+ let time = self.now();
1062
+ let node = self.storage.get_mut(inode).ok_or(FsError::UnknownError)?;
1063
+
1064
+ node.set_name(new_name);
1065
+ node.metadata_mut().modified = time;
1066
+
1067
+ Ok(())
1068
+ }
1069
+
1070
+ /// Add a child to a directory node represented by `inode`.
1071
+ ///
1072
+ /// This function also updates the modified time of the directory.
1073
+ ///
1074
+ /// # Safety
1075
+ ///
1076
+ /// `inode` must represents an existing directory.
1077
+ pub(super) fn add_child_to_node(&mut self, inode: Inode, new_child: Inode) -> Result<()> {
1078
+ let time = self.now();
1079
+ match self.storage.get_mut(inode) {
1080
+ Some(Node::Directory(DirectoryNode {
1081
+ children,
1082
+ metadata: Metadata { modified, .. },
1083
+ ..
1084
+ })) => {
1085
+ children.push(new_child);
1086
+ *modified = time;
1087
+
1088
+ Ok(())
1089
+ }
1090
+ _ => Err(FsError::UnknownError),
1091
+ }
1092
+ }
1093
+
1094
+ /// Remove the child at position `position` of a directory node
1095
+ /// represented by `inode`.
1096
+ ///
1097
+ /// This function also updates the modified time of the directory.
1098
+ ///
1099
+ /// # Safety
1100
+ ///
1101
+ /// `inode` must represents an existing directory.
1102
+ pub(super) fn remove_child_from_node(&mut self, inode: Inode, position: usize) -> Result<()> {
1103
+ let time = self.now();
1104
+ match self.storage.get_mut(inode) {
1105
+ Some(Node::Directory(DirectoryNode {
1106
+ children,
1107
+ metadata: Metadata { modified, .. },
1108
+ ..
1109
+ })) => {
1110
+ children.remove(position);
1111
+ *modified = time;
1112
+
1113
+ Ok(())
1114
+ }
1115
+ _ => Err(FsError::UnknownError),
1116
+ }
1117
+ }
1118
+
1119
+ /// Canonicalize a path, i.e. try to resolve to a canonical,
1120
+ /// absolute form of the path with all intermediate components
1121
+ /// normalized:
1122
+ ///
1123
+ /// * A path must starts with a root (`/`),
1124
+ /// * A path can contain `..` or `.` components,
1125
+ /// * A path must not contain a Windows prefix (`C:` or `\\server`),
1126
+ /// * A normalized path exists in the file system.
1127
+ pub(super) fn canonicalize(&self, path: &Path) -> Result<(PathBuf, InodeResolution)> {
1128
+ let new_path = self.canonicalize_without_inode(path)?;
1129
+ let inode = self.inode_of(&new_path)?;
1130
+
1131
+ Ok((new_path, inode))
1132
+ }
1133
+
1134
+ /// Like `Self::canonicalize` but without returning the inode of
1135
+ /// the path, which means that there is no guarantee that the path
1136
+ /// exists in the file system.
1137
+ pub(super) fn canonicalize_without_inode(&self, path: &Path) -> Result<PathBuf> {
1138
+ let mut components = path.components();
1139
+
1140
+ match components.next() {
1141
+ Some(Component::RootDir) => {}
1142
+ _ => return Err(FsError::InvalidInput),
1143
+ }
1144
+
1145
+ let mut new_path = PathBuf::with_capacity(path.as_os_str().len());
1146
+ new_path.push("/");
1147
+
1148
+ for component in components {
1149
+ match component {
1150
+ // That's an error to get a `RootDir` a second time.
1151
+ Component::RootDir => return Err(FsError::UnknownError),
1152
+
1153
+ // Nothing to do on `new_path`.
1154
+ Component::CurDir => (),
1155
+
1156
+ // Pop the lastly inserted component on `new_path` if
1157
+ // any, otherwise it's an error.
1158
+ Component::ParentDir => {
1159
+ if !new_path.pop() {
1160
+ return Err(FsError::InvalidInput);
1161
+ }
1162
+ }
1163
+
1164
+ // A normal
1165
+ Component::Normal(name) => {
1166
+ new_path.push(name);
1167
+ }
1168
+
1169
+ // We don't support Windows path prefix.
1170
+ Component::Prefix(_) => return Err(FsError::InvalidInput),
1171
+ }
1172
+ }
1173
+
1174
+ Ok(new_path)
1175
+ }
1176
+ }
1177
+
1178
+ impl fmt::Debug for FileSystemInner {
1179
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1180
+ writeln!(
1181
+ formatter,
1182
+ "\n{inode:<8} {ty:<4} name",
1183
+ inode = "inode",
1184
+ ty = "type",
1185
+ )?;
1186
+
1187
+ fn debug(
1188
+ nodes: Vec<&Node>,
1189
+ slf: &FileSystemInner,
1190
+ formatter: &mut fmt::Formatter<'_>,
1191
+ indentation: usize,
1192
+ ) -> fmt::Result {
1193
+ for node in nodes {
1194
+ writeln!(
1195
+ formatter,
1196
+ "{inode:<8} {ty:<4} {indentation_symbol:indentation_width$}{name}",
1197
+ inode = node.inode(),
1198
+ ty = match node {
1199
+ Node::File { .. } => "file",
1200
+ Node::OffloadedFile { .. } => "offloaded-file",
1201
+ Node::ReadOnlyFile { .. } => "ro-file",
1202
+ Node::ArcFile { .. } => "arc-file",
1203
+ Node::CustomFile { .. } => "custom-file",
1204
+ Node::Symlink { .. } => "symlink",
1205
+ Node::Directory { .. } => "dir",
1206
+ Node::ArcDirectory { .. } => "arc-dir",
1207
+ },
1208
+ name = node.name().to_string_lossy(),
1209
+ indentation_symbol = " ",
1210
+ indentation_width = indentation * 2 + 1,
1211
+ )?;
1212
+
1213
+ if let Node::Directory(DirectoryNode { children, .. }) = node {
1214
+ debug(
1215
+ children
1216
+ .iter()
1217
+ .filter_map(|inode| slf.storage.get(*inode))
1218
+ .collect(),
1219
+ slf,
1220
+ formatter,
1221
+ indentation + 1,
1222
+ )?;
1223
+ }
1224
+ }
1225
+
1226
+ Ok(())
1227
+ }
1228
+
1229
+ debug(
1230
+ vec![self.storage.get(ROOT_INODE).unwrap()],
1231
+ self,
1232
+ formatter,
1233
+ 0,
1234
+ )
1235
+ }
1236
+ }
1237
+
1238
+ impl Default for FileSystemInner {
1239
+ fn default() -> Self {
1240
+ Self::new(MetadataClock::System)
1241
+ }
1242
+ }
1243
+
1244
+ #[allow(dead_code)] // The `No` variant.
1245
+ pub(super) enum DirectoryMustBeEmpty {
1246
+ Yes,
1247
+ No,
1248
+ }
1249
+
1250
+ impl DirectoryMustBeEmpty {
1251
+ pub(super) fn yes(&self) -> bool {
1252
+ matches!(self, Self::Yes)
1253
+ }
1254
+
1255
+ pub(super) fn no(&self) -> bool {
1256
+ !self.yes()
1257
+ }
1258
+ }
1259
+
1260
+ #[cfg(test)]
1261
+ mod test_filesystem {
1262
+ use std::{path::Path, sync::Arc};
1263
+
1264
+ use shared_buffer::OwnedBuffer;
1265
+ use tokio::io::AsyncReadExt;
1266
+
1267
+ use crate::{DirEntry, FileSystem as FS, FileType, FsError, mem_fs::*, ops};
1268
+
1269
+ macro_rules! path {
1270
+ ($path:expr) => {
1271
+ std::path::Path::new($path)
1272
+ };
1273
+
1274
+ (buf $path:expr) => {
1275
+ std::path::PathBuf::from($path)
1276
+ };
1277
+ }
1278
+
1279
+ #[tokio::test]
1280
+ async fn test_new_filesystem() {
1281
+ let fs = FileSystem::default();
1282
+ let fs_inner = fs.inner.read().unwrap();
1283
+
1284
+ assert_eq!(fs_inner.storage.len(), 1, "storage has a root");
1285
+ assert!(
1286
+ matches!(
1287
+ fs_inner.storage.get(ROOT_INODE),
1288
+ Some(Node::Directory(DirectoryNode {
1289
+ inode: ROOT_INODE,
1290
+ name,
1291
+ children,
1292
+ ..
1293
+ })) if name == "/" && children.is_empty(),
1294
+ ),
1295
+ "storage has a well-defined root",
1296
+ );
1297
+ }
1298
+
1299
+ #[tokio::test]
1300
+ async fn test_create_dir() {
1301
+ let fs = FileSystem::default();
1302
+
1303
+ assert_eq!(
1304
+ fs.create_dir(path!("/")),
1305
+ Err(FsError::AlreadyExists),
1306
+ "creating the root which already exists",
1307
+ );
1308
+
1309
+ assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating a directory");
1310
+
1311
+ {
1312
+ let fs_inner = fs.inner.read().unwrap();
1313
+ assert_eq!(
1314
+ fs_inner.storage.len(),
1315
+ 2,
1316
+ "storage contains the new directory"
1317
+ );
1318
+ assert!(
1319
+ matches!(
1320
+ fs_inner.storage.get(ROOT_INODE),
1321
+ Some(Node::Directory(DirectoryNode {
1322
+ inode: ROOT_INODE,
1323
+ name,
1324
+ children,
1325
+ ..
1326
+ })) if name == "/" && children == &[1]
1327
+ ),
1328
+ "the root is updated and well-defined",
1329
+ );
1330
+ assert!(
1331
+ matches!(
1332
+ fs_inner.storage.get(1),
1333
+ Some(Node::Directory(DirectoryNode {
1334
+ inode: 1,
1335
+ name,
1336
+ children,
1337
+ ..
1338
+ })) if name == "foo" && children.is_empty(),
1339
+ ),
1340
+ "the new directory is well-defined",
1341
+ );
1342
+ }
1343
+
1344
+ assert_eq!(
1345
+ fs.create_dir(path!("/foo/bar")),
1346
+ Ok(()),
1347
+ "creating a sub-directory",
1348
+ );
1349
+
1350
+ {
1351
+ let fs_inner = fs.inner.read().unwrap();
1352
+ assert_eq!(
1353
+ fs_inner.storage.len(),
1354
+ 3,
1355
+ "storage contains the new sub-directory",
1356
+ );
1357
+ assert!(
1358
+ matches!(
1359
+ fs_inner.storage.get(ROOT_INODE),
1360
+ Some(Node::Directory(DirectoryNode {
1361
+ inode: ROOT_INODE,
1362
+ name,
1363
+ children,
1364
+ ..
1365
+ })) if name == "/" && children == &[1]
1366
+ ),
1367
+ "the root is updated again and well-defined",
1368
+ );
1369
+ assert!(
1370
+ matches!(
1371
+ fs_inner.storage.get(1),
1372
+ Some(Node::Directory(DirectoryNode {
1373
+ inode: 1,
1374
+ name,
1375
+ children,
1376
+ ..
1377
+ })) if name == "foo" && children == &[2]
1378
+ ),
1379
+ "the new directory is updated and well-defined",
1380
+ );
1381
+ assert!(
1382
+ matches!(
1383
+ fs_inner.storage.get(2),
1384
+ Some(Node::Directory(DirectoryNode {
1385
+ inode: 2,
1386
+ name,
1387
+ children,
1388
+ ..
1389
+ })) if name == "bar" && children.is_empty()
1390
+ ),
1391
+ "the new directory is well-defined",
1392
+ );
1393
+ }
1394
+ }
1395
+
1396
+ #[tokio::test]
1397
+ async fn test_remove_dir() {
1398
+ let fs = FileSystem::default();
1399
+
1400
+ assert_eq!(
1401
+ fs.remove_dir(path!("/")),
1402
+ Err(FsError::BaseNotDirectory),
1403
+ "removing a directory that has no parent",
1404
+ );
1405
+
1406
+ assert_eq!(
1407
+ fs.remove_dir(path!("/foo")),
1408
+ Err(FsError::EntryNotFound),
1409
+ "cannot remove a directory that doesn't exist",
1410
+ );
1411
+
1412
+ assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating a directory");
1413
+
1414
+ assert_eq!(
1415
+ fs.create_dir(path!("/foo/bar")),
1416
+ Ok(()),
1417
+ "creating a sub-directory",
1418
+ );
1419
+
1420
+ {
1421
+ let fs_inner = fs.inner.read().unwrap();
1422
+ assert_eq!(
1423
+ fs_inner.storage.len(),
1424
+ 3,
1425
+ "storage contains all the directories",
1426
+ );
1427
+ }
1428
+
1429
+ assert_eq!(
1430
+ fs.remove_dir(path!("/foo")),
1431
+ Err(FsError::DirectoryNotEmpty),
1432
+ "removing a directory that has children",
1433
+ );
1434
+
1435
+ assert_eq!(
1436
+ fs.remove_dir(path!("/foo/bar")),
1437
+ Ok(()),
1438
+ "removing a sub-directory",
1439
+ );
1440
+
1441
+ assert_eq!(fs.remove_dir(path!("/foo")), Ok(()), "removing a directory");
1442
+
1443
+ {
1444
+ let fs_inner = fs.inner.read().unwrap();
1445
+ assert_eq!(
1446
+ fs_inner.storage.len(),
1447
+ 1,
1448
+ "storage contains all the directories",
1449
+ );
1450
+ }
1451
+ }
1452
+
1453
+ #[tokio::test]
1454
+ async fn test_rename() {
1455
+ let fs = FileSystem::default();
1456
+
1457
+ let fs2 = FileSystem::default();
1458
+ fs2.create_dir(path!("/boz")).unwrap();
1459
+ let arc_fs2: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(fs2);
1460
+
1461
+ assert_eq!(
1462
+ fs.rename(path!("/"), path!("/bar")).await,
1463
+ Err(FsError::BaseNotDirectory),
1464
+ "renaming a directory that has no parent",
1465
+ );
1466
+ assert_eq!(
1467
+ fs.rename(path!("/foo"), path!("/")).await,
1468
+ Err(FsError::BaseNotDirectory),
1469
+ "renaming to a directory that has no parent",
1470
+ );
1471
+
1472
+ assert_eq!(fs.create_dir(path!("/foo")), Ok(()));
1473
+ assert_eq!(fs.create_dir(path!("/foo/qux")), Ok(()));
1474
+
1475
+ assert_eq!(
1476
+ fs.rename(path!("/foo"), path!("/bar/baz")).await,
1477
+ Err(FsError::EntryNotFound),
1478
+ "renaming to a directory that has parent that doesn't exist",
1479
+ );
1480
+
1481
+ assert_eq!(fs.create_dir(path!("/bar")), Ok(()));
1482
+
1483
+ assert!(
1484
+ fs.new_open_options()
1485
+ .write(true)
1486
+ .create_new(true)
1487
+ .open(path!("/bar/hello1.txt"))
1488
+ .is_ok(),
1489
+ "creating a new file (`hello1.txt`)",
1490
+ );
1491
+ assert!(
1492
+ fs.new_open_options()
1493
+ .write(true)
1494
+ .create_new(true)
1495
+ .open(path!("/bar/hello2.txt"))
1496
+ .is_ok(),
1497
+ "creating a new file (`hello2.txt`)",
1498
+ );
1499
+
1500
+ fs.mount(path!(buf "/mnt"), &arc_fs2, path!(buf "/"))
1501
+ .unwrap();
1502
+
1503
+ {
1504
+ let fs_inner = fs.inner.read().unwrap();
1505
+
1506
+ assert_eq!(fs_inner.storage.len(), 7, "storage has all files");
1507
+ assert!(
1508
+ matches!(
1509
+ fs_inner.storage.get(ROOT_INODE),
1510
+ Some(Node::Directory(DirectoryNode {
1511
+ inode: ROOT_INODE,
1512
+ name,
1513
+ children,
1514
+ ..
1515
+ })) if name == "/" && children == &[1, 3, 6]
1516
+ ),
1517
+ "`/` contains `foo` and `bar` and `mnt`",
1518
+ );
1519
+ assert!(
1520
+ matches!(
1521
+ fs_inner.storage.get(1),
1522
+ Some(Node::Directory(DirectoryNode {
1523
+ inode: 1,
1524
+ name,
1525
+ children,
1526
+ ..
1527
+ })) if name == "foo" && children == &[2]
1528
+ ),
1529
+ "`foo` contains `qux`",
1530
+ );
1531
+ assert!(
1532
+ matches!(
1533
+ fs_inner.storage.get(2),
1534
+ Some(Node::Directory(DirectoryNode {
1535
+ inode: 2,
1536
+ name,
1537
+ children,
1538
+ ..
1539
+ })) if name == "qux" && children.is_empty()
1540
+ ),
1541
+ "`qux` is empty",
1542
+ );
1543
+ assert!(
1544
+ matches!(
1545
+ fs_inner.storage.get(3),
1546
+ Some(Node::Directory(DirectoryNode {
1547
+ inode: 3,
1548
+ name,
1549
+ children,
1550
+ ..
1551
+ })) if name == "bar" && children == &[4, 5]
1552
+ ),
1553
+ "`bar` is contains `hello.txt`",
1554
+ );
1555
+ assert!(
1556
+ matches!(
1557
+ fs_inner.storage.get(4),
1558
+ Some(Node::File(FileNode {
1559
+ inode: 4,
1560
+ name,
1561
+ ..
1562
+ })) if name == "hello1.txt"
1563
+ ),
1564
+ "`hello1.txt` exists",
1565
+ );
1566
+ assert!(
1567
+ matches!(
1568
+ fs_inner.storage.get(5),
1569
+ Some(Node::File(FileNode {
1570
+ inode: 5,
1571
+ name,
1572
+ ..
1573
+ })) if name == "hello2.txt"
1574
+ ),
1575
+ "`hello2.txt` exists",
1576
+ );
1577
+ }
1578
+
1579
+ assert_eq!(
1580
+ fs.rename(path!("/bar/hello2.txt"), path!("/foo/world2.txt"))
1581
+ .await,
1582
+ Ok(()),
1583
+ "renaming (and moving) a file",
1584
+ );
1585
+
1586
+ assert_eq!(
1587
+ fs.rename(path!("/foo"), path!("/bar/baz")).await,
1588
+ Ok(()),
1589
+ "renaming a directory",
1590
+ );
1591
+
1592
+ assert_eq!(
1593
+ fs.rename(path!("/mnt/boz"), path!("/mnt/cat")).await,
1594
+ Ok(()),
1595
+ "renaming a directory within a mounted FS"
1596
+ );
1597
+
1598
+ assert!(
1599
+ arc_fs2.read_dir(path!("/cat")).is_ok(),
1600
+ "The directory was renamed in the inner FS"
1601
+ );
1602
+
1603
+ assert_eq!(
1604
+ fs.rename(path!("/bar/hello1.txt"), path!("/bar/world1.txt"))
1605
+ .await,
1606
+ Ok(()),
1607
+ "renaming a file (in the same directory)",
1608
+ );
1609
+
1610
+ {
1611
+ let fs_inner = fs.inner.read().unwrap();
1612
+
1613
+ dbg!(&fs_inner);
1614
+
1615
+ assert_eq!(
1616
+ fs_inner.storage.len(),
1617
+ 7,
1618
+ "storage has still all directories"
1619
+ );
1620
+ assert!(
1621
+ matches!(
1622
+ fs_inner.storage.get(ROOT_INODE),
1623
+ Some(Node::Directory(DirectoryNode {
1624
+ inode: ROOT_INODE,
1625
+ name,
1626
+ children,
1627
+ ..
1628
+ })) if name == "/" && children == &[3, 6]
1629
+ ),
1630
+ "`/` contains `bar` and `mnt`",
1631
+ );
1632
+ assert!(
1633
+ matches!(
1634
+ fs_inner.storage.get(1),
1635
+ Some(Node::Directory(DirectoryNode {
1636
+ inode: 1,
1637
+ name,
1638
+ children,
1639
+ ..
1640
+ })) if name == "baz" && children == &[2, 5]
1641
+ ),
1642
+ "`foo` has been renamed to `baz` and contains `qux` and `world2.txt`",
1643
+ );
1644
+ assert!(
1645
+ matches!(
1646
+ fs_inner.storage.get(2),
1647
+ Some(Node::Directory(DirectoryNode {
1648
+ inode: 2,
1649
+ name,
1650
+ children,
1651
+ ..
1652
+ })) if name == "qux" && children.is_empty()
1653
+ ),
1654
+ "`qux` is empty",
1655
+ );
1656
+ assert!(
1657
+ matches!(
1658
+ fs_inner.storage.get(3),
1659
+ Some(Node::Directory(DirectoryNode {
1660
+ inode: 3,
1661
+ name,
1662
+ children,
1663
+ ..
1664
+ })) if name == "bar" && children == &[4, 1]
1665
+ ),
1666
+ "`bar` contains `bar` (ex `foo`) and `world1.txt` (ex `hello1`)",
1667
+ );
1668
+ assert!(
1669
+ matches!(
1670
+ fs_inner.storage.get(4),
1671
+ Some(Node::File(FileNode {
1672
+ inode: 4,
1673
+ name,
1674
+ ..
1675
+ })) if name == "world1.txt"
1676
+ ),
1677
+ "`hello1.txt` has been renamed to `world1.txt`",
1678
+ );
1679
+ assert!(
1680
+ matches!(
1681
+ fs_inner.storage.get(5),
1682
+ Some(Node::File(FileNode {
1683
+ inode: 5,
1684
+ name,
1685
+ ..
1686
+ })) if name == "world2.txt"
1687
+ ),
1688
+ "`hello2.txt` has been renamed to `world2.txt`",
1689
+ );
1690
+ }
1691
+ }
1692
+
1693
+ #[tokio::test]
1694
+ async fn test_metadata() {
1695
+ use std::thread::sleep;
1696
+ use std::time::Duration;
1697
+
1698
+ let fs = FileSystem::default();
1699
+ let root_metadata = fs.metadata(path!("/"));
1700
+
1701
+ assert!(matches!(
1702
+ root_metadata,
1703
+ Ok(Metadata {
1704
+ ft: FileType { dir: true, .. },
1705
+ accessed,
1706
+ created,
1707
+ modified,
1708
+ len: 0
1709
+ }) if accessed == created && created == modified && modified > 0
1710
+ ));
1711
+
1712
+ assert_eq!(fs.create_dir(path!("/foo")), Ok(()));
1713
+
1714
+ let foo_metadata = fs.metadata(path!("/foo"));
1715
+ assert!(foo_metadata.is_ok());
1716
+ let foo_metadata = foo_metadata.unwrap();
1717
+
1718
+ assert!(matches!(
1719
+ foo_metadata,
1720
+ Metadata {
1721
+ ft: FileType { dir: true, .. },
1722
+ accessed,
1723
+ created,
1724
+ modified,
1725
+ len: 0
1726
+ } if accessed == created && created == modified && modified > 0
1727
+ ));
1728
+
1729
+ sleep(Duration::from_secs(3));
1730
+
1731
+ assert_eq!(fs.rename(path!("/foo"), path!("/bar")).await, Ok(()));
1732
+
1733
+ assert!(
1734
+ matches!(
1735
+ fs.metadata(path!("/bar")),
1736
+ Ok(Metadata {
1737
+ ft: FileType { dir: true, .. },
1738
+ accessed,
1739
+ created,
1740
+ modified,
1741
+ len: 0
1742
+ }) if
1743
+ accessed == foo_metadata.accessed &&
1744
+ created == foo_metadata.created &&
1745
+ modified > foo_metadata.modified
1746
+ ),
1747
+ "the modified time is updated when file is renamed",
1748
+ );
1749
+ assert!(
1750
+ matches!(
1751
+ fs.metadata(path!("/")),
1752
+ Ok(Metadata {
1753
+ ft: FileType { dir: true, .. },
1754
+ accessed,
1755
+ created,
1756
+ modified,
1757
+ len: 0
1758
+ }) if
1759
+ accessed <= foo_metadata.accessed &&
1760
+ created <= foo_metadata.created &&
1761
+ modified > foo_metadata.modified
1762
+ ),
1763
+ "the modified time of the parent is updated when file is renamed \n{:?}\n{:?}",
1764
+ fs.metadata(path!("/")),
1765
+ foo_metadata,
1766
+ );
1767
+ }
1768
+
1769
+ #[tokio::test]
1770
+ async fn test_remove_file() {
1771
+ let fs = FileSystem::default();
1772
+
1773
+ let mut file = fs
1774
+ .new_open_options()
1775
+ .read(true)
1776
+ .write(true)
1777
+ .create_new(true)
1778
+ .open(path!("/foo.txt"))
1779
+ .expect("creating a new file");
1780
+ use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
1781
+ file.write_all(b"foo").await.expect("write before remove");
1782
+
1783
+ {
1784
+ let fs_inner = fs.inner.read().unwrap();
1785
+
1786
+ assert_eq!(fs_inner.storage.len(), 2, "storage has all files");
1787
+ assert!(
1788
+ matches!(
1789
+ fs_inner.storage.get(ROOT_INODE),
1790
+ Some(Node::Directory(DirectoryNode {
1791
+ inode: ROOT_INODE,
1792
+ name,
1793
+ children,
1794
+ ..
1795
+ })) if name == "/" && children == &[1]
1796
+ ),
1797
+ "`/` contains `foo.txt`",
1798
+ );
1799
+ assert!(
1800
+ matches!(
1801
+ fs_inner.storage.get(1),
1802
+ Some(Node::File(FileNode {
1803
+ inode: 1,
1804
+ name,
1805
+ ..
1806
+ })) if name == "foo.txt"
1807
+ ),
1808
+ "`foo.txt` exists and is a file",
1809
+ );
1810
+ }
1811
+
1812
+ assert_eq!(
1813
+ fs.remove_file(path!("/foo.txt")),
1814
+ Ok(()),
1815
+ "removing a file that exists",
1816
+ );
1817
+
1818
+ {
1819
+ let fs_inner = fs.inner.read().unwrap();
1820
+
1821
+ assert_eq!(
1822
+ fs_inner.storage.len(),
1823
+ 2,
1824
+ "storage keeps the unlinked file alive while a handle is open"
1825
+ );
1826
+ assert!(
1827
+ matches!(
1828
+ fs_inner.storage.get(ROOT_INODE),
1829
+ Some(Node::Directory(DirectoryNode {
1830
+ inode: ROOT_INODE,
1831
+ name,
1832
+ children,
1833
+ ..
1834
+ })) if name == "/" && children.is_empty()
1835
+ ),
1836
+ "`/` is empty",
1837
+ );
1838
+ }
1839
+
1840
+ assert!(
1841
+ matches!(
1842
+ fs.new_open_options().read(true).open(path!("/foo.txt")),
1843
+ Err(FsError::EntryNotFound)
1844
+ ),
1845
+ "the removed path can no longer be reopened",
1846
+ );
1847
+
1848
+ file.write_all(b"bar")
1849
+ .await
1850
+ .expect("write after remove_file should still work");
1851
+ file.seek(std::io::SeekFrom::Start(0))
1852
+ .await
1853
+ .expect("rewind after remove_file");
1854
+ let mut contents = String::new();
1855
+ file.read_to_string(&mut contents)
1856
+ .await
1857
+ .expect("read after remove_file should still work");
1858
+ assert_eq!(contents, "foobar");
1859
+
1860
+ drop(file);
1861
+
1862
+ let fs_inner = fs.inner.read().unwrap();
1863
+ assert_eq!(
1864
+ fs_inner.storage.len(),
1865
+ 1,
1866
+ "storage drops the file once the last open handle closes"
1867
+ );
1868
+
1869
+ assert_eq!(
1870
+ fs.remove_file(path!("/foo.txt")),
1871
+ Err(FsError::EntryNotFound),
1872
+ "removing a file that exists",
1873
+ );
1874
+ }
1875
+
1876
+ #[tokio::test]
1877
+ async fn test_readdir() {
1878
+ let fs = FileSystem::default();
1879
+
1880
+ assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating `foo`");
1881
+ assert_eq!(fs.create_dir(path!("/foo/sub")), Ok(()), "creating `sub`");
1882
+ assert_eq!(fs.create_dir(path!("/bar")), Ok(()), "creating `bar`");
1883
+ assert_eq!(fs.create_dir(path!("/baz")), Ok(()), "creating `bar`");
1884
+ assert!(
1885
+ fs.new_open_options()
1886
+ .write(true)
1887
+ .create_new(true)
1888
+ .open(path!("/a.txt"))
1889
+ .is_ok(),
1890
+ "creating `a.txt`",
1891
+ );
1892
+ assert!(
1893
+ fs.new_open_options()
1894
+ .write(true)
1895
+ .create_new(true)
1896
+ .open(path!("/b.txt"))
1897
+ .is_ok(),
1898
+ "creating `b.txt`",
1899
+ );
1900
+
1901
+ let readdir = fs.read_dir(path!("/"));
1902
+
1903
+ assert!(readdir.is_ok(), "reading the directory `/`");
1904
+
1905
+ let mut readdir = readdir.unwrap();
1906
+
1907
+ assert!(
1908
+ matches!(
1909
+ readdir.next(),
1910
+ Some(Ok(DirEntry {
1911
+ path,
1912
+ metadata: Ok(Metadata { ft, .. }),
1913
+ }))
1914
+ if path.as_path() == path!("/foo") && ft.is_dir()
1915
+ ),
1916
+ "checking entry #1",
1917
+ );
1918
+ assert!(
1919
+ matches!(
1920
+ readdir.next(),
1921
+ Some(Ok(DirEntry {
1922
+ path,
1923
+ metadata: Ok(Metadata { ft, .. }),
1924
+ }))
1925
+ if path.as_path() == path!("/bar") && ft.is_dir()
1926
+ ),
1927
+ "checking entry #2",
1928
+ );
1929
+ assert!(
1930
+ matches!(
1931
+ readdir.next(),
1932
+ Some(Ok(DirEntry {
1933
+ path,
1934
+ metadata: Ok(Metadata { ft, .. }),
1935
+ }))
1936
+ if path.as_path() == path!("/baz") && ft.is_dir()
1937
+ ),
1938
+ "checking entry #3",
1939
+ );
1940
+ assert!(
1941
+ matches!(
1942
+ readdir.next(),
1943
+ Some(Ok(DirEntry {
1944
+ path,
1945
+ metadata: Ok(Metadata { ft, .. }),
1946
+ }))
1947
+ if path.as_path() == path!("/a.txt") && ft.is_file()
1948
+ ),
1949
+ "checking entry #4",
1950
+ );
1951
+ assert!(
1952
+ matches!(
1953
+ readdir.next(),
1954
+ Some(Ok(DirEntry {
1955
+ path,
1956
+ metadata: Ok(Metadata { ft, .. }),
1957
+ }))
1958
+ if path.as_path() == path!("/b.txt") && ft.is_file()
1959
+ ),
1960
+ "checking entry #5",
1961
+ );
1962
+ assert!(readdir.next().is_none(), "no more entries");
1963
+ }
1964
+
1965
+ #[tokio::test]
1966
+ async fn test_canonicalize() {
1967
+ let fs = FileSystem::default();
1968
+
1969
+ assert_eq!(fs.create_dir(path!("/foo")), Ok(()), "creating `foo`");
1970
+ assert_eq!(fs.create_dir(path!("/foo/bar")), Ok(()), "creating `bar`");
1971
+ assert_eq!(
1972
+ fs.create_dir(path!("/foo/bar/baz")),
1973
+ Ok(()),
1974
+ "creating `baz`",
1975
+ );
1976
+ assert_eq!(
1977
+ fs.create_dir(path!("/foo/bar/baz/qux")),
1978
+ Ok(()),
1979
+ "creating `qux`",
1980
+ );
1981
+ assert!(
1982
+ fs.new_open_options()
1983
+ .write(true)
1984
+ .create_new(true)
1985
+ .open(path!("/foo/bar/baz/qux/hello.txt"))
1986
+ .is_ok(),
1987
+ "creating `hello.txt`",
1988
+ );
1989
+
1990
+ let fs_inner = fs.inner.read().unwrap();
1991
+
1992
+ assert_eq!(
1993
+ fs_inner
1994
+ .canonicalize(path!("/"))
1995
+ .map(|(a, b)| (a, b.unwrap())),
1996
+ Ok((path!(buf "/"), ROOT_INODE)),
1997
+ "canonicalizing `/`",
1998
+ );
1999
+ assert_eq!(
2000
+ fs_inner
2001
+ .canonicalize(path!("foo"))
2002
+ .map(|(a, b)| (a, b.unwrap())),
2003
+ Err(FsError::InvalidInput),
2004
+ "canonicalizing `foo`",
2005
+ );
2006
+ assert_eq!(
2007
+ fs_inner
2008
+ .canonicalize(path!("/././././foo/"))
2009
+ .map(|(a, b)| (a, b.unwrap())),
2010
+ Ok((path!(buf "/foo"), 1)),
2011
+ "canonicalizing `/././././foo/`",
2012
+ );
2013
+ assert_eq!(
2014
+ fs_inner
2015
+ .canonicalize(path!("/foo/bar//"))
2016
+ .map(|(a, b)| (a, b.unwrap())),
2017
+ Ok((path!(buf "/foo/bar"), 2)),
2018
+ "canonicalizing `/foo/bar//`",
2019
+ );
2020
+ assert_eq!(
2021
+ fs_inner
2022
+ .canonicalize(path!("/foo/bar/../bar"))
2023
+ .map(|(a, b)| (a, b.unwrap())),
2024
+ Ok((path!(buf "/foo/bar"), 2)),
2025
+ "canonicalizing `/foo/bar/../bar`",
2026
+ );
2027
+ assert_eq!(
2028
+ fs_inner
2029
+ .canonicalize(path!("/foo/bar/../.."))
2030
+ .map(|(a, b)| (a, b.unwrap())),
2031
+ Ok((path!(buf "/"), ROOT_INODE)),
2032
+ "canonicalizing `/foo/bar/../..`",
2033
+ );
2034
+ assert_eq!(
2035
+ fs_inner
2036
+ .canonicalize(path!("/foo/bar/../../.."))
2037
+ .map(|(a, b)| (a, b.unwrap())),
2038
+ Err(FsError::InvalidInput),
2039
+ "canonicalizing `/foo/bar/../../..`",
2040
+ );
2041
+ assert_eq!(
2042
+ fs_inner
2043
+ .canonicalize(path!("C:/foo/"))
2044
+ .map(|(a, b)| (a, b.unwrap())),
2045
+ Err(FsError::InvalidInput),
2046
+ "canonicalizing `C:/foo/`",
2047
+ );
2048
+ assert_eq!(
2049
+ fs_inner
2050
+ .canonicalize(path!(
2051
+ "/foo/./../foo/bar/../../foo/bar/./baz/./../baz/qux/../../baz/./qux/hello.txt"
2052
+ ))
2053
+ .map(|(a, b)| (a, b.unwrap())),
2054
+ Ok((path!(buf "/foo/bar/baz/qux/hello.txt"), 5)),
2055
+ "canonicalizing a crazily stupid path name",
2056
+ );
2057
+ }
2058
+
2059
+ #[tokio::test]
2060
+ #[ignore = "Not yet supported. See https://github.com/wasmerio/wasmer/issues/3678"]
2061
+ async fn mount_to_overlapping_directories() {
2062
+ let top_level = FileSystem::default();
2063
+ ops::touch(&top_level, "/file.txt").unwrap();
2064
+ let nested = FileSystem::default();
2065
+ ops::touch(&nested, "/another-file.txt").unwrap();
2066
+ let top_level: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(top_level);
2067
+ let nested: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(nested);
2068
+
2069
+ let fs = FileSystem::default();
2070
+ fs.mount("/top-level".into(), &top_level, "/".into())
2071
+ .unwrap();
2072
+ fs.mount("/top-level/nested".into(), &nested, "/".into())
2073
+ .unwrap();
2074
+
2075
+ assert!(ops::is_dir(&fs, "/top-level"));
2076
+ assert!(ops::is_file(&fs, "/top-level/file.txt"));
2077
+ assert!(ops::is_dir(&fs, "/top-level/nested"));
2078
+ assert!(ops::is_file(&fs, "/top-level/nested/another-file.txt"));
2079
+ }
2080
+
2081
+ #[tokio::test]
2082
+ async fn read_dir_rebases_mount_paths() {
2083
+ let mounted = FileSystem::default();
2084
+ ops::touch(&mounted, "/file.txt").unwrap();
2085
+ let mounted: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(mounted);
2086
+
2087
+ let fs = FileSystem::default();
2088
+ fs.mount("/mnt".into(), &mounted, "/".into()).unwrap();
2089
+
2090
+ let entries: Vec<_> = fs
2091
+ .read_dir(Path::new("/mnt"))
2092
+ .unwrap()
2093
+ .map(|e| e.unwrap().path)
2094
+ .collect();
2095
+
2096
+ assert_eq!(entries, vec![Path::new("/mnt/file.txt").to_path_buf()]);
2097
+ }
2098
+
2099
+ #[tokio::test]
2100
+ async fn test_merge_flat() {
2101
+ let main = FileSystem::default();
2102
+
2103
+ let other = FileSystem::default();
2104
+ crate::ops::create_dir_all(&other, "/a/x").unwrap();
2105
+ other
2106
+ .insert_ro_file(Path::new("/a/x/a.txt"), OwnedBuffer::from_static(b"a"))
2107
+ .unwrap();
2108
+ other
2109
+ .insert_ro_file(Path::new("/a/x/b.txt"), OwnedBuffer::from_static(b"b"))
2110
+ .unwrap();
2111
+ other
2112
+ .insert_ro_file(Path::new("/a/x/c.txt"), OwnedBuffer::from_static(b"c"))
2113
+ .unwrap();
2114
+
2115
+ let out = other.read_dir(Path::new("/")).unwrap();
2116
+ dbg!(&out);
2117
+
2118
+ let other: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(other);
2119
+
2120
+ main.mount_directory_entries(Path::new("/"), &other, Path::new("/a"))
2121
+ .unwrap();
2122
+
2123
+ let mut buf = Vec::new();
2124
+
2125
+ let mut f = main
2126
+ .new_open_options()
2127
+ .read(true)
2128
+ .open(Path::new("/x/a.txt"))
2129
+ .unwrap();
2130
+ f.read_to_end(&mut buf).await.unwrap();
2131
+
2132
+ assert_eq!(buf, b"a");
2133
+ }
2134
+ }