Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to obtain working directory fd? #323

Closed
yagehu opened this issue Oct 3, 2020 · 5 comments
Closed

How to obtain working directory fd? #323

yagehu opened this issue Oct 3, 2020 · 5 comments

Comments

@yagehu
Copy link

yagehu commented Oct 3, 2020

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 ;)
      ???
    )
  )
)
@caspervonb
Copy link
Contributor

caspervonb commented Oct 3, 2020

You need to pick from the pre-opened directories.

@yagehu
Copy link
Author

yagehu commented Oct 3, 2020

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?

@caspervonb
Copy link
Contributor

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.

@yagehu
Copy link
Author

yagehu commented Oct 3, 2020

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))
)

@turbolent
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants