@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,232 @@
1
+ use crate::random_file::RandomFile;
2
+ use crate::{FileSystem, MountFileSystem, VirtualFile};
3
+ use std::{
4
+ path::{Path, PathBuf},
5
+ sync::Arc,
6
+ };
7
+ use tracing::*;
8
+
9
+ use super::ZeroFile;
10
+ use super::{DeviceFile, NullFile};
11
+ use crate::limiter::DynFsMemoryLimiter;
12
+ use crate::tmp_fs::TmpFileSystem;
13
+
14
+ pub struct RootFileSystemBuilder {
15
+ default_root_dirs: bool,
16
+ default_dev_files: bool,
17
+ add_wasmer_command: bool,
18
+ stdin: Option<Box<dyn VirtualFile + Send + Sync>>,
19
+ stdout: Option<Box<dyn VirtualFile + Send + Sync>>,
20
+ stderr: Option<Box<dyn VirtualFile + Send + Sync>>,
21
+ tty: Option<Box<dyn VirtualFile + Send + Sync>>,
22
+ memory_limiter: Option<DynFsMemoryLimiter>,
23
+ }
24
+
25
+ impl Default for RootFileSystemBuilder {
26
+ fn default() -> Self {
27
+ Self {
28
+ default_root_dirs: true,
29
+ default_dev_files: true,
30
+ add_wasmer_command: true,
31
+ stdin: None,
32
+ stdout: None,
33
+ stderr: None,
34
+ tty: None,
35
+ memory_limiter: None,
36
+ }
37
+ }
38
+ }
39
+
40
+ impl RootFileSystemBuilder {
41
+ pub fn new() -> Self {
42
+ Self::default()
43
+ }
44
+
45
+ pub fn with_stdin(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
46
+ self.stdin.replace(file);
47
+ self
48
+ }
49
+
50
+ pub fn with_stdout(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
51
+ self.stdout.replace(file);
52
+ self
53
+ }
54
+
55
+ pub fn with_stderr(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
56
+ self.stderr.replace(file);
57
+ self
58
+ }
59
+
60
+ pub fn with_tty(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
61
+ self.tty.replace(file);
62
+ self
63
+ }
64
+
65
+ pub fn with_memory_limiter(mut self, limiter: DynFsMemoryLimiter) -> Self {
66
+ self.memory_limiter = Some(limiter);
67
+ self
68
+ }
69
+
70
+ pub fn with_memory_limiter_opt(mut self, limiter: Option<DynFsMemoryLimiter>) -> Self {
71
+ self.memory_limiter = limiter;
72
+ self
73
+ }
74
+
75
+ pub fn default_root_dirs(mut self, val: bool) -> Self {
76
+ self.default_root_dirs = val;
77
+ self
78
+ }
79
+
80
+ pub fn build(self) -> MountFileSystem {
81
+ self.build_ext(&[])
82
+ }
83
+
84
+ pub fn build_ext(self, mapped_dirs: &[&str]) -> MountFileSystem {
85
+ let tmp = self.build_tmp_ext(mapped_dirs);
86
+ let root = MountFileSystem::new();
87
+ root.mount(Path::new("/"), Arc::new(tmp))
88
+ .expect("mounting the root fs on an empty mount fs should succeed");
89
+ root
90
+ }
91
+
92
+ pub fn build_tmp(self) -> TmpFileSystem {
93
+ self.build_tmp_ext(&[])
94
+ }
95
+
96
+ pub fn build_tmp_ext(self, mapped_dirs: &[&str]) -> TmpFileSystem {
97
+ let tmp = TmpFileSystem::new();
98
+
99
+ if let Some(limiter) = &self.memory_limiter {
100
+ tmp.set_memory_limiter(limiter.clone());
101
+ }
102
+
103
+ if self.default_root_dirs {
104
+ let default_dirs = ["/.app", "/.private", "/bin", "/dev", "/etc", "/tmp"]
105
+ .into_iter()
106
+ .filter(|d| !mapped_dirs.contains(d))
107
+ .collect::<Vec<_>>();
108
+
109
+ for root_dir in &default_dirs {
110
+ if let Err(err) = tmp.create_dir(Path::new(root_dir)) {
111
+ debug!("failed to create dir [{}] - {}", root_dir, err);
112
+ }
113
+ }
114
+ }
115
+ if self.add_wasmer_command {
116
+ let _ = tmp
117
+ .new_open_options_ext()
118
+ .insert_device_file(PathBuf::from("/bin/wasmer"), Box::<NullFile>::default());
119
+ }
120
+ if self.default_dev_files {
121
+ let _ = tmp
122
+ .new_open_options_ext()
123
+ .insert_device_file(PathBuf::from("/dev/null"), Box::<NullFile>::default());
124
+ let _ = tmp
125
+ .new_open_options_ext()
126
+ .insert_device_file(PathBuf::from("/dev/zero"), Box::<ZeroFile>::default());
127
+ let _ = tmp
128
+ .new_open_options_ext()
129
+ .insert_device_file(PathBuf::from("/dev/urandom"), Box::<RandomFile>::default());
130
+ let _ = tmp.new_open_options_ext().insert_device_file(
131
+ PathBuf::from("/dev/stdin"),
132
+ self.stdin
133
+ .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDIN))),
134
+ );
135
+ let _ = tmp.new_open_options_ext().insert_device_file(
136
+ PathBuf::from("/dev/stdout"),
137
+ self.stdout
138
+ .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDOUT))),
139
+ );
140
+ let _ = tmp.new_open_options_ext().insert_device_file(
141
+ PathBuf::from("/dev/stderr"),
142
+ self.stderr
143
+ .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDERR))),
144
+ );
145
+ let _ = tmp.new_open_options_ext().insert_device_file(
146
+ PathBuf::from("/dev/tty"),
147
+ self.tty.unwrap_or_else(|| Box::<NullFile>::default()),
148
+ );
149
+
150
+ let _ = tmp.create_dir(Path::new("/dev/shm"));
151
+ }
152
+ tmp
153
+ }
154
+ }
155
+
156
+ #[cfg(test)]
157
+ mod test_builder {
158
+ use crate::{FileSystem, RootFileSystemBuilder};
159
+ use std::path::Path;
160
+ use tokio::io::{AsyncReadExt, AsyncWriteExt};
161
+
162
+ #[tokio::test]
163
+ async fn test_root_file_system() {
164
+ let root_fs = RootFileSystemBuilder::new().build();
165
+ let mut dev_null = root_fs
166
+ .new_open_options()
167
+ .read(true)
168
+ .write(true)
169
+ .open("/dev/null")
170
+ .unwrap();
171
+ assert_eq!(dev_null.write(b"hello").await.unwrap(), 5);
172
+ let mut buf = Vec::new();
173
+ dev_null.read_to_end(&mut buf).await.unwrap();
174
+ assert!(buf.is_empty());
175
+ assert!(dev_null.get_special_fd().is_none());
176
+
177
+ let mut dev_zero = root_fs
178
+ .new_open_options()
179
+ .read(true)
180
+ .write(true)
181
+ .open("/dev/zero")
182
+ .unwrap();
183
+ assert_eq!(dev_zero.write(b"hello").await.unwrap(), 5);
184
+ let mut buf = vec![1; 10];
185
+ dev_zero.read_exact(&mut buf[..]).await.unwrap();
186
+ assert_eq!(buf, vec![0; 10]);
187
+ assert!(dev_zero.get_special_fd().is_none());
188
+
189
+ let mut dev_tty = root_fs
190
+ .new_open_options()
191
+ .read(true)
192
+ .write(true)
193
+ .open("/dev/tty")
194
+ .unwrap();
195
+ assert_eq!(dev_tty.write(b"hello").await.unwrap(), 5);
196
+ let mut buf = Vec::new();
197
+ dev_tty.read_to_end(&mut buf).await.unwrap();
198
+ assert!(buf.is_empty());
199
+ assert!(dev_tty.get_special_fd().is_none());
200
+
201
+ root_fs
202
+ .new_open_options()
203
+ .read(true)
204
+ .open("/bin/wasmer")
205
+ .unwrap();
206
+
207
+ let dev_stdin = root_fs
208
+ .new_open_options()
209
+ .read(true)
210
+ .write(true)
211
+ .open("/dev/stdin")
212
+ .unwrap();
213
+ assert_eq!(dev_stdin.get_special_fd().unwrap(), 0);
214
+ let dev_stdout = root_fs
215
+ .new_open_options()
216
+ .read(true)
217
+ .write(true)
218
+ .open("/dev/stdout")
219
+ .unwrap();
220
+ assert_eq!(dev_stdout.get_special_fd().unwrap(), 1);
221
+ let dev_stderr = root_fs
222
+ .new_open_options()
223
+ .read(true)
224
+ .write(true)
225
+ .open("/dev/stderr")
226
+ .unwrap();
227
+ assert_eq!(dev_stderr.get_special_fd().unwrap(), 2);
228
+
229
+ let dev_shm_metadata = root_fs.metadata(Path::new("/dev/shm")).unwrap();
230
+ assert!(dev_shm_metadata.is_dir());
231
+ }
232
+ }
@@ -0,0 +1,101 @@
1
+ use super::*;
2
+
3
+ use crate::VirtualFile;
4
+
5
+ #[derive(Debug)]
6
+ pub struct CombineFile {
7
+ tx: Box<dyn VirtualFile + Send + Sync + 'static>,
8
+ rx: Box<dyn VirtualFile + Send + Sync + 'static>,
9
+ }
10
+
11
+ impl CombineFile {
12
+ pub fn new(
13
+ tx: Box<dyn VirtualFile + Send + Sync + 'static>,
14
+ rx: Box<dyn VirtualFile + Send + Sync + 'static>,
15
+ ) -> Self {
16
+ Self { tx, rx }
17
+ }
18
+ }
19
+
20
+ impl VirtualFile for CombineFile {
21
+ fn last_accessed(&self) -> u64 {
22
+ self.rx.last_accessed()
23
+ }
24
+
25
+ fn last_modified(&self) -> u64 {
26
+ self.tx.last_modified()
27
+ }
28
+
29
+ fn created_time(&self) -> u64 {
30
+ self.tx.created_time()
31
+ }
32
+
33
+ fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
34
+ self.tx.set_times(atime, mtime)
35
+ }
36
+
37
+ fn size(&self) -> u64 {
38
+ self.rx.size()
39
+ }
40
+
41
+ fn set_len(&mut self, new_size: u64) -> Result<()> {
42
+ self.tx.set_len(new_size)
43
+ }
44
+
45
+ fn unlink(&mut self) -> Result<()> {
46
+ self.tx.unlink()
47
+ }
48
+
49
+ fn poll_read_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
50
+ Pin::new(self.rx.as_mut()).poll_read_ready(cx)
51
+ }
52
+
53
+ fn poll_write_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
54
+ Pin::new(self.tx.as_mut()).poll_write_ready(cx)
55
+ }
56
+ }
57
+
58
+ impl AsyncWrite for CombineFile {
59
+ fn poll_write(
60
+ mut self: Pin<&mut Self>,
61
+ cx: &mut std::task::Context<'_>,
62
+ buf: &[u8],
63
+ ) -> Poll<std::io::Result<usize>> {
64
+ Pin::new(&mut self.tx).poll_write(cx, buf)
65
+ }
66
+
67
+ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
68
+ Pin::new(&mut self.tx).poll_flush(cx)
69
+ }
70
+
71
+ fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
72
+ Pin::new(&mut self.tx).poll_shutdown(cx)
73
+ }
74
+ }
75
+
76
+ impl AsyncRead for CombineFile {
77
+ fn poll_read(
78
+ mut self: Pin<&mut Self>,
79
+ cx: &mut std::task::Context<'_>,
80
+ buf: &mut tokio::io::ReadBuf<'_>,
81
+ ) -> Poll<io::Result<()>> {
82
+ Pin::new(&mut self.rx).poll_read(cx, buf)
83
+ }
84
+ }
85
+
86
+ impl AsyncSeek for CombineFile {
87
+ fn start_seek(mut self: Pin<&mut Self>, position: io::SeekFrom) -> io::Result<()> {
88
+ Pin::new(&mut self.tx).start_seek(position)?;
89
+ Pin::new(&mut self.rx).start_seek(position)
90
+ }
91
+
92
+ fn poll_complete(
93
+ mut self: Pin<&mut Self>,
94
+ cx: &mut std::task::Context<'_>,
95
+ ) -> Poll<io::Result<u64>> {
96
+ if Pin::new(&mut self.tx).poll_complete(cx).is_pending() {
97
+ return Poll::Pending;
98
+ }
99
+ Pin::new(&mut self.rx).poll_complete(cx)
100
+ }
101
+ }
@@ -0,0 +1,345 @@
1
+ //! Used for /dev/zero - infinitely returns zero
2
+ //! which is useful for commands like `dd if=/dev/zero of=bigfile.img size=1G`
3
+
4
+ use derive_more::Debug;
5
+ use replace_with::replace_with_or_abort;
6
+ use std::pin::Pin;
7
+ use std::task::{Context, Poll};
8
+ use std::{
9
+ future::Future,
10
+ io::{self, *},
11
+ };
12
+
13
+ use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, AsyncWrite};
14
+
15
+ use crate::{BufferFile, VirtualFile};
16
+
17
+ #[derive(Debug)]
18
+ enum CowState {
19
+ ReadOnly(Box<dyn VirtualFile + Send + Sync>),
20
+ Copying {
21
+ #[debug(skip)]
22
+ future: Pin<Box<dyn Future<Output = io::Result<BufferFile>> + Send + Sync>>,
23
+ requested_size: Option<u64>,
24
+ requested_position: Option<SeekFrom>,
25
+ cached_size: u64,
26
+ },
27
+ Copied(BufferFile),
28
+ }
29
+
30
+ impl CowState {
31
+ fn inner_mut(&mut self) -> &mut (dyn VirtualFile + Send + Sync) {
32
+ match self {
33
+ Self::ReadOnly(inner) => inner.as_mut(),
34
+ Self::Copying { .. } => panic!("Cannot access inner file while copying"),
35
+ Self::Copied(inner) => inner,
36
+ }
37
+ }
38
+ }
39
+
40
+ #[derive(Debug)]
41
+ pub struct CopyOnWriteFile {
42
+ last_accessed: u64,
43
+ last_modified: u64,
44
+ created_time: u64,
45
+ state: CowState,
46
+ }
47
+
48
+ impl CopyOnWriteFile {
49
+ pub fn new(inner: Box<dyn VirtualFile + Send + Sync>) -> Self {
50
+ Self {
51
+ last_accessed: inner.last_accessed(),
52
+ last_modified: inner.last_modified(),
53
+ created_time: inner.created_time(),
54
+ state: CowState::ReadOnly(inner),
55
+ }
56
+ }
57
+
58
+ async fn copy(mut inner: Box<dyn VirtualFile + Send + Sync>) -> io::Result<BufferFile> {
59
+ let initial_position = inner.seek(SeekFrom::Current(0)).await?;
60
+ inner.seek(SeekFrom::Start(0)).await?;
61
+
62
+ let mut buffer = [0u8; 8192];
63
+ let mut buffer_file = BufferFile::default();
64
+ loop {
65
+ let read_bytes = inner.read_buf(&mut &mut buffer[..]).await?;
66
+ if read_bytes == 0 {
67
+ break;
68
+ }
69
+ buffer_file.data.write_all(&buffer[0..read_bytes])?;
70
+ }
71
+
72
+ buffer_file.seek(SeekFrom::Start(initial_position)).await?;
73
+
74
+ Ok(buffer_file)
75
+ }
76
+
77
+ fn poll_copy_progress(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
78
+ match self.state {
79
+ CowState::Copying {
80
+ ref mut future,
81
+ requested_size,
82
+ requested_position,
83
+ ..
84
+ } => match future.as_mut().poll(cx) {
85
+ Poll::Ready(Ok(mut buf)) => {
86
+ if let Some(requested_size) = requested_size {
87
+ buf.set_len(requested_size)?;
88
+ }
89
+ if let Some(requested_position) = requested_position {
90
+ Pin::new(&mut buf).start_seek(requested_position)?;
91
+ }
92
+ self.state = CowState::Copied(buf);
93
+ Poll::Ready(Ok(()))
94
+ }
95
+ Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
96
+ Poll::Pending => Poll::Pending,
97
+ },
98
+ _ => Poll::Ready(Ok(())),
99
+ }
100
+ }
101
+
102
+ fn start_copy(&mut self) {
103
+ replace_with_or_abort(&mut self.state, |state| match state {
104
+ CowState::ReadOnly(inner) => CowState::Copying {
105
+ cached_size: inner.size(),
106
+ requested_size: None,
107
+ requested_position: None,
108
+ future: Box::pin(Self::copy(inner)),
109
+ },
110
+ state => state,
111
+ });
112
+ }
113
+
114
+ fn poll_copy_start_and_progress(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
115
+ self.start_copy();
116
+ self.poll_copy_progress(cx)
117
+ }
118
+ }
119
+
120
+ impl AsyncSeek for CopyOnWriteFile {
121
+ fn start_seek(mut self: Pin<&mut Self>, position: io::SeekFrom) -> io::Result<()> {
122
+ match self.state {
123
+ CowState::Copying {
124
+ ref mut requested_position,
125
+ ..
126
+ } => {
127
+ *requested_position = Some(position);
128
+ Ok(())
129
+ }
130
+
131
+ _ => Pin::new(self.state.inner_mut()).start_seek(position),
132
+ }
133
+ }
134
+
135
+ fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
136
+ match self.poll_copy_progress(cx) {
137
+ Poll::Ready(Ok(())) => {}
138
+ Poll::Pending => return Poll::Pending,
139
+ Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
140
+ }
141
+
142
+ Pin::new(self.state.inner_mut()).poll_complete(cx)
143
+ }
144
+ }
145
+
146
+ impl AsyncWrite for CopyOnWriteFile {
147
+ fn poll_write(
148
+ mut self: Pin<&mut Self>,
149
+ cx: &mut Context<'_>,
150
+ buf: &[u8],
151
+ ) -> Poll<io::Result<usize>> {
152
+ match self.poll_copy_start_and_progress(cx) {
153
+ Poll::Pending => return Poll::Pending,
154
+ Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
155
+ Poll::Ready(Ok(())) => {}
156
+ }
157
+ Pin::new(self.state.inner_mut()).poll_write(cx, buf)
158
+ }
159
+
160
+ fn poll_write_vectored(
161
+ mut self: Pin<&mut Self>,
162
+ cx: &mut Context<'_>,
163
+ bufs: &[io::IoSlice<'_>],
164
+ ) -> Poll<io::Result<usize>> {
165
+ match self.poll_copy_start_and_progress(cx) {
166
+ Poll::Pending => return Poll::Pending,
167
+ Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
168
+ Poll::Ready(Ok(())) => {}
169
+ }
170
+ Pin::new(self.state.inner_mut()).poll_write_vectored(cx, bufs)
171
+ }
172
+
173
+ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
174
+ match self.poll_copy_start_and_progress(cx) {
175
+ Poll::Ready(Ok(())) => {}
176
+ p => return p,
177
+ }
178
+ Pin::new(self.state.inner_mut()).poll_flush(cx)
179
+ }
180
+
181
+ fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
182
+ match self.poll_copy_start_and_progress(cx) {
183
+ Poll::Ready(Ok(())) => {}
184
+ p => return p,
185
+ }
186
+ Pin::new(self.state.inner_mut()).poll_shutdown(cx)
187
+ }
188
+ }
189
+
190
+ impl AsyncRead for CopyOnWriteFile {
191
+ fn poll_read(
192
+ mut self: Pin<&mut Self>,
193
+ cx: &mut Context<'_>,
194
+ buf: &mut tokio::io::ReadBuf<'_>,
195
+ ) -> Poll<io::Result<()>> {
196
+ match self.poll_copy_progress(cx) {
197
+ Poll::Ready(Ok(())) => {}
198
+ p => return p,
199
+ }
200
+ Pin::new(self.state.inner_mut()).poll_read(cx, buf)
201
+ }
202
+ }
203
+
204
+ impl VirtualFile for CopyOnWriteFile {
205
+ fn last_accessed(&self) -> u64 {
206
+ self.last_accessed
207
+ }
208
+
209
+ fn last_modified(&self) -> u64 {
210
+ self.last_modified
211
+ }
212
+
213
+ fn created_time(&self) -> u64 {
214
+ self.created_time
215
+ }
216
+
217
+ fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
218
+ if let Some(atime) = atime {
219
+ self.last_accessed = atime;
220
+ }
221
+ if let Some(mtime) = mtime {
222
+ self.last_modified = mtime;
223
+ }
224
+
225
+ Ok(())
226
+ }
227
+
228
+ fn size(&self) -> u64 {
229
+ match &self.state {
230
+ CowState::ReadOnly(inner) => inner.size(),
231
+ CowState::Copying {
232
+ requested_size: Some(size),
233
+ ..
234
+ } => *size,
235
+ CowState::Copying { cached_size, .. } => *cached_size,
236
+ CowState::Copied(buffer_file) => buffer_file.size(),
237
+ }
238
+ }
239
+
240
+ fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
241
+ match self.state {
242
+ CowState::ReadOnly(_) => {
243
+ self.start_copy();
244
+ let CowState::Copying {
245
+ ref mut requested_size,
246
+ ..
247
+ } = self.state
248
+ else {
249
+ unreachable!()
250
+ };
251
+ *requested_size = Some(new_size);
252
+ }
253
+
254
+ CowState::Copying {
255
+ ref mut requested_size,
256
+ ..
257
+ } => {
258
+ *requested_size = Some(new_size);
259
+ }
260
+
261
+ CowState::Copied(ref mut buf) => {
262
+ buf.set_len(new_size)?;
263
+ }
264
+ }
265
+
266
+ Ok(())
267
+ }
268
+
269
+ fn unlink(&mut self) -> crate::Result<()> {
270
+ // TODO: one can imagine interrupting an in-progress copy here
271
+ self.set_len(0)
272
+ }
273
+
274
+ fn poll_read_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
275
+ match self.poll_copy_progress(cx) {
276
+ Poll::Pending => return Poll::Pending,
277
+ Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
278
+ Poll::Ready(Ok(())) => {}
279
+ }
280
+ Pin::new(self.state.inner_mut()).poll_read_ready(cx)
281
+ }
282
+
283
+ fn poll_write_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
284
+ self.poll_copy_progress(cx).map_ok(|_| 8192)
285
+ }
286
+ }
287
+
288
+ #[cfg(test)]
289
+ mod tests {
290
+ use super::*;
291
+
292
+ // This is as weird a test as it gets, yes, but I'm (unashamedly!) cramming
293
+ // everything we know was wrong with the impl into this one test to save time.
294
+ #[tokio::test]
295
+ async fn cow_file_works() {
296
+ let mut data = Vec::with_capacity(16385);
297
+ for i in 0..16385 {
298
+ data.push(i as u8);
299
+ }
300
+ let inner = BufferFile {
301
+ data: Cursor::new(data),
302
+ };
303
+ let mut file = CopyOnWriteFile::new(Box::new(inner));
304
+
305
+ assert!(matches!(file.state, CowState::ReadOnly(_)));
306
+ assert_eq!(file.size(), 16385);
307
+ assert_ne!(file.created_time(), 0);
308
+ assert_ne!(file.last_accessed(), 0);
309
+ assert_ne!(file.last_modified(), 0);
310
+
311
+ let mut buf = [0u8; 4];
312
+ let read = file.read_exact(buf.as_mut()).await.unwrap();
313
+ assert_eq!(read, 4);
314
+ assert_eq!(buf, [0, 1, 2, 3]);
315
+ assert_eq!(file.seek(SeekFrom::Current(0)).await.unwrap(), 4);
316
+ assert!(matches!(file.state, CowState::ReadOnly { .. }));
317
+
318
+ // After this call, the file will "start" copying, but the actual
319
+ // future won't be polled until we try to read or write.
320
+ file.start_copy();
321
+ assert!(matches!(file.state, CowState::Copying { .. }));
322
+ assert_eq!(file.size(), 16385);
323
+
324
+ // The cached length should be returned while copying
325
+ file.set_len(16400).unwrap();
326
+ assert!(matches!(file.state, CowState::Copying { .. }));
327
+ assert_eq!(file.size(), 16400);
328
+
329
+ // Now try to read from the file, which will trigger the copy
330
+ let read = file.read_exact(buf.as_mut()).await.unwrap();
331
+ assert!(matches!(file.state, CowState::Copied { .. }));
332
+ assert_eq!(read, 4);
333
+ assert_eq!(buf, [4, 5, 6, 7]);
334
+ assert_eq!(file.seek(SeekFrom::Current(0)).await.unwrap(), 8);
335
+ assert_eq!(file.size(), 16400);
336
+
337
+ file.seek(SeekFrom::Start(16383)).await.unwrap();
338
+ let read = file.read_exact(buf.as_mut()).await.unwrap();
339
+ assert_eq!(read, 4);
340
+ // set_len should have filled the rest with zeroes
341
+ assert_eq!(buf, [(16383 % 256) as u8, (16384 % 256) as u8, 0, 0]);
342
+ assert_eq!(file.seek(SeekFrom::Current(0)).await.unwrap(), 16387);
343
+ assert!(matches!(file.state, CowState::Copied { .. }));
344
+ }
345
+ }