@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.
- package/LICENSE +21 -0
- package/README.md +6 -0
- package/THIRD_PARTY_NOTICES.md +302 -0
- package/crates/runtime-core/Cargo.lock +5099 -0
- package/crates/runtime-core/Cargo.toml +66 -0
- package/crates/runtime-core/README.md +47 -0
- package/crates/runtime-core/src/bin/wasm-oj-compiler.rs +418 -0
- package/crates/runtime-core/src/bin/wasm-oj-runner.rs +294 -0
- package/crates/runtime-core/src/capabilities.rs +118 -0
- package/crates/runtime-core/src/compiler.rs +658 -0
- package/crates/runtime-core/src/contract.rs +5 -0
- package/crates/runtime-core/src/deterministic.rs +1051 -0
- package/crates/runtime-core/src/error.rs +58 -0
- package/crates/runtime-core/src/filesystem.rs +547 -0
- package/crates/runtime-core/src/filesystem_quota.rs +167 -0
- package/crates/runtime-core/src/go_compiler_session.rs +297 -0
- package/crates/runtime-core/src/interactive.rs +1019 -0
- package/crates/runtime-core/src/judge_package.rs +1539 -0
- package/crates/runtime-core/src/lib.rs +98 -0
- package/crates/runtime-core/src/memory.rs +84 -0
- package/crates/runtime-core/src/meter.rs +549 -0
- package/crates/runtime-core/src/module_imports.rs +149 -0
- package/crates/runtime-core/src/module_policy.rs +714 -0
- package/crates/runtime-core/src/output.rs +204 -0
- package/crates/runtime-core/src/run/mod.rs +208 -0
- package/crates/runtime-core/src/run/native.rs +260 -0
- package/crates/runtime-core/src/run/web.rs +229 -0
- package/crates/runtime-core/src/run/web_runtime.rs +109 -0
- package/crates/runtime-core/src/types.rs +268 -0
- package/crates/runtime-core/src/web.rs +83 -0
- package/dist/chunks/go-toolchain-Dbt-lp2L.js +426 -0
- package/dist/chunks/java-toolchain-DajoRCHu.js +44 -0
- package/dist/chunks/python-toolchain-Dx834o2A.js +4 -0
- package/dist/chunks/rust-toolchain-CJ3sMxPE.js +252 -0
- package/dist/chunks/toolchains-C6KuA1yM.js +224 -0
- package/dist/go-stage.mjs +193 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.js +4393 -0
- package/dist/java-stage.mjs +111 -0
- package/dist/python-stage.mjs +90 -0
- package/dist/rustc-stage.mjs +301 -0
- package/dist/server-build-stage.mjs +2564 -0
- package/dist/server-runner-stage.mjs +155 -0
- package/licenses/fflate-MIT.txt +21 -0
- package/licenses/runtime-core-dependencies.html +6253 -0
- package/licenses/runtime-core-dependencies.json +3041 -0
- package/licenses/wasmer-sdk-MIT.txt +21 -0
- package/licenses/wasmer-sdk-dependencies.html +6901 -0
- package/licenses/wasmer-sdk-dependencies.json +3013 -0
- package/package.json +70 -0
- package/rust-toolchain.toml +5 -0
- package/testdata/wojjdg02-v2-text.hex +1 -0
- package/vendor/shared-buffer/Cargo.toml +22 -0
- package/vendor/shared-buffer/LICENSE_APACHE.md +176 -0
- package/vendor/shared-buffer/LICENSE_MIT.md +25 -0
- package/vendor/shared-buffer/README.md +34 -0
- package/vendor/shared-buffer/src/lib.rs +58 -0
- package/vendor/shared-buffer/src/mmap.rs +250 -0
- package/vendor/shared-buffer/src/owned.rs +389 -0
- package/vendor/virtual-fs/Cargo.toml +181 -0
- package/vendor/virtual-fs/LICENSE +25 -0
- package/vendor/virtual-fs/src/arc_box_file.rs +142 -0
- package/vendor/virtual-fs/src/arc_file.rs +182 -0
- package/vendor/virtual-fs/src/arc_fs.rs +68 -0
- package/vendor/virtual-fs/src/buffer_file.rs +103 -0
- package/vendor/virtual-fs/src/builder.rs +232 -0
- package/vendor/virtual-fs/src/combine_file.rs +101 -0
- package/vendor/virtual-fs/src/cow_file.rs +345 -0
- package/vendor/virtual-fs/src/dual_write_file.rs +113 -0
- package/vendor/virtual-fs/src/empty_fs.rs +81 -0
- package/vendor/virtual-fs/src/filesystems.rs +108 -0
- package/vendor/virtual-fs/src/host_fs.rs +1390 -0
- package/vendor/virtual-fs/src/lib.rs +782 -0
- package/vendor/virtual-fs/src/limiter.rs +252 -0
- package/vendor/virtual-fs/src/mem_fs/file.rs +1799 -0
- package/vendor/virtual-fs/src/mem_fs/file_opener.rs +941 -0
- package/vendor/virtual-fs/src/mem_fs/filesystem.rs +2134 -0
- package/vendor/virtual-fs/src/mem_fs/mod.rs +245 -0
- package/vendor/virtual-fs/src/mem_fs/offloaded_file.rs +474 -0
- package/vendor/virtual-fs/src/mem_fs/stdio.rs +318 -0
- package/vendor/virtual-fs/src/mount_fs.rs +2225 -0
- package/vendor/virtual-fs/src/null_file.rs +87 -0
- package/vendor/virtual-fs/src/ops.rs +364 -0
- package/vendor/virtual-fs/src/overlay_fs.rs +2216 -0
- package/vendor/virtual-fs/src/passthru_fs.rs +119 -0
- package/vendor/virtual-fs/src/pipe.rs +603 -0
- package/vendor/virtual-fs/src/random_file.rs +88 -0
- package/vendor/virtual-fs/src/special_file.rs +108 -0
- package/vendor/virtual-fs/src/static_file.rs +133 -0
- package/vendor/virtual-fs/src/static_fs.rs +460 -0
- package/vendor/virtual-fs/src/tmp_fs.rs +95 -0
- package/vendor/virtual-fs/src/trace_fs.rs +258 -0
- package/vendor/virtual-fs/src/webc_volume_fs.rs +829 -0
- 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
|
+
}
|