@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,1799 @@
1
+ //! This module contains the `FileHandle` and `File`
2
+ //! implementations. They aren't exposed to the public API. Only
3
+ //! `FileHandle` can be used through the `VirtualFile` trait object.
4
+
5
+ use futures::future::BoxFuture;
6
+ use shared_buffer::OwnedBuffer;
7
+ use tokio::io::AsyncRead;
8
+ use tokio::io::{AsyncSeek, AsyncWrite};
9
+
10
+ use self::offloaded_file::OffloadWrite;
11
+
12
+ use super::*;
13
+ use crate::limiter::TrackedVec;
14
+ use crate::{CopyOnWriteFile, FsError, Result, VirtualFile};
15
+ use std::cmp;
16
+ use std::convert::TryInto;
17
+ use std::fmt;
18
+ use std::io;
19
+ use std::pin::Pin;
20
+ use std::task::{Context, Poll};
21
+
22
+ /// A file handle. The file system doesn't return the [`File`] type
23
+ /// directly, but rather this `FileHandle` type, which contains the
24
+ /// inode, the flags, and (a light copy of) the filesystem. For each
25
+ /// operations, it is checked that the permissions allow the
26
+ /// operations to be executed, and then it is checked that the file
27
+ /// still exists in the file system. After that, the operation is
28
+ /// delegated to the file itself.
29
+ pub(super) struct FileHandle {
30
+ inode: Inode,
31
+ filesystem: FileSystem,
32
+ lifecycle: Arc<FileLifecycle>,
33
+ readable: bool,
34
+ writable: bool,
35
+ append_mode: bool,
36
+ cursor: u64,
37
+ arc_file: Option<Result<Box<dyn VirtualFile + Send + Sync + 'static>>>,
38
+ }
39
+
40
+ impl Clone for FileHandle {
41
+ fn clone(&self) -> Self {
42
+ self.lifecycle.opened();
43
+ Self {
44
+ inode: self.inode,
45
+ filesystem: self.filesystem.clone(),
46
+ lifecycle: self.lifecycle.clone(),
47
+ readable: self.readable,
48
+ writable: self.writable,
49
+ append_mode: self.append_mode,
50
+ cursor: self.cursor,
51
+ arc_file: None,
52
+ }
53
+ }
54
+ }
55
+
56
+ impl FileHandle {
57
+ pub(super) fn new_opened(
58
+ inode: Inode,
59
+ filesystem: FileSystem,
60
+ lifecycle: Arc<FileLifecycle>,
61
+ readable: bool,
62
+ writable: bool,
63
+ append_mode: bool,
64
+ cursor: u64,
65
+ ) -> Self {
66
+ Self {
67
+ inode,
68
+ filesystem,
69
+ lifecycle,
70
+ readable,
71
+ writable,
72
+ append_mode,
73
+ cursor,
74
+ arc_file: None,
75
+ }
76
+ }
77
+
78
+ fn lazy_load_arc_file_mut(&mut self) -> Result<&mut dyn VirtualFile> {
79
+ if self.arc_file.is_none() {
80
+ let fs = match self.filesystem.inner.read() {
81
+ Ok(fs) => fs,
82
+ _ => return Err(FsError::EntryNotFound),
83
+ };
84
+
85
+ let inode = fs.storage.get(self.inode);
86
+ match inode {
87
+ Some(Node::ArcFile(node)) => {
88
+ self.arc_file.replace(
89
+ node.fs
90
+ .new_open_options()
91
+ .read(self.readable)
92
+ .write(self.writable)
93
+ .append(self.append_mode)
94
+ .open(node.path.as_path()),
95
+ );
96
+ }
97
+ _ => return Err(FsError::EntryNotFound),
98
+ }
99
+ }
100
+ Ok(self
101
+ .arc_file
102
+ .as_mut()
103
+ .unwrap()
104
+ .as_mut()
105
+ .map_err(|err| *err)?
106
+ .as_mut())
107
+ }
108
+
109
+ fn write_cursor(&self, file_size: u64) -> u64 {
110
+ if self.append_mode {
111
+ file_size
112
+ } else {
113
+ self.cursor
114
+ }
115
+ }
116
+ }
117
+
118
+ impl VirtualFile for FileHandle {
119
+ fn last_accessed(&self) -> u64 {
120
+ let fs = match self.filesystem.inner.read() {
121
+ Ok(fs) => fs,
122
+ _ => return 0,
123
+ };
124
+
125
+ let inode = fs.storage.get(self.inode);
126
+ match inode {
127
+ Some(node) => node.metadata().accessed,
128
+ _ => 0,
129
+ }
130
+ }
131
+
132
+ fn last_modified(&self) -> u64 {
133
+ let fs = match self.filesystem.inner.read() {
134
+ Ok(fs) => fs,
135
+ _ => return 0,
136
+ };
137
+
138
+ let inode = fs.storage.get(self.inode);
139
+ match inode {
140
+ Some(node) => node.metadata().modified,
141
+ _ => 0,
142
+ }
143
+ }
144
+
145
+ fn created_time(&self) -> u64 {
146
+ let fs = match self.filesystem.inner.read() {
147
+ Ok(fs) => fs,
148
+ _ => return 0,
149
+ };
150
+
151
+ let inode = fs.storage.get(self.inode);
152
+ let node = match inode {
153
+ Some(node) => node,
154
+ _ => return 0,
155
+ };
156
+
157
+ node.metadata().created
158
+ }
159
+
160
+ fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
161
+ let mut fs = match self.filesystem.inner.write() {
162
+ Ok(fs) => fs,
163
+ _ => return Err(crate::FsError::Lock),
164
+ };
165
+
166
+ let inode = fs.storage.get_mut(self.inode);
167
+ if let Some(node) = inode {
168
+ if let Some(atime) = atime {
169
+ node.metadata_mut().accessed = atime;
170
+ }
171
+ if let Some(mtime) = mtime {
172
+ node.metadata_mut().modified = mtime;
173
+ }
174
+
175
+ return Ok(());
176
+ }
177
+
178
+ Err(crate::FsError::UnknownError)
179
+ }
180
+
181
+ fn size(&self) -> u64 {
182
+ let fs = match self.filesystem.inner.read() {
183
+ Ok(fs) => fs,
184
+ _ => return 0,
185
+ };
186
+
187
+ let inode = fs.storage.get(self.inode);
188
+ match inode {
189
+ Some(Node::File(node)) => node.file.len().try_into().unwrap_or(0),
190
+ Some(Node::OffloadedFile(node)) => node.file.len(),
191
+ Some(Node::ReadOnlyFile(node)) => node.file.len().try_into().unwrap_or(0),
192
+ Some(Node::CustomFile(node)) => {
193
+ let file = node.file.lock().unwrap();
194
+ file.size()
195
+ }
196
+ Some(Node::ArcFile(node)) => match self.arc_file.as_ref() {
197
+ Some(file) => file.as_ref().map(|file| file.size()).unwrap_or(0),
198
+ None => node
199
+ .fs
200
+ .new_open_options()
201
+ .read(self.readable)
202
+ .write(self.writable)
203
+ .append(self.append_mode)
204
+ .open(node.path.as_path())
205
+ .map(|file| file.size())
206
+ .unwrap_or(0),
207
+ },
208
+ _ => 0,
209
+ }
210
+ }
211
+
212
+ fn set_len(&mut self, new_size: u64) -> Result<()> {
213
+ let mut fs = self.filesystem.inner.write().map_err(|_| FsError::Lock)?;
214
+
215
+ let inode = fs.storage.get_mut(self.inode);
216
+ match inode {
217
+ Some(Node::File(FileNode { file, metadata, .. })) => {
218
+ file.buffer
219
+ .resize(new_size.try_into().map_err(|_| FsError::UnknownError)?, 0)?;
220
+ metadata.len = new_size;
221
+ }
222
+ Some(Node::OffloadedFile(OffloadedFileNode { file, metadata, .. })) => {
223
+ file.resize(new_size, 0);
224
+ metadata.len = new_size;
225
+ }
226
+ Some(Node::CustomFile(node)) => {
227
+ let mut file = node.file.lock().unwrap();
228
+ file.set_len(new_size)?;
229
+ node.metadata.len = new_size;
230
+ }
231
+ Some(Node::ReadOnlyFile { .. }) => return Err(FsError::PermissionDenied),
232
+ Some(Node::ArcFile { .. }) => {
233
+ drop(fs);
234
+ let file = self.lazy_load_arc_file_mut()?;
235
+ file.set_len(new_size)?;
236
+ }
237
+ _ => return Err(FsError::NotAFile),
238
+ }
239
+
240
+ // Update current cursor position if file got truncated
241
+ self.cursor = self.cursor.min(new_size);
242
+ Ok(())
243
+ }
244
+
245
+ fn unlink(&mut self) -> Result<()> {
246
+ let filesystem = self.filesystem.clone();
247
+ let inode = self.inode;
248
+
249
+ let (inode_of_parent, position, inode_of_file) = {
250
+ // Read lock.
251
+ let fs = filesystem.inner.read().map_err(|_| FsError::Lock)?;
252
+
253
+ // The inode of the file.
254
+ let inode_of_file = inode;
255
+
256
+ // Find the position of the file in the parent, and the
257
+ // inode of the parent.
258
+ let (position, inode_of_parent) = fs
259
+ .storage
260
+ .iter()
261
+ .find_map(|(inode_of_parent, node)| match node {
262
+ Node::Directory(DirectoryNode { children, .. }) => {
263
+ children.iter().enumerate().find_map(|(nth, inode)| {
264
+ if inode == &inode_of_file {
265
+ Some((nth, inode_of_parent))
266
+ } else {
267
+ None
268
+ }
269
+ })
270
+ }
271
+
272
+ _ => None,
273
+ })
274
+ .ok_or(FsError::BaseNotDirectory)?;
275
+
276
+ (inode_of_parent, position, inode_of_file)
277
+ };
278
+
279
+ {
280
+ // Write lock.
281
+ let mut fs = filesystem.inner.write().map_err(|_| FsError::Lock)?;
282
+ fs.unlink_file_inode(inode_of_parent, position, inode_of_file)?;
283
+ }
284
+
285
+ Ok(())
286
+ }
287
+
288
+ fn get_special_fd(&self) -> Option<u32> {
289
+ let fs = match self.filesystem.inner.read() {
290
+ Ok(a) => a,
291
+ Err(_) => {
292
+ return None;
293
+ }
294
+ };
295
+
296
+ let inode = fs.storage.get(self.inode);
297
+ match inode {
298
+ Some(Node::CustomFile(node)) => {
299
+ let file = node.file.lock().unwrap();
300
+ file.get_special_fd()
301
+ }
302
+ Some(Node::ArcFile(node)) => match self.arc_file.as_ref() {
303
+ Some(file) => file
304
+ .as_ref()
305
+ .map(|file| file.get_special_fd())
306
+ .unwrap_or(None),
307
+ None => node
308
+ .fs
309
+ .new_open_options()
310
+ .read(self.readable)
311
+ .write(self.writable)
312
+ .append(self.append_mode)
313
+ .open(node.path.as_path())
314
+ .map(|file| file.get_special_fd())
315
+ .unwrap_or(None),
316
+ },
317
+ _ => None,
318
+ }
319
+ }
320
+
321
+ fn copy_reference(
322
+ &mut self,
323
+ src: Box<dyn VirtualFile + Send + Sync + 'static>,
324
+ ) -> BoxFuture<'_, std::io::Result<()>> {
325
+ let inner = self.filesystem.inner.clone();
326
+ Box::pin(async move {
327
+ let mut fs = inner.write().unwrap();
328
+ let inode = fs.storage.get_mut(self.inode);
329
+ match inode {
330
+ Some(inode) => {
331
+ let metadata = Metadata {
332
+ ft: crate::FileType {
333
+ file: true,
334
+ ..Default::default()
335
+ },
336
+ accessed: src.last_accessed(),
337
+ created: src.created_time(),
338
+ modified: src.last_modified(),
339
+ len: src.size(),
340
+ };
341
+
342
+ *inode = Node::CustomFile(CustomFileNode {
343
+ inode: inode.inode(),
344
+ name: inode.name().to_string_lossy().to_string().into(),
345
+ file: Mutex::new(Box::new(CopyOnWriteFile::new(src))),
346
+ metadata,
347
+ lifecycle: inode.file_lifecycle().cloned().unwrap_or_else(Arc::default),
348
+ });
349
+ Ok(())
350
+ }
351
+ None => Err(std::io::ErrorKind::InvalidInput.into()),
352
+ }
353
+ })
354
+ }
355
+
356
+ fn copy_from_owned_buffer(&mut self, src: &OwnedBuffer) -> BoxFuture<'_, std::io::Result<()>> {
357
+ let inner = self.filesystem.inner.clone();
358
+ let src = src.clone();
359
+ Box::pin(async move {
360
+ let mut fs = inner.write().unwrap();
361
+ let inode = fs.storage.get_mut(self.inode);
362
+ match inode {
363
+ Some(inode) => {
364
+ let metadata = Metadata {
365
+ ft: crate::FileType {
366
+ file: true,
367
+ ..Default::default()
368
+ },
369
+ accessed: 1,
370
+ created: 1,
371
+ modified: 1,
372
+ len: src.len() as u64,
373
+ };
374
+
375
+ *inode = Node::ReadOnlyFile(ReadOnlyFileNode {
376
+ inode: inode.inode(),
377
+ name: inode.name().to_string_lossy().to_string().into(),
378
+ file: ReadOnlyFile { buffer: src },
379
+ metadata,
380
+ lifecycle: inode.file_lifecycle().cloned().unwrap_or_else(Arc::default),
381
+ });
382
+ Ok(())
383
+ }
384
+ None => Err(std::io::ErrorKind::InvalidInput.into()),
385
+ }
386
+ })
387
+ }
388
+
389
+ fn poll_read_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
390
+ if !self.readable {
391
+ return Poll::Ready(Err(io::Error::new(
392
+ io::ErrorKind::PermissionDenied,
393
+ format!(
394
+ "the file (inode `{}) doesn't have the `read` permission",
395
+ self.inode
396
+ ),
397
+ )));
398
+ }
399
+
400
+ let mut fs = self
401
+ .filesystem
402
+ .inner
403
+ .write()
404
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
405
+
406
+ let inode = fs.storage.get_mut(self.inode);
407
+ match inode {
408
+ Some(Node::File(node)) => {
409
+ let remaining = node.file.buffer.len() - (self.cursor as usize);
410
+ Poll::Ready(Ok(remaining))
411
+ }
412
+ Some(Node::OffloadedFile(node)) => {
413
+ let remaining = node.file.len() as usize - (self.cursor as usize);
414
+ Poll::Ready(Ok(remaining))
415
+ }
416
+ Some(Node::ReadOnlyFile(node)) => {
417
+ let remaining = node.file.buffer.len() - (self.cursor as usize);
418
+ Poll::Ready(Ok(remaining))
419
+ }
420
+ Some(Node::CustomFile(node)) => {
421
+ let mut file = node.file.lock().unwrap();
422
+ let file = Pin::new(file.as_mut());
423
+ file.poll_read_ready(cx)
424
+ }
425
+ Some(Node::ArcFile(_)) => {
426
+ drop(fs);
427
+ match self.lazy_load_arc_file_mut() {
428
+ Ok(file) => {
429
+ let file = Pin::new(file);
430
+ file.poll_read_ready(cx)
431
+ }
432
+ Err(_) => Poll::Ready(Err(io::Error::new(
433
+ io::ErrorKind::NotFound,
434
+ format!("inode `{}` doesn't match a file", self.inode),
435
+ ))),
436
+ }
437
+ }
438
+ _ => Poll::Ready(Err(io::Error::new(
439
+ io::ErrorKind::NotFound,
440
+ format!("inode `{}` doesn't match a file", self.inode),
441
+ ))),
442
+ }
443
+ }
444
+
445
+ fn poll_write_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
446
+ if !self.writable {
447
+ return Poll::Ready(Err(io::Error::new(
448
+ io::ErrorKind::PermissionDenied,
449
+ format!(
450
+ "the file (inode `{}) doesn't have the `write` permission",
451
+ self.inode
452
+ ),
453
+ )));
454
+ }
455
+
456
+ let mut fs = self
457
+ .filesystem
458
+ .inner
459
+ .write()
460
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
461
+
462
+ let inode = fs.storage.get_mut(self.inode);
463
+ match inode {
464
+ Some(Node::File(_)) => Poll::Ready(Ok(8192)),
465
+ Some(Node::OffloadedFile(_)) => Poll::Ready(Ok(8192)),
466
+ Some(Node::ReadOnlyFile(_)) => Poll::Ready(Ok(0)),
467
+ Some(Node::CustomFile(node)) => {
468
+ let mut file = node.file.lock().unwrap();
469
+ let file = Pin::new(file.as_mut());
470
+ file.poll_write_ready(cx)
471
+ }
472
+ Some(Node::ArcFile(_)) => {
473
+ drop(fs);
474
+ match self.lazy_load_arc_file_mut() {
475
+ Ok(file) => {
476
+ let file = Pin::new(file);
477
+ file.poll_write_ready(cx)
478
+ }
479
+ Err(_) => Poll::Ready(Err(io::Error::new(
480
+ io::ErrorKind::NotFound,
481
+ format!("inode `{}` doesn't match a file", self.inode),
482
+ ))),
483
+ }
484
+ }
485
+ _ => Poll::Ready(Err(io::Error::new(
486
+ io::ErrorKind::NotFound,
487
+ format!("inode `{}` doesn't match a file", self.inode),
488
+ ))),
489
+ }
490
+ }
491
+
492
+ fn write_from_mmap(&mut self, offset: u64, size: u64) -> std::io::Result<()> {
493
+ if !self.writable {
494
+ return Err(io::Error::new(
495
+ io::ErrorKind::PermissionDenied,
496
+ format!(
497
+ "the file (inode `{}) doesn't have the `write` permission",
498
+ self.inode
499
+ ),
500
+ ));
501
+ }
502
+
503
+ let mut cursor = self.cursor;
504
+ {
505
+ let mut fs = self
506
+ .filesystem
507
+ .inner
508
+ .write()
509
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
510
+
511
+ let inode = fs.storage.get_mut(self.inode);
512
+ match inode {
513
+ Some(Node::OffloadedFile(node)) => {
514
+ node.file
515
+ .write(OffloadWrite::MmapOffset { offset, size }, &mut cursor)?;
516
+ node.metadata.len = node.file.len();
517
+ }
518
+ _ => {
519
+ return Err(io::ErrorKind::Unsupported.into());
520
+ }
521
+ }
522
+ }
523
+ self.cursor = cursor;
524
+ Ok(())
525
+ }
526
+
527
+ fn as_owned_buffer(&self) -> Option<OwnedBuffer> {
528
+ let fs = self.filesystem.inner.read().ok()?;
529
+
530
+ let inode = fs.storage.get(self.inode)?;
531
+ match inode {
532
+ Node::ReadOnlyFile(f) => Some(f.file.buffer.clone()),
533
+ Node::CustomFile(f) => f.file.lock().ok()?.as_owned_buffer(),
534
+ _ => None,
535
+ }
536
+ }
537
+ }
538
+
539
+ #[cfg(test)]
540
+ mod test_virtual_file {
541
+ use crate::{BufferFile, FileSystem as FS, FsError, mem_fs::*};
542
+ use std::io;
543
+ use std::pin::Pin;
544
+ use std::task::{Context, Poll, Waker};
545
+ use std::thread::sleep;
546
+ use std::time::Duration;
547
+ use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
548
+
549
+ macro_rules! path {
550
+ ($path:expr) => {
551
+ std::path::Path::new($path)
552
+ };
553
+ }
554
+
555
+ #[tokio::test]
556
+ async fn test_last_accessed() {
557
+ let fs = FileSystem::default();
558
+
559
+ let file = fs
560
+ .new_open_options()
561
+ .write(true)
562
+ .create_new(true)
563
+ .open(path!("/foo.txt"))
564
+ .expect("failed to create a new file");
565
+ let last_accessed_time = file.last_accessed();
566
+
567
+ assert!(last_accessed_time > 0, "last accessed time is not zero");
568
+
569
+ sleep(Duration::from_secs(3));
570
+
571
+ let file = fs
572
+ .new_open_options()
573
+ .read(true)
574
+ .open(path!("/foo.txt"))
575
+ .expect("failed to open a file");
576
+ let next_last_accessed_time = file.last_accessed();
577
+
578
+ assert!(
579
+ next_last_accessed_time > last_accessed_time,
580
+ "the last accessed time is updated"
581
+ );
582
+ }
583
+
584
+ #[tokio::test]
585
+ async fn test_last_modified() {
586
+ let fs = FileSystem::default();
587
+
588
+ let file = fs
589
+ .new_open_options()
590
+ .write(true)
591
+ .create_new(true)
592
+ .open(path!("/foo.txt"))
593
+ .expect("failed to create a new file");
594
+
595
+ assert!(file.last_modified() > 0, "last modified time is not zero");
596
+ }
597
+
598
+ #[tokio::test]
599
+ async fn test_created_time() {
600
+ let fs = FileSystem::default();
601
+
602
+ let file = fs
603
+ .new_open_options()
604
+ .write(true)
605
+ .create_new(true)
606
+ .open(path!("/foo.txt"))
607
+ .expect("failed to create a new file");
608
+ let created_time = file.created_time();
609
+
610
+ assert!(created_time > 0, "created time is not zero");
611
+
612
+ let file = fs
613
+ .new_open_options()
614
+ .read(true)
615
+ .open(path!("/foo.txt"))
616
+ .expect("failed to create a new file");
617
+ let next_created_time = file.created_time();
618
+
619
+ assert_eq!(
620
+ next_created_time, created_time,
621
+ "created time stays constant"
622
+ );
623
+ }
624
+
625
+ #[tokio::test]
626
+ async fn test_size() {
627
+ let fs = FileSystem::default();
628
+
629
+ let file = fs
630
+ .new_open_options()
631
+ .write(true)
632
+ .create_new(true)
633
+ .open(path!("/foo.txt"))
634
+ .expect("failed to create a new file");
635
+
636
+ assert_eq!(file.size(), 0, "new file is empty");
637
+ }
638
+
639
+ #[tokio::test]
640
+ async fn test_set_len() {
641
+ let fs = FileSystem::default();
642
+
643
+ let mut file = fs
644
+ .new_open_options()
645
+ .write(true)
646
+ .create_new(true)
647
+ .open(path!("/foo.txt"))
648
+ .expect("failed to create a new file");
649
+
650
+ assert!(matches!(file.set_len(7), Ok(())), "setting a new length");
651
+ assert_eq!(file.size(), 7, "file has a new length");
652
+ }
653
+
654
+ #[tokio::test]
655
+ async fn test_unlink() {
656
+ let fs = FileSystem::default();
657
+
658
+ let mut file = fs
659
+ .new_open_options()
660
+ .read(true)
661
+ .write(true)
662
+ .create_new(true)
663
+ .open(path!("/foo.txt"))
664
+ .expect("failed to create a new file");
665
+
666
+ file.write_all(b"foo").await.expect("write before unlink");
667
+
668
+ {
669
+ let fs_inner = fs.inner.read().unwrap();
670
+
671
+ assert_eq!(fs_inner.storage.len(), 2, "storage has the new file");
672
+ assert!(
673
+ matches!(
674
+ fs_inner.storage.get(ROOT_INODE),
675
+ Some(Node::Directory(DirectoryNode {
676
+ inode: ROOT_INODE,
677
+ name,
678
+ children,
679
+ ..
680
+ })) if name == "/" && children == &[1]
681
+ ),
682
+ "`/` contains `foo.txt`",
683
+ );
684
+ assert!(
685
+ matches!(
686
+ fs_inner.storage.get(1),
687
+ Some(Node::File(FileNode {
688
+ inode: 1,
689
+ name,
690
+ ..
691
+ })) if name == "foo.txt"
692
+ ),
693
+ "`foo.txt` exists and is a file",
694
+ );
695
+ }
696
+
697
+ assert_eq!(file.unlink(), Ok(()), "unlinking the file");
698
+ assert!(
699
+ matches!(
700
+ fs.new_open_options().read(true).open(path!("/foo.txt")),
701
+ Err(FsError::EntryNotFound)
702
+ ),
703
+ "the path disappears immediately after unlink",
704
+ );
705
+
706
+ file.write_all(b"bar")
707
+ .await
708
+ .expect("write after unlink should still work");
709
+ file.seek(io::SeekFrom::Start(0))
710
+ .await
711
+ .expect("rewind after unlink");
712
+
713
+ let mut contents = String::new();
714
+ file.read_to_string(&mut contents)
715
+ .await
716
+ .expect("read after unlink should still work");
717
+ assert_eq!(contents, "foobar");
718
+
719
+ {
720
+ let fs_inner = fs.inner.read().unwrap();
721
+
722
+ assert_eq!(
723
+ fs_inner.storage.len(),
724
+ 2,
725
+ "storage keeps the unlinked file alive while a handle is open"
726
+ );
727
+ assert!(
728
+ matches!(
729
+ fs_inner.storage.get(ROOT_INODE),
730
+ Some(Node::Directory(DirectoryNode {
731
+ inode: ROOT_INODE,
732
+ name,
733
+ children,
734
+ ..
735
+ })) if name == "/" && children.is_empty()
736
+ ),
737
+ "`/` is empty",
738
+ );
739
+ }
740
+
741
+ drop(file);
742
+
743
+ let fs_inner = fs.inner.read().unwrap();
744
+ assert_eq!(
745
+ fs_inner.storage.len(),
746
+ 1,
747
+ "storage drops the unlinked file after the last handle closes"
748
+ );
749
+ }
750
+
751
+ #[tokio::test]
752
+ async fn test_unlink_with_multiple_handles() {
753
+ let fs = FileSystem::default();
754
+
755
+ let mut first = fs
756
+ .new_open_options()
757
+ .read(true)
758
+ .write(true)
759
+ .create_new(true)
760
+ .open(path!("/foo.txt"))
761
+ .expect("failed to create a new file");
762
+ first.write_all(b"foo").await.expect("seed contents");
763
+
764
+ let mut second = fs
765
+ .new_open_options()
766
+ .read(true)
767
+ .write(true)
768
+ .open(path!("/foo.txt"))
769
+ .expect("failed to open the same file a second time");
770
+
771
+ assert_eq!(first.unlink(), Ok(()), "unlinking through the first handle");
772
+
773
+ second
774
+ .seek(io::SeekFrom::End(0))
775
+ .await
776
+ .expect("seek to end after unlink");
777
+ second
778
+ .write_all(b"bar")
779
+ .await
780
+ .expect("second handle stays writable after unlink");
781
+
782
+ drop(first);
783
+
784
+ {
785
+ let fs_inner = fs.inner.read().unwrap();
786
+ assert_eq!(
787
+ fs_inner.storage.len(),
788
+ 2,
789
+ "the inode stays alive until the last open handle is dropped"
790
+ );
791
+ }
792
+
793
+ second
794
+ .seek(io::SeekFrom::Start(0))
795
+ .await
796
+ .expect("rewind second handle");
797
+ let mut contents = String::new();
798
+ second
799
+ .read_to_string(&mut contents)
800
+ .await
801
+ .expect("read through surviving handle");
802
+ assert_eq!(contents, "foobar");
803
+
804
+ drop(second);
805
+
806
+ let fs_inner = fs.inner.read().unwrap();
807
+ assert_eq!(
808
+ fs_inner.storage.len(),
809
+ 1,
810
+ "the inode is reclaimed after the last handle closes"
811
+ );
812
+ }
813
+
814
+ #[tokio::test]
815
+ async fn test_poll_write_ready_for_write_only_file() {
816
+ let fs = FileSystem::default();
817
+ let mut file = fs
818
+ .new_open_options()
819
+ .write(true)
820
+ .create_new(true)
821
+ .open(path!("/foo.txt"))
822
+ .expect("failed to create a new file");
823
+ let waker = Waker::noop();
824
+ let mut cx = Context::from_waker(waker);
825
+
826
+ assert!(matches!(
827
+ Pin::new(file.as_mut()).poll_write_ready(&mut cx),
828
+ Poll::Ready(Ok(8192))
829
+ ));
830
+ }
831
+
832
+ #[tokio::test]
833
+ async fn test_poll_write_ready_for_custom_device_file() {
834
+ let fs = FileSystem::default();
835
+ fs.insert_device_file(path!("/dev").to_path_buf(), Box::<BufferFile>::default())
836
+ .expect("failed to insert a device file");
837
+
838
+ let mut file = fs
839
+ .new_open_options()
840
+ .write(true)
841
+ .open(path!("/dev"))
842
+ .expect("failed to open device file");
843
+ let waker = Waker::noop();
844
+ let mut cx = Context::from_waker(waker);
845
+
846
+ assert!(matches!(
847
+ Pin::new(file.as_mut()).poll_write_ready(&mut cx),
848
+ Poll::Ready(Ok(8192))
849
+ ));
850
+ }
851
+ }
852
+
853
+ impl AsyncRead for FileHandle {
854
+ fn poll_read(
855
+ mut self: Pin<&mut Self>,
856
+ cx: &mut Context<'_>,
857
+ buf: &mut tokio::io::ReadBuf<'_>,
858
+ ) -> Poll<io::Result<()>> {
859
+ if !self.readable {
860
+ return Poll::Ready(Err(io::Error::new(
861
+ io::ErrorKind::PermissionDenied,
862
+ format!(
863
+ "the file (inode `{}) doesn't have the `read` permission",
864
+ self.inode
865
+ ),
866
+ )));
867
+ }
868
+
869
+ let mut cursor = self.cursor;
870
+ let ret = {
871
+ let mut fs = self
872
+ .filesystem
873
+ .inner
874
+ .write()
875
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
876
+
877
+ let inode = fs.storage.get_mut(self.inode);
878
+ match inode {
879
+ Some(Node::File(node)) => {
880
+ let read = unsafe {
881
+ node.file.read(
882
+ std::mem::transmute::<&mut [std::mem::MaybeUninit<u8>], &mut [u8]>(
883
+ buf.unfilled_mut(),
884
+ ),
885
+ &mut cursor,
886
+ )
887
+ };
888
+ if let Ok(read) = &read {
889
+ unsafe { buf.assume_init(*read) };
890
+ buf.advance(*read);
891
+ }
892
+ Poll::Ready(read.map(|_| ()))
893
+ }
894
+ Some(Node::OffloadedFile(node)) => {
895
+ let read = unsafe {
896
+ node.file.read(
897
+ std::mem::transmute::<&mut [std::mem::MaybeUninit<u8>], &mut [u8]>(
898
+ buf.unfilled_mut(),
899
+ ),
900
+ &mut cursor,
901
+ )
902
+ };
903
+ if let Ok(read) = &read {
904
+ unsafe { buf.assume_init(*read) };
905
+ buf.advance(*read);
906
+ }
907
+ Poll::Ready(read.map(|_| ()))
908
+ }
909
+ Some(Node::ReadOnlyFile(node)) => {
910
+ let read = unsafe {
911
+ node.file.read(
912
+ std::mem::transmute::<&mut [std::mem::MaybeUninit<u8>], &mut [u8]>(
913
+ buf.unfilled_mut(),
914
+ ),
915
+ &mut cursor,
916
+ )
917
+ };
918
+ if let Ok(read) = &read {
919
+ unsafe { buf.assume_init(*read) };
920
+ buf.advance(*read);
921
+ }
922
+ Poll::Ready(read.map(|_| ()))
923
+ }
924
+ Some(Node::CustomFile(node)) => {
925
+ let mut file = node.file.lock().unwrap();
926
+ let file = Pin::new(file.as_mut());
927
+ file.poll_read(cx, buf)
928
+ }
929
+ Some(Node::ArcFile(_)) => {
930
+ drop(fs);
931
+ match self.lazy_load_arc_file_mut() {
932
+ Ok(file) => {
933
+ let file = Pin::new(file);
934
+ file.poll_read(cx, buf)
935
+ }
936
+ Err(_) => {
937
+ return Poll::Ready(Err(io::Error::new(
938
+ io::ErrorKind::NotFound,
939
+ format!("inode `{}` doesn't match a file", self.inode),
940
+ )));
941
+ }
942
+ }
943
+ }
944
+ _ => {
945
+ return Poll::Ready(Err(io::Error::new(
946
+ io::ErrorKind::NotFound,
947
+ format!("inode `{}` doesn't match a file", self.inode),
948
+ )));
949
+ }
950
+ }
951
+ };
952
+ self.cursor = cursor;
953
+ ret
954
+ }
955
+ }
956
+
957
+ impl AsyncSeek for FileHandle {
958
+ fn start_seek(mut self: Pin<&mut Self>, position: io::SeekFrom) -> io::Result<()> {
959
+ let mut cursor = self.cursor;
960
+ let ret = {
961
+ let mut fs = self
962
+ .filesystem
963
+ .inner
964
+ .write()
965
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
966
+
967
+ let inode = fs.storage.get_mut(self.inode);
968
+ match inode {
969
+ Some(Node::File(node)) => {
970
+ node.file.seek(position, &mut cursor)?;
971
+ Ok(())
972
+ }
973
+ Some(Node::OffloadedFile(node)) => {
974
+ node.file.seek(position, &mut cursor)?;
975
+ Ok(())
976
+ }
977
+ Some(Node::ReadOnlyFile(node)) => {
978
+ node.file.seek(position, &mut cursor)?;
979
+ Ok(())
980
+ }
981
+ Some(Node::CustomFile(node)) => {
982
+ let mut file = node.file.lock().unwrap();
983
+ let file = Pin::new(file.as_mut());
984
+ file.start_seek(position)
985
+ }
986
+ Some(Node::ArcFile(_)) => {
987
+ drop(fs);
988
+ match self.lazy_load_arc_file_mut() {
989
+ Ok(file) => {
990
+ let file = Pin::new(file);
991
+ file.start_seek(position)
992
+ }
993
+ Err(_) => {
994
+ return Err(io::Error::new(
995
+ io::ErrorKind::NotFound,
996
+ format!("inode `{}` doesn't match a file", self.inode),
997
+ ));
998
+ }
999
+ }
1000
+ }
1001
+ _ => {
1002
+ return Err(io::Error::new(
1003
+ io::ErrorKind::NotFound,
1004
+ format!("inode `{}` doesn't match a file", self.inode),
1005
+ ));
1006
+ }
1007
+ }
1008
+ };
1009
+ self.cursor = cursor;
1010
+ ret
1011
+ }
1012
+
1013
+ fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
1014
+ let mut fs = self
1015
+ .filesystem
1016
+ .inner
1017
+ .write()
1018
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1019
+
1020
+ let inode = fs.storage.get_mut(self.inode);
1021
+ match inode {
1022
+ Some(Node::File { .. }) => Poll::Ready(Ok(self.cursor)),
1023
+ Some(Node::OffloadedFile { .. }) => Poll::Ready(Ok(self.cursor)),
1024
+ Some(Node::ReadOnlyFile { .. }) => Poll::Ready(Ok(self.cursor)),
1025
+ Some(Node::CustomFile(node)) => {
1026
+ let mut file = node.file.lock().unwrap();
1027
+ let file = Pin::new(file.as_mut());
1028
+ file.poll_complete(cx)
1029
+ }
1030
+ Some(Node::ArcFile { .. }) => {
1031
+ drop(fs);
1032
+ match self.lazy_load_arc_file_mut() {
1033
+ Ok(file) => {
1034
+ let file = Pin::new(file);
1035
+ file.poll_complete(cx)
1036
+ }
1037
+ Err(_) => Poll::Ready(Err(io::Error::new(
1038
+ io::ErrorKind::NotFound,
1039
+ format!("inode `{}` doesn't match a file", self.inode),
1040
+ ))),
1041
+ }
1042
+ }
1043
+ _ => Poll::Ready(Err(io::Error::new(
1044
+ io::ErrorKind::NotFound,
1045
+ format!("inode `{}` doesn't match a file", self.inode),
1046
+ ))),
1047
+ }
1048
+ }
1049
+ }
1050
+
1051
+ impl AsyncWrite for FileHandle {
1052
+ fn poll_write(
1053
+ mut self: Pin<&mut Self>,
1054
+ cx: &mut Context<'_>,
1055
+ buf: &[u8],
1056
+ ) -> Poll<io::Result<usize>> {
1057
+ if !self.writable {
1058
+ return Poll::Ready(Err(io::Error::new(
1059
+ io::ErrorKind::PermissionDenied,
1060
+ format!(
1061
+ "the file (inode `{}) doesn't have the `write` permission",
1062
+ self.inode
1063
+ ),
1064
+ )));
1065
+ }
1066
+
1067
+ let mut cursor;
1068
+ let bytes_written = {
1069
+ let mut fs = self
1070
+ .filesystem
1071
+ .inner
1072
+ .write()
1073
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1074
+
1075
+ let inode = fs.storage.get_mut(self.inode);
1076
+ match inode {
1077
+ Some(Node::File(node)) => {
1078
+ cursor = self.write_cursor(node.file.len() as u64);
1079
+ let bytes_written = node.file.write(buf, &mut cursor)?;
1080
+ node.metadata.len = node.file.len().try_into().unwrap();
1081
+ bytes_written
1082
+ }
1083
+ Some(Node::OffloadedFile(node)) => {
1084
+ cursor = self.write_cursor(node.file.len());
1085
+ let bytes_written = node.file.write(OffloadWrite::Buffer(buf), &mut cursor)?;
1086
+ node.metadata.len = node.file.len();
1087
+ bytes_written
1088
+ }
1089
+ Some(Node::ReadOnlyFile(node)) => {
1090
+ cursor = self.write_cursor(node.file.len() as u64);
1091
+ let bytes_written = node.file.write(buf, &mut cursor)?;
1092
+ node.metadata.len = node.file.len().try_into().unwrap();
1093
+ bytes_written
1094
+ }
1095
+ Some(Node::CustomFile(node)) => {
1096
+ let mut guard = node.file.lock().unwrap();
1097
+ cursor = self.write_cursor(guard.size());
1098
+
1099
+ let file = Pin::new(guard.as_mut());
1100
+ if let Err(err) = file.start_seek(io::SeekFrom::Start(cursor)) {
1101
+ return Poll::Ready(Err(err));
1102
+ }
1103
+
1104
+ let file = Pin::new(guard.as_mut());
1105
+ let _ = file.poll_complete(cx);
1106
+
1107
+ let file = Pin::new(guard.as_mut());
1108
+ let bytes_written = match file.poll_write(cx, buf) {
1109
+ Poll::Ready(Ok(a)) => a,
1110
+ Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
1111
+ Poll::Pending => return Poll::Pending,
1112
+ };
1113
+ cursor += bytes_written as u64;
1114
+ node.metadata.len = guard.size();
1115
+ bytes_written
1116
+ }
1117
+ Some(Node::ArcFile(_)) => {
1118
+ drop(fs);
1119
+ match self.lazy_load_arc_file_mut() {
1120
+ Ok(file) => {
1121
+ let file = Pin::new(file);
1122
+ return file.poll_write(cx, buf);
1123
+ }
1124
+ Err(_) => {
1125
+ return Poll::Ready(Err(io::Error::new(
1126
+ io::ErrorKind::NotFound,
1127
+ format!("inode `{}` doesn't match a file", self.inode),
1128
+ )));
1129
+ }
1130
+ }
1131
+ }
1132
+ _ => {
1133
+ return Poll::Ready(Err(io::Error::new(
1134
+ io::ErrorKind::NotFound,
1135
+ format!("inode `{}` doesn't match a file", self.inode),
1136
+ )));
1137
+ }
1138
+ }
1139
+ };
1140
+ self.cursor = cursor;
1141
+ Poll::Ready(Ok(bytes_written))
1142
+ }
1143
+
1144
+ fn poll_write_vectored(
1145
+ mut self: Pin<&mut Self>,
1146
+ cx: &mut Context<'_>,
1147
+ bufs: &[io::IoSlice<'_>],
1148
+ ) -> Poll<io::Result<usize>> {
1149
+ let mut cursor = self.cursor;
1150
+ let ret = {
1151
+ let mut fs = self
1152
+ .filesystem
1153
+ .inner
1154
+ .write()
1155
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1156
+
1157
+ let inode = fs.storage.get_mut(self.inode);
1158
+ match inode {
1159
+ Some(Node::File(node)) => {
1160
+ cursor = self.write_cursor(node.file.len() as u64);
1161
+ let buf = bufs
1162
+ .iter()
1163
+ .find(|b| !b.is_empty())
1164
+ .map_or(&[][..], |b| &**b);
1165
+ let bytes_written = node.file.write(buf, &mut cursor)?;
1166
+ node.metadata.len = node.file.buffer.len() as u64;
1167
+ Poll::Ready(Ok(bytes_written))
1168
+ }
1169
+ Some(Node::OffloadedFile(node)) => {
1170
+ cursor = self.write_cursor(node.file.len());
1171
+ let buf = bufs
1172
+ .iter()
1173
+ .find(|b| !b.is_empty())
1174
+ .map_or(&[][..], |b| &**b);
1175
+ let bytes_written = node.file.write(OffloadWrite::Buffer(buf), &mut cursor)?;
1176
+ node.metadata.len = node.file.len();
1177
+ Poll::Ready(Ok(bytes_written))
1178
+ }
1179
+ Some(Node::ReadOnlyFile(node)) => {
1180
+ cursor = self.write_cursor(node.file.len() as u64);
1181
+ let buf = bufs
1182
+ .iter()
1183
+ .find(|b| !b.is_empty())
1184
+ .map_or(&[][..], |b| &**b);
1185
+ let bytes_written = node.file.write(buf, &mut cursor)?;
1186
+ node.metadata.len = node.file.buffer.len() as u64;
1187
+ Poll::Ready(Ok(bytes_written))
1188
+ }
1189
+ Some(Node::CustomFile(node)) => {
1190
+ let mut file = node.file.lock().unwrap();
1191
+ let file = Pin::new(file.as_mut());
1192
+ file.poll_write_vectored(cx, bufs)
1193
+ }
1194
+ Some(Node::ArcFile(_)) => {
1195
+ drop(fs);
1196
+ match self.lazy_load_arc_file_mut() {
1197
+ Ok(file) => {
1198
+ let file = Pin::new(file);
1199
+ file.poll_write_vectored(cx, bufs)
1200
+ }
1201
+ Err(_) => Poll::Ready(Err(io::Error::new(
1202
+ io::ErrorKind::NotFound,
1203
+ format!("inode `{}` doesn't match a file", self.inode),
1204
+ ))),
1205
+ }
1206
+ }
1207
+ _ => Poll::Ready(Err(io::Error::new(
1208
+ io::ErrorKind::NotFound,
1209
+ format!("inode `{}` doesn't match a file", self.inode),
1210
+ ))),
1211
+ }
1212
+ };
1213
+ self.cursor = cursor;
1214
+ ret
1215
+ }
1216
+
1217
+ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1218
+ let mut fs = self
1219
+ .filesystem
1220
+ .inner
1221
+ .write()
1222
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1223
+
1224
+ let inode = fs.storage.get_mut(self.inode);
1225
+ match inode {
1226
+ Some(Node::File(node)) => Poll::Ready(node.file.flush()),
1227
+ Some(Node::OffloadedFile(node)) => Poll::Ready(node.file.flush()),
1228
+ Some(Node::ReadOnlyFile(node)) => Poll::Ready(node.file.flush()),
1229
+ Some(Node::CustomFile(node)) => {
1230
+ let mut file = node.file.lock().unwrap();
1231
+ let file = Pin::new(file.as_mut());
1232
+ file.poll_flush(cx)
1233
+ }
1234
+ Some(Node::ArcFile { .. }) => {
1235
+ drop(fs);
1236
+ match self.lazy_load_arc_file_mut() {
1237
+ Ok(file) => {
1238
+ let file = Pin::new(file);
1239
+ file.poll_flush(cx)
1240
+ }
1241
+ Err(_) => Poll::Ready(Err(io::Error::new(
1242
+ io::ErrorKind::NotFound,
1243
+ format!("inode `{}` doesn't match a file", self.inode),
1244
+ ))),
1245
+ }
1246
+ }
1247
+ _ => Poll::Ready(Err(io::Error::new(
1248
+ io::ErrorKind::NotFound,
1249
+ format!("inode `{}` doesn't match a file", self.inode),
1250
+ ))),
1251
+ }
1252
+ }
1253
+
1254
+ fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1255
+ let mut fs = self
1256
+ .filesystem
1257
+ .inner
1258
+ .write()
1259
+ .map_err(|_| io::Error::other("failed to acquire a write lock"))?;
1260
+
1261
+ let inode = fs.storage.get_mut(self.inode);
1262
+ match inode {
1263
+ Some(Node::File { .. }) => Poll::Ready(Ok(())),
1264
+ Some(Node::OffloadedFile { .. }) => Poll::Ready(Ok(())),
1265
+ Some(Node::ReadOnlyFile { .. }) => Poll::Ready(Ok(())),
1266
+ Some(Node::CustomFile(node)) => {
1267
+ let mut file = node.file.lock().unwrap();
1268
+ let file = Pin::new(file.as_mut());
1269
+ file.poll_shutdown(cx)
1270
+ }
1271
+ Some(Node::ArcFile { .. }) => {
1272
+ drop(fs);
1273
+ match self.lazy_load_arc_file_mut() {
1274
+ Ok(file) => {
1275
+ let file = Pin::new(file);
1276
+ file.poll_shutdown(cx)
1277
+ }
1278
+ Err(_) => Poll::Ready(Err(io::Error::new(
1279
+ io::ErrorKind::NotFound,
1280
+ format!("inode `{}` doesn't match a file", self.inode),
1281
+ ))),
1282
+ }
1283
+ }
1284
+ _ => Poll::Ready(Err(io::Error::new(
1285
+ io::ErrorKind::NotFound,
1286
+ format!("inode `{}` doesn't match a file", self.inode),
1287
+ ))),
1288
+ }
1289
+ }
1290
+
1291
+ fn is_write_vectored(&self) -> bool {
1292
+ let mut fs = match self.filesystem.inner.write() {
1293
+ Ok(a) => a,
1294
+ Err(_) => return false,
1295
+ };
1296
+
1297
+ let inode = fs.storage.get_mut(self.inode);
1298
+ match inode {
1299
+ Some(Node::File { .. }) => false,
1300
+ Some(Node::OffloadedFile { .. }) => false,
1301
+ Some(Node::ReadOnlyFile { .. }) => false,
1302
+ Some(Node::CustomFile(node)) => {
1303
+ let file = node.file.lock().unwrap();
1304
+ file.is_write_vectored()
1305
+ }
1306
+ Some(Node::ArcFile { .. }) => {
1307
+ drop(fs);
1308
+ match self.arc_file.as_ref() {
1309
+ Some(Ok(file)) => file.is_write_vectored(),
1310
+ _ => false,
1311
+ }
1312
+ }
1313
+ _ => false,
1314
+ }
1315
+ }
1316
+ }
1317
+
1318
+ #[cfg(test)]
1319
+ mod test_read_write_seek {
1320
+ use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
1321
+
1322
+ use crate::{FileSystem as FS, mem_fs::*};
1323
+ use std::io;
1324
+
1325
+ macro_rules! path {
1326
+ ($path:expr) => {
1327
+ std::path::Path::new($path)
1328
+ };
1329
+ }
1330
+
1331
+ #[tokio::test]
1332
+ async fn test_writing_at_various_positions() {
1333
+ let fs = FileSystem::default();
1334
+
1335
+ let mut file = fs
1336
+ .new_open_options()
1337
+ .read(true)
1338
+ .write(true)
1339
+ .create_new(true)
1340
+ .open(path!("/foo.txt"))
1341
+ .expect("failed to create a new file");
1342
+
1343
+ assert!(
1344
+ matches!(file.write(b"foo").await, Ok(3)),
1345
+ "writing `foo` at the end of the file",
1346
+ );
1347
+ assert_eq!(file.size(), 3, "checking the size of the file");
1348
+
1349
+ assert!(
1350
+ matches!(file.write(b"bar").await, Ok(3)),
1351
+ "writing `bar` at the end of the file",
1352
+ );
1353
+ assert_eq!(file.size(), 6, "checking the size of the file");
1354
+
1355
+ assert!(
1356
+ matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1357
+ "seeking to 0",
1358
+ );
1359
+
1360
+ assert!(
1361
+ matches!(file.write(b"baz").await, Ok(3)),
1362
+ "writing `baz` at the beginning of the file",
1363
+ );
1364
+ assert_eq!(file.size(), 6, "checking the size of the file");
1365
+
1366
+ assert!(
1367
+ matches!(file.write(b"qux").await, Ok(3)),
1368
+ "writing `qux` in the middle of the file",
1369
+ );
1370
+ assert_eq!(file.size(), 6, "checking the size of the file");
1371
+
1372
+ assert!(
1373
+ matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1374
+ "seeking to 0",
1375
+ );
1376
+
1377
+ let mut string = String::new();
1378
+ assert!(
1379
+ matches!(file.read_to_string(&mut string).await, Ok(6)),
1380
+ "reading `bazqux`",
1381
+ );
1382
+ assert_eq!(string, "bazqux");
1383
+
1384
+ assert!(
1385
+ matches!(file.seek(io::SeekFrom::Current(-3)).await, Ok(3)),
1386
+ "seeking to 3",
1387
+ );
1388
+
1389
+ let mut string = String::new();
1390
+ assert!(
1391
+ matches!(file.read_to_string(&mut string).await, Ok(3)),
1392
+ "reading `qux`",
1393
+ );
1394
+ assert_eq!(string, "qux");
1395
+
1396
+ assert!(
1397
+ matches!(file.seek(io::SeekFrom::End(0)).await, Ok(6)),
1398
+ "seeking to 6",
1399
+ );
1400
+
1401
+ let mut string = String::new();
1402
+ assert!(
1403
+ matches!(file.read_to_string(&mut string).await, Ok(0)),
1404
+ "reading ``",
1405
+ );
1406
+ assert_eq!(string, "");
1407
+ }
1408
+
1409
+ #[test]
1410
+ pub fn writing_to_middle() {
1411
+ fn assert_contents(file: &File, expected: &[u8]) {
1412
+ let mut buf = vec![0; expected.len() + 1];
1413
+ let mut cursor = 0;
1414
+ let read = file.read(buf.as_mut(), &mut cursor).unwrap();
1415
+ assert_eq!(read, expected.len(), "Must have the same amount of data");
1416
+ assert_eq!(buf[0..expected.len()], *expected);
1417
+ }
1418
+
1419
+ let mut file = File::new(None);
1420
+
1421
+ let mut cursor = 0;
1422
+
1423
+ // Write to empty file
1424
+ file.write(b"hello, world.", &mut cursor).unwrap();
1425
+ assert_eq!(cursor, 13);
1426
+ assert_contents(&file, b"hello, world.");
1427
+
1428
+ // Write to end of file
1429
+ file.write(b"goodbye!", &mut cursor).unwrap();
1430
+ assert_eq!(cursor, 21);
1431
+ assert_contents(&file, b"hello, world.goodbye!");
1432
+
1433
+ // Write to middle of file
1434
+ cursor = 5;
1435
+ file.write(b"BOOM", &mut cursor).unwrap();
1436
+ assert_eq!(cursor, 9);
1437
+ assert_contents(&file, b"helloBOOMrld.goodbye!");
1438
+
1439
+ // Write to middle of file until last byte
1440
+ cursor = 17;
1441
+ file.write(b"BANG", &mut cursor).unwrap();
1442
+ assert_eq!(cursor, 21);
1443
+ assert_contents(&file, b"helloBOOMrld.goodBANG");
1444
+
1445
+ // Write to middle past end of file
1446
+ cursor = 17;
1447
+ file.write(b"OUCH!", &mut cursor).unwrap();
1448
+ assert_eq!(cursor, 22);
1449
+ assert_contents(&file, b"helloBOOMrld.goodOUCH!");
1450
+ }
1451
+
1452
+ #[tokio::test]
1453
+ async fn test_reading() {
1454
+ let fs = FileSystem::default();
1455
+
1456
+ let mut file = fs
1457
+ .new_open_options()
1458
+ .read(true)
1459
+ .write(true)
1460
+ .create_new(true)
1461
+ .open(path!("/foo.txt"))
1462
+ .expect("failed to create a new file");
1463
+
1464
+ assert!(
1465
+ matches!(fs.metadata(path!("/foo.txt")), Ok(Metadata { len: 0, .. })),
1466
+ "checking the `metadata.len` is 0",
1467
+ );
1468
+ assert!(
1469
+ matches!(file.write(b"foobarbazqux").await, Ok(12)),
1470
+ "writing `foobarbazqux`",
1471
+ );
1472
+
1473
+ assert!(
1474
+ matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1475
+ "seeking to 0",
1476
+ );
1477
+
1478
+ let mut buffer = [0; 6];
1479
+ assert!(
1480
+ matches!(file.read(&mut buffer[..]).await, Ok(6)),
1481
+ "reading 6 bytes",
1482
+ );
1483
+ assert_eq!(buffer, b"foobar"[..], "checking the 6 bytes");
1484
+
1485
+ assert!(
1486
+ matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1487
+ "seeking to 0",
1488
+ );
1489
+
1490
+ let mut buffer = [0; 16];
1491
+ assert!(
1492
+ matches!(file.read(&mut buffer[..]).await, Ok(12)),
1493
+ "reading more bytes than available",
1494
+ );
1495
+ assert_eq!(buffer[..12], b"foobarbazqux"[..], "checking the 12 bytes");
1496
+ assert!(
1497
+ matches!(fs.metadata(path!("/foo.txt")), Ok(Metadata { len: 12, .. })),
1498
+ "checking the `metadata.len` is 0",
1499
+ );
1500
+ }
1501
+
1502
+ #[tokio::test]
1503
+ async fn test_reading_to_the_end() {
1504
+ let fs = FileSystem::default();
1505
+
1506
+ let mut file = fs
1507
+ .new_open_options()
1508
+ .read(true)
1509
+ .write(true)
1510
+ .create_new(true)
1511
+ .open(path!("/foo.txt"))
1512
+ .expect("failed to create a new file");
1513
+
1514
+ assert!(
1515
+ matches!(file.write(b"foobarbazqux").await, Ok(12)),
1516
+ "writing `foobarbazqux`",
1517
+ );
1518
+
1519
+ assert!(
1520
+ matches!(file.seek(io::SeekFrom::Start(0)).await, Ok(0)),
1521
+ "seeking to 0",
1522
+ );
1523
+
1524
+ let mut buffer = Vec::new();
1525
+ assert!(
1526
+ matches!(file.read_to_end(&mut buffer).await, Ok(12)),
1527
+ "reading all bytes",
1528
+ );
1529
+ assert_eq!(buffer, b"foobarbazqux"[..], "checking all the bytes");
1530
+ }
1531
+
1532
+ #[tokio::test]
1533
+ async fn test_reading_to_string() {
1534
+ let fs = FileSystem::default();
1535
+
1536
+ let mut file = fs
1537
+ .new_open_options()
1538
+ .read(true)
1539
+ .write(true)
1540
+ .create_new(true)
1541
+ .open(path!("/foo.txt"))
1542
+ .expect("failed to create a new file");
1543
+
1544
+ assert!(
1545
+ matches!(file.write(b"foobarbazqux").await, Ok(12)),
1546
+ "writing `foobarbazqux`",
1547
+ );
1548
+
1549
+ assert!(
1550
+ matches!(file.seek(io::SeekFrom::Start(6)).await, Ok(6)),
1551
+ "seeking to 0",
1552
+ );
1553
+
1554
+ let mut string = String::new();
1555
+ assert!(
1556
+ matches!(file.read_to_string(&mut string).await, Ok(6)),
1557
+ "reading a string",
1558
+ );
1559
+ assert_eq!(string, "bazqux", "checking the string");
1560
+ }
1561
+
1562
+ #[tokio::test]
1563
+ async fn test_reading_exact_buffer() {
1564
+ let fs = FileSystem::default();
1565
+
1566
+ let mut file = fs
1567
+ .new_open_options()
1568
+ .read(true)
1569
+ .write(true)
1570
+ .create_new(true)
1571
+ .open(path!("/foo.txt"))
1572
+ .expect("failed to create a new file");
1573
+
1574
+ assert!(
1575
+ matches!(file.write(b"foobarbazqux").await, Ok(12)),
1576
+ "writing `foobarbazqux`",
1577
+ );
1578
+
1579
+ assert!(
1580
+ matches!(file.seek(io::SeekFrom::Start(6)).await, Ok(6)),
1581
+ "seeking to 0",
1582
+ );
1583
+
1584
+ let mut buffer = [0; 16];
1585
+ assert!(
1586
+ file.read_exact(&mut buffer).await.is_err(),
1587
+ "failing to read an exact buffer",
1588
+ );
1589
+
1590
+ assert!(
1591
+ matches!(file.seek(io::SeekFrom::End(-5)).await, Ok(7)),
1592
+ "seeking to 7",
1593
+ );
1594
+
1595
+ let mut buffer = [0; 3];
1596
+ assert!(
1597
+ file.read_exact(&mut buffer).await.is_ok(),
1598
+ "failing to read an exact buffer",
1599
+ );
1600
+ }
1601
+ }
1602
+
1603
+ impl fmt::Debug for FileHandle {
1604
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1605
+ formatter
1606
+ .debug_struct("FileHandle")
1607
+ .field("inode", &self.inode)
1608
+ .field("readable", &self.readable)
1609
+ .field("writable", &self.writable)
1610
+ .finish()
1611
+ }
1612
+ }
1613
+
1614
+ impl Drop for FileHandle {
1615
+ fn drop(&mut self) {
1616
+ let remaining = self.lifecycle.closed();
1617
+ if remaining != 0 || !self.lifecycle.is_unlinked() {
1618
+ return;
1619
+ }
1620
+
1621
+ let Ok(mut fs) = self.filesystem.inner.write() else {
1622
+ return;
1623
+ };
1624
+
1625
+ let Some(node) = fs.storage.get(self.inode) else {
1626
+ return;
1627
+ };
1628
+
1629
+ let Some(lifecycle) = node.file_lifecycle() else {
1630
+ return;
1631
+ };
1632
+
1633
+ if Arc::ptr_eq(lifecycle, &self.lifecycle)
1634
+ && lifecycle.is_unlinked()
1635
+ && lifecycle.open_handle_count() == 0
1636
+ {
1637
+ fs.remove_node(self.inode);
1638
+ }
1639
+ }
1640
+ }
1641
+
1642
+ /// The real file! It is simply a buffer of bytes with a cursor that
1643
+ /// represents a read/write position in the buffer.
1644
+ #[derive(Debug)]
1645
+ pub(super) struct File {
1646
+ buffer: TrackedVec,
1647
+ }
1648
+
1649
+ impl File {
1650
+ pub(super) fn new(limiter: Option<crate::limiter::DynFsMemoryLimiter>) -> Self {
1651
+ Self {
1652
+ buffer: TrackedVec::new(limiter),
1653
+ }
1654
+ }
1655
+
1656
+ pub(super) fn truncate(&mut self) {
1657
+ self.buffer.clear();
1658
+ }
1659
+
1660
+ pub(super) fn len(&self) -> usize {
1661
+ self.buffer.len()
1662
+ }
1663
+ }
1664
+
1665
+ impl File {
1666
+ pub fn read(&self, buf: &mut [u8], cursor: &mut u64) -> io::Result<usize> {
1667
+ let cur_pos = *cursor as usize;
1668
+
1669
+ // POSIX regular files return EOF, not an error, when reading at or beyond EOF.
1670
+ if cur_pos >= self.buffer.len() {
1671
+ return Ok(0);
1672
+ }
1673
+
1674
+ let max_to_read = cmp::min(self.buffer.len() - cur_pos, buf.len());
1675
+ let data_to_copy = &self.buffer[cur_pos..][..max_to_read];
1676
+
1677
+ // SAFETY: `buf[..max_to_read]` and `data_to_copy` have the same size, due to
1678
+ // how `max_to_read` is computed.
1679
+ buf[..max_to_read].copy_from_slice(data_to_copy);
1680
+
1681
+ *cursor += max_to_read as u64;
1682
+
1683
+ Ok(max_to_read)
1684
+ }
1685
+ }
1686
+
1687
+ impl File {
1688
+ pub fn seek(&self, position: io::SeekFrom, cursor: &mut u64) -> io::Result<u64> {
1689
+ let to_err = |_| io::ErrorKind::InvalidInput;
1690
+
1691
+ // Calculate the next cursor.
1692
+ let next_cursor: i64 = match position {
1693
+ // Calculate from the beginning, so `0 + offset`.
1694
+ io::SeekFrom::Start(offset) => offset.try_into().map_err(to_err)?,
1695
+
1696
+ // Calculate from the end, so `buffer.len() + offset`.
1697
+ io::SeekFrom::End(offset) => {
1698
+ TryInto::<i64>::try_into(self.buffer.len()).map_err(to_err)? + offset
1699
+ }
1700
+
1701
+ // Calculate from the current cursor, so `cursor + offset`.
1702
+ io::SeekFrom::Current(offset) => {
1703
+ TryInto::<i64>::try_into(*cursor).map_err(to_err)? + offset
1704
+ }
1705
+ };
1706
+
1707
+ // It's an error to seek before byte 0.
1708
+ if next_cursor < 0 {
1709
+ return Err(io::Error::new(
1710
+ io::ErrorKind::InvalidInput,
1711
+ "seeking before the byte 0",
1712
+ ));
1713
+ }
1714
+
1715
+ let next_cursor = next_cursor.try_into().map_err(to_err)?;
1716
+ *cursor = next_cursor;
1717
+
1718
+ let cursor = *cursor;
1719
+ Ok(cursor)
1720
+ }
1721
+ }
1722
+
1723
+ impl File {
1724
+ pub fn write(&mut self, buf: &[u8], cursor: &mut u64) -> io::Result<usize> {
1725
+ let position = *cursor as usize;
1726
+ let end = position
1727
+ .checked_add(buf.len())
1728
+ .ok_or(io::ErrorKind::InvalidInput)?;
1729
+
1730
+ if end > self.buffer.len() {
1731
+ // Grow the entire sparse range transactionally before modifying any
1732
+ // visible bytes. A quota failure therefore cannot leave a partial gap.
1733
+ self.buffer.resize(end, 0)?;
1734
+ }
1735
+ self.buffer[position..end].copy_from_slice(buf);
1736
+
1737
+ *cursor += buf.len() as u64;
1738
+
1739
+ Ok(buf.len())
1740
+ }
1741
+
1742
+ fn flush(&mut self) -> io::Result<()> {
1743
+ Ok(())
1744
+ }
1745
+ }
1746
+
1747
+ /// Read only file that uses copy-on-write
1748
+ #[derive(Debug)]
1749
+ pub(super) struct ReadOnlyFile {
1750
+ buffer: OwnedBuffer,
1751
+ }
1752
+
1753
+ impl ReadOnlyFile {
1754
+ pub(super) fn new(buffer: OwnedBuffer) -> Self {
1755
+ Self { buffer }
1756
+ }
1757
+
1758
+ pub(super) fn len(&self) -> usize {
1759
+ self.buffer.len()
1760
+ }
1761
+ }
1762
+
1763
+ impl ReadOnlyFile {
1764
+ pub fn read(&self, buf: &mut [u8], cursor: &mut u64) -> io::Result<usize> {
1765
+ let cur_pos = *cursor as usize;
1766
+ let max_to_read = cmp::min(self.buffer.len() - cur_pos, buf.len());
1767
+ let data_to_copy = &self.buffer[cur_pos..][..max_to_read];
1768
+
1769
+ // SAFETY: `buf[..max_to_read]` and `data_to_copy` have the same size, due to
1770
+ // how `max_to_read` is computed.
1771
+ buf[..max_to_read].copy_from_slice(data_to_copy);
1772
+
1773
+ *cursor += max_to_read as u64;
1774
+
1775
+ Ok(max_to_read)
1776
+ }
1777
+ }
1778
+
1779
+ impl ReadOnlyFile {
1780
+ pub fn seek(&self, _position: io::SeekFrom, _cursor: &mut u64) -> io::Result<u64> {
1781
+ Err(io::Error::new(
1782
+ io::ErrorKind::PermissionDenied,
1783
+ "file is read-only",
1784
+ ))
1785
+ }
1786
+ }
1787
+
1788
+ impl ReadOnlyFile {
1789
+ pub fn write(&mut self, _buf: &[u8], _cursor: &mut u64) -> io::Result<usize> {
1790
+ Err(io::Error::new(
1791
+ io::ErrorKind::PermissionDenied,
1792
+ "file is read-only",
1793
+ ))
1794
+ }
1795
+
1796
+ pub fn flush(&mut self) -> io::Result<()> {
1797
+ Ok(())
1798
+ }
1799
+ }