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