wasmtime_wasi/
p1.rs

1//! Bindings for WASIp1 aka Preview 1 aka `wasi_snapshot_preview1`.
2//!
3//! This module contains runtime support for configuring and executing
4//! WASIp1-using core WebAssembly modules. Support for WASIp1 is built on top of
5//! support for WASIp2 available at [the crate root](crate), but that's just an
6//! internal implementation detail.
7//!
8//! Unlike the crate root, support for WASIp1 centers around two APIs:
9//!
10//! * [`WasiP1Ctx`]
11//! * [`add_to_linker_sync`] (or [`add_to_linker_async`])
12//!
13//! First a [`WasiCtxBuilder`] will be used and finalized with the [`build_p1`]
14//! method to create a [`WasiCtx`]. Next a [`wasmtime::Linker`] is configured
15//! with WASI imports by using the `add_to_linker_*` desired (sync or async
16//! depending on [`Config::async_support`]).
17//!
18//! Note that WASIp1 is not as extensible or configurable as WASIp2 so the
19//! support in this module is enough to run wasm modules but any customization
20//! beyond that [`WasiCtxBuilder`] already supports is not possible yet.
21//!
22//! [`WasiCtxBuilder`]: crate::p2::WasiCtxBuilder
23//! [`build_p1`]: crate::p2::WasiCtxBuilder::build_p1
24//! [`Config::async_support`]: wasmtime::Config::async_support
25//!
26//! # Components vs Modules
27//!
28//! Note that WASIp1 does not work for components at this time, only core wasm
29//! modules. That means this module is only for users of [`wasmtime::Module`]
30//! and [`wasmtime::Linker`], for example. If you're using
31//! [`wasmtime::component::Component`] or [`wasmtime::component::Linker`] you'll
32//! want the WASIp2 [support this crate has](crate) instead.
33//!
34//! # Examples
35//!
36//! ```no_run
37//! use wasmtime::{Result, Engine, Linker, Module, Store};
38//! use wasmtime_wasi::p1::{self, WasiP1Ctx};
39//! use wasmtime_wasi::WasiCtxBuilder;
40//!
41//! // An example of executing a WASIp1 "command"
42//! fn main() -> Result<()> {
43//!     let args = std::env::args().skip(1).collect::<Vec<_>>();
44//!     let engine = Engine::default();
45//!     let module = Module::from_file(&engine, &args[0])?;
46//!
47//!     let mut linker: Linker<WasiP1Ctx> = Linker::new(&engine);
48//!     p1::add_to_linker_async(&mut linker, |t| t)?;
49//!     let pre = linker.instantiate_pre(&module)?;
50//!
51//!     let wasi_ctx = WasiCtxBuilder::new()
52//!         .inherit_stdio()
53//!         .inherit_env()
54//!         .args(&args)
55//!         .build_p1();
56//!
57//!     let mut store = Store::new(&engine, wasi_ctx);
58//!     let instance = pre.instantiate(&mut store)?;
59//!     let func = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
60//!     func.call(&mut store, ())?;
61//!
62//!     Ok(())
63//! }
64//! ```
65
66use crate::cli::WasiCliView as _;
67use crate::clocks::WasiClocksView as _;
68use crate::filesystem::WasiFilesystemView as _;
69use crate::p2::bindings::{
70    cli::{
71        stderr::Host as _, stdin::Host as _, stdout::Host as _, terminal_input, terminal_output,
72        terminal_stderr::Host as _, terminal_stdin::Host as _, terminal_stdout::Host as _,
73    },
74    clocks::{monotonic_clock, wall_clock},
75    filesystem::types as filesystem,
76};
77use crate::p2::{FsError, IsATTY};
78use crate::{ResourceTable, WasiCtx, WasiCtxView, WasiView};
79use anyhow::{Context, bail};
80use std::collections::{BTreeMap, BTreeSet, HashSet, btree_map};
81use std::mem::{self, size_of, size_of_val};
82use std::slice;
83use std::sync::Arc;
84use std::sync::atomic::{AtomicU64, Ordering};
85use system_interface::fs::FileIoExt;
86use wasmtime::component::Resource;
87use wasmtime_wasi_io::{
88    bindings::wasi::io::streams,
89    streams::{StreamError, StreamResult},
90};
91use wiggle::tracing::instrument;
92use wiggle::{GuestError, GuestMemory, GuestPtr, GuestType};
93
94// Bring all WASI traits in scope that this implementation builds on.
95use crate::p2::bindings::cli::environment::Host as _;
96use crate::p2::bindings::filesystem::types::HostDescriptor as _;
97use crate::p2::bindings::random::random::Host as _;
98use wasmtime_wasi_io::bindings::wasi::io::poll::Host as _;
99
100/// Structure containing state for WASIp1.
101///
102/// This structure is created through [`WasiCtxBuilder::build_p1`] and is
103/// configured through the various methods of [`WasiCtxBuilder`]. This structure
104/// itself implements generated traits for WASIp1 as well as [`WasiView`] to
105/// have access to WASIp2.
106///
107/// Instances of [`WasiP1Ctx`] are typically stored within the `T` of
108/// [`Store<T>`](wasmtime::Store).
109///
110/// [`WasiCtxBuilder::build_p1`]: crate::p2::WasiCtxBuilder::build_p1
111/// [`WasiCtxBuilder`]: crate::p2::WasiCtxBuilder
112///
113/// # Examples
114///
115/// ```no_run
116/// use wasmtime::{Result, Linker};
117/// use wasmtime_wasi::p1::{self, WasiP1Ctx};
118/// use wasmtime_wasi::WasiCtxBuilder;
119///
120/// struct MyState {
121///     // ... custom state as necessary ...
122///
123///     wasi: WasiP1Ctx,
124/// }
125///
126/// impl MyState {
127///     fn new() -> MyState {
128///         MyState {
129///             // .. initialize custom state if needed ..
130///
131///             wasi: WasiCtxBuilder::new()
132///                 .arg("./foo.wasm")
133///                 // .. more customization if necesssary ..
134///                 .build_p1(),
135///         }
136///     }
137/// }
138///
139/// fn add_to_linker(linker: &mut Linker<MyState>) -> Result<()> {
140///     p1::add_to_linker_sync(linker, |my_state| &mut my_state.wasi)?;
141///     Ok(())
142/// }
143/// ```
144pub struct WasiP1Ctx {
145    table: ResourceTable,
146    wasi: WasiCtx,
147    adapter: WasiP1Adapter,
148}
149
150impl WasiP1Ctx {
151    pub(crate) fn new(wasi: WasiCtx) -> Self {
152        Self {
153            table: ResourceTable::new(),
154            wasi,
155            adapter: WasiP1Adapter::new(),
156        }
157    }
158}
159
160impl WasiView for WasiP1Ctx {
161    fn ctx(&mut self) -> WasiCtxView<'_> {
162        WasiCtxView {
163            ctx: &mut self.wasi,
164            table: &mut self.table,
165        }
166    }
167}
168
169#[derive(Debug)]
170struct File {
171    /// The handle to the preview2 descriptor of type [`crate::filesystem::Descriptor::File`].
172    fd: Resource<filesystem::Descriptor>,
173
174    /// The current-position pointer.
175    position: Arc<AtomicU64>,
176
177    /// In append mode, all writes append to the file.
178    append: bool,
179
180    /// When blocking, read and write calls dispatch to blocking_read and
181    /// blocking_check_write on the underlying streams. When false, read and write
182    /// dispatch to stream's plain read and check_write.
183    blocking_mode: BlockingMode,
184}
185
186/// NB: p1 files always use blocking writes regardless of what
187/// they're configured to use since OSes don't have nonblocking
188/// reads/writes anyway. This behavior originated in the first
189/// implementation of WASIp1 where flags were propagated to the
190/// OS and the OS ignored the nonblocking flag for files
191/// generally.
192#[derive(Clone, Copy, Debug)]
193enum BlockingMode {
194    Blocking,
195    NonBlocking,
196}
197impl BlockingMode {
198    fn from_fdflags(flags: &types::Fdflags) -> Self {
199        if flags.contains(types::Fdflags::NONBLOCK) {
200            BlockingMode::NonBlocking
201        } else {
202            BlockingMode::Blocking
203        }
204    }
205    async fn read(
206        &self,
207        host: &mut impl streams::HostInputStream,
208        input_stream: Resource<streams::InputStream>,
209        max_size: usize,
210    ) -> Result<Vec<u8>, types::Error> {
211        let max_size = max_size.try_into().unwrap_or(u64::MAX);
212        match streams::HostInputStream::blocking_read(host, input_stream, max_size).await {
213            Ok(r) if r.is_empty() => Err(types::Errno::Intr.into()),
214            Ok(r) => Ok(r),
215            Err(StreamError::Closed) => Ok(Vec::new()),
216            Err(e) => Err(e.into()),
217        }
218    }
219    async fn write(
220        &self,
221        memory: &mut GuestMemory<'_>,
222        host: &mut impl streams::HostOutputStream,
223        output_stream: Resource<streams::OutputStream>,
224        bytes: GuestPtr<[u8]>,
225    ) -> StreamResult<usize> {
226        use streams::HostOutputStream as Streams;
227
228        let bytes = memory
229            .as_cow(bytes)
230            .map_err(|e| StreamError::Trap(e.into()))?;
231        let mut bytes = &bytes[..];
232
233        let total = bytes.len();
234        while !bytes.is_empty() {
235            // NOTE: blocking_write_and_flush takes at most one 4k buffer.
236            let len = bytes.len().min(4096);
237            let (chunk, rest) = bytes.split_at(len);
238            bytes = rest;
239
240            Streams::blocking_write_and_flush(host, output_stream.borrowed(), Vec::from(chunk))
241                .await?
242        }
243
244        Ok(total)
245    }
246}
247
248#[derive(Debug)]
249enum Descriptor {
250    Stdin {
251        stream: Resource<streams::InputStream>,
252        isatty: IsATTY,
253    },
254    Stdout {
255        stream: Resource<streams::OutputStream>,
256        isatty: IsATTY,
257    },
258    Stderr {
259        stream: Resource<streams::OutputStream>,
260        isatty: IsATTY,
261    },
262    /// A fd of type [`crate::filesystem::Descriptor::Dir`]
263    Directory {
264        fd: Resource<filesystem::Descriptor>,
265        /// The path this directory was preopened as.
266        /// `None` means this directory was opened using `open-at`.
267        preopen_path: Option<String>,
268    },
269    /// A fd of type [`crate::filesystem::Descriptor::File`]
270    File(File),
271}
272
273#[derive(Debug, Default)]
274struct WasiP1Adapter {
275    descriptors: Option<Descriptors>,
276}
277
278#[derive(Debug, Default)]
279struct Descriptors {
280    used: BTreeMap<u32, Descriptor>,
281    free: BTreeSet<u32>,
282}
283
284impl Descriptors {
285    /// Initializes [Self] using `preopens`
286    fn new(host: &mut WasiP1Ctx) -> Result<Self, types::Error> {
287        let mut descriptors = Self::default();
288        descriptors.push(Descriptor::Stdin {
289            stream: host
290                .cli()
291                .get_stdin()
292                .context("failed to call `get-stdin`")
293                .map_err(types::Error::trap)?,
294            isatty: if let Some(term_in) = host
295                .cli()
296                .get_terminal_stdin()
297                .context("failed to call `get-terminal-stdin`")
298                .map_err(types::Error::trap)?
299            {
300                terminal_input::HostTerminalInput::drop(&mut host.cli(), term_in)
301                    .context("failed to call `drop-terminal-input`")
302                    .map_err(types::Error::trap)?;
303                IsATTY::Yes
304            } else {
305                IsATTY::No
306            },
307        })?;
308        descriptors.push(Descriptor::Stdout {
309            stream: host
310                .cli()
311                .get_stdout()
312                .context("failed to call `get-stdout`")
313                .map_err(types::Error::trap)?,
314            isatty: if let Some(term_out) = host
315                .cli()
316                .get_terminal_stdout()
317                .context("failed to call `get-terminal-stdout`")
318                .map_err(types::Error::trap)?
319            {
320                terminal_output::HostTerminalOutput::drop(&mut host.cli(), term_out)
321                    .context("failed to call `drop-terminal-output`")
322                    .map_err(types::Error::trap)?;
323                IsATTY::Yes
324            } else {
325                IsATTY::No
326            },
327        })?;
328        descriptors.push(Descriptor::Stderr {
329            stream: host
330                .cli()
331                .get_stderr()
332                .context("failed to call `get-stderr`")
333                .map_err(types::Error::trap)?,
334            isatty: if let Some(term_out) = host
335                .cli()
336                .get_terminal_stderr()
337                .context("failed to call `get-terminal-stderr`")
338                .map_err(types::Error::trap)?
339            {
340                terminal_output::HostTerminalOutput::drop(&mut host.cli(), term_out)
341                    .context("failed to call `drop-terminal-output`")
342                    .map_err(types::Error::trap)?;
343                IsATTY::Yes
344            } else {
345                IsATTY::No
346            },
347        })?;
348
349        for dir in host
350            .filesystem()
351            .get_directories()
352            .context("failed to call `get-directories`")
353            .map_err(types::Error::trap)?
354        {
355            descriptors.push(Descriptor::Directory {
356                fd: dir.0,
357                preopen_path: Some(dir.1),
358            })?;
359        }
360        Ok(descriptors)
361    }
362
363    /// Returns next descriptor number, which was never assigned
364    fn unused(&self) -> Result<u32> {
365        match self.used.last_key_value() {
366            Some((fd, _)) => {
367                if let Some(fd) = fd.checked_add(1) {
368                    return Ok(fd);
369                }
370                if self.used.len() == u32::MAX as usize {
371                    return Err(types::Errno::Loop.into());
372                }
373                // TODO: Optimize
374                Ok((0..u32::MAX)
375                    .rev()
376                    .find(|fd| !self.used.contains_key(fd))
377                    .expect("failed to find an unused file descriptor"))
378            }
379            None => Ok(0),
380        }
381    }
382
383    /// Pushes the [Descriptor] returning corresponding number.
384    /// This operation will try to reuse numbers previously removed via [`Self::remove`]
385    /// and rely on [`Self::unused`] if no free numbers are recorded
386    fn push(&mut self, desc: Descriptor) -> Result<u32> {
387        let fd = if let Some(fd) = self.free.pop_last() {
388            fd
389        } else {
390            self.unused()?
391        };
392        assert!(self.used.insert(fd, desc).is_none());
393        Ok(fd)
394    }
395}
396
397impl WasiP1Adapter {
398    fn new() -> Self {
399        Self::default()
400    }
401}
402
403/// A mutably-borrowed `WasiP1Ctx`, which provides access to the stored
404/// state. It can be thought of as an in-flight [`WasiP1Adapter`] transaction, all
405/// changes will be recorded in the underlying [`WasiP1Adapter`] returned by
406/// [`WasiPreview1View::adapter_mut`] on [`Drop`] of this struct.
407// NOTE: This exists for the most part just due to the fact that `bindgen` generates methods with
408// `&mut self` receivers and so this struct lets us extend the lifetime of the `&mut self` borrow
409// of the [`WasiPreview1View`] to provide means to return mutably and immutably borrowed [`Descriptors`]
410// without having to rely on something like `Arc<Mutex<Descriptors>>`, while also being able to
411// call methods like [`Descriptor::is_file`] and hiding complexity from p1 method implementations.
412struct Transaction<'a> {
413    view: &'a mut WasiP1Ctx,
414    descriptors: Descriptors,
415}
416
417impl Drop for Transaction<'_> {
418    /// Record changes in the [`WasiP1Adapter`] .
419    fn drop(&mut self) {
420        let descriptors = mem::take(&mut self.descriptors);
421        self.view.adapter.descriptors = Some(descriptors);
422    }
423}
424
425impl Transaction<'_> {
426    /// Borrows [`Descriptor`] corresponding to `fd`.
427    ///
428    /// # Errors
429    ///
430    /// Returns [`types::Errno::Badf`] if no [`Descriptor`] is found
431    fn get_descriptor(&self, fd: types::Fd) -> Result<&Descriptor> {
432        let fd = fd.into();
433        let desc = self.descriptors.used.get(&fd).ok_or(types::Errno::Badf)?;
434        Ok(desc)
435    }
436
437    /// Borrows [`File`] corresponding to `fd`
438    /// if it describes a [`Descriptor::File`]
439    fn get_file(&self, fd: types::Fd) -> Result<&File> {
440        let fd = fd.into();
441        match self.descriptors.used.get(&fd) {
442            Some(Descriptor::File(file)) => Ok(file),
443            _ => Err(types::Errno::Badf.into()),
444        }
445    }
446
447    /// Mutably borrows [`File`] corresponding to `fd`
448    /// if it describes a [`Descriptor::File`]
449    fn get_file_mut(&mut self, fd: types::Fd) -> Result<&mut File> {
450        let fd = fd.into();
451        match self.descriptors.used.get_mut(&fd) {
452            Some(Descriptor::File(file)) => Ok(file),
453            _ => Err(types::Errno::Badf.into()),
454        }
455    }
456
457    /// Borrows [`File`] corresponding to `fd`
458    /// if it describes a [`Descriptor::File`]
459    ///
460    /// # Errors
461    ///
462    /// Returns [`types::Errno::Spipe`] if the descriptor corresponds to stdio
463    fn get_seekable(&self, fd: types::Fd) -> Result<&File> {
464        let fd = fd.into();
465        match self.descriptors.used.get(&fd) {
466            Some(Descriptor::File(file)) => Ok(file),
467            Some(
468                Descriptor::Stdin { .. } | Descriptor::Stdout { .. } | Descriptor::Stderr { .. },
469            ) => {
470                // NOTE: legacy implementation returns SPIPE here
471                Err(types::Errno::Spipe.into())
472            }
473            _ => Err(types::Errno::Badf.into()),
474        }
475    }
476
477    /// Returns [`filesystem::Descriptor`] corresponding to `fd`
478    fn get_fd(&self, fd: types::Fd) -> Result<Resource<filesystem::Descriptor>> {
479        match self.get_descriptor(fd)? {
480            Descriptor::File(File { fd, .. }) => Ok(fd.borrowed()),
481            Descriptor::Directory { fd, .. } => Ok(fd.borrowed()),
482            Descriptor::Stdin { .. } | Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => {
483                Err(types::Errno::Badf.into())
484            }
485        }
486    }
487
488    /// Returns [`filesystem::Descriptor`] corresponding to `fd`
489    /// if it describes a [`Descriptor::File`]
490    fn get_file_fd(&self, fd: types::Fd) -> Result<Resource<filesystem::Descriptor>> {
491        self.get_file(fd).map(|File { fd, .. }| fd.borrowed())
492    }
493
494    /// Returns [`filesystem::Descriptor`] corresponding to `fd`
495    /// if it describes a [`Descriptor::Directory`]
496    fn get_dir_fd(&self, fd: types::Fd) -> Result<Resource<filesystem::Descriptor>> {
497        let fd = fd.into();
498        match self.descriptors.used.get(&fd) {
499            Some(Descriptor::Directory { fd, .. }) => Ok(fd.borrowed()),
500            _ => Err(types::Errno::Badf.into()),
501        }
502    }
503}
504
505impl WasiP1Ctx {
506    /// Lazily initializes [`WasiP1Adapter`] returned by [`WasiPreview1View::adapter_mut`]
507    /// and returns [`Transaction`] on success
508    fn transact(&mut self) -> Result<Transaction<'_>, types::Error> {
509        let descriptors = if let Some(descriptors) = self.adapter.descriptors.take() {
510            descriptors
511        } else {
512            Descriptors::new(self)?
513        };
514        Ok(Transaction {
515            view: self,
516            descriptors,
517        })
518    }
519
520    /// Lazily initializes [`WasiP1Adapter`] returned by [`WasiPreview1View::adapter_mut`]
521    /// and returns [`filesystem::Descriptor`] corresponding to `fd`
522    fn get_fd(&mut self, fd: types::Fd) -> Result<Resource<filesystem::Descriptor>, types::Error> {
523        let st = self.transact()?;
524        let fd = st.get_fd(fd)?;
525        Ok(fd)
526    }
527
528    /// Lazily initializes [`WasiP1Adapter`] returned by [`WasiPreview1View::adapter_mut`]
529    /// and returns [`filesystem::Descriptor`] corresponding to `fd`
530    /// if it describes a [`Descriptor::File`] of [`crate::p2::filesystem::File`] type
531    fn get_file_fd(
532        &mut self,
533        fd: types::Fd,
534    ) -> Result<Resource<filesystem::Descriptor>, types::Error> {
535        let st = self.transact()?;
536        let fd = st.get_file_fd(fd)?;
537        Ok(fd)
538    }
539
540    /// Lazily initializes [`WasiP1Adapter`] returned by [`WasiPreview1View::adapter_mut`]
541    /// and returns [`filesystem::Descriptor`] corresponding to `fd`
542    /// if it describes a [`Descriptor::File`] or [`Descriptor::PreopenDirectory`]
543    /// of [`crate::p2::filesystem::Dir`] type
544    fn get_dir_fd(
545        &mut self,
546        fd: types::Fd,
547    ) -> Result<Resource<filesystem::Descriptor>, types::Error> {
548        let st = self.transact()?;
549        let fd = st.get_dir_fd(fd)?;
550        Ok(fd)
551    }
552
553    /// Shared implementation of `fd_write` and `fd_pwrite`.
554    async fn fd_write_impl(
555        &mut self,
556        memory: &mut GuestMemory<'_>,
557        fd: types::Fd,
558        ciovs: types::CiovecArray,
559        write: FdWrite,
560    ) -> Result<types::Size, types::Error> {
561        let t = self.transact()?;
562        let desc = t.get_descriptor(fd)?;
563        match desc {
564            Descriptor::File(File {
565                fd,
566                append,
567                position,
568                // NB: files always use blocking writes regardless of what
569                // they're configured to use since OSes don't have nonblocking
570                // reads/writes anyway. This behavior originated in the first
571                // implementation of WASIp1 where flags were propagated to the
572                // OS and the OS ignored the nonblocking flag for files
573                // generally.
574                blocking_mode: _,
575            }) => {
576                let fd = fd.borrowed();
577                let position = position.clone();
578                let pos = position.load(Ordering::Relaxed);
579                let append = *append;
580                drop(t);
581                let f = self.table.get(&fd)?.file()?;
582                let buf = first_non_empty_ciovec(memory, ciovs)?;
583
584                let do_write = move |f: &cap_std::fs::File, buf: &[u8]| match (append, write) {
585                    // Note that this is implementing Linux semantics of
586                    // `pwrite` where the offset is ignored if the file was
587                    // opened in append mode.
588                    (true, _) => f.append(&buf),
589                    (false, FdWrite::At(pos)) => f.write_at(&buf, pos),
590                    (false, FdWrite::AtCur) => f.write_at(&buf, pos),
591                };
592
593                let nwritten = match f.as_blocking_file() {
594                    // If we can block then skip the copy out of wasm memory and
595                    // write directly to `f`.
596                    Some(f) => do_write(f, &memory.as_cow(buf)?),
597                    // ... otherwise copy out of wasm memory and use
598                    // `spawn_blocking` to do this write in a thread that can
599                    // block.
600                    None => {
601                        let buf = memory.to_vec(buf)?;
602                        f.run_blocking(move |f| do_write(f, &buf)).await
603                    }
604                };
605
606                let nwritten = nwritten.map_err(|e| StreamError::LastOperationFailed(e.into()))?;
607
608                // If this was a write at the current position then update the
609                // current position with the result, otherwise the current
610                // position is left unmodified.
611                if let FdWrite::AtCur = write {
612                    if append {
613                        let len = self.filesystem().stat(fd).await?;
614                        position.store(len.size, Ordering::Relaxed);
615                    } else {
616                        let pos = pos
617                            .checked_add(nwritten as u64)
618                            .ok_or(types::Errno::Overflow)?;
619                        position.store(pos, Ordering::Relaxed);
620                    }
621                }
622                Ok(nwritten.try_into()?)
623            }
624            Descriptor::Stdout { stream, .. } | Descriptor::Stderr { stream, .. } => {
625                match write {
626                    // Reject calls to `fd_pwrite` on stdio descriptors...
627                    FdWrite::At(_) => return Err(types::Errno::Spipe.into()),
628                    // ... but allow calls to `fd_write`
629                    FdWrite::AtCur => {}
630                }
631                let stream = stream.borrowed();
632                drop(t);
633                let buf = first_non_empty_ciovec(memory, ciovs)?;
634                let n = BlockingMode::Blocking
635                    .write(memory, &mut self.table, stream, buf)
636                    .await?
637                    .try_into()?;
638                Ok(n)
639            }
640            _ => Err(types::Errno::Badf.into()),
641        }
642    }
643}
644
645#[derive(Copy, Clone)]
646enum FdWrite {
647    At(u64),
648    AtCur,
649}
650
651/// Adds asynchronous versions of all WASIp1 functions to the
652/// [`wasmtime::Linker`] provided.
653///
654/// This method will add WASIp1 functions to `linker`. Access to [`WasiP1Ctx`]
655/// is provided with `f` by projecting from the store-local state of `T` to
656/// [`WasiP1Ctx`]. The closure `f` is invoked every time a WASIp1 function is
657/// called to get access to [`WasiP1Ctx`] from `T`. The returned [`WasiP1Ctx`] is
658/// used to implement I/O and controls what each function will return.
659///
660/// It's recommended that [`WasiP1Ctx`] is stored as a field in `T` or that `T =
661/// WasiP1Ctx` itself. The closure `f` should be a small projection (e.g. `&mut
662/// arg.field`) or something otherwise "small" as it will be executed every time
663/// a WASI call is made.
664///
665/// Note that this function is intended for use with
666/// [`Config::async_support(true)`]. If you're looking for a synchronous version
667/// see [`add_to_linker_sync`].
668///
669/// [`Config::async_support(true)`]: wasmtime::Config::async_support
670///
671/// # Examples
672///
673/// If the `T` in `Linker<T>` is just `WasiP1Ctx`:
674///
675/// ```no_run
676/// use wasmtime::{Result, Linker, Engine, Config};
677/// use wasmtime_wasi::p1::{self, WasiP1Ctx};
678///
679/// fn main() -> Result<()> {
680///     let mut config = Config::new();
681///     config.async_support(true);
682///     let engine = Engine::new(&config)?;
683///
684///     let mut linker: Linker<WasiP1Ctx> = Linker::new(&engine);
685///     p1::add_to_linker_async(&mut linker, |cx| cx)?;
686///
687///     // ... continue to add more to `linker` as necessary and use it ...
688///
689///     Ok(())
690/// }
691/// ```
692///
693/// If the `T` in `Linker<T>` is custom state:
694///
695/// ```no_run
696/// use wasmtime::{Result, Linker, Engine, Config};
697/// use wasmtime_wasi::p1::{self, WasiP1Ctx};
698///
699/// struct MyState {
700///     // .. other custom state here ..
701///
702///     wasi: WasiP1Ctx,
703/// }
704///
705/// fn main() -> Result<()> {
706///     let mut config = Config::new();
707///     config.async_support(true);
708///     let engine = Engine::new(&config)?;
709///
710///     let mut linker: Linker<MyState> = Linker::new(&engine);
711///     p1::add_to_linker_async(&mut linker, |cx| &mut cx.wasi)?;
712///
713///     // ... continue to add more to `linker` as necessary and use it ...
714///
715///     Ok(())
716/// }
717/// ```
718pub fn add_to_linker_async<T: Send + 'static>(
719    linker: &mut wasmtime::Linker<T>,
720    f: impl Fn(&mut T) -> &mut WasiP1Ctx + Copy + Send + Sync + 'static,
721) -> anyhow::Result<()> {
722    crate::p1::wasi_snapshot_preview1::add_to_linker(linker, f)
723}
724
725/// Adds synchronous versions of all WASIp1 functions to the
726/// [`wasmtime::Linker`] provided.
727///
728/// This method will add WASIp1 functions to `linker`. Access to [`WasiP1Ctx`]
729/// is provided with `f` by projecting from the store-local state of `T` to
730/// [`WasiP1Ctx`]. The closure `f` is invoked every time a WASIp1 function is
731/// called to get access to [`WasiP1Ctx`] from `T`. The returned [`WasiP1Ctx`] is
732/// used to implement I/O and controls what each function will return.
733///
734/// It's recommended that [`WasiP1Ctx`] is stored as a field in `T` or that `T =
735/// WasiP1Ctx` itself. The closure `f` should be a small projection (e.g. `&mut
736/// arg.field`) or something otherwise "small" as it will be executed every time
737/// a WASI call is made.
738///
739/// Note that this function is intended for use with
740/// [`Config::async_support(false)`]. If you're looking for a synchronous version
741/// see [`add_to_linker_async`].
742///
743/// [`Config::async_support(false)`]: wasmtime::Config::async_support
744///
745/// # Examples
746///
747/// If the `T` in `Linker<T>` is just `WasiP1Ctx`:
748///
749/// ```no_run
750/// use wasmtime::{Result, Linker, Engine, Config};
751/// use wasmtime_wasi::p1::{self, WasiP1Ctx};
752///
753/// fn main() -> Result<()> {
754///     let mut config = Config::new();
755///     config.async_support(true);
756///     let engine = Engine::new(&config)?;
757///
758///     let mut linker: Linker<WasiP1Ctx> = Linker::new(&engine);
759///     p1::add_to_linker_async(&mut linker, |cx| cx)?;
760///
761///     // ... continue to add more to `linker` as necessary and use it ...
762///
763///     Ok(())
764/// }
765/// ```
766///
767/// If the `T` in `Linker<T>` is custom state:
768///
769/// ```no_run
770/// use wasmtime::{Result, Linker, Engine, Config};
771/// use wasmtime_wasi::p1::{self, WasiP1Ctx};
772///
773/// struct MyState {
774///     // .. other custom state here ..
775///
776///     wasi: WasiP1Ctx,
777/// }
778///
779/// fn main() -> Result<()> {
780///     let mut config = Config::new();
781///     config.async_support(true);
782///     let engine = Engine::new(&config)?;
783///
784///     let mut linker: Linker<MyState> = Linker::new(&engine);
785///     p1::add_to_linker_async(&mut linker, |cx| &mut cx.wasi)?;
786///
787///     // ... continue to add more to `linker` as necessary and use it ...
788///
789///     Ok(())
790/// }
791/// ```
792pub fn add_to_linker_sync<T: Send + 'static>(
793    linker: &mut wasmtime::Linker<T>,
794    f: impl Fn(&mut T) -> &mut WasiP1Ctx + Copy + Send + Sync + 'static,
795) -> anyhow::Result<()> {
796    sync::add_wasi_snapshot_preview1_to_linker(linker, f)
797}
798
799// Generate the wasi_snapshot_preview1::WasiSnapshotPreview1 trait,
800// and the module types.
801// None of the generated modules, traits, or types should be used externally
802// to this module.
803wiggle::from_witx!({
804    witx: ["witx/p1/wasi_snapshot_preview1.witx"],
805    async: {
806        wasi_snapshot_preview1::{
807            fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size,
808            fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write,
809            fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get,
810            path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory,
811            path_rename, path_symlink, path_unlink_file
812        }
813    },
814    errors: { errno => trappable Error },
815});
816
817pub(crate) mod sync {
818    use anyhow::Result;
819    use std::future::Future;
820
821    wiggle::wasmtime_integration!({
822        witx: ["witx/p1/wasi_snapshot_preview1.witx"],
823        target: super,
824        block_on[in_tokio]: {
825            wasi_snapshot_preview1::{
826                fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size,
827                fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write,
828                fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get,
829                path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory,
830                path_rename, path_symlink, path_unlink_file
831            }
832        },
833        errors: { errno => trappable Error },
834    });
835
836    // Small wrapper around `in_tokio` to add a `Result` layer which is always
837    // `Ok`
838    fn in_tokio<F: Future>(future: F) -> Result<F::Output> {
839        Ok(crate::runtime::in_tokio(future))
840    }
841}
842
843impl wiggle::GuestErrorType for types::Errno {
844    fn success() -> Self {
845        Self::Success
846    }
847}
848
849impl From<StreamError> for types::Error {
850    fn from(err: StreamError) -> Self {
851        match err {
852            StreamError::Closed => types::Errno::Io.into(),
853            StreamError::LastOperationFailed(e) => match e.downcast::<std::io::Error>() {
854                Ok(err) => filesystem::ErrorCode::from(err).into(),
855                Err(e) => {
856                    tracing::debug!("dropping error {e:?}");
857                    types::Errno::Io.into()
858                }
859            },
860            StreamError::Trap(e) => types::Error::trap(e),
861        }
862    }
863}
864
865impl From<FsError> for types::Error {
866    fn from(err: FsError) -> Self {
867        match err.downcast() {
868            Ok(code) => code.into(),
869            Err(e) => types::Error::trap(e),
870        }
871    }
872}
873
874fn systimespec(set: bool, ts: types::Timestamp, now: bool) -> Result<filesystem::NewTimestamp> {
875    if set && now {
876        Err(types::Errno::Inval.into())
877    } else if set {
878        Ok(filesystem::NewTimestamp::Timestamp(filesystem::Datetime {
879            seconds: ts / 1_000_000_000,
880            nanoseconds: (ts % 1_000_000_000) as _,
881        }))
882    } else if now {
883        Ok(filesystem::NewTimestamp::Now)
884    } else {
885        Ok(filesystem::NewTimestamp::NoChange)
886    }
887}
888
889impl TryFrom<wall_clock::Datetime> for types::Timestamp {
890    type Error = types::Errno;
891
892    fn try_from(
893        wall_clock::Datetime {
894            seconds,
895            nanoseconds,
896        }: wall_clock::Datetime,
897    ) -> Result<Self, Self::Error> {
898        types::Timestamp::from(seconds)
899            .checked_mul(1_000_000_000)
900            .and_then(|ns| ns.checked_add(nanoseconds.into()))
901            .ok_or(types::Errno::Overflow)
902    }
903}
904
905impl From<types::Lookupflags> for filesystem::PathFlags {
906    fn from(flags: types::Lookupflags) -> Self {
907        if flags.contains(types::Lookupflags::SYMLINK_FOLLOW) {
908            filesystem::PathFlags::SYMLINK_FOLLOW
909        } else {
910            filesystem::PathFlags::empty()
911        }
912    }
913}
914
915impl From<types::Oflags> for filesystem::OpenFlags {
916    fn from(flags: types::Oflags) -> Self {
917        let mut out = filesystem::OpenFlags::empty();
918        if flags.contains(types::Oflags::CREAT) {
919            out |= filesystem::OpenFlags::CREATE;
920        }
921        if flags.contains(types::Oflags::DIRECTORY) {
922            out |= filesystem::OpenFlags::DIRECTORY;
923        }
924        if flags.contains(types::Oflags::EXCL) {
925            out |= filesystem::OpenFlags::EXCLUSIVE;
926        }
927        if flags.contains(types::Oflags::TRUNC) {
928            out |= filesystem::OpenFlags::TRUNCATE;
929        }
930        out
931    }
932}
933
934impl From<types::Advice> for filesystem::Advice {
935    fn from(advice: types::Advice) -> Self {
936        match advice {
937            types::Advice::Normal => filesystem::Advice::Normal,
938            types::Advice::Sequential => filesystem::Advice::Sequential,
939            types::Advice::Random => filesystem::Advice::Random,
940            types::Advice::Willneed => filesystem::Advice::WillNeed,
941            types::Advice::Dontneed => filesystem::Advice::DontNeed,
942            types::Advice::Noreuse => filesystem::Advice::NoReuse,
943        }
944    }
945}
946
947impl TryFrom<filesystem::DescriptorType> for types::Filetype {
948    type Error = anyhow::Error;
949
950    fn try_from(ty: filesystem::DescriptorType) -> Result<Self, Self::Error> {
951        match ty {
952            filesystem::DescriptorType::RegularFile => Ok(types::Filetype::RegularFile),
953            filesystem::DescriptorType::Directory => Ok(types::Filetype::Directory),
954            filesystem::DescriptorType::BlockDevice => Ok(types::Filetype::BlockDevice),
955            filesystem::DescriptorType::CharacterDevice => Ok(types::Filetype::CharacterDevice),
956            // p1 never had a FIFO code.
957            filesystem::DescriptorType::Fifo => Ok(types::Filetype::Unknown),
958            // TODO: Add a way to disginguish between FILETYPE_SOCKET_STREAM and
959            // FILETYPE_SOCKET_DGRAM.
960            filesystem::DescriptorType::Socket => {
961                bail!("sockets are not currently supported")
962            }
963            filesystem::DescriptorType::SymbolicLink => Ok(types::Filetype::SymbolicLink),
964            filesystem::DescriptorType::Unknown => Ok(types::Filetype::Unknown),
965        }
966    }
967}
968
969impl From<IsATTY> for types::Filetype {
970    fn from(isatty: IsATTY) -> Self {
971        match isatty {
972            IsATTY::Yes => types::Filetype::CharacterDevice,
973            IsATTY::No => types::Filetype::Unknown,
974        }
975    }
976}
977
978impl From<crate::filesystem::ErrorCode> for types::Errno {
979    fn from(code: crate::filesystem::ErrorCode) -> Self {
980        match code {
981            crate::filesystem::ErrorCode::Access => types::Errno::Acces,
982            crate::filesystem::ErrorCode::Already => types::Errno::Already,
983            crate::filesystem::ErrorCode::BadDescriptor => types::Errno::Badf,
984            crate::filesystem::ErrorCode::Busy => types::Errno::Busy,
985            crate::filesystem::ErrorCode::Exist => types::Errno::Exist,
986            crate::filesystem::ErrorCode::FileTooLarge => types::Errno::Fbig,
987            crate::filesystem::ErrorCode::IllegalByteSequence => types::Errno::Ilseq,
988            crate::filesystem::ErrorCode::InProgress => types::Errno::Inprogress,
989            crate::filesystem::ErrorCode::Interrupted => types::Errno::Intr,
990            crate::filesystem::ErrorCode::Invalid => types::Errno::Inval,
991            crate::filesystem::ErrorCode::Io => types::Errno::Io,
992            crate::filesystem::ErrorCode::IsDirectory => types::Errno::Isdir,
993            crate::filesystem::ErrorCode::Loop => types::Errno::Loop,
994            crate::filesystem::ErrorCode::TooManyLinks => types::Errno::Mlink,
995            crate::filesystem::ErrorCode::NameTooLong => types::Errno::Nametoolong,
996            crate::filesystem::ErrorCode::NoEntry => types::Errno::Noent,
997            crate::filesystem::ErrorCode::InsufficientMemory => types::Errno::Nomem,
998            crate::filesystem::ErrorCode::InsufficientSpace => types::Errno::Nospc,
999            crate::filesystem::ErrorCode::Unsupported => types::Errno::Notsup,
1000            crate::filesystem::ErrorCode::NotDirectory => types::Errno::Notdir,
1001            crate::filesystem::ErrorCode::NotEmpty => types::Errno::Notempty,
1002            crate::filesystem::ErrorCode::Overflow => types::Errno::Overflow,
1003            crate::filesystem::ErrorCode::NotPermitted => types::Errno::Perm,
1004            crate::filesystem::ErrorCode::Pipe => types::Errno::Pipe,
1005            crate::filesystem::ErrorCode::InvalidSeek => types::Errno::Spipe,
1006        }
1007    }
1008}
1009
1010impl From<filesystem::ErrorCode> for types::Errno {
1011    fn from(code: filesystem::ErrorCode) -> Self {
1012        match code {
1013            filesystem::ErrorCode::Access => types::Errno::Acces,
1014            filesystem::ErrorCode::WouldBlock => types::Errno::Again,
1015            filesystem::ErrorCode::Already => types::Errno::Already,
1016            filesystem::ErrorCode::BadDescriptor => types::Errno::Badf,
1017            filesystem::ErrorCode::Busy => types::Errno::Busy,
1018            filesystem::ErrorCode::Deadlock => types::Errno::Deadlk,
1019            filesystem::ErrorCode::Quota => types::Errno::Dquot,
1020            filesystem::ErrorCode::Exist => types::Errno::Exist,
1021            filesystem::ErrorCode::FileTooLarge => types::Errno::Fbig,
1022            filesystem::ErrorCode::IllegalByteSequence => types::Errno::Ilseq,
1023            filesystem::ErrorCode::InProgress => types::Errno::Inprogress,
1024            filesystem::ErrorCode::Interrupted => types::Errno::Intr,
1025            filesystem::ErrorCode::Invalid => types::Errno::Inval,
1026            filesystem::ErrorCode::Io => types::Errno::Io,
1027            filesystem::ErrorCode::IsDirectory => types::Errno::Isdir,
1028            filesystem::ErrorCode::Loop => types::Errno::Loop,
1029            filesystem::ErrorCode::TooManyLinks => types::Errno::Mlink,
1030            filesystem::ErrorCode::MessageSize => types::Errno::Msgsize,
1031            filesystem::ErrorCode::NameTooLong => types::Errno::Nametoolong,
1032            filesystem::ErrorCode::NoDevice => types::Errno::Nodev,
1033            filesystem::ErrorCode::NoEntry => types::Errno::Noent,
1034            filesystem::ErrorCode::NoLock => types::Errno::Nolck,
1035            filesystem::ErrorCode::InsufficientMemory => types::Errno::Nomem,
1036            filesystem::ErrorCode::InsufficientSpace => types::Errno::Nospc,
1037            filesystem::ErrorCode::Unsupported => types::Errno::Notsup,
1038            filesystem::ErrorCode::NotDirectory => types::Errno::Notdir,
1039            filesystem::ErrorCode::NotEmpty => types::Errno::Notempty,
1040            filesystem::ErrorCode::NotRecoverable => types::Errno::Notrecoverable,
1041            filesystem::ErrorCode::NoTty => types::Errno::Notty,
1042            filesystem::ErrorCode::NoSuchDevice => types::Errno::Nxio,
1043            filesystem::ErrorCode::Overflow => types::Errno::Overflow,
1044            filesystem::ErrorCode::NotPermitted => types::Errno::Perm,
1045            filesystem::ErrorCode::Pipe => types::Errno::Pipe,
1046            filesystem::ErrorCode::ReadOnly => types::Errno::Rofs,
1047            filesystem::ErrorCode::InvalidSeek => types::Errno::Spipe,
1048            filesystem::ErrorCode::TextFileBusy => types::Errno::Txtbsy,
1049            filesystem::ErrorCode::CrossDevice => types::Errno::Xdev,
1050        }
1051    }
1052}
1053
1054impl From<std::num::TryFromIntError> for types::Error {
1055    fn from(_: std::num::TryFromIntError) -> Self {
1056        types::Errno::Overflow.into()
1057    }
1058}
1059
1060impl From<GuestError> for types::Error {
1061    fn from(err: GuestError) -> Self {
1062        use wiggle::GuestError::*;
1063        match err {
1064            InvalidFlagValue { .. } => types::Errno::Inval.into(),
1065            InvalidEnumValue { .. } => types::Errno::Inval.into(),
1066            // As per
1067            // https://github.com/WebAssembly/wasi/blob/main/legacy/tools/witx-docs.md#pointers
1068            //
1069            // > If a misaligned pointer is passed to a function, the function
1070            // > shall trap.
1071            // >
1072            // > If an out-of-bounds pointer is passed to a function and the
1073            // > function needs to dereference it, the function shall trap.
1074            //
1075            // so this turns OOB and misalignment errors into traps.
1076            PtrOverflow { .. } | PtrOutOfBounds { .. } | PtrNotAligned { .. } => {
1077                types::Error::trap(err.into())
1078            }
1079            InvalidUtf8 { .. } => types::Errno::Ilseq.into(),
1080            TryFromIntError { .. } => types::Errno::Overflow.into(),
1081            SliceLengthsDiffer { .. } => types::Errno::Fault.into(),
1082            InFunc { err, .. } => types::Error::from(*err),
1083        }
1084    }
1085}
1086
1087impl From<filesystem::ErrorCode> for types::Error {
1088    fn from(code: filesystem::ErrorCode) -> Self {
1089        types::Errno::from(code).into()
1090    }
1091}
1092
1093impl From<crate::filesystem::ErrorCode> for types::Error {
1094    fn from(code: crate::filesystem::ErrorCode) -> Self {
1095        types::Errno::from(code).into()
1096    }
1097}
1098
1099impl From<wasmtime::component::ResourceTableError> for types::Error {
1100    fn from(err: wasmtime::component::ResourceTableError) -> Self {
1101        types::Error::trap(err.into())
1102    }
1103}
1104
1105type Result<T, E = types::Error> = std::result::Result<T, E>;
1106
1107fn write_bytes(
1108    memory: &mut GuestMemory<'_>,
1109    ptr: GuestPtr<u8>,
1110    buf: &[u8],
1111) -> Result<GuestPtr<u8>, types::Error> {
1112    // NOTE: legacy implementation always returns Inval errno
1113
1114    let len = u32::try_from(buf.len())?;
1115
1116    memory.copy_from_slice(buf, ptr.as_array(len))?;
1117    let next = ptr.add(len)?;
1118    Ok(next)
1119}
1120
1121fn write_byte(memory: &mut GuestMemory<'_>, ptr: GuestPtr<u8>, byte: u8) -> Result<GuestPtr<u8>> {
1122    memory.write(ptr, byte)?;
1123    let next = ptr.add(1)?;
1124    Ok(next)
1125}
1126
1127fn read_string<'a>(memory: &'a GuestMemory<'_>, ptr: GuestPtr<str>) -> Result<String> {
1128    Ok(memory.as_cow_str(ptr)?.into_owned())
1129}
1130
1131// Returns the first non-empty buffer in `ciovs` or a single empty buffer if
1132// they're all empty.
1133fn first_non_empty_ciovec(
1134    memory: &GuestMemory<'_>,
1135    ciovs: types::CiovecArray,
1136) -> Result<GuestPtr<[u8]>> {
1137    for iov in ciovs.iter() {
1138        let iov = memory.read(iov?)?;
1139        if iov.buf_len == 0 {
1140            continue;
1141        }
1142        return Ok(iov.buf.as_array(iov.buf_len));
1143    }
1144    Ok(GuestPtr::new((0, 0)))
1145}
1146
1147// Returns the first non-empty buffer in `iovs` or a single empty buffer if
1148// they're all empty.
1149fn first_non_empty_iovec(
1150    memory: &GuestMemory<'_>,
1151    iovs: types::IovecArray,
1152) -> Result<GuestPtr<[u8]>> {
1153    for iov in iovs.iter() {
1154        let iov = memory.read(iov?)?;
1155        if iov.buf_len == 0 {
1156            continue;
1157        }
1158        return Ok(iov.buf.as_array(iov.buf_len));
1159    }
1160    Ok(GuestPtr::new((0, 0)))
1161}
1162
1163#[async_trait::async_trait]
1164// Implement the WasiSnapshotPreview1 trait using only the traits that are
1165// required for T, i.e., in terms of the preview 2 wit interface, and state
1166// stored in the WasiP1Adapter struct.
1167impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx {
1168    #[instrument(skip(self, memory))]
1169    fn args_get(
1170        &mut self,
1171        memory: &mut GuestMemory<'_>,
1172        argv: GuestPtr<GuestPtr<u8>>,
1173        argv_buf: GuestPtr<u8>,
1174    ) -> Result<(), types::Error> {
1175        self.cli()
1176            .get_arguments()
1177            .context("failed to call `get-arguments`")
1178            .map_err(types::Error::trap)?
1179            .into_iter()
1180            .try_fold((argv, argv_buf), |(argv, argv_buf), arg| -> Result<_> {
1181                memory.write(argv, argv_buf)?;
1182                let argv = argv.add(1)?;
1183
1184                let argv_buf = write_bytes(memory, argv_buf, arg.as_bytes())?;
1185                let argv_buf = write_byte(memory, argv_buf, 0)?;
1186
1187                Ok((argv, argv_buf))
1188            })?;
1189        Ok(())
1190    }
1191
1192    #[instrument(skip(self, _memory))]
1193    fn args_sizes_get(
1194        &mut self,
1195        _memory: &mut GuestMemory<'_>,
1196    ) -> Result<(types::Size, types::Size), types::Error> {
1197        let args = self
1198            .cli()
1199            .get_arguments()
1200            .context("failed to call `get-arguments`")
1201            .map_err(types::Error::trap)?;
1202        let num = args.len().try_into().map_err(|_| types::Errno::Overflow)?;
1203        let len = args
1204            .iter()
1205            .map(|buf| buf.len() + 1) // Each argument is expected to be `\0` terminated.
1206            .sum::<usize>()
1207            .try_into()
1208            .map_err(|_| types::Errno::Overflow)?;
1209        Ok((num, len))
1210    }
1211
1212    #[instrument(skip(self, memory))]
1213    fn environ_get(
1214        &mut self,
1215        memory: &mut GuestMemory<'_>,
1216        environ: GuestPtr<GuestPtr<u8>>,
1217        environ_buf: GuestPtr<u8>,
1218    ) -> Result<(), types::Error> {
1219        self.cli()
1220            .get_environment()
1221            .context("failed to call `get-environment`")
1222            .map_err(types::Error::trap)?
1223            .into_iter()
1224            .try_fold(
1225                (environ, environ_buf),
1226                |(environ, environ_buf), (k, v)| -> Result<_, types::Error> {
1227                    memory.write(environ, environ_buf)?;
1228                    let environ = environ.add(1)?;
1229
1230                    let environ_buf = write_bytes(memory, environ_buf, k.as_bytes())?;
1231                    let environ_buf = write_byte(memory, environ_buf, b'=')?;
1232                    let environ_buf = write_bytes(memory, environ_buf, v.as_bytes())?;
1233                    let environ_buf = write_byte(memory, environ_buf, 0)?;
1234
1235                    Ok((environ, environ_buf))
1236                },
1237            )?;
1238        Ok(())
1239    }
1240
1241    #[instrument(skip(self, _memory))]
1242    fn environ_sizes_get(
1243        &mut self,
1244        _memory: &mut GuestMemory<'_>,
1245    ) -> Result<(types::Size, types::Size), types::Error> {
1246        let environ = self
1247            .cli()
1248            .get_environment()
1249            .context("failed to call `get-environment`")
1250            .map_err(types::Error::trap)?;
1251        let num = environ.len().try_into()?;
1252        let len = environ
1253            .iter()
1254            .map(|(k, v)| k.len() + 1 + v.len() + 1) // Key/value pairs are expected to be joined with `=`s, and terminated with `\0`s.
1255            .sum::<usize>()
1256            .try_into()?;
1257        Ok((num, len))
1258    }
1259
1260    #[instrument(skip(self, _memory))]
1261    fn clock_res_get(
1262        &mut self,
1263        _memory: &mut GuestMemory<'_>,
1264        id: types::Clockid,
1265    ) -> Result<types::Timestamp, types::Error> {
1266        let res = match id {
1267            types::Clockid::Realtime => wall_clock::Host::resolution(&mut self.clocks())
1268                .context("failed to call `wall_clock::resolution`")
1269                .map_err(types::Error::trap)?
1270                .try_into()?,
1271            types::Clockid::Monotonic => monotonic_clock::Host::resolution(&mut self.clocks())
1272                .context("failed to call `monotonic_clock::resolution`")
1273                .map_err(types::Error::trap)?,
1274            types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
1275                return Err(types::Errno::Badf.into());
1276            }
1277        };
1278        Ok(res)
1279    }
1280
1281    #[instrument(skip(self, _memory))]
1282    fn clock_time_get(
1283        &mut self,
1284        _memory: &mut GuestMemory<'_>,
1285        id: types::Clockid,
1286        _precision: types::Timestamp,
1287    ) -> Result<types::Timestamp, types::Error> {
1288        let now = match id {
1289            types::Clockid::Realtime => wall_clock::Host::now(&mut self.clocks())
1290                .context("failed to call `wall_clock::now`")
1291                .map_err(types::Error::trap)?
1292                .try_into()?,
1293            types::Clockid::Monotonic => monotonic_clock::Host::now(&mut self.clocks())
1294                .context("failed to call `monotonic_clock::now`")
1295                .map_err(types::Error::trap)?,
1296            types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
1297                return Err(types::Errno::Badf.into());
1298            }
1299        };
1300        Ok(now)
1301    }
1302
1303    #[instrument(skip(self, _memory))]
1304    async fn fd_advise(
1305        &mut self,
1306        _memory: &mut GuestMemory<'_>,
1307        fd: types::Fd,
1308        offset: types::Filesize,
1309        len: types::Filesize,
1310        advice: types::Advice,
1311    ) -> Result<(), types::Error> {
1312        let fd = self.get_file_fd(fd)?;
1313        self.filesystem()
1314            .advise(fd, offset, len, advice.into())
1315            .await?;
1316        Ok(())
1317    }
1318
1319    /// Force the allocation of space in a file.
1320    /// NOTE: This is similar to `posix_fallocate` in POSIX.
1321    #[instrument(skip(self, _memory))]
1322    fn fd_allocate(
1323        &mut self,
1324        _memory: &mut GuestMemory<'_>,
1325        fd: types::Fd,
1326        _offset: types::Filesize,
1327        _len: types::Filesize,
1328    ) -> Result<(), types::Error> {
1329        self.get_file_fd(fd)?;
1330        Err(types::Errno::Notsup.into())
1331    }
1332
1333    /// Close a file descriptor.
1334    /// NOTE: This is similar to `close` in POSIX.
1335    #[instrument(skip(self, _memory))]
1336    async fn fd_close(
1337        &mut self,
1338        _memory: &mut GuestMemory<'_>,
1339        fd: types::Fd,
1340    ) -> Result<(), types::Error> {
1341        let desc = {
1342            let fd = fd.into();
1343            let mut st = self.transact()?;
1344            let desc = st.descriptors.used.remove(&fd).ok_or(types::Errno::Badf)?;
1345            st.descriptors.free.insert(fd);
1346            desc
1347        };
1348        match desc {
1349            Descriptor::Stdin { stream, .. } => {
1350                streams::HostInputStream::drop(&mut self.table, stream)
1351                    .await
1352                    .context("failed to call `drop` on `input-stream`")
1353            }
1354            Descriptor::Stdout { stream, .. } | Descriptor::Stderr { stream, .. } => {
1355                streams::HostOutputStream::drop(&mut self.table, stream)
1356                    .await
1357                    .context("failed to call `drop` on `output-stream`")
1358            }
1359            Descriptor::File(File { fd, .. }) | Descriptor::Directory { fd, .. } => {
1360                filesystem::HostDescriptor::drop(&mut self.filesystem(), fd)
1361                    .context("failed to call `drop`")
1362            }
1363        }
1364        .map_err(types::Error::trap)
1365    }
1366
1367    /// Synchronize the data of a file to disk.
1368    /// NOTE: This is similar to `fdatasync` in POSIX.
1369    #[instrument(skip(self, _memory))]
1370    async fn fd_datasync(
1371        &mut self,
1372        _memory: &mut GuestMemory<'_>,
1373        fd: types::Fd,
1374    ) -> Result<(), types::Error> {
1375        let fd = self.get_file_fd(fd)?;
1376        self.filesystem().sync_data(fd).await?;
1377        Ok(())
1378    }
1379
1380    /// Get the attributes of a file descriptor.
1381    /// NOTE: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields.
1382    #[instrument(skip(self, _memory))]
1383    async fn fd_fdstat_get(
1384        &mut self,
1385        _memory: &mut GuestMemory<'_>,
1386        fd: types::Fd,
1387    ) -> Result<types::Fdstat, types::Error> {
1388        let (fd, blocking, append) = match self.transact()?.get_descriptor(fd)? {
1389            Descriptor::Stdin { isatty, .. } => {
1390                let fs_rights_base = types::Rights::FD_READ;
1391                return Ok(types::Fdstat {
1392                    fs_filetype: (*isatty).into(),
1393                    fs_flags: types::Fdflags::empty(),
1394                    fs_rights_base,
1395                    fs_rights_inheriting: fs_rights_base,
1396                });
1397            }
1398            Descriptor::Stdout { isatty, .. } | Descriptor::Stderr { isatty, .. } => {
1399                let fs_rights_base = types::Rights::FD_WRITE;
1400                return Ok(types::Fdstat {
1401                    fs_filetype: (*isatty).into(),
1402                    fs_flags: types::Fdflags::empty(),
1403                    fs_rights_base,
1404                    fs_rights_inheriting: fs_rights_base,
1405                });
1406            }
1407            Descriptor::Directory {
1408                preopen_path: Some(_),
1409                ..
1410            } => {
1411                // Hard-coded set or rights expected by many userlands:
1412                let fs_rights_base = types::Rights::PATH_CREATE_DIRECTORY
1413                    | types::Rights::PATH_CREATE_FILE
1414                    | types::Rights::PATH_LINK_SOURCE
1415                    | types::Rights::PATH_LINK_TARGET
1416                    | types::Rights::PATH_OPEN
1417                    | types::Rights::FD_READDIR
1418                    | types::Rights::PATH_READLINK
1419                    | types::Rights::PATH_RENAME_SOURCE
1420                    | types::Rights::PATH_RENAME_TARGET
1421                    | types::Rights::PATH_SYMLINK
1422                    | types::Rights::PATH_REMOVE_DIRECTORY
1423                    | types::Rights::PATH_UNLINK_FILE
1424                    | types::Rights::PATH_FILESTAT_GET
1425                    | types::Rights::PATH_FILESTAT_SET_TIMES
1426                    | types::Rights::FD_FILESTAT_GET
1427                    | types::Rights::FD_FILESTAT_SET_TIMES;
1428
1429                let fs_rights_inheriting = fs_rights_base
1430                    | types::Rights::FD_DATASYNC
1431                    | types::Rights::FD_READ
1432                    | types::Rights::FD_SEEK
1433                    | types::Rights::FD_FDSTAT_SET_FLAGS
1434                    | types::Rights::FD_SYNC
1435                    | types::Rights::FD_TELL
1436                    | types::Rights::FD_WRITE
1437                    | types::Rights::FD_ADVISE
1438                    | types::Rights::FD_ALLOCATE
1439                    | types::Rights::FD_FILESTAT_GET
1440                    | types::Rights::FD_FILESTAT_SET_SIZE
1441                    | types::Rights::FD_FILESTAT_SET_TIMES
1442                    | types::Rights::POLL_FD_READWRITE;
1443
1444                return Ok(types::Fdstat {
1445                    fs_filetype: types::Filetype::Directory,
1446                    fs_flags: types::Fdflags::empty(),
1447                    fs_rights_base,
1448                    fs_rights_inheriting,
1449                });
1450            }
1451            Descriptor::Directory { fd, .. } => (fd.borrowed(), BlockingMode::Blocking, false),
1452            Descriptor::File(File {
1453                fd,
1454                blocking_mode,
1455                append,
1456                ..
1457            }) => (fd.borrowed(), *blocking_mode, *append),
1458        };
1459        let flags = self.filesystem().get_flags(fd.borrowed()).await?;
1460        let fs_filetype = self
1461            .filesystem()
1462            .get_type(fd.borrowed())
1463            .await?
1464            .try_into()
1465            .map_err(types::Error::trap)?;
1466        let mut fs_flags = types::Fdflags::empty();
1467        let mut fs_rights_base = types::Rights::all();
1468        if let types::Filetype::Directory = fs_filetype {
1469            fs_rights_base &= !types::Rights::FD_SEEK;
1470            fs_rights_base &= !types::Rights::FD_FILESTAT_SET_SIZE;
1471            fs_rights_base &= !types::Rights::PATH_FILESTAT_SET_SIZE;
1472        }
1473        if !flags.contains(filesystem::DescriptorFlags::READ) {
1474            fs_rights_base &= !types::Rights::FD_READ;
1475            fs_rights_base &= !types::Rights::FD_READDIR;
1476        }
1477        if !flags.contains(filesystem::DescriptorFlags::WRITE) {
1478            fs_rights_base &= !types::Rights::FD_WRITE;
1479        }
1480        if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
1481            fs_flags |= types::Fdflags::DSYNC;
1482        }
1483        if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
1484            fs_flags |= types::Fdflags::RSYNC;
1485        }
1486        if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
1487            fs_flags |= types::Fdflags::SYNC;
1488        }
1489        if append {
1490            fs_flags |= types::Fdflags::APPEND;
1491        }
1492        if matches!(blocking, BlockingMode::NonBlocking) {
1493            fs_flags |= types::Fdflags::NONBLOCK;
1494        }
1495        Ok(types::Fdstat {
1496            fs_filetype,
1497            fs_flags,
1498            fs_rights_base,
1499            fs_rights_inheriting: fs_rights_base,
1500        })
1501    }
1502
1503    /// Adjust the flags associated with a file descriptor.
1504    /// NOTE: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
1505    #[instrument(skip(self, _memory))]
1506    fn fd_fdstat_set_flags(
1507        &mut self,
1508        _memory: &mut GuestMemory<'_>,
1509        fd: types::Fd,
1510        flags: types::Fdflags,
1511    ) -> Result<(), types::Error> {
1512        let mut st = self.transact()?;
1513        let File {
1514            append,
1515            blocking_mode,
1516            ..
1517        } = st.get_file_mut(fd)?;
1518
1519        // Only support changing the NONBLOCK or APPEND flags.
1520        if flags.contains(types::Fdflags::DSYNC)
1521            || flags.contains(types::Fdflags::SYNC)
1522            || flags.contains(types::Fdflags::RSYNC)
1523        {
1524            return Err(types::Errno::Inval.into());
1525        }
1526        *append = flags.contains(types::Fdflags::APPEND);
1527        *blocking_mode = BlockingMode::from_fdflags(&flags);
1528        Ok(())
1529    }
1530
1531    /// Does not do anything if `fd` corresponds to a valid descriptor and returns `[types::Errno::Badf]` error otherwise.
1532    #[instrument(skip(self, _memory))]
1533    fn fd_fdstat_set_rights(
1534        &mut self,
1535        _memory: &mut GuestMemory<'_>,
1536        fd: types::Fd,
1537        _fs_rights_base: types::Rights,
1538        _fs_rights_inheriting: types::Rights,
1539    ) -> Result<(), types::Error> {
1540        self.get_fd(fd)?;
1541        Err(types::Errno::Notsup.into())
1542    }
1543
1544    /// Return the attributes of an open file.
1545    #[instrument(skip(self, _memory))]
1546    async fn fd_filestat_get(
1547        &mut self,
1548        _memory: &mut GuestMemory<'_>,
1549        fd: types::Fd,
1550    ) -> Result<types::Filestat, types::Error> {
1551        let t = self.transact()?;
1552        let desc = t.get_descriptor(fd)?;
1553        match desc {
1554            Descriptor::Stdin { isatty, .. }
1555            | Descriptor::Stdout { isatty, .. }
1556            | Descriptor::Stderr { isatty, .. } => Ok(types::Filestat {
1557                dev: 0,
1558                ino: 0,
1559                filetype: (*isatty).into(),
1560                nlink: 0,
1561                size: 0,
1562                atim: 0,
1563                mtim: 0,
1564                ctim: 0,
1565            }),
1566            Descriptor::Directory { fd, .. } | Descriptor::File(File { fd, .. }) => {
1567                let fd = fd.borrowed();
1568                drop(t);
1569                let filesystem::DescriptorStat {
1570                    type_,
1571                    link_count: nlink,
1572                    size,
1573                    data_access_timestamp,
1574                    data_modification_timestamp,
1575                    status_change_timestamp,
1576                } = self.filesystem().stat(fd.borrowed()).await?;
1577                let metadata_hash = self.filesystem().metadata_hash(fd).await?;
1578                let filetype = type_.try_into().map_err(types::Error::trap)?;
1579                let zero = wall_clock::Datetime {
1580                    seconds: 0,
1581                    nanoseconds: 0,
1582                };
1583                let atim = data_access_timestamp.unwrap_or(zero).try_into()?;
1584                let mtim = data_modification_timestamp.unwrap_or(zero).try_into()?;
1585                let ctim = status_change_timestamp.unwrap_or(zero).try_into()?;
1586                Ok(types::Filestat {
1587                    dev: 1,
1588                    ino: metadata_hash.lower,
1589                    filetype,
1590                    nlink,
1591                    size,
1592                    atim,
1593                    mtim,
1594                    ctim,
1595                })
1596            }
1597        }
1598    }
1599
1600    /// Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros.
1601    /// NOTE: This is similar to `ftruncate` in POSIX.
1602    #[instrument(skip(self, _memory))]
1603    async fn fd_filestat_set_size(
1604        &mut self,
1605        _memory: &mut GuestMemory<'_>,
1606        fd: types::Fd,
1607        size: types::Filesize,
1608    ) -> Result<(), types::Error> {
1609        let fd = self.get_file_fd(fd)?;
1610        self.filesystem().set_size(fd, size).await?;
1611        Ok(())
1612    }
1613
1614    /// Adjust the timestamps of an open file or directory.
1615    /// NOTE: This is similar to `futimens` in POSIX.
1616    #[instrument(skip(self, _memory))]
1617    async fn fd_filestat_set_times(
1618        &mut self,
1619        _memory: &mut GuestMemory<'_>,
1620        fd: types::Fd,
1621        atim: types::Timestamp,
1622        mtim: types::Timestamp,
1623        fst_flags: types::Fstflags,
1624    ) -> Result<(), types::Error> {
1625        let atim = systimespec(
1626            fst_flags.contains(types::Fstflags::ATIM),
1627            atim,
1628            fst_flags.contains(types::Fstflags::ATIM_NOW),
1629        )?;
1630        let mtim = systimespec(
1631            fst_flags.contains(types::Fstflags::MTIM),
1632            mtim,
1633            fst_flags.contains(types::Fstflags::MTIM_NOW),
1634        )?;
1635
1636        let fd = self.get_fd(fd)?;
1637        self.filesystem().set_times(fd, atim, mtim).await?;
1638        Ok(())
1639    }
1640
1641    /// Read from a file descriptor.
1642    /// NOTE: This is similar to `readv` in POSIX.
1643    #[instrument(skip(self, memory))]
1644    async fn fd_read(
1645        &mut self,
1646        memory: &mut GuestMemory<'_>,
1647        fd: types::Fd,
1648        iovs: types::IovecArray,
1649    ) -> Result<types::Size, types::Error> {
1650        let t = self.transact()?;
1651        let desc = t.get_descriptor(fd)?;
1652        match desc {
1653            Descriptor::File(File {
1654                fd,
1655                position,
1656                // NB: the nonblocking flag is intentionally ignored here and
1657                // blocking reads/writes are always performed.
1658                blocking_mode: _,
1659                ..
1660            }) => {
1661                let fd = fd.borrowed();
1662                let position = position.clone();
1663                drop(t);
1664                let pos = position.load(Ordering::Relaxed);
1665                let file = self.table.get(&fd)?.file()?;
1666                let iov = first_non_empty_iovec(memory, iovs)?;
1667                let bytes_read = match (file.as_blocking_file(), memory.as_slice_mut(iov)?) {
1668                    // Try to read directly into wasm memory where possible
1669                    // when the current thread can block and additionally wasm
1670                    // memory isn't shared.
1671                    (Some(file), Some(mut buf)) => file
1672                        .read_at(&mut buf, pos)
1673                        .map_err(|e| StreamError::LastOperationFailed(e.into()))?,
1674                    // ... otherwise fall back to performing the read on a
1675                    // blocking thread and which copies the data back into wasm
1676                    // memory.
1677                    (_, buf) => {
1678                        drop(buf);
1679                        let mut buf = vec![0; iov.len() as usize];
1680                        let buf = file
1681                            .run_blocking(move |file| -> Result<_, types::Error> {
1682                                let bytes_read = file
1683                                    .read_at(&mut buf, pos)
1684                                    .map_err(|e| StreamError::LastOperationFailed(e.into()))?;
1685                                buf.truncate(bytes_read);
1686                                Ok(buf)
1687                            })
1688                            .await?;
1689                        let iov = iov.get_range(0..u32::try_from(buf.len())?).unwrap();
1690                        memory.copy_from_slice(&buf, iov)?;
1691                        buf.len()
1692                    }
1693                };
1694
1695                let pos = pos
1696                    .checked_add(bytes_read.try_into()?)
1697                    .ok_or(types::Errno::Overflow)?;
1698                position.store(pos, Ordering::Relaxed);
1699
1700                Ok(bytes_read.try_into()?)
1701            }
1702            Descriptor::Stdin { stream, .. } => {
1703                let stream = stream.borrowed();
1704                drop(t);
1705                let buf = first_non_empty_iovec(memory, iovs)?;
1706                let read = BlockingMode::Blocking
1707                    .read(&mut self.table, stream, buf.len().try_into()?)
1708                    .await?;
1709                if read.len() > buf.len().try_into()? {
1710                    return Err(types::Errno::Range.into());
1711                }
1712                let buf = buf.get_range(0..u32::try_from(read.len())?).unwrap();
1713                memory.copy_from_slice(&read, buf)?;
1714                let n = read.len().try_into()?;
1715                Ok(n)
1716            }
1717            _ => return Err(types::Errno::Badf.into()),
1718        }
1719    }
1720
1721    /// Read from a file descriptor, without using and updating the file descriptor's offset.
1722    /// NOTE: This is similar to `preadv` in POSIX.
1723    #[instrument(skip(self, memory))]
1724    async fn fd_pread(
1725        &mut self,
1726        memory: &mut GuestMemory<'_>,
1727        fd: types::Fd,
1728        iovs: types::IovecArray,
1729        offset: types::Filesize,
1730    ) -> Result<types::Size, types::Error> {
1731        let t = self.transact()?;
1732        let desc = t.get_descriptor(fd)?;
1733        let (buf, read) = match desc {
1734            Descriptor::File(File {
1735                fd, blocking_mode, ..
1736            }) => {
1737                let fd = fd.borrowed();
1738                let blocking_mode = *blocking_mode;
1739                drop(t);
1740                let buf = first_non_empty_iovec(memory, iovs)?;
1741
1742                let stream = self.filesystem().read_via_stream(fd, offset)?;
1743                let read = blocking_mode
1744                    .read(&mut self.table, stream.borrowed(), buf.len().try_into()?)
1745                    .await;
1746                streams::HostInputStream::drop(&mut self.table, stream)
1747                    .await
1748                    .map_err(|e| types::Error::trap(e))?;
1749                (buf, read?)
1750            }
1751            Descriptor::Stdin { .. } => {
1752                // NOTE: legacy implementation returns SPIPE here
1753                return Err(types::Errno::Spipe.into());
1754            }
1755            _ => return Err(types::Errno::Badf.into()),
1756        };
1757        if read.len() > buf.len().try_into()? {
1758            return Err(types::Errno::Range.into());
1759        }
1760        let buf = buf.get_range(0..u32::try_from(read.len())?).unwrap();
1761        memory.copy_from_slice(&read, buf)?;
1762        let n = read.len().try_into()?;
1763        Ok(n)
1764    }
1765
1766    /// Write to a file descriptor.
1767    /// NOTE: This is similar to `writev` in POSIX.
1768    #[instrument(skip(self, memory))]
1769    async fn fd_write(
1770        &mut self,
1771        memory: &mut GuestMemory<'_>,
1772        fd: types::Fd,
1773        ciovs: types::CiovecArray,
1774    ) -> Result<types::Size, types::Error> {
1775        self.fd_write_impl(memory, fd, ciovs, FdWrite::AtCur).await
1776    }
1777
1778    /// Write to a file descriptor, without using and updating the file descriptor's offset.
1779    /// NOTE: This is similar to `pwritev` in POSIX.
1780    #[instrument(skip(self, memory))]
1781    async fn fd_pwrite(
1782        &mut self,
1783        memory: &mut GuestMemory<'_>,
1784        fd: types::Fd,
1785        ciovs: types::CiovecArray,
1786        offset: types::Filesize,
1787    ) -> Result<types::Size, types::Error> {
1788        self.fd_write_impl(memory, fd, ciovs, FdWrite::At(offset))
1789            .await
1790    }
1791
1792    /// Return a description of the given preopened file descriptor.
1793    #[instrument(skip(self, _memory))]
1794    fn fd_prestat_get(
1795        &mut self,
1796        _memory: &mut GuestMemory<'_>,
1797        fd: types::Fd,
1798    ) -> Result<types::Prestat, types::Error> {
1799        if let Descriptor::Directory {
1800            preopen_path: Some(p),
1801            ..
1802        } = self.transact()?.get_descriptor(fd)?
1803        {
1804            let pr_name_len = p.len().try_into()?;
1805            return Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len }));
1806        }
1807        Err(types::Errno::Badf.into()) // NOTE: legacy implementation returns BADF here
1808    }
1809
1810    /// Return a description of the given preopened file descriptor.
1811    #[instrument(skip(self, memory))]
1812    fn fd_prestat_dir_name(
1813        &mut self,
1814        memory: &mut GuestMemory<'_>,
1815        fd: types::Fd,
1816        path: GuestPtr<u8>,
1817        path_max_len: types::Size,
1818    ) -> Result<(), types::Error> {
1819        let path_max_len = path_max_len.try_into()?;
1820        if let Descriptor::Directory {
1821            preopen_path: Some(p),
1822            ..
1823        } = self.transact()?.get_descriptor(fd)?
1824        {
1825            if p.len() > path_max_len {
1826                return Err(types::Errno::Nametoolong.into());
1827            }
1828            write_bytes(memory, path, p.as_bytes())?;
1829            return Ok(());
1830        }
1831        Err(types::Errno::Notdir.into()) // NOTE: legacy implementation returns NOTDIR here
1832    }
1833
1834    /// Atomically replace a file descriptor by renumbering another file descriptor.
1835    #[instrument(skip(self, _memory))]
1836    fn fd_renumber(
1837        &mut self,
1838        _memory: &mut GuestMemory<'_>,
1839        from: types::Fd,
1840        to: types::Fd,
1841    ) -> Result<(), types::Error> {
1842        let mut st = self.transact()?;
1843        let from = from.into();
1844        let to = to.into();
1845        if !st.descriptors.used.contains_key(&to) {
1846            return Err(types::Errno::Badf.into());
1847        }
1848        let btree_map::Entry::Occupied(desc) = st.descriptors.used.entry(from) else {
1849            return Err(types::Errno::Badf.into());
1850        };
1851        if from != to {
1852            let desc = desc.remove();
1853            st.descriptors.free.insert(from);
1854            st.descriptors.free.remove(&to);
1855            st.descriptors.used.insert(to, desc);
1856        }
1857        Ok(())
1858    }
1859
1860    /// Move the offset of a file descriptor.
1861    /// NOTE: This is similar to `lseek` in POSIX.
1862    #[instrument(skip(self, _memory))]
1863    async fn fd_seek(
1864        &mut self,
1865        _memory: &mut GuestMemory<'_>,
1866        fd: types::Fd,
1867        offset: types::Filedelta,
1868        whence: types::Whence,
1869    ) -> Result<types::Filesize, types::Error> {
1870        let t = self.transact()?;
1871        let File { fd, position, .. } = t.get_seekable(fd)?;
1872        let fd = fd.borrowed();
1873        let position = position.clone();
1874        drop(t);
1875        let pos = match whence {
1876            types::Whence::Set if offset >= 0 => {
1877                offset.try_into().map_err(|_| types::Errno::Inval)?
1878            }
1879            types::Whence::Cur => position
1880                .load(Ordering::Relaxed)
1881                .checked_add_signed(offset)
1882                .ok_or(types::Errno::Inval)?,
1883            types::Whence::End => {
1884                let filesystem::DescriptorStat { size, .. } = self.filesystem().stat(fd).await?;
1885                size.checked_add_signed(offset).ok_or(types::Errno::Inval)?
1886            }
1887            _ => return Err(types::Errno::Inval.into()),
1888        };
1889        position.store(pos, Ordering::Relaxed);
1890        Ok(pos)
1891    }
1892
1893    /// Synchronize the data and metadata of a file to disk.
1894    /// NOTE: This is similar to `fsync` in POSIX.
1895    #[instrument(skip(self, _memory))]
1896    async fn fd_sync(
1897        &mut self,
1898        _memory: &mut GuestMemory<'_>,
1899        fd: types::Fd,
1900    ) -> Result<(), types::Error> {
1901        let fd = self.get_file_fd(fd)?;
1902        self.filesystem().sync(fd).await?;
1903        Ok(())
1904    }
1905
1906    /// Return the current offset of a file descriptor.
1907    /// NOTE: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
1908    #[instrument(skip(self, _memory))]
1909    fn fd_tell(
1910        &mut self,
1911        _memory: &mut GuestMemory<'_>,
1912        fd: types::Fd,
1913    ) -> Result<types::Filesize, types::Error> {
1914        let pos = self
1915            .transact()?
1916            .get_seekable(fd)
1917            .map(|File { position, .. }| position.load(Ordering::Relaxed))?;
1918        Ok(pos)
1919    }
1920
1921    #[instrument(skip(self, memory))]
1922    async fn fd_readdir(
1923        &mut self,
1924        memory: &mut GuestMemory<'_>,
1925        fd: types::Fd,
1926        buf: GuestPtr<u8>,
1927        buf_len: types::Size,
1928        cookie: types::Dircookie,
1929    ) -> Result<types::Size, types::Error> {
1930        let fd = self.get_dir_fd(fd)?;
1931        let stream = self.filesystem().read_directory(fd.borrowed()).await?;
1932        let dir_metadata_hash = self.filesystem().metadata_hash(fd.borrowed()).await?;
1933        let cookie = cookie.try_into().map_err(|_| types::Errno::Overflow)?;
1934
1935        let head = [
1936            (
1937                types::Dirent {
1938                    d_next: 1u64.to_le(),
1939                    d_ino: dir_metadata_hash.lower.to_le(),
1940                    d_type: types::Filetype::Directory,
1941                    d_namlen: 1u32.to_le(),
1942                },
1943                ".".into(),
1944            ),
1945            (
1946                types::Dirent {
1947                    d_next: 2u64.to_le(),
1948                    d_ino: dir_metadata_hash.lower.to_le(), // NOTE: incorrect, but legacy implementation returns `fd` inode here
1949                    d_type: types::Filetype::Directory,
1950                    d_namlen: 2u32.to_le(),
1951                },
1952                "..".into(),
1953            ),
1954        ];
1955
1956        let mut dir = Vec::new();
1957        for (entry, d_next) in self
1958            .table
1959            // remove iterator from table and use it directly:
1960            .delete(stream)?
1961            .into_iter()
1962            .zip(3u64..)
1963        {
1964            let filesystem::DirectoryEntry { type_, name } = entry?;
1965            let metadata_hash = self
1966                .filesystem()
1967                .metadata_hash_at(fd.borrowed(), filesystem::PathFlags::empty(), name.clone())
1968                .await?;
1969            let d_type = type_.try_into().map_err(types::Error::trap)?;
1970            let d_namlen: u32 = name.len().try_into().map_err(|_| types::Errno::Overflow)?;
1971            dir.push((
1972                types::Dirent {
1973                    d_next: d_next.to_le(),
1974                    d_ino: metadata_hash.lower.to_le(),
1975                    d_type, // endian-invariant
1976                    d_namlen: d_namlen.to_le(),
1977                },
1978                name,
1979            ))
1980        }
1981
1982        // assume that `types::Dirent` size always fits in `u32`
1983        const DIRENT_SIZE: u32 = size_of::<types::Dirent>() as _;
1984        assert_eq!(
1985            types::Dirent::guest_size(),
1986            DIRENT_SIZE,
1987            "Dirent guest repr and host repr should match"
1988        );
1989        let mut buf = buf;
1990        let mut cap = buf_len;
1991        for (ref entry, path) in head.into_iter().chain(dir.into_iter()).skip(cookie) {
1992            let mut path = path.into_bytes();
1993            assert_eq!(
1994                1,
1995                size_of_val(&entry.d_type),
1996                "Dirent member d_type should be endian-invariant"
1997            );
1998            let entry_len = cap.min(DIRENT_SIZE);
1999            let entry = entry as *const _ as _;
2000            let entry = unsafe { slice::from_raw_parts(entry, entry_len as _) };
2001            cap = cap.checked_sub(entry_len).unwrap();
2002            buf = write_bytes(memory, buf, entry)?;
2003            if cap == 0 {
2004                return Ok(buf_len);
2005            }
2006
2007            if let Ok(cap) = cap.try_into() {
2008                // `path` cannot be longer than `usize`, only truncate if `cap` fits in `usize`
2009                path.truncate(cap);
2010            }
2011            cap = cap.checked_sub(path.len() as _).unwrap();
2012            buf = write_bytes(memory, buf, &path)?;
2013            if cap == 0 {
2014                return Ok(buf_len);
2015            }
2016        }
2017        Ok(buf_len.checked_sub(cap).unwrap())
2018    }
2019
2020    #[instrument(skip(self, memory))]
2021    async fn path_create_directory(
2022        &mut self,
2023        memory: &mut GuestMemory<'_>,
2024        dirfd: types::Fd,
2025        path: GuestPtr<str>,
2026    ) -> Result<(), types::Error> {
2027        let dirfd = self.get_dir_fd(dirfd)?;
2028        let path = read_string(memory, path)?;
2029        self.filesystem()
2030            .create_directory_at(dirfd.borrowed(), path)
2031            .await?;
2032        Ok(())
2033    }
2034
2035    /// Return the attributes of a file or directory.
2036    /// NOTE: This is similar to `stat` in POSIX.
2037    #[instrument(skip(self, memory))]
2038    async fn path_filestat_get(
2039        &mut self,
2040        memory: &mut GuestMemory<'_>,
2041        dirfd: types::Fd,
2042        flags: types::Lookupflags,
2043        path: GuestPtr<str>,
2044    ) -> Result<types::Filestat, types::Error> {
2045        let dirfd = self.get_dir_fd(dirfd)?;
2046        let path = read_string(memory, path)?;
2047        let filesystem::DescriptorStat {
2048            type_,
2049            link_count: nlink,
2050            size,
2051            data_access_timestamp,
2052            data_modification_timestamp,
2053            status_change_timestamp,
2054        } = self
2055            .filesystem()
2056            .stat_at(dirfd.borrowed(), flags.into(), path.clone())
2057            .await?;
2058        let metadata_hash = self
2059            .filesystem()
2060            .metadata_hash_at(dirfd, flags.into(), path)
2061            .await?;
2062        let filetype = type_.try_into().map_err(types::Error::trap)?;
2063        let zero = wall_clock::Datetime {
2064            seconds: 0,
2065            nanoseconds: 0,
2066        };
2067        let atim = data_access_timestamp.unwrap_or(zero).try_into()?;
2068        let mtim = data_modification_timestamp.unwrap_or(zero).try_into()?;
2069        let ctim = status_change_timestamp.unwrap_or(zero).try_into()?;
2070        Ok(types::Filestat {
2071            dev: 1,
2072            ino: metadata_hash.lower,
2073            filetype,
2074            nlink,
2075            size,
2076            atim,
2077            mtim,
2078            ctim,
2079        })
2080    }
2081
2082    /// Adjust the timestamps of a file or directory.
2083    /// NOTE: This is similar to `utimensat` in POSIX.
2084    #[instrument(skip(self, memory))]
2085    async fn path_filestat_set_times(
2086        &mut self,
2087        memory: &mut GuestMemory<'_>,
2088        dirfd: types::Fd,
2089        flags: types::Lookupflags,
2090        path: GuestPtr<str>,
2091        atim: types::Timestamp,
2092        mtim: types::Timestamp,
2093        fst_flags: types::Fstflags,
2094    ) -> Result<(), types::Error> {
2095        let atim = systimespec(
2096            fst_flags.contains(types::Fstflags::ATIM),
2097            atim,
2098            fst_flags.contains(types::Fstflags::ATIM_NOW),
2099        )?;
2100        let mtim = systimespec(
2101            fst_flags.contains(types::Fstflags::MTIM),
2102            mtim,
2103            fst_flags.contains(types::Fstflags::MTIM_NOW),
2104        )?;
2105
2106        let dirfd = self.get_dir_fd(dirfd)?;
2107        let path = read_string(memory, path)?;
2108        self.filesystem()
2109            .set_times_at(dirfd, flags.into(), path, atim, mtim)
2110            .await?;
2111        Ok(())
2112    }
2113
2114    /// Create a hard link.
2115    /// NOTE: This is similar to `linkat` in POSIX.
2116    #[instrument(skip(self, memory))]
2117    async fn path_link(
2118        &mut self,
2119        memory: &mut GuestMemory<'_>,
2120        src_fd: types::Fd,
2121        src_flags: types::Lookupflags,
2122        src_path: GuestPtr<str>,
2123        target_fd: types::Fd,
2124        target_path: GuestPtr<str>,
2125    ) -> Result<(), types::Error> {
2126        let src_fd = self.get_dir_fd(src_fd)?;
2127        let target_fd = self.get_dir_fd(target_fd)?;
2128        let src_path = read_string(memory, src_path)?;
2129        let target_path = read_string(memory, target_path)?;
2130        self.filesystem()
2131            .link_at(src_fd, src_flags.into(), src_path, target_fd, target_path)
2132            .await?;
2133        Ok(())
2134    }
2135
2136    /// Open a file or directory.
2137    /// NOTE: This is similar to `openat` in POSIX.
2138    #[instrument(skip(self, memory))]
2139    async fn path_open(
2140        &mut self,
2141        memory: &mut GuestMemory<'_>,
2142        dirfd: types::Fd,
2143        dirflags: types::Lookupflags,
2144        path: GuestPtr<str>,
2145        oflags: types::Oflags,
2146        fs_rights_base: types::Rights,
2147        _fs_rights_inheriting: types::Rights,
2148        fdflags: types::Fdflags,
2149    ) -> Result<types::Fd, types::Error> {
2150        let path = read_string(memory, path)?;
2151
2152        let mut flags = filesystem::DescriptorFlags::empty();
2153        if fs_rights_base.contains(types::Rights::FD_READ) {
2154            flags |= filesystem::DescriptorFlags::READ;
2155        }
2156        if fs_rights_base.contains(types::Rights::FD_WRITE) {
2157            flags |= filesystem::DescriptorFlags::WRITE;
2158        }
2159        if fdflags.contains(types::Fdflags::SYNC) {
2160            flags |= filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC;
2161        }
2162        if fdflags.contains(types::Fdflags::DSYNC) {
2163            flags |= filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC;
2164        }
2165        if fdflags.contains(types::Fdflags::RSYNC) {
2166            flags |= filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC;
2167        }
2168
2169        let t = self.transact()?;
2170        let dirfd = match t.get_descriptor(dirfd)? {
2171            Descriptor::Directory { fd, .. } => fd.borrowed(),
2172            Descriptor::File(_) => return Err(types::Errno::Notdir.into()),
2173            _ => return Err(types::Errno::Badf.into()),
2174        };
2175        drop(t);
2176        let fd = self
2177            .filesystem()
2178            .open_at(dirfd, dirflags.into(), path, oflags.into(), flags)
2179            .await?;
2180        let mut t = self.transact()?;
2181        let desc = match t.view.table.get(&fd)? {
2182            crate::filesystem::Descriptor::Dir(_) => Descriptor::Directory {
2183                fd,
2184                preopen_path: None,
2185            },
2186            crate::filesystem::Descriptor::File(_) => Descriptor::File(File {
2187                fd,
2188                position: Default::default(),
2189                append: fdflags.contains(types::Fdflags::APPEND),
2190                blocking_mode: BlockingMode::from_fdflags(&fdflags),
2191            }),
2192        };
2193        let fd = t.descriptors.push(desc)?;
2194        Ok(fd.into())
2195    }
2196
2197    /// Read the contents of a symbolic link.
2198    /// NOTE: This is similar to `readlinkat` in POSIX.
2199    #[instrument(skip(self, memory))]
2200    async fn path_readlink(
2201        &mut self,
2202        memory: &mut GuestMemory<'_>,
2203        dirfd: types::Fd,
2204        path: GuestPtr<str>,
2205        buf: GuestPtr<u8>,
2206        buf_len: types::Size,
2207    ) -> Result<types::Size, types::Error> {
2208        let dirfd = self.get_dir_fd(dirfd)?;
2209        let path = read_string(memory, path)?;
2210        let mut path = self
2211            .filesystem()
2212            .readlink_at(dirfd, path)
2213            .await?
2214            .into_bytes();
2215        if let Ok(buf_len) = buf_len.try_into() {
2216            // `path` cannot be longer than `usize`, only truncate if `buf_len` fits in `usize`
2217            path.truncate(buf_len);
2218        }
2219        let n = path.len().try_into().map_err(|_| types::Errno::Overflow)?;
2220        write_bytes(memory, buf, &path)?;
2221        Ok(n)
2222    }
2223
2224    #[instrument(skip(self, memory))]
2225    async fn path_remove_directory(
2226        &mut self,
2227        memory: &mut GuestMemory<'_>,
2228        dirfd: types::Fd,
2229        path: GuestPtr<str>,
2230    ) -> Result<(), types::Error> {
2231        let dirfd = self.get_dir_fd(dirfd)?;
2232        let path = read_string(memory, path)?;
2233        self.filesystem().remove_directory_at(dirfd, path).await?;
2234        Ok(())
2235    }
2236
2237    /// Rename a file or directory.
2238    /// NOTE: This is similar to `renameat` in POSIX.
2239    #[instrument(skip(self, memory))]
2240    async fn path_rename(
2241        &mut self,
2242        memory: &mut GuestMemory<'_>,
2243        src_fd: types::Fd,
2244        src_path: GuestPtr<str>,
2245        dest_fd: types::Fd,
2246        dest_path: GuestPtr<str>,
2247    ) -> Result<(), types::Error> {
2248        let src_fd = self.get_dir_fd(src_fd)?;
2249        let dest_fd = self.get_dir_fd(dest_fd)?;
2250        let src_path = read_string(memory, src_path)?;
2251        let dest_path = read_string(memory, dest_path)?;
2252        self.filesystem()
2253            .rename_at(src_fd, src_path, dest_fd, dest_path)
2254            .await?;
2255        Ok(())
2256    }
2257
2258    #[instrument(skip(self, memory))]
2259    async fn path_symlink(
2260        &mut self,
2261        memory: &mut GuestMemory<'_>,
2262        src_path: GuestPtr<str>,
2263        dirfd: types::Fd,
2264        dest_path: GuestPtr<str>,
2265    ) -> Result<(), types::Error> {
2266        let dirfd = self.get_dir_fd(dirfd)?;
2267        let src_path = read_string(memory, src_path)?;
2268        let dest_path = read_string(memory, dest_path)?;
2269        self.filesystem()
2270            .symlink_at(dirfd.borrowed(), src_path, dest_path)
2271            .await?;
2272        Ok(())
2273    }
2274
2275    #[instrument(skip(self, memory))]
2276    async fn path_unlink_file(
2277        &mut self,
2278        memory: &mut GuestMemory<'_>,
2279        dirfd: types::Fd,
2280        path: GuestPtr<str>,
2281    ) -> Result<(), types::Error> {
2282        let dirfd = self.get_dir_fd(dirfd)?;
2283        let path = memory.as_cow_str(path)?.into_owned();
2284        self.filesystem()
2285            .unlink_file_at(dirfd.borrowed(), path)
2286            .await?;
2287        Ok(())
2288    }
2289
2290    #[instrument(skip(self, memory))]
2291    async fn poll_oneoff(
2292        &mut self,
2293        memory: &mut GuestMemory<'_>,
2294        subs: GuestPtr<types::Subscription>,
2295        events: GuestPtr<types::Event>,
2296        nsubscriptions: types::Size,
2297    ) -> Result<types::Size, types::Error> {
2298        if nsubscriptions == 0 {
2299            // Indefinite sleeping is not supported in p1.
2300            return Err(types::Errno::Inval.into());
2301        }
2302
2303        // This is a special case where `poll_oneoff` is just sleeping
2304        // on a single relative timer event. This special case was added
2305        // after experimental observations showed that std::thread::sleep
2306        // results in more consistent sleep times. This design ensures that
2307        // wasmtime can handle real-time requirements more accurately.
2308        if nsubscriptions == 1 {
2309            let sub = memory.read(subs)?;
2310            if let types::SubscriptionU::Clock(clocksub) = sub.u {
2311                if !clocksub
2312                    .flags
2313                    .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
2314                    && self.wasi.filesystem.allow_blocking_current_thread
2315                {
2316                    std::thread::sleep(std::time::Duration::from_nanos(clocksub.timeout));
2317                    memory.write(
2318                        events,
2319                        types::Event {
2320                            userdata: sub.userdata,
2321                            error: types::Errno::Success,
2322                            type_: types::Eventtype::Clock,
2323                            fd_readwrite: types::EventFdReadwrite {
2324                                flags: types::Eventrwflags::empty(),
2325                                nbytes: 1,
2326                            },
2327                        },
2328                    )?;
2329                    return Ok(1);
2330                }
2331            }
2332        }
2333
2334        let subs = subs.as_array(nsubscriptions);
2335        let events = events.as_array(nsubscriptions);
2336
2337        let n = usize::try_from(nsubscriptions).unwrap_or(usize::MAX);
2338        let mut pollables = Vec::with_capacity(n);
2339        for sub in subs.iter() {
2340            let sub = memory.read(sub?)?;
2341            let p = match sub.u {
2342                types::SubscriptionU::Clock(types::SubscriptionClock {
2343                    id,
2344                    timeout,
2345                    flags,
2346                    ..
2347                }) => {
2348                    let absolute = flags.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME);
2349                    let (timeout, absolute) = match id {
2350                        types::Clockid::Monotonic => (timeout, absolute),
2351                        types::Clockid::Realtime if !absolute => (timeout, false),
2352                        types::Clockid::Realtime => {
2353                            let now = wall_clock::Host::now(&mut self.clocks())
2354                                .context("failed to call `wall_clock::now`")
2355                                .map_err(types::Error::trap)?;
2356
2357                            // Convert `timeout` to `Datetime` format.
2358                            let seconds = timeout / 1_000_000_000;
2359                            let nanoseconds = timeout % 1_000_000_000;
2360
2361                            let timeout = if now.seconds < seconds
2362                                || now.seconds == seconds
2363                                    && u64::from(now.nanoseconds) < nanoseconds
2364                            {
2365                                // `now` is less than `timeout`, which is expressible as u64,
2366                                // subtract the nanosecond counts directly
2367                                now.seconds * 1_000_000_000 + u64::from(now.nanoseconds) - timeout
2368                            } else {
2369                                0
2370                            };
2371                            (timeout, false)
2372                        }
2373                        _ => return Err(types::Errno::Inval.into()),
2374                    };
2375                    if absolute {
2376                        monotonic_clock::Host::subscribe_instant(&mut self.clocks(), timeout)
2377                            .context("failed to call `monotonic_clock::subscribe_instant`")
2378                            .map_err(types::Error::trap)?
2379                    } else {
2380                        monotonic_clock::Host::subscribe_duration(&mut self.clocks(), timeout)
2381                            .context("failed to call `monotonic_clock::subscribe_duration`")
2382                            .map_err(types::Error::trap)?
2383                    }
2384                }
2385                types::SubscriptionU::FdRead(types::SubscriptionFdReadwrite {
2386                    file_descriptor,
2387                }) => {
2388                    let stream = {
2389                        let t = self.transact()?;
2390                        let desc = t.get_descriptor(file_descriptor)?;
2391                        match desc {
2392                            Descriptor::Stdin { stream, .. } => stream.borrowed(),
2393                            Descriptor::File(File { fd, position, .. }) => {
2394                                let pos = position.load(Ordering::Relaxed);
2395                                let fd = fd.borrowed();
2396                                drop(t);
2397                                self.filesystem().read_via_stream(fd, pos)?
2398                            }
2399                            // TODO: Support sockets
2400                            _ => return Err(types::Errno::Badf.into()),
2401                        }
2402                    };
2403                    streams::HostInputStream::subscribe(&mut self.table, stream)
2404                        .context("failed to call `subscribe` on `input-stream`")
2405                        .map_err(types::Error::trap)?
2406                }
2407                types::SubscriptionU::FdWrite(types::SubscriptionFdReadwrite {
2408                    file_descriptor,
2409                }) => {
2410                    let stream = {
2411                        let t = self.transact()?;
2412                        let desc = t.get_descriptor(file_descriptor)?;
2413                        match desc {
2414                            Descriptor::Stdout { stream, .. }
2415                            | Descriptor::Stderr { stream, .. } => stream.borrowed(),
2416                            Descriptor::File(File {
2417                                fd,
2418                                position,
2419                                append,
2420                                ..
2421                            }) => {
2422                                let fd = fd.borrowed();
2423                                let position = position.clone();
2424                                let append = *append;
2425                                drop(t);
2426                                if append {
2427                                    self.filesystem().append_via_stream(fd)?
2428                                } else {
2429                                    let pos = position.load(Ordering::Relaxed);
2430                                    self.filesystem().write_via_stream(fd, pos)?
2431                                }
2432                            }
2433                            // TODO: Support sockets
2434                            _ => return Err(types::Errno::Badf.into()),
2435                        }
2436                    };
2437                    streams::HostOutputStream::subscribe(&mut self.table, stream)
2438                        .context("failed to call `subscribe` on `output-stream`")
2439                        .map_err(types::Error::trap)?
2440                }
2441            };
2442            pollables.push(p);
2443        }
2444        let ready: HashSet<_> = self
2445            .table
2446            .poll(pollables)
2447            .await
2448            .context("failed to call `poll-oneoff`")
2449            .map_err(types::Error::trap)?
2450            .into_iter()
2451            .collect();
2452
2453        let mut count: types::Size = 0;
2454        for (sub, event) in (0..)
2455            .zip(subs.iter())
2456            .filter_map(|(idx, sub)| ready.contains(&idx).then_some(sub))
2457            .zip(events.iter())
2458        {
2459            let sub = memory.read(sub?)?;
2460            let event = event?;
2461            let e = match sub.u {
2462                types::SubscriptionU::Clock(..) => types::Event {
2463                    userdata: sub.userdata,
2464                    error: types::Errno::Success,
2465                    type_: types::Eventtype::Clock,
2466                    fd_readwrite: types::EventFdReadwrite {
2467                        flags: types::Eventrwflags::empty(),
2468                        nbytes: 0,
2469                    },
2470                },
2471                types::SubscriptionU::FdRead(types::SubscriptionFdReadwrite {
2472                    file_descriptor,
2473                }) => {
2474                    let t = self.transact()?;
2475                    let desc = t.get_descriptor(file_descriptor)?;
2476                    match desc {
2477                        Descriptor::Stdin { .. } => types::Event {
2478                            userdata: sub.userdata,
2479                            error: types::Errno::Success,
2480                            type_: types::Eventtype::FdRead,
2481                            fd_readwrite: types::EventFdReadwrite {
2482                                flags: types::Eventrwflags::empty(),
2483                                nbytes: 1,
2484                            },
2485                        },
2486                        Descriptor::File(File { fd, position, .. }) => {
2487                            let fd = fd.borrowed();
2488                            let position = position.clone();
2489                            drop(t);
2490                            match self.filesystem().stat(fd).await? {
2491                                filesystem::DescriptorStat { size, .. } => {
2492                                    let pos = position.load(Ordering::Relaxed);
2493                                    let nbytes = size.saturating_sub(pos);
2494                                    types::Event {
2495                                        userdata: sub.userdata,
2496                                        error: types::Errno::Success,
2497                                        type_: types::Eventtype::FdRead,
2498                                        fd_readwrite: types::EventFdReadwrite {
2499                                            flags: if nbytes == 0 {
2500                                                types::Eventrwflags::FD_READWRITE_HANGUP
2501                                            } else {
2502                                                types::Eventrwflags::empty()
2503                                            },
2504                                            nbytes: 1,
2505                                        },
2506                                    }
2507                                }
2508                            }
2509                        }
2510                        // TODO: Support sockets
2511                        _ => return Err(types::Errno::Badf.into()),
2512                    }
2513                }
2514                types::SubscriptionU::FdWrite(types::SubscriptionFdReadwrite {
2515                    file_descriptor,
2516                }) => {
2517                    let t = self.transact()?;
2518                    let desc = t.get_descriptor(file_descriptor)?;
2519                    match desc {
2520                        Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => types::Event {
2521                            userdata: sub.userdata,
2522                            error: types::Errno::Success,
2523                            type_: types::Eventtype::FdWrite,
2524                            fd_readwrite: types::EventFdReadwrite {
2525                                flags: types::Eventrwflags::empty(),
2526                                nbytes: 1,
2527                            },
2528                        },
2529                        Descriptor::File(_) => types::Event {
2530                            userdata: sub.userdata,
2531                            error: types::Errno::Success,
2532                            type_: types::Eventtype::FdWrite,
2533                            fd_readwrite: types::EventFdReadwrite {
2534                                flags: types::Eventrwflags::empty(),
2535                                nbytes: 1,
2536                            },
2537                        },
2538                        // TODO: Support sockets
2539                        _ => return Err(types::Errno::Badf.into()),
2540                    }
2541                }
2542            };
2543            memory.write(event, e)?;
2544            count = count
2545                .checked_add(1)
2546                .ok_or_else(|| types::Error::from(types::Errno::Overflow))?
2547        }
2548        Ok(count)
2549    }
2550
2551    #[instrument(skip(self, _memory))]
2552    fn proc_exit(
2553        &mut self,
2554        _memory: &mut GuestMemory<'_>,
2555        status: types::Exitcode,
2556    ) -> anyhow::Error {
2557        // Check that the status is within WASI's range.
2558        if status >= 126 {
2559            return anyhow::Error::msg("exit with invalid exit status outside of [0..126)");
2560        }
2561        crate::I32Exit(status as i32).into()
2562    }
2563
2564    #[instrument(skip(self, _memory))]
2565    fn proc_raise(
2566        &mut self,
2567        _memory: &mut GuestMemory<'_>,
2568        _sig: types::Signal,
2569    ) -> Result<(), types::Error> {
2570        Err(types::Errno::Notsup.into())
2571    }
2572
2573    #[instrument(skip(self, _memory))]
2574    fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), types::Error> {
2575        // No such thing in preview 2. Intentionally left empty.
2576        Ok(())
2577    }
2578
2579    #[instrument(skip(self, memory))]
2580    fn random_get(
2581        &mut self,
2582        memory: &mut GuestMemory<'_>,
2583        buf: GuestPtr<u8>,
2584        buf_len: types::Size,
2585    ) -> Result<(), types::Error> {
2586        let rand = self
2587            .wasi
2588            .random
2589            .get_random_bytes(buf_len.into())
2590            .context("failed to call `get-random-bytes`")
2591            .map_err(types::Error::trap)?;
2592        write_bytes(memory, buf, &rand)?;
2593        Ok(())
2594    }
2595
2596    #[instrument(skip(self, _memory))]
2597    fn sock_accept(
2598        &mut self,
2599        _memory: &mut GuestMemory<'_>,
2600        fd: types::Fd,
2601        flags: types::Fdflags,
2602    ) -> Result<types::Fd, types::Error> {
2603        tracing::warn!("p1 sock_accept is not implemented");
2604        self.transact()?.get_descriptor(fd)?;
2605        Err(types::Errno::Notsock.into())
2606    }
2607
2608    #[instrument(skip(self, _memory))]
2609    fn sock_recv(
2610        &mut self,
2611        _memory: &mut GuestMemory<'_>,
2612        fd: types::Fd,
2613        ri_data: types::IovecArray,
2614        ri_flags: types::Riflags,
2615    ) -> Result<(types::Size, types::Roflags), types::Error> {
2616        tracing::warn!("p1 sock_recv is not implemented");
2617        self.transact()?.get_descriptor(fd)?;
2618        Err(types::Errno::Notsock.into())
2619    }
2620
2621    #[instrument(skip(self, _memory))]
2622    fn sock_send(
2623        &mut self,
2624        _memory: &mut GuestMemory<'_>,
2625        fd: types::Fd,
2626        si_data: types::CiovecArray,
2627        _si_flags: types::Siflags,
2628    ) -> Result<types::Size, types::Error> {
2629        tracing::warn!("p1 sock_send is not implemented");
2630        self.transact()?.get_descriptor(fd)?;
2631        Err(types::Errno::Notsock.into())
2632    }
2633
2634    #[instrument(skip(self, _memory))]
2635    fn sock_shutdown(
2636        &mut self,
2637        _memory: &mut GuestMemory<'_>,
2638        fd: types::Fd,
2639        how: types::Sdflags,
2640    ) -> Result<(), types::Error> {
2641        tracing::warn!("p1 sock_shutdown is not implemented");
2642        self.transact()?.get_descriptor(fd)?;
2643        Err(types::Errno::Notsock.into())
2644    }
2645}
2646
2647trait ResourceExt<T> {
2648    fn borrowed(&self) -> Resource<T>;
2649}
2650
2651impl<T: 'static> ResourceExt<T> for Resource<T> {
2652    fn borrowed(&self) -> Resource<T> {
2653        Resource::new_borrow(self.rep())
2654    }
2655}