wasmtime_wasi/
p1.rs

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