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