kobako 0.29.0 → 0.30.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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +217 -10
- data/Cargo.lock +139 -141
- data/README.md +96 -574
- data/ROADMAP.md +2 -1
- data/crates/kobako-runtime/CHANGELOG.md +269 -1
- data/crates/kobako-runtime/Cargo.toml +25 -15
- data/crates/kobako-runtime/README.md +7 -3
- data/crates/kobako-runtime/src/dispatch.rs +1 -1
- data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
- data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
- data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
- data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
- data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
- data/crates/kobako-runtime/src/endpoint.rs +166 -0
- data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
- data/crates/kobako-runtime/src/filesystem/{directory/spelling.rs → bind/lookup.rs} +91 -49
- data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
- data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
- data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
- data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
- data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +227 -734
- data/crates/kobako-runtime/src/filesystem/overlay.rs +2649 -1135
- data/crates/kobako-runtime/src/filesystem/path.rs +2 -41
- data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
- data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
- data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
- data/crates/kobako-runtime/src/filesystem.rs +21 -387
- data/crates/kobako-runtime/src/hashtable.rs +44 -0
- data/crates/kobako-runtime/src/lib.rs +11 -6
- data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
- data/crates/kobako-runtime/src/locking.rs +218 -0
- data/crates/kobako-runtime/src/mount.rs +31 -104
- data/crates/kobako-runtime/src/profile.rs +137 -1
- data/crates/kobako-runtime/src/sched.rs +210 -0
- data/crates/kobako-runtime/src/security.rs +696 -0
- data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
- data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
- data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
- data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
- data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
- data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
- data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
- data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
- data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
- data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
- data/crates/kobako-runtime/src/vfs/path.rs +64 -0
- data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
- data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
- data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
- data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
- data/crates/kobako-runtime/src/vfs.rs +60 -0
- data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
- data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
- data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
- data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
- data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
- data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
- data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
- data/crates/kobako-transport/CHANGELOG.md +8 -2
- data/crates/kobako-transport/Cargo.toml +3 -3
- data/crates/kobako-transport/README.md +6 -5
- data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
- data/crates/kobako-transport/src/envelope/call.rs +5 -5
- data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
- data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
- data/crates/kobako-transport/src/envelope/fault.rs +2 -2
- data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
- data/crates/kobako-transport/src/envelope/preamble.rs +12 -12
- data/crates/kobako-transport/src/envelope/reply.rs +2 -2
- data/crates/kobako-transport/src/envelope/request.rs +2 -2
- data/crates/kobako-transport/src/envelope/run.rs +1 -1
- data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
- data/crates/kobako-transport/src/envelope.rs +2 -2
- data/crates/kobako-wasmtime/CHANGELOG.md +154 -8
- data/crates/kobako-wasmtime/Cargo.toml +15 -14
- data/crates/kobako-wasmtime/README.md +9 -5
- data/crates/kobako-wasmtime/src/abi.rs +12 -20
- data/crates/kobako-wasmtime/src/ambient.rs +1 -2
- data/crates/kobako-wasmtime/src/cache.rs +5 -5
- data/crates/kobako-wasmtime/src/config.rs +22 -7
- data/crates/kobako-wasmtime/src/descriptors.rs +133 -100
- data/crates/kobako-wasmtime/src/driver.rs +29 -96
- data/crates/kobako-wasmtime/src/exports.rs +5 -16
- data/crates/kobako-wasmtime/src/guest.rs +38 -0
- data/crates/kobako-wasmtime/src/imports.rs +14 -8
- data/crates/kobako-wasmtime/src/instance_pre.rs +3 -3
- data/crates/kobako-wasmtime/src/invocation.rs +26 -46
- data/crates/kobako-wasmtime/src/lib.rs +1 -4
- data/crates/kobako-wasmtime/src/limiter.rs +6 -230
- data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +69 -46
- data/crates/kobako-wasmtime/src/preview1/differential.rs +121 -7
- data/crates/kobako-wasmtime/src/preview1/errno.rs +74 -206
- data/crates/kobako-wasmtime/src/preview1/fd.rs +1248 -500
- data/crates/kobako-wasmtime/src/preview1/memory.rs +117 -150
- data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view.rs +1939 -672
- data/crates/kobako-wasmtime/src/preview1/path.rs +230 -349
- data/crates/kobako-wasmtime/src/preview1/poll.rs +148 -100
- data/crates/kobako-wasmtime/src/preview1/sock.rs +6 -23
- data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
- data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
- data/crates/kobako-wasmtime/src/preview1.rs +464 -413
- data/crates/kobako-wasmtime/src/trap.rs +9 -9
- data/crates/kobako-wasmtime/src/wasi.rs +53 -37
- data/crates/kobako-wasmtime/tests/{guest_file.rs → integration/guest_file.rs} +232 -201
- data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
- data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{unanswered_call.rs → integration/unanswered_call.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +1 -1
- data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
- data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +1 -1
- data/ext/kobako/src/runtime/bridge.rs +1 -1
- data/ext/kobako/src/runtime/errors.rs +1 -1
- data/ext/kobako/src/runtime/escape.rs +28 -14
- data/ext/kobako/src/runtime/filesystem/grants.rs +21 -5
- data/ext/kobako/src/runtime/filesystem/ruby.rs +464 -156
- data/ext/kobako/src/runtime/filesystem.rs +109 -137
- data/ext/kobako/src/runtime/gvl.rs +43 -8
- data/ext/kobako/src/runtime/snapshot.rs +1 -1
- data/ext/kobako/src/runtime.rs +6 -1
- data/lib/kobako/catalog/extensions.rb +5 -5
- data/lib/kobako/catalog/flags.rb +2 -2
- data/lib/kobako/catalog/handles.rb +6 -6
- data/lib/kobako/catalog/mount.rb +2 -10
- data/lib/kobako/catalog/services.rb +2 -2
- data/lib/kobako/catalog/snippets.rb +7 -7
- data/lib/kobako/catalog.rb +1 -1
- data/lib/kobako/codec/decoder.rb +10 -10
- data/lib/kobako/codec/ext_types.rb +3 -5
- data/lib/kobako/codec/handle_walk.rb +4 -6
- data/lib/kobako/codec/nesting.rb +7 -7
- data/lib/kobako/codec/utils.rb +1 -1
- data/lib/kobako/codec/value_budget.rb +3 -3
- data/lib/kobako/context.rb +5 -5
- data/lib/kobako/filesystem.rb +44 -13
- data/lib/kobako/payload/arguments.rb +2 -2
- data/lib/kobako/pool.rb +4 -4
- data/lib/kobako/sandbox.rb +28 -28
- data/lib/kobako/sandbox_options.rb +2 -3
- data/lib/kobako/snippet/bytecode.rb +1 -1
- data/lib/kobako/snippet/source.rb +1 -1
- data/lib/kobako/transport/dispatcher.rb +5 -5
- data/lib/kobako/transport/exposure.rb +2 -2
- data/lib/kobako/transport/reflection.rb +7 -7
- data/lib/kobako/transport/run.rb +5 -5
- data/lib/kobako/transport/yielder.rb +1 -1
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +34 -0
- data/release-please-config.json +1 -0
- data/sig/kobako/catalog/extensions.rbs +3 -3
- data/sig/kobako/catalog/handles.rbs +3 -3
- data/sig/kobako/catalog/mount.rbs +0 -4
- data/sig/kobako/catalog/services.rbs +1 -1
- data/sig/kobako/catalog/snippets.rbs +3 -3
- data/sig/kobako/catalog.rbs +1 -1
- data/sig/kobako/codec/decoder.rbs +1 -1
- data/sig/kobako/codec/handle_walk.rbs +1 -1
- data/sig/kobako/codec/nesting.rbs +2 -2
- data/sig/kobako/codec/utils.rbs +1 -1
- data/sig/kobako/codec/value_budget.rbs +1 -1
- data/sig/kobako/codec.rbs +1 -2
- data/sig/kobako/context.rbs +1 -1
- data/sig/kobako/filesystem.rbs +19 -14
- data/sig/kobako/guest_yielder.rbs +1 -1
- data/sig/kobako/payload/arguments.rbs +1 -1
- data/sig/kobako/pool.rbs +2 -2
- data/sig/kobako/runtime.rbs +1 -1
- data/sig/kobako/sandbox.rbs +3 -5
- data/sig/kobako/sandbox_options.rbs +1 -1
- data/sig/kobako/transport/dispatcher.rbs +2 -2
- data/sig/kobako/transport/exposure.rbs +1 -1
- data/sig/kobako/transport/reflection.rbs +2 -2
- data/sig/kobako/transport/run.rbs +3 -3
- data/sig/kobako/transport.rbs +1 -1
- metadata +67 -19
- data/crates/kobako-runtime/src/filesystem/directory.rs +0 -1082
- data/crates/kobako-runtime/src/filesystem/memory.rs +0 -1100
- data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +0 -88
- data/crates/kobako-runtime/src/filesystem/temporary.rs +0 -167
- data/crates/kobako-wasmtime/src/capture.rs +0 -96
- data/crates/kobako-wasmtime/src/dispatch.rs +0 -125
- data/crates/kobako-wasmtime/src/guest_mem.rs +0 -271
- data/crates/kobako-wasmtime/src/io.rs +0 -89
- data/crates/kobako-wasmtime/src/preview1/args.rs +0 -60
- data/crates/kobako-wasmtime/src/preview1/beneath.rs +0 -188
- /data/crates/kobako-wasmtime/tests/{compile_posture.rs → integration/compile_posture.rs} +0 -0
- /data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +0 -0
|
@@ -15,7 +15,7 @@ use wasmtime_wasi::p1::{self, WasiP1Ctx};
|
|
|
15
15
|
use wasmtime_wasi::p2::pipe::{MemoryInputPipe, MemoryOutputPipe};
|
|
16
16
|
use wasmtime_wasi::WasiCtxBuilder;
|
|
17
17
|
|
|
18
|
-
use crate::cache::
|
|
18
|
+
use crate::cache::load_engine;
|
|
19
19
|
use crate::config::Config;
|
|
20
20
|
use crate::imports;
|
|
21
21
|
use crate::invocation::Invocation;
|
|
@@ -91,6 +91,7 @@ const PROBE_WAT: &str = r#"
|
|
|
91
91
|
(func (export "fd_fdstat_set_rights") (param i32) (result i32) (call $fd_fdstat_set_rights (local.get 0) (i64.const 0) (i64.const 0)))
|
|
92
92
|
(func (export "fd_filestat_get") (param i32) (result i32) (call $fd_filestat_get (local.get 0) (i32.const 256)))
|
|
93
93
|
(func (export "fd_filestat_set_size") (param i32) (result i32) (call $fd_filestat_set_size (local.get 0) (i64.const 0)))
|
|
94
|
+
(func (export "fd_filestat_set_size_huge") (param i32) (result i32) (call $fd_filestat_set_size (local.get 0) (i64.const -1)))
|
|
94
95
|
(func (export "fd_filestat_set_times") (param i32) (result i32) (call $fd_filestat_set_times (local.get 0) (i64.const 0) (i64.const 0) (i32.const 0)))
|
|
95
96
|
(func (export "fd_pread") (param i32) (result i32) (call $fd_pread (local.get 0) (i32.const 64) (i32.const 1) (i64.const 0) (i32.const 128)))
|
|
96
97
|
(func (export "fd_prestat_get") (param i32) (result i32) (call $fd_prestat_get (local.get 0) (i32.const 256)))
|
|
@@ -282,7 +283,7 @@ fn observe<T>(
|
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
fn through_kobako(call: &str, fd: i32, limit: Option<usize>) -> Observation {
|
|
285
|
-
let engine =
|
|
286
|
+
let engine = load_engine().expect("shared engine must be constructible");
|
|
286
287
|
let config = Config {
|
|
287
288
|
timeout: None,
|
|
288
289
|
setup_timeout: None,
|
|
@@ -291,8 +292,9 @@ fn through_kobako(call: &str, fd: i32, limit: Option<usize>) -> Observation {
|
|
|
291
292
|
stderr_limit: limit,
|
|
292
293
|
open_file_limit: None,
|
|
293
294
|
profile: Profile::Hermetic,
|
|
295
|
+
security: Vec::new(),
|
|
294
296
|
};
|
|
295
|
-
let wasi = Wasi::for_invocation(&config,
|
|
297
|
+
let wasi = Wasi::for_invocation(&config, stdin_bytes(), &[]).expect("WASI context must build");
|
|
296
298
|
let mut store = Store::new(engine, Invocation::new(None, wasi));
|
|
297
299
|
store.set_epoch_deadline(NO_TIMEOUT_EPOCH_DELTA);
|
|
298
300
|
let linker = imports::linker().expect("host imports must link");
|
|
@@ -310,9 +312,9 @@ struct Oracle {
|
|
|
310
312
|
}
|
|
311
313
|
|
|
312
314
|
fn through_wasmtime_wasi(call: &str, fd: i32, limit: Option<usize>) -> Observation {
|
|
313
|
-
let engine =
|
|
314
|
-
let stdout = MemoryOutputPipe::new(
|
|
315
|
-
let stderr = MemoryOutputPipe::new(
|
|
315
|
+
let engine = load_engine().expect("shared engine must be constructible");
|
|
316
|
+
let stdout = MemoryOutputPipe::new(kobako_runtime::endpoint::pipe_capacity(limit));
|
|
317
|
+
let stderr = MemoryOutputPipe::new(kobako_runtime::endpoint::pipe_capacity(limit));
|
|
316
318
|
let ctx = WasiCtxBuilder::new()
|
|
317
319
|
.stdin(MemoryInputPipe::new(stdin_bytes()))
|
|
318
320
|
.stdout(stdout.clone())
|
|
@@ -360,8 +362,18 @@ fn every_descriptor_call_answers_as_wasmtime_wasi_preview1() {
|
|
|
360
362
|
for limit in CHANNEL_LIMITS {
|
|
361
363
|
for call in CALLS {
|
|
362
364
|
for fd in 0..=3 {
|
|
365
|
+
let kobako = through_kobako(call, fd, limit);
|
|
366
|
+
if let Some(errno) = answered_as_linux(call, fd) {
|
|
367
|
+
assert_eq!(
|
|
368
|
+
kobako.errno,
|
|
369
|
+
Some(errno),
|
|
370
|
+
"{call} on descriptor {fd} through kobako's preview1 imports must answer \
|
|
371
|
+
as Linux does"
|
|
372
|
+
);
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
363
375
|
assert_eq!(
|
|
364
|
-
|
|
376
|
+
kobako,
|
|
365
377
|
through_wasmtime_wasi(call, fd, limit),
|
|
366
378
|
"{call} on descriptor {fd} with Channels capped at {limit:?} through \
|
|
367
379
|
kobako's preview1 imports must answer as wasmtime-wasi's preview1 does"
|
|
@@ -370,3 +382,105 @@ fn every_descriptor_call_answers_as_wasmtime_wasi_preview1() {
|
|
|
370
382
|
}
|
|
371
383
|
}
|
|
372
384
|
}
|
|
385
|
+
|
|
386
|
+
/// Where kobako parts from wasmtime-wasi, the errno Linux answers, which
|
|
387
|
+
/// outranks it. For a standard stream it answers as for a pipe: an offset, a
|
|
388
|
+
/// sync, or a size is no pipe's, and a pipe is no directory to open beneath.
|
|
389
|
+
/// Linux also takes `O_APPEND` on a pipe, reports one link where wasmtime-wasi
|
|
390
|
+
/// reports none, checks neither path nor descriptor for a call setting no
|
|
391
|
+
/// time, as `utimensat` returns first, and reports a wait on a descriptor not
|
|
392
|
+
/// open in its event, as `poll` marks it `POLLNVAL`.
|
|
393
|
+
fn answered_as_linux(call: &str, fd: i32) -> Option<i32> {
|
|
394
|
+
const ERRNO_SUCCESS: i32 = 0;
|
|
395
|
+
const ERRNO_INVAL: i32 = 28;
|
|
396
|
+
const ERRNO_NOTDIR: i32 = 54;
|
|
397
|
+
const ERRNO_SPIPE: i32 = 70;
|
|
398
|
+
match (call, fd) {
|
|
399
|
+
("fd_filestat_set_times" | "path_filestat_set_times", 3) => Some(ERRNO_SUCCESS),
|
|
400
|
+
("poll_oneoff_read" | "poll_oneoff_write", 3) => Some(ERRNO_SUCCESS),
|
|
401
|
+
("fd_advise", 0..=2) => Some(ERRNO_SPIPE),
|
|
402
|
+
("fd_allocate" | "fd_sync" | "fd_datasync" | "fd_filestat_set_size", 0..=2) => {
|
|
403
|
+
Some(ERRNO_INVAL)
|
|
404
|
+
}
|
|
405
|
+
(
|
|
406
|
+
"fd_fdstat_set_flags"
|
|
407
|
+
| "fd_filestat_get"
|
|
408
|
+
| "fd_filestat_set_times"
|
|
409
|
+
| "path_filestat_set_times",
|
|
410
|
+
0..=2,
|
|
411
|
+
) => Some(ERRNO_SUCCESS),
|
|
412
|
+
("fd_read_empty", 0) => Some(ERRNO_SUCCESS),
|
|
413
|
+
("fd_readdir" | "fd_readdir_far", 0..=2) => Some(ERRNO_NOTDIR),
|
|
414
|
+
(
|
|
415
|
+
"path_create_directory"
|
|
416
|
+
| "path_filestat_get"
|
|
417
|
+
| "path_link"
|
|
418
|
+
| "path_open"
|
|
419
|
+
| "path_readlink"
|
|
420
|
+
| "path_remove_directory"
|
|
421
|
+
| "path_rename"
|
|
422
|
+
| "path_symlink"
|
|
423
|
+
| "path_unlink_file",
|
|
424
|
+
0..=2,
|
|
425
|
+
) => Some(ERRNO_NOTDIR),
|
|
426
|
+
("fd_pread" | "fd_pread_far", 1 | 2) => Some(ERRNO_SPIPE),
|
|
427
|
+
("fd_pwrite" | "fd_pwrite_far", 0) => Some(ERRNO_SPIPE),
|
|
428
|
+
_ => None,
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// wasmtime-wasi fails the whole wait; the witx gives each event its own error,
|
|
433
|
+
// and Linux's `do_pollfd` marks the descriptor `POLLNVAL`, which wasi-libc reads
|
|
434
|
+
// back from an event's EBADF.
|
|
435
|
+
// @behavior RT-295
|
|
436
|
+
#[test]
|
|
437
|
+
fn a_wait_on_a_descriptor_not_open_reports_it_in_its_event() {
|
|
438
|
+
let read = through_kobako("poll_oneoff_read", 3, None);
|
|
439
|
+
let write = through_kobako("poll_oneoff_write", 3, None);
|
|
440
|
+
let event = |seen: &Observation| {
|
|
441
|
+
(
|
|
442
|
+
seen.errno,
|
|
443
|
+
u64::from_le_bytes(seen.memory[896..904].try_into().expect("eight bytes")),
|
|
444
|
+
u16::from_le_bytes([seen.memory[904], seen.memory[905]]),
|
|
445
|
+
u32::from_le_bytes(seen.memory[128..132].try_into().expect("four bytes")),
|
|
446
|
+
)
|
|
447
|
+
};
|
|
448
|
+
assert_eq!(
|
|
449
|
+
(event(&read), event(&write)),
|
|
450
|
+
((Some(0), 7, 8, 1), (Some(0), 7, 8, 1)),
|
|
451
|
+
"poll_oneoff on descriptor 3, not open, through kobako's preview1 imports must succeed \
|
|
452
|
+
with one event whose error is EBADF, as Linux's poll marks it POLLNVAL"
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Linux's `ksys_ftruncate` reads the length as a signed `loff_t` and refuses a
|
|
457
|
+
// negative one before it looks for the descriptor.
|
|
458
|
+
// @behavior RT-296
|
|
459
|
+
#[test]
|
|
460
|
+
fn a_size_no_file_can_take_answers_before_the_descriptor_is_found() {
|
|
461
|
+
assert_eq!(
|
|
462
|
+
through_kobako("fd_filestat_set_size_huge", 3, None).errno,
|
|
463
|
+
Some(28),
|
|
464
|
+
"fd_filestat_set_size of 2^64 - 1 on descriptor 3, not open, through kobako's preview1 \
|
|
465
|
+
imports must answer EINVAL, as ftruncate checks the length before the descriptor"
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// wasmtime-wasi answers EBADF for the end a call cannot use; Linux reads
|
|
470
|
+
// `FMODE_PREAD` first, and a pipe has none on either end.
|
|
471
|
+
// @behavior RT-178
|
|
472
|
+
#[test]
|
|
473
|
+
fn a_standard_stream_takes_no_offset() {
|
|
474
|
+
let errno = |call, fd| through_kobako(call, fd, None).errno;
|
|
475
|
+
assert_eq!(
|
|
476
|
+
[
|
|
477
|
+
errno("fd_pread", 0),
|
|
478
|
+
errno("fd_pread", 1),
|
|
479
|
+
errno("fd_pwrite", 0),
|
|
480
|
+
errno("fd_pwrite", 1),
|
|
481
|
+
],
|
|
482
|
+
[Some(70), Some(70), Some(70), Some(70)],
|
|
483
|
+
"fd_pread and fd_pwrite on standard input and output through kobako's preview1 imports \
|
|
484
|
+
must answer ESPIPE, as Linux answers for a pipe's either end"
|
|
485
|
+
);
|
|
486
|
+
}
|
|
@@ -2,223 +2,86 @@
|
|
|
2
2
|
//! the guest reads, or a trap that ends the invocation.
|
|
3
3
|
|
|
4
4
|
use kobako_runtime::filesystem::FilesystemError;
|
|
5
|
-
use
|
|
5
|
+
use wiggle::GuestError;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
#[repr(u16)]
|
|
10
|
-
pub(crate) enum Errno {
|
|
11
|
-
TooBig = 1,
|
|
12
|
-
Acces = 2,
|
|
13
|
-
Addrinuse = 3,
|
|
14
|
-
Addrnotavail = 4,
|
|
15
|
-
Afnosupport = 5,
|
|
16
|
-
Again = 6,
|
|
17
|
-
Already = 7,
|
|
18
|
-
Badf = 8,
|
|
19
|
-
Badmsg = 9,
|
|
20
|
-
Busy = 10,
|
|
21
|
-
Canceled = 11,
|
|
22
|
-
Child = 12,
|
|
23
|
-
Connaborted = 13,
|
|
24
|
-
Connrefused = 14,
|
|
25
|
-
Connreset = 15,
|
|
26
|
-
Deadlk = 16,
|
|
27
|
-
Destaddrreq = 17,
|
|
28
|
-
Dom = 18,
|
|
29
|
-
Dquot = 19,
|
|
30
|
-
Exist = 20,
|
|
31
|
-
Fault = 21,
|
|
32
|
-
Fbig = 22,
|
|
33
|
-
Hostunreach = 23,
|
|
34
|
-
Idrm = 24,
|
|
35
|
-
Ilseq = 25,
|
|
36
|
-
Inprogress = 26,
|
|
37
|
-
Intr = 27,
|
|
38
|
-
Inval = 28,
|
|
39
|
-
Io = 29,
|
|
40
|
-
Isconn = 30,
|
|
41
|
-
Isdir = 31,
|
|
42
|
-
Loop = 32,
|
|
43
|
-
Mfile = 33,
|
|
44
|
-
Mlink = 34,
|
|
45
|
-
Msgsize = 35,
|
|
46
|
-
Multihop = 36,
|
|
47
|
-
Nametoolong = 37,
|
|
48
|
-
Netdown = 38,
|
|
49
|
-
Netreset = 39,
|
|
50
|
-
Netunreach = 40,
|
|
51
|
-
Nfile = 41,
|
|
52
|
-
Nobufs = 42,
|
|
53
|
-
Nodev = 43,
|
|
54
|
-
Noent = 44,
|
|
55
|
-
Noexec = 45,
|
|
56
|
-
Nolck = 46,
|
|
57
|
-
Nolink = 47,
|
|
58
|
-
Nomem = 48,
|
|
59
|
-
Nomsg = 49,
|
|
60
|
-
Noprotoopt = 50,
|
|
61
|
-
Nospc = 51,
|
|
62
|
-
Nosys = 52,
|
|
63
|
-
Notconn = 53,
|
|
64
|
-
Notdir = 54,
|
|
65
|
-
Notempty = 55,
|
|
66
|
-
Notrecoverable = 56,
|
|
67
|
-
Notsock = 57,
|
|
68
|
-
Notsup = 58,
|
|
69
|
-
Notty = 59,
|
|
70
|
-
Nxio = 60,
|
|
71
|
-
Overflow = 61,
|
|
72
|
-
Ownerdead = 62,
|
|
73
|
-
Perm = 63,
|
|
74
|
-
Pipe = 64,
|
|
75
|
-
Proto = 65,
|
|
76
|
-
Protonosupport = 66,
|
|
77
|
-
Prototype = 67,
|
|
78
|
-
Range = 68,
|
|
79
|
-
Rofs = 69,
|
|
80
|
-
Spipe = 70,
|
|
81
|
-
Srch = 71,
|
|
82
|
-
Stale = 72,
|
|
83
|
-
Timedout = 73,
|
|
84
|
-
Txtbsy = 74,
|
|
85
|
-
Xdev = 75,
|
|
86
|
-
Notcapable = 76,
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
impl Errno {
|
|
90
|
-
/// The code a POSIX error name stands for: the witx case, `E`
|
|
91
|
-
/// prefixed and upper-cased. `ENOTCAPABLE` is WASI's own.
|
|
92
|
-
fn from_posix_name(name: &str) -> Option<Self> {
|
|
93
|
-
Some(match name {
|
|
94
|
-
"E2BIG" => Self::TooBig,
|
|
95
|
-
"EACCES" => Self::Acces,
|
|
96
|
-
"EADDRINUSE" => Self::Addrinuse,
|
|
97
|
-
"EADDRNOTAVAIL" => Self::Addrnotavail,
|
|
98
|
-
"EAFNOSUPPORT" => Self::Afnosupport,
|
|
99
|
-
"EAGAIN" => Self::Again,
|
|
100
|
-
"EALREADY" => Self::Already,
|
|
101
|
-
"EBADF" => Self::Badf,
|
|
102
|
-
"EBADMSG" => Self::Badmsg,
|
|
103
|
-
"EBUSY" => Self::Busy,
|
|
104
|
-
"ECANCELED" => Self::Canceled,
|
|
105
|
-
"ECHILD" => Self::Child,
|
|
106
|
-
"ECONNABORTED" => Self::Connaborted,
|
|
107
|
-
"ECONNREFUSED" => Self::Connrefused,
|
|
108
|
-
"ECONNRESET" => Self::Connreset,
|
|
109
|
-
"EDEADLK" => Self::Deadlk,
|
|
110
|
-
"EDESTADDRREQ" => Self::Destaddrreq,
|
|
111
|
-
"EDOM" => Self::Dom,
|
|
112
|
-
"EDQUOT" => Self::Dquot,
|
|
113
|
-
"EEXIST" => Self::Exist,
|
|
114
|
-
"EFAULT" => Self::Fault,
|
|
115
|
-
"EFBIG" => Self::Fbig,
|
|
116
|
-
"EHOSTUNREACH" => Self::Hostunreach,
|
|
117
|
-
"EIDRM" => Self::Idrm,
|
|
118
|
-
"EILSEQ" => Self::Ilseq,
|
|
119
|
-
"EINPROGRESS" => Self::Inprogress,
|
|
120
|
-
"EINTR" => Self::Intr,
|
|
121
|
-
"EINVAL" => Self::Inval,
|
|
122
|
-
"EIO" => Self::Io,
|
|
123
|
-
"EISCONN" => Self::Isconn,
|
|
124
|
-
"EISDIR" => Self::Isdir,
|
|
125
|
-
"ELOOP" => Self::Loop,
|
|
126
|
-
"EMFILE" => Self::Mfile,
|
|
127
|
-
"EMLINK" => Self::Mlink,
|
|
128
|
-
"EMSGSIZE" => Self::Msgsize,
|
|
129
|
-
"EMULTIHOP" => Self::Multihop,
|
|
130
|
-
"ENAMETOOLONG" => Self::Nametoolong,
|
|
131
|
-
"ENETDOWN" => Self::Netdown,
|
|
132
|
-
"ENETRESET" => Self::Netreset,
|
|
133
|
-
"ENETUNREACH" => Self::Netunreach,
|
|
134
|
-
"ENFILE" => Self::Nfile,
|
|
135
|
-
"ENOBUFS" => Self::Nobufs,
|
|
136
|
-
"ENODEV" => Self::Nodev,
|
|
137
|
-
"ENOENT" => Self::Noent,
|
|
138
|
-
"ENOEXEC" => Self::Noexec,
|
|
139
|
-
"ENOLCK" => Self::Nolck,
|
|
140
|
-
"ENOLINK" => Self::Nolink,
|
|
141
|
-
"ENOMEM" => Self::Nomem,
|
|
142
|
-
"ENOMSG" => Self::Nomsg,
|
|
143
|
-
"ENOPROTOOPT" => Self::Noprotoopt,
|
|
144
|
-
"ENOSPC" => Self::Nospc,
|
|
145
|
-
"ENOSYS" => Self::Nosys,
|
|
146
|
-
"ENOTCONN" => Self::Notconn,
|
|
147
|
-
"ENOTDIR" => Self::Notdir,
|
|
148
|
-
"ENOTEMPTY" => Self::Notempty,
|
|
149
|
-
"ENOTRECOVERABLE" => Self::Notrecoverable,
|
|
150
|
-
"ENOTSOCK" => Self::Notsock,
|
|
151
|
-
"ENOTSUP" => Self::Notsup,
|
|
152
|
-
"ENOTTY" => Self::Notty,
|
|
153
|
-
"ENXIO" => Self::Nxio,
|
|
154
|
-
"EOVERFLOW" => Self::Overflow,
|
|
155
|
-
"EOWNERDEAD" => Self::Ownerdead,
|
|
156
|
-
"EPERM" => Self::Perm,
|
|
157
|
-
"EPIPE" => Self::Pipe,
|
|
158
|
-
"EPROTO" => Self::Proto,
|
|
159
|
-
"EPROTONOSUPPORT" => Self::Protonosupport,
|
|
160
|
-
"EPROTOTYPE" => Self::Prototype,
|
|
161
|
-
"ERANGE" => Self::Range,
|
|
162
|
-
"EROFS" => Self::Rofs,
|
|
163
|
-
"ESPIPE" => Self::Spipe,
|
|
164
|
-
"ESRCH" => Self::Srch,
|
|
165
|
-
"ESTALE" => Self::Stale,
|
|
166
|
-
"ETIMEDOUT" => Self::Timedout,
|
|
167
|
-
"ETXTBSY" => Self::Txtbsy,
|
|
168
|
-
"EXDEV" => Self::Xdev,
|
|
169
|
-
"ENOTCAPABLE" => Self::Notcapable,
|
|
170
|
-
_ => return None,
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
}
|
|
7
|
+
use super::types;
|
|
8
|
+
pub(crate) use super::types::Errno;
|
|
174
9
|
|
|
175
|
-
|
|
176
|
-
/// for a pointer outside guest memory or an argument outside what the
|
|
177
|
-
/// witx defines, as wasmtime-wasi does.
|
|
178
|
-
pub(crate) enum Failure {
|
|
179
|
-
Errno(Errno),
|
|
180
|
-
Trap(wasmtime::Error),
|
|
181
|
-
}
|
|
10
|
+
pub(crate) type Answer<T = ()> = Result<T, types::Error>;
|
|
182
11
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
12
|
+
/// The code a POSIX error name a Filesystem's failure carries stands for:
|
|
13
|
+
/// the witx case, `E` prefixed and upper-cased.
|
|
14
|
+
fn from_posix_name(name: &str) -> Option<Errno> {
|
|
15
|
+
Some(match name {
|
|
16
|
+
"EAGAIN" => Errno::Again,
|
|
17
|
+
"ENOENT" => Errno::Noent,
|
|
18
|
+
"EEXIST" => Errno::Exist,
|
|
19
|
+
"ENOTDIR" => Errno::Notdir,
|
|
20
|
+
"EISDIR" => Errno::Isdir,
|
|
21
|
+
"ENOTEMPTY" => Errno::Notempty,
|
|
22
|
+
"EACCES" => Errno::Acces,
|
|
23
|
+
"EROFS" => Errno::Rofs,
|
|
24
|
+
"EINVAL" => Errno::Inval,
|
|
25
|
+
"ENOTSUP" => Errno::Notsup,
|
|
26
|
+
"ENOSPC" => Errno::Nospc,
|
|
27
|
+
"EFBIG" => Errno::Fbig,
|
|
28
|
+
"EXDEV" => Errno::Xdev,
|
|
29
|
+
"EDEADLK" => Errno::Deadlk,
|
|
30
|
+
"ELOOP" => Errno::Loop,
|
|
31
|
+
"EPERM" => Errno::Perm,
|
|
32
|
+
"EBADF" => Errno::Badf,
|
|
33
|
+
"EBUSY" => Errno::Busy,
|
|
34
|
+
"EILSEQ" => Errno::Ilseq,
|
|
35
|
+
"EINTR" => Errno::Intr,
|
|
36
|
+
"EMFILE" => Errno::Mfile,
|
|
37
|
+
"ENAMETOOLONG" => Errno::Nametoolong,
|
|
38
|
+
"ENOMEM" => Errno::Nomem,
|
|
39
|
+
"ENOTSOCK" => Errno::Notsock,
|
|
40
|
+
"EOVERFLOW" => Errno::Overflow,
|
|
41
|
+
"ESPIPE" => Errno::Spipe,
|
|
42
|
+
"ENODEV" => Errno::Nodev,
|
|
43
|
+
"ESTALE" => Errno::Stale,
|
|
44
|
+
_ => return None,
|
|
45
|
+
})
|
|
188
46
|
}
|
|
189
47
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
48
|
+
/// A pointer outside the memory or misaligned traps, as the witx
|
|
49
|
+
/// documentation requires; a value inside memory that no witx type holds is
|
|
50
|
+
/// `INVAL`, as in wasmtime-wasi.
|
|
51
|
+
impl From<GuestError> for types::Error {
|
|
52
|
+
fn from(error: GuestError) -> Self {
|
|
53
|
+
match error {
|
|
54
|
+
GuestError::InvalidFlagValue { .. } | GuestError::InvalidEnumValue { .. } => {
|
|
55
|
+
Errno::Inval.into()
|
|
56
|
+
}
|
|
57
|
+
GuestError::PtrOverflow
|
|
58
|
+
| GuestError::PtrOutOfBounds { .. }
|
|
59
|
+
| GuestError::PtrNotAligned { .. } => Self::trap(error.into()),
|
|
60
|
+
GuestError::InvalidUtf8 { .. } => Errno::Ilseq.into(),
|
|
61
|
+
GuestError::TryFromIntError { .. } => Errno::Overflow.into(),
|
|
62
|
+
GuestError::SliceLengthsDiffer => Errno::Fault.into(),
|
|
63
|
+
GuestError::InFunc { err, .. } => Self::from(*err),
|
|
64
|
+
}
|
|
193
65
|
}
|
|
194
66
|
}
|
|
195
67
|
|
|
196
|
-
/// The code a
|
|
197
|
-
///
|
|
198
|
-
|
|
68
|
+
/// The code a call's failure reaches the guest as: the one its POSIX name
|
|
69
|
+
/// stands for, or `IO` for a failure without one. A path escaping its
|
|
70
|
+
/// descriptor is `NOTCAPABLE`, as preview1 names a capability the guest does
|
|
71
|
+
/// not hold, where Linux answers EXDEV.
|
|
72
|
+
impl From<FilesystemError> for types::Error {
|
|
199
73
|
fn from(error: FilesystemError) -> Self {
|
|
200
|
-
|
|
201
|
-
|
|
74
|
+
match error {
|
|
75
|
+
FilesystemError::Escape => Errno::Notcapable,
|
|
76
|
+
error => error
|
|
202
77
|
.posix_name()
|
|
203
|
-
.and_then(
|
|
78
|
+
.and_then(from_posix_name)
|
|
204
79
|
.unwrap_or(Errno::Io),
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/// A change beneath a read-only Mount is `ROFS`; a Policy's refusal is
|
|
210
|
-
/// `ACCES`.
|
|
211
|
-
impl From<Denial> for Failure {
|
|
212
|
-
fn from(denial: Denial) -> Self {
|
|
213
|
-
Self::Errno(match denial {
|
|
214
|
-
Denial::ReadOnly => Errno::Rofs,
|
|
215
|
-
Denial::Refused => Errno::Acces,
|
|
216
|
-
})
|
|
80
|
+
}
|
|
81
|
+
.into()
|
|
217
82
|
}
|
|
218
83
|
}
|
|
219
84
|
|
|
220
|
-
pub(crate) type Answer<T = ()> = Result<T, Failure>;
|
|
221
|
-
|
|
222
85
|
#[cfg(test)]
|
|
223
86
|
mod tests {
|
|
224
87
|
use super::*;
|
|
@@ -238,6 +101,7 @@ mod tests {
|
|
|
238
101
|
(FilesystemError::StorageFull, 51),
|
|
239
102
|
(FilesystemError::FileTooLarge, 22),
|
|
240
103
|
(FilesystemError::CrossesDevices, 75),
|
|
104
|
+
(FilesystemError::Escape, 76),
|
|
241
105
|
(FilesystemError::Deadlock, 16),
|
|
242
106
|
(FilesystemError::FilesystemLoop, 32),
|
|
243
107
|
(FilesystemError::NotPermitted, 63),
|
|
@@ -245,22 +109,26 @@ mod tests {
|
|
|
245
109
|
(FilesystemError::ResourceBusy, 10),
|
|
246
110
|
(FilesystemError::IllegalSequence, 25),
|
|
247
111
|
(FilesystemError::Interrupted, 27),
|
|
112
|
+
(FilesystemError::WouldBlock, 6),
|
|
248
113
|
(FilesystemError::TooManyOpenFiles, 33),
|
|
249
114
|
(FilesystemError::InvalidFilename, 37),
|
|
250
115
|
(FilesystemError::OutOfMemory, 48),
|
|
251
116
|
(FilesystemError::NotASocket, 57),
|
|
252
117
|
(FilesystemError::ValueTooLarge, 61),
|
|
253
118
|
(FilesystemError::NotSeekable, 70),
|
|
119
|
+
(FilesystemError::NoSuchDevice, 43),
|
|
120
|
+
(FilesystemError::StaleNetworkFileHandle, 72),
|
|
254
121
|
(FilesystemError::Other("x".into()), 29),
|
|
255
122
|
];
|
|
256
123
|
for (error, code) in cases {
|
|
257
124
|
let label = format!("{error:?}");
|
|
258
|
-
let
|
|
125
|
+
let Ok(errno) = types::Error::from(error).downcast() else {
|
|
259
126
|
panic!("{label} must answer an error code, not a trap");
|
|
260
127
|
};
|
|
261
128
|
assert_eq!(
|
|
262
|
-
errno
|
|
263
|
-
|
|
129
|
+
u16::from(errno),
|
|
130
|
+
code,
|
|
131
|
+
"{label} through types::Error::from must reach the guest as preview1 errno {code}"
|
|
264
132
|
);
|
|
265
133
|
}
|
|
266
134
|
}
|