@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,460 @@
|
|
|
1
|
+
use anyhow::anyhow;
|
|
2
|
+
use futures::future::BoxFuture;
|
|
3
|
+
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
|
|
4
|
+
|
|
5
|
+
use std::convert::TryInto;
|
|
6
|
+
use std::io::{self, Error as IoError, ErrorKind as IoErrorKind, SeekFrom};
|
|
7
|
+
use std::path::Path;
|
|
8
|
+
use std::path::PathBuf;
|
|
9
|
+
use std::pin::Pin;
|
|
10
|
+
use std::sync::Arc;
|
|
11
|
+
use std::task::{Context, Poll};
|
|
12
|
+
|
|
13
|
+
use crate::mem_fs::FileSystem as MemFileSystem;
|
|
14
|
+
use crate::{
|
|
15
|
+
FileOpener, FileSystem, FsError, Metadata, OpenOptions, OpenOptionsConfig, ReadDir, VirtualFile,
|
|
16
|
+
};
|
|
17
|
+
use indexmap::IndexMap;
|
|
18
|
+
use webc::v1::{FsEntry, FsEntryType, OwnedFsEntryFile};
|
|
19
|
+
|
|
20
|
+
/// Custom file system wrapper to map requested file paths
|
|
21
|
+
#[derive(Debug)]
|
|
22
|
+
pub struct StaticFileSystem {
|
|
23
|
+
pub package: String,
|
|
24
|
+
pub volumes: Arc<IndexMap<String, webc::v1::Volume<'static>>>,
|
|
25
|
+
pub memory: Arc<MemFileSystem>,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
impl StaticFileSystem {
|
|
29
|
+
pub fn init(bytes: &'static [u8], package: &str) -> Option<Self> {
|
|
30
|
+
let volumes = Arc::new(webc::v1::WebC::parse_volumes_from_fileblock(bytes).ok()?);
|
|
31
|
+
let fs = Self {
|
|
32
|
+
package: package.to_string(),
|
|
33
|
+
volumes: volumes.clone(),
|
|
34
|
+
memory: Arc::new(MemFileSystem::default()),
|
|
35
|
+
};
|
|
36
|
+
let volume_names = fs.volumes.keys().cloned().collect::<Vec<_>>();
|
|
37
|
+
for volume_name in volume_names {
|
|
38
|
+
let directories = volumes.get(&volume_name).unwrap().list_directories();
|
|
39
|
+
for directory in directories {
|
|
40
|
+
let _ = fs.create_dir(Path::new(&directory));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
Some(fs)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Custom file opener, returns a WebCFile
|
|
48
|
+
impl FileOpener for StaticFileSystem {
|
|
49
|
+
fn open(
|
|
50
|
+
&self,
|
|
51
|
+
path: &Path,
|
|
52
|
+
_conf: &OpenOptionsConfig,
|
|
53
|
+
) -> Result<Box<dyn VirtualFile + Send + Sync>, FsError> {
|
|
54
|
+
match get_volume_name_opt(path) {
|
|
55
|
+
Some(volume) => {
|
|
56
|
+
let file = (*self.volumes)
|
|
57
|
+
.get(&volume)
|
|
58
|
+
.ok_or(FsError::EntryNotFound)?
|
|
59
|
+
.get_file_entry(path.to_string_lossy().as_ref())
|
|
60
|
+
.map_err(|_e| FsError::EntryNotFound)?;
|
|
61
|
+
|
|
62
|
+
Ok(Box::new(WebCFile {
|
|
63
|
+
package: self.package.clone(),
|
|
64
|
+
volume,
|
|
65
|
+
volumes: self.volumes.clone(),
|
|
66
|
+
path: path.to_path_buf(),
|
|
67
|
+
entry: file,
|
|
68
|
+
cursor: 0,
|
|
69
|
+
}))
|
|
70
|
+
}
|
|
71
|
+
None => {
|
|
72
|
+
for (volume, v) in self.volumes.iter() {
|
|
73
|
+
let entry = match v.get_file_entry(path.to_string_lossy().as_ref()) {
|
|
74
|
+
Ok(s) => s,
|
|
75
|
+
Err(_) => continue, // error
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return Ok(Box::new(WebCFile {
|
|
79
|
+
package: self.package.clone(),
|
|
80
|
+
volume: volume.clone(),
|
|
81
|
+
volumes: self.volumes.clone(),
|
|
82
|
+
path: path.to_path_buf(),
|
|
83
|
+
entry,
|
|
84
|
+
cursor: 0,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
self.memory.new_open_options().open(path)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#[derive(Debug)]
|
|
94
|
+
pub struct WebCFile {
|
|
95
|
+
pub volumes: Arc<IndexMap<String, webc::v1::Volume<'static>>>,
|
|
96
|
+
pub package: String,
|
|
97
|
+
pub volume: String,
|
|
98
|
+
pub path: PathBuf,
|
|
99
|
+
pub entry: OwnedFsEntryFile,
|
|
100
|
+
pub cursor: u64,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#[async_trait::async_trait]
|
|
104
|
+
impl VirtualFile for WebCFile {
|
|
105
|
+
fn last_accessed(&self) -> u64 {
|
|
106
|
+
0
|
|
107
|
+
}
|
|
108
|
+
fn last_modified(&self) -> u64 {
|
|
109
|
+
0
|
|
110
|
+
}
|
|
111
|
+
fn created_time(&self) -> u64 {
|
|
112
|
+
0
|
|
113
|
+
}
|
|
114
|
+
fn size(&self) -> u64 {
|
|
115
|
+
self.entry.get_len()
|
|
116
|
+
}
|
|
117
|
+
fn set_len(&mut self, _new_size: u64) -> crate::Result<()> {
|
|
118
|
+
Ok(())
|
|
119
|
+
}
|
|
120
|
+
fn unlink(&mut self) -> Result<(), FsError> {
|
|
121
|
+
Ok(())
|
|
122
|
+
}
|
|
123
|
+
fn poll_read_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
|
|
124
|
+
let remaining = self.entry.get_len() - self.cursor;
|
|
125
|
+
Poll::Ready(Ok(remaining as usize))
|
|
126
|
+
}
|
|
127
|
+
fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
|
|
128
|
+
Poll::Ready(Ok(0))
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
impl AsyncRead for WebCFile {
|
|
133
|
+
fn poll_read(
|
|
134
|
+
self: Pin<&mut Self>,
|
|
135
|
+
_cx: &mut Context<'_>,
|
|
136
|
+
buf: &mut tokio::io::ReadBuf<'_>,
|
|
137
|
+
) -> Poll<io::Result<()>> {
|
|
138
|
+
let this = self.get_mut();
|
|
139
|
+
let bytes = this
|
|
140
|
+
.volumes
|
|
141
|
+
.get(&this.volume)
|
|
142
|
+
.ok_or_else(|| {
|
|
143
|
+
IoError::new(
|
|
144
|
+
IoErrorKind::NotFound,
|
|
145
|
+
anyhow!("Unknown volume {:?}", this.volume),
|
|
146
|
+
)
|
|
147
|
+
})?
|
|
148
|
+
.get_file_bytes(&this.entry)
|
|
149
|
+
.map_err(|e| IoError::new(IoErrorKind::NotFound, e))?;
|
|
150
|
+
|
|
151
|
+
let cursor: usize = this.cursor.try_into().unwrap_or(u32::MAX as usize);
|
|
152
|
+
let start = cursor.min(bytes.len());
|
|
153
|
+
let bytes = &bytes[start..];
|
|
154
|
+
let bytes_read = bytes.len().min(buf.remaining());
|
|
155
|
+
|
|
156
|
+
if bytes_read > 0 {
|
|
157
|
+
buf.put_slice(&bytes[..bytes_read]);
|
|
158
|
+
this.cursor = this.cursor.saturating_add(bytes_read as u64);
|
|
159
|
+
}
|
|
160
|
+
Poll::Ready(Ok(()))
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// WebC file is not writable, the FileOpener will return a MemoryFile for writing instead
|
|
165
|
+
// This code should never be executed (since writes are redirected to memory instead).
|
|
166
|
+
impl AsyncWrite for WebCFile {
|
|
167
|
+
fn poll_write(
|
|
168
|
+
self: Pin<&mut Self>,
|
|
169
|
+
_cx: &mut Context<'_>,
|
|
170
|
+
buf: &[u8],
|
|
171
|
+
) -> Poll<io::Result<usize>> {
|
|
172
|
+
Poll::Ready(Ok(buf.len()))
|
|
173
|
+
}
|
|
174
|
+
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
175
|
+
Poll::Ready(Ok(()))
|
|
176
|
+
}
|
|
177
|
+
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
|
178
|
+
Poll::Ready(Ok(()))
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
impl AsyncSeek for WebCFile {
|
|
183
|
+
fn start_seek(mut self: Pin<&mut Self>, pos: io::SeekFrom) -> io::Result<()> {
|
|
184
|
+
let self_size = self.size();
|
|
185
|
+
match pos {
|
|
186
|
+
SeekFrom::Start(s) => {
|
|
187
|
+
self.cursor = s.min(self_size);
|
|
188
|
+
}
|
|
189
|
+
SeekFrom::End(e) => {
|
|
190
|
+
let self_size_i64 = self_size.try_into().unwrap_or(i64::MAX);
|
|
191
|
+
self.cursor = ((self_size_i64).saturating_add(e))
|
|
192
|
+
.min(self_size_i64)
|
|
193
|
+
.try_into()
|
|
194
|
+
.unwrap_or(i64::MAX as u64);
|
|
195
|
+
}
|
|
196
|
+
SeekFrom::Current(c) => {
|
|
197
|
+
self.cursor = (self
|
|
198
|
+
.cursor
|
|
199
|
+
.saturating_add(c.try_into().unwrap_or(i64::MAX as u64)))
|
|
200
|
+
.min(self_size);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
Ok(())
|
|
204
|
+
}
|
|
205
|
+
fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
|
206
|
+
Poll::Ready(Ok(self.cursor))
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
fn get_volume_name_opt<P: AsRef<Path>>(path: P) -> Option<String> {
|
|
211
|
+
use std::path::Component::Normal;
|
|
212
|
+
if let Some(Normal(n)) = path.as_ref().components().next()
|
|
213
|
+
&& let Some(s) = n.to_str()
|
|
214
|
+
&& s.ends_with(':')
|
|
215
|
+
{
|
|
216
|
+
return Some(s.replace(':', ""));
|
|
217
|
+
}
|
|
218
|
+
None
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
fn transform_into_read_dir(path: &Path, fs_entries: &[FsEntry<'_>]) -> crate::ReadDir {
|
|
222
|
+
let entries = fs_entries
|
|
223
|
+
.iter()
|
|
224
|
+
.map(|e| crate::DirEntry {
|
|
225
|
+
path: path.join(&*e.text),
|
|
226
|
+
metadata: Ok(crate::Metadata {
|
|
227
|
+
ft: translate_file_type(e.fs_type),
|
|
228
|
+
accessed: 0,
|
|
229
|
+
created: 0,
|
|
230
|
+
modified: 0,
|
|
231
|
+
len: e.get_len(),
|
|
232
|
+
}),
|
|
233
|
+
})
|
|
234
|
+
.collect();
|
|
235
|
+
|
|
236
|
+
crate::ReadDir::new(entries)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
impl FileSystem for StaticFileSystem {
|
|
240
|
+
fn readlink(&self, path: &Path) -> crate::Result<PathBuf> {
|
|
241
|
+
let path = normalizes_path(path);
|
|
242
|
+
if self
|
|
243
|
+
.volumes
|
|
244
|
+
.values()
|
|
245
|
+
.find_map(|v| v.get_file_entry(&path).ok())
|
|
246
|
+
.is_some()
|
|
247
|
+
{
|
|
248
|
+
Err(FsError::InvalidInput)
|
|
249
|
+
} else {
|
|
250
|
+
self.memory.readlink(Path::new(&path))
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
fn read_dir(&self, path: &Path) -> Result<ReadDir, FsError> {
|
|
255
|
+
let path = normalizes_path(path);
|
|
256
|
+
for volume in self.volumes.values() {
|
|
257
|
+
let read_dir_result = volume
|
|
258
|
+
.read_dir(&path)
|
|
259
|
+
.map(|o| transform_into_read_dir(Path::new(&path), o.as_ref()))
|
|
260
|
+
.map_err(|_| FsError::EntryNotFound);
|
|
261
|
+
|
|
262
|
+
match read_dir_result {
|
|
263
|
+
Ok(o) => {
|
|
264
|
+
return Ok(o);
|
|
265
|
+
}
|
|
266
|
+
Err(_) => {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
self.memory.read_dir(Path::new(&path))
|
|
273
|
+
}
|
|
274
|
+
fn create_dir(&self, path: &Path) -> Result<(), FsError> {
|
|
275
|
+
let path = normalizes_path(path);
|
|
276
|
+
self.memory.create_dir(Path::new(&path))
|
|
277
|
+
}
|
|
278
|
+
fn remove_dir(&self, path: &Path) -> Result<(), FsError> {
|
|
279
|
+
let path = normalizes_path(path);
|
|
280
|
+
let result = self.memory.remove_dir(Path::new(&path));
|
|
281
|
+
if self
|
|
282
|
+
.volumes
|
|
283
|
+
.values()
|
|
284
|
+
.find_map(|v| v.get_file_entry(&path).ok())
|
|
285
|
+
.is_some()
|
|
286
|
+
{
|
|
287
|
+
Ok(())
|
|
288
|
+
} else {
|
|
289
|
+
result
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<(), FsError>> {
|
|
293
|
+
Box::pin(async {
|
|
294
|
+
let from = normalizes_path(from);
|
|
295
|
+
let to = normalizes_path(to);
|
|
296
|
+
let result = self.memory.rename(Path::new(&from), Path::new(&to)).await;
|
|
297
|
+
if self
|
|
298
|
+
.volumes
|
|
299
|
+
.values()
|
|
300
|
+
.find_map(|v| v.get_file_entry(&from).ok())
|
|
301
|
+
.is_some()
|
|
302
|
+
{
|
|
303
|
+
Ok(())
|
|
304
|
+
} else {
|
|
305
|
+
result
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
fn metadata(&self, path: &Path) -> Result<Metadata, FsError> {
|
|
310
|
+
let path = normalizes_path(path);
|
|
311
|
+
if let Some(fs_entry) = self
|
|
312
|
+
.volumes
|
|
313
|
+
.values()
|
|
314
|
+
.find_map(|v| v.get_file_entry(&path).ok())
|
|
315
|
+
{
|
|
316
|
+
Ok(Metadata {
|
|
317
|
+
ft: translate_file_type(FsEntryType::File),
|
|
318
|
+
accessed: 0,
|
|
319
|
+
created: 0,
|
|
320
|
+
modified: 0,
|
|
321
|
+
len: fs_entry.get_len(),
|
|
322
|
+
})
|
|
323
|
+
} else if let Some(_fs) = self.volumes.values().find_map(|v| v.read_dir(&path).ok()) {
|
|
324
|
+
Ok(Metadata {
|
|
325
|
+
ft: translate_file_type(FsEntryType::Dir),
|
|
326
|
+
accessed: 0,
|
|
327
|
+
created: 0,
|
|
328
|
+
modified: 0,
|
|
329
|
+
len: 0,
|
|
330
|
+
})
|
|
331
|
+
} else {
|
|
332
|
+
self.memory.metadata(Path::new(&path))
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
fn remove_file(&self, path: &Path) -> Result<(), FsError> {
|
|
336
|
+
let path = normalizes_path(path);
|
|
337
|
+
let result = self.memory.remove_file(Path::new(&path));
|
|
338
|
+
if self
|
|
339
|
+
.volumes
|
|
340
|
+
.values()
|
|
341
|
+
.find_map(|v| v.get_file_entry(&path).ok())
|
|
342
|
+
.is_some()
|
|
343
|
+
{
|
|
344
|
+
Ok(())
|
|
345
|
+
} else {
|
|
346
|
+
result
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
fn new_open_options(&self) -> OpenOptions<'_> {
|
|
350
|
+
OpenOptions::new(self)
|
|
351
|
+
}
|
|
352
|
+
fn symlink_metadata(&self, path: &Path) -> Result<Metadata, FsError> {
|
|
353
|
+
let path = normalizes_path(path);
|
|
354
|
+
if let Some(fs_entry) = self
|
|
355
|
+
.volumes
|
|
356
|
+
.values()
|
|
357
|
+
.find_map(|v| v.get_file_entry(&path).ok())
|
|
358
|
+
{
|
|
359
|
+
Ok(Metadata {
|
|
360
|
+
ft: translate_file_type(FsEntryType::File),
|
|
361
|
+
accessed: 0,
|
|
362
|
+
created: 0,
|
|
363
|
+
modified: 0,
|
|
364
|
+
len: fs_entry.get_len(),
|
|
365
|
+
})
|
|
366
|
+
} else if self
|
|
367
|
+
.volumes
|
|
368
|
+
.values()
|
|
369
|
+
.find_map(|v| v.read_dir(&path).ok())
|
|
370
|
+
.is_some()
|
|
371
|
+
{
|
|
372
|
+
Ok(Metadata {
|
|
373
|
+
ft: translate_file_type(FsEntryType::Dir),
|
|
374
|
+
accessed: 0,
|
|
375
|
+
created: 0,
|
|
376
|
+
modified: 0,
|
|
377
|
+
len: 0,
|
|
378
|
+
})
|
|
379
|
+
} else {
|
|
380
|
+
self.memory.symlink_metadata(Path::new(&path))
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
fn normalizes_path(path: &Path) -> String {
|
|
386
|
+
let path = format!("{}", path.display());
|
|
387
|
+
if !path.starts_with('/') {
|
|
388
|
+
format!("/{path}")
|
|
389
|
+
} else {
|
|
390
|
+
path
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
fn translate_file_type(f: FsEntryType) -> crate::FileType {
|
|
395
|
+
crate::FileType {
|
|
396
|
+
dir: f == FsEntryType::Dir,
|
|
397
|
+
file: f == FsEntryType::File,
|
|
398
|
+
symlink: false,
|
|
399
|
+
char_device: false,
|
|
400
|
+
block_device: false,
|
|
401
|
+
socket: false,
|
|
402
|
+
fifo: false,
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
#[cfg(test)]
|
|
407
|
+
mod tests {
|
|
408
|
+
use super::*;
|
|
409
|
+
use indexmap::IndexMap;
|
|
410
|
+
use std::path::Path;
|
|
411
|
+
use tokio::io::AsyncReadExt;
|
|
412
|
+
use webc::{
|
|
413
|
+
metadata::Manifest,
|
|
414
|
+
v1::{DirOrFile, Volume, WebC},
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
fn fileblock_with_wasm_file() -> &'static [u8] {
|
|
418
|
+
let volume_bytes = Volume::serialize_files(
|
|
419
|
+
[(
|
|
420
|
+
DirOrFile::File(Path::new("lib/python.wasm").to_path_buf()),
|
|
421
|
+
vec![0, 97, 115, 109, 1, 0, 0, 0],
|
|
422
|
+
)]
|
|
423
|
+
.into_iter()
|
|
424
|
+
.collect(),
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
let mut volumes = IndexMap::new();
|
|
428
|
+
volumes.insert("atom".to_string(), Volume::parse(&volume_bytes).unwrap());
|
|
429
|
+
|
|
430
|
+
let fileblock = WebC {
|
|
431
|
+
version: 1,
|
|
432
|
+
checksum: None,
|
|
433
|
+
signature: None,
|
|
434
|
+
manifest: Manifest::default(),
|
|
435
|
+
atoms: Volume::parse(&volume_bytes).unwrap(),
|
|
436
|
+
volumes,
|
|
437
|
+
}
|
|
438
|
+
.get_volumes_as_fileblock();
|
|
439
|
+
|
|
440
|
+
Box::leak(fileblock.into_boxed_slice())
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
#[tokio::test]
|
|
444
|
+
async fn static_webc_reads_advance_the_cursor() {
|
|
445
|
+
let fs = StaticFileSystem::init(fileblock_with_wasm_file(), "python").unwrap();
|
|
446
|
+
let mut file = fs
|
|
447
|
+
.new_open_options()
|
|
448
|
+
.read(true)
|
|
449
|
+
.open("/lib/python.wasm")
|
|
450
|
+
.unwrap();
|
|
451
|
+
|
|
452
|
+
let mut first = [0; 4];
|
|
453
|
+
file.read_exact(&mut first).await.unwrap();
|
|
454
|
+
assert_eq!(&first, b"\0asm");
|
|
455
|
+
|
|
456
|
+
let mut second = [0; 4];
|
|
457
|
+
file.read_exact(&mut second).await.unwrap();
|
|
458
|
+
assert_eq!(second, [1, 0, 0, 0]);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
//! Wraps the memory file system implementation - this has been
|
|
2
|
+
//! enhanced to support shared static files, readonly files, etc...
|
|
3
|
+
|
|
4
|
+
use std::path::{Path, PathBuf};
|
|
5
|
+
|
|
6
|
+
use crate::{
|
|
7
|
+
BoxFuture, FileSystem, Metadata, OpenOptions, ReadDir, Result, limiter::DynFsMemoryLimiter,
|
|
8
|
+
mem_fs,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
#[derive(Debug, Default, Clone)]
|
|
12
|
+
pub struct TmpFileSystem {
|
|
13
|
+
fs: mem_fs::FileSystem,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl TmpFileSystem {
|
|
17
|
+
pub fn new() -> Self {
|
|
18
|
+
Self::default()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Creates a temporary filesystem with a fixed timestamp for every
|
|
22
|
+
/// implicit metadata update.
|
|
23
|
+
pub fn with_fixed_timestamp(timestamp: u64) -> Self {
|
|
24
|
+
Self {
|
|
25
|
+
fs: mem_fs::FileSystem::with_fixed_timestamp(timestamp),
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
pub fn set_memory_limiter(&self, limiter: DynFsMemoryLimiter) {
|
|
30
|
+
self.fs.set_memory_limiter(limiter);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn new_open_options_ext(&self) -> &mem_fs::FileSystem {
|
|
34
|
+
self.fs.new_open_options_ext()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub fn union(&self, other: &std::sync::Arc<dyn FileSystem + Send + Sync>) {
|
|
38
|
+
self.fs.union(other)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Canonicalize a path without validating that it actually exists.
|
|
42
|
+
pub fn canonicalize_unchecked(&self, path: &Path) -> Result<PathBuf> {
|
|
43
|
+
self.fs.canonicalize_unchecked(path)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn create_symlink(&self, source: &Path, target: &Path) -> Result<()> {
|
|
47
|
+
self.fs.create_symlink(source, target)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
impl FileSystem for TmpFileSystem {
|
|
52
|
+
fn readlink(&self, path: &Path) -> Result<PathBuf> {
|
|
53
|
+
self.fs.readlink(path)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
|
|
57
|
+
self.fs.read_dir(path)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn create_dir(&self, path: &Path) -> Result<()> {
|
|
61
|
+
self.fs.create_dir(path)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn create_symlink(&self, source: &Path, target: &Path) -> Result<()> {
|
|
65
|
+
self.fs.create_symlink(source, target)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fn hard_link(&self, source: &Path, target: &Path) -> Result<()> {
|
|
69
|
+
self.fs.hard_link(source, target)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn remove_dir(&self, path: &Path) -> Result<()> {
|
|
73
|
+
self.fs.remove_dir(path)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>> {
|
|
77
|
+
Box::pin(async { self.fs.rename(from, to).await })
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn metadata(&self, path: &Path) -> Result<Metadata> {
|
|
81
|
+
self.fs.metadata(path)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
|
|
85
|
+
self.fs.symlink_metadata(path)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn remove_file(&self, path: &Path) -> Result<()> {
|
|
89
|
+
self.fs.remove_file(path)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fn new_open_options(&self) -> OpenOptions<'_> {
|
|
93
|
+
self.fs.new_open_options()
|
|
94
|
+
}
|
|
95
|
+
}
|