Skip to main content

wasmtime_wasi/
p1.rs

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