How to obtain working directory fd?
Closed this issue · 5 comments
I'm writing small WASI programs in text format to test the syscall support in various Wasm runtimes like Wasmtime and Wasmer. I'm trying to call path_open
which I've imported as $__wasi_path_open
. According to the docs and this, the first argument to the path_open
syscall is an fd for "the working directory at which the resolution of the path starts." How can I obtain such an fd?
(module
(type (;0;) (func (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
(type (;1;) (func))
(import "wasi_snapshot_preview" "path_open" (func $__wasi_path_open (type 0)))
(func $_start (type 1) (export "_start")
(call $__wasi_path_open
(; The first argument is a dirfd ;)
???
)
)
)
You need to pick from the pre-opened directories.
Sorry if these are dumb questions... But how do I access pre-opened directories? Is there a convention for how they get passed to a Wasm module by the runtimes? Can you provide a minimal example?
Iterate over fd_prestat_get until it returns BADFD.
Its up to the runtime how the preopen table is managed, for wasmtime it's via the --mapdir and --dir flags.
fd_prestat_get
is exactly what I'm looking for! Thanks.
For posterity, a short text format program that uses fd_prestat_get
:
;; Exit status codes:
;;
;; - 8: Could not find a preopened fd.
(module
(type (;0;) (func (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
(type (;1;) (func))
(type (;2;) (func (param i32 i32) (result i32)))
(type (;3;) (func (param i32)))
(type (;4;) (func (param i32) (result i32)))
(import "wasi_snapshot_preview1" "fd_prestat_get"
(func $__wasi_fd_prestat_get (type 2))
)
(import "wasi_snapshot_preview1" "path_open"
(func $__wasi_path_open (type 0))
)
(import "wasi_snapshot_preview1" "proc_exit"
(func $__wasi_proc_exit (type 3))
)
(func $_start (type 1)
(local $dirfd i32)
(local $errno i32)
(call $getFirstGoodDirfd (i32.const 0))
(if
(i32.ne (local.tee $errno) (i32.const 0))
(then
(call $__wasi_proc_exit (i32.const 8))
)
(else)
)
(local.set $dirfd (i32.load (i32.const 0)))
(call $__wasi_proc_exit (i32.const 0))
)
(func $getFirstGoodDirfd (type 4) (param $fd i32) (result i32)
(local $dirfd i32)
(local $errno i32)
;; Start with the lowest fd after stdin, stdout, and stderr.
(local.set $dirfd (i32.const 3))
(block ;; label = @1
(block ;; label = @2
(loop $loop ;; label = @3
(call $__wasi_fd_prestat_get
(local.get $dirfd)
(i32.const 0)
)
;; If errno is 0, we found a good dirfd. Break the loop.
(br_if 2 (;@1;) (i32.eq (local.tee $errno) (i32.const 0)))
;; If errno is 8, we encountered `badf`. Return 8 indicating failure.
(br_if 1 (;@2;) (i32.eq (local.get $errno) (i32.const 8)))
;; Increment dirfd.
(local.set $dirfd (i32.add (local.get $dirfd) (i32.const 1)))
(br $loop)
)
)
(return (i32.const 8))
)
(i32.store (local.get $fd) (local.get $dirfd))
i32.const 0
)
(memory (;0;) (export "memory") 1)
(export "_start" (func $_start))
)
Thank you so much for asking this question and the example @yagehu, and thank you for answering @caspervonb!
Is this documented anywhere? I looked at the API spec and was pretty confused.
I felt like there must be some sort of function which returns the preopened file descriptors, something like args/environ_sizes_get
. It seems odd that repeated syscalls are necessary to iterate.