wasi_common/snapshots/
preview_0.rs

1use crate::file::TableFileExt;
2use crate::sched::{
3    subscription::{RwEventFlags, SubscriptionResult},
4    Poll, Userdata,
5};
6use crate::snapshots::preview_1::types as snapshot1_types;
7use crate::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
8use crate::{ErrorExt, WasiCtx};
9use cap_std::time::Duration;
10use std::collections::HashSet;
11use wiggle::{GuestMemory, GuestPtr};
12
13wiggle::from_witx!({
14    witx: ["$CARGO_MANIFEST_DIR/witx/preview0/wasi_unstable.witx"],
15    errors: { errno => trappable Error },
16    async: *,
17    wasmtime: false,
18});
19
20use types::Error;
21
22impl ErrorExt for Error {
23    fn not_found() -> Self {
24        types::Errno::Noent.into()
25    }
26    fn too_big() -> Self {
27        types::Errno::TooBig.into()
28    }
29    fn badf() -> Self {
30        types::Errno::Badf.into()
31    }
32    fn exist() -> Self {
33        types::Errno::Exist.into()
34    }
35    fn illegal_byte_sequence() -> Self {
36        types::Errno::Ilseq.into()
37    }
38    fn invalid_argument() -> Self {
39        types::Errno::Inval.into()
40    }
41    fn io() -> Self {
42        types::Errno::Io.into()
43    }
44    fn name_too_long() -> Self {
45        types::Errno::Nametoolong.into()
46    }
47    fn not_dir() -> Self {
48        types::Errno::Notdir.into()
49    }
50    fn not_supported() -> Self {
51        types::Errno::Notsup.into()
52    }
53    fn overflow() -> Self {
54        types::Errno::Overflow.into()
55    }
56    fn range() -> Self {
57        types::Errno::Range.into()
58    }
59    fn seek_pipe() -> Self {
60        types::Errno::Spipe.into()
61    }
62    fn perm() -> Self {
63        types::Errno::Perm.into()
64    }
65}
66
67impl wiggle::GuestErrorType for types::Errno {
68    fn success() -> Self {
69        Self::Success
70    }
71}
72
73impl From<wiggle::GuestError> for Error {
74    fn from(err: wiggle::GuestError) -> Error {
75        snapshot1_types::Error::from(err).into()
76    }
77}
78
79impl From<snapshot1_types::Error> for Error {
80    fn from(error: snapshot1_types::Error) -> Error {
81        match error.downcast() {
82            Ok(errno) => Error::from(types::Errno::from(errno)),
83            Err(trap) => Error::trap(trap),
84        }
85    }
86}
87
88impl From<std::num::TryFromIntError> for Error {
89    fn from(_err: std::num::TryFromIntError) -> Error {
90        types::Errno::Overflow.into()
91    }
92}
93
94// Type conversions
95// The vast majority of the types defined in `types` and `snapshot1_types` are identical. However,
96// since they are defined in separate places for mechanical (wiggle) reasons, we need to manually
97// define conversion functions between them.
98// Below we have defined these functions as they are needed.
99
100/// Fd is a newtype wrapper around u32. Unwrap and wrap it.
101impl From<types::Fd> for snapshot1_types::Fd {
102    fn from(fd: types::Fd) -> snapshot1_types::Fd {
103        u32::from(fd).into()
104    }
105}
106/// Fd is a newtype wrapper around u32. Unwrap and wrap it.
107impl From<snapshot1_types::Fd> for types::Fd {
108    fn from(fd: snapshot1_types::Fd) -> types::Fd {
109        u32::from(fd).into()
110    }
111}
112
113/// Trivial conversion between two c-style enums that have the exact same set of variants.
114/// Could we do something unsafe and not list all these variants out? Probably, but doing
115/// it this way doesn't bother me much. I copy-pasted the list of variants out of the
116/// rendered rustdocs.
117/// LLVM ought to compile these From impls into no-ops, inshallah
118macro_rules! convert_enum {
119    ($from:ty, $to:ty, $($var:ident),+) => {
120        impl From<$from> for $to {
121            fn from(e: $from) -> $to {
122                match e {
123                    $( <$from>::$var => <$to>::$var, )+
124                }
125            }
126        }
127    }
128}
129convert_enum!(
130    snapshot1_types::Errno,
131    types::Errno,
132    Success,
133    TooBig,
134    Acces,
135    Addrinuse,
136    Addrnotavail,
137    Afnosupport,
138    Again,
139    Already,
140    Badf,
141    Badmsg,
142    Busy,
143    Canceled,
144    Child,
145    Connaborted,
146    Connrefused,
147    Connreset,
148    Deadlk,
149    Destaddrreq,
150    Dom,
151    Dquot,
152    Exist,
153    Fault,
154    Fbig,
155    Hostunreach,
156    Idrm,
157    Ilseq,
158    Inprogress,
159    Intr,
160    Inval,
161    Io,
162    Isconn,
163    Isdir,
164    Loop,
165    Mfile,
166    Mlink,
167    Msgsize,
168    Multihop,
169    Nametoolong,
170    Netdown,
171    Netreset,
172    Netunreach,
173    Nfile,
174    Nobufs,
175    Nodev,
176    Noent,
177    Noexec,
178    Nolck,
179    Nolink,
180    Nomem,
181    Nomsg,
182    Noprotoopt,
183    Nospc,
184    Nosys,
185    Notconn,
186    Notdir,
187    Notempty,
188    Notrecoverable,
189    Notsock,
190    Notsup,
191    Notty,
192    Nxio,
193    Overflow,
194    Ownerdead,
195    Perm,
196    Pipe,
197    Proto,
198    Protonosupport,
199    Prototype,
200    Range,
201    Rofs,
202    Spipe,
203    Srch,
204    Stale,
205    Timedout,
206    Txtbsy,
207    Xdev,
208    Notcapable
209);
210convert_enum!(
211    types::Clockid,
212    snapshot1_types::Clockid,
213    Realtime,
214    Monotonic,
215    ProcessCputimeId,
216    ThreadCputimeId
217);
218
219convert_enum!(
220    types::Advice,
221    snapshot1_types::Advice,
222    Normal,
223    Sequential,
224    Random,
225    Willneed,
226    Dontneed,
227    Noreuse
228);
229convert_enum!(
230    snapshot1_types::Filetype,
231    types::Filetype,
232    Directory,
233    BlockDevice,
234    CharacterDevice,
235    RegularFile,
236    SocketDgram,
237    SocketStream,
238    SymbolicLink,
239    Unknown
240);
241convert_enum!(types::Whence, snapshot1_types::Whence, Cur, End, Set);
242
243/// Prestat isn't a c-style enum, its a union where the variant has a payload. Its the only one of
244/// those we need to convert, so write it by hand.
245impl From<snapshot1_types::Prestat> for types::Prestat {
246    fn from(p: snapshot1_types::Prestat) -> types::Prestat {
247        match p {
248            snapshot1_types::Prestat::Dir(d) => types::Prestat::Dir(d.into()),
249        }
250    }
251}
252
253/// Trivial conversion between two structs that have the exact same set of fields,
254/// with recursive descent into the field types.
255macro_rules! convert_struct {
256    ($from:ty, $to:path, $($field:ident),+) => {
257        impl From<$from> for $to {
258            fn from(e: $from) -> $to {
259                $to {
260                    $( $field: e.$field.into(), )+
261                }
262            }
263        }
264    }
265}
266
267convert_struct!(snapshot1_types::PrestatDir, types::PrestatDir, pr_name_len);
268convert_struct!(
269    snapshot1_types::Fdstat,
270    types::Fdstat,
271    fs_filetype,
272    fs_rights_base,
273    fs_rights_inheriting,
274    fs_flags
275);
276
277/// Snapshot1 Filestat is incompatible with Snapshot0 Filestat - the nlink
278/// field is u32 on this Filestat, and u64 on theirs. If you've got more than
279/// 2^32 links I don't know what to tell you
280impl From<snapshot1_types::Filestat> for types::Filestat {
281    fn from(f: snapshot1_types::Filestat) -> types::Filestat {
282        types::Filestat {
283            dev: f.dev.into(),
284            ino: f.ino.into(),
285            filetype: f.filetype.into(),
286            nlink: f.nlink.try_into().unwrap_or(u32::MAX),
287            size: f.size.into(),
288            atim: f.atim.into(),
289            mtim: f.mtim.into(),
290            ctim: f.ctim.into(),
291        }
292    }
293}
294
295/// Trivial conversion between two bitflags that have the exact same set of flags.
296macro_rules! convert_flags {
297    ($from:ty, $to:ty, $($flag:ident),+) => {
298        impl From<$from> for $to {
299            fn from(f: $from) -> $to {
300                let mut out = <$to>::empty();
301                $(
302                    if f.contains(<$from>::$flag) {
303                        out |= <$to>::$flag;
304                    }
305                )+
306                out
307            }
308        }
309    }
310}
311
312/// Need to convert in both directions? This saves listing out the flags twice
313macro_rules! convert_flags_bidirectional {
314    ($from:ty, $to:ty, $($flag:tt)*) => {
315        convert_flags!($from, $to, $($flag)*);
316        convert_flags!($to, $from, $($flag)*);
317    }
318}
319
320convert_flags_bidirectional!(
321    snapshot1_types::Fdflags,
322    types::Fdflags,
323    APPEND,
324    DSYNC,
325    NONBLOCK,
326    RSYNC,
327    SYNC
328);
329convert_flags!(
330    types::Lookupflags,
331    snapshot1_types::Lookupflags,
332    SYMLINK_FOLLOW
333);
334convert_flags!(
335    types::Fstflags,
336    snapshot1_types::Fstflags,
337    ATIM,
338    ATIM_NOW,
339    MTIM,
340    MTIM_NOW
341);
342convert_flags!(
343    types::Oflags,
344    snapshot1_types::Oflags,
345    CREAT,
346    DIRECTORY,
347    EXCL,
348    TRUNC
349);
350convert_flags_bidirectional!(
351    types::Rights,
352    snapshot1_types::Rights,
353    FD_DATASYNC,
354    FD_READ,
355    FD_SEEK,
356    FD_FDSTAT_SET_FLAGS,
357    FD_SYNC,
358    FD_TELL,
359    FD_WRITE,
360    FD_ADVISE,
361    FD_ALLOCATE,
362    PATH_CREATE_DIRECTORY,
363    PATH_CREATE_FILE,
364    PATH_LINK_SOURCE,
365    PATH_LINK_TARGET,
366    PATH_OPEN,
367    FD_READDIR,
368    PATH_READLINK,
369    PATH_RENAME_SOURCE,
370    PATH_RENAME_TARGET,
371    PATH_FILESTAT_GET,
372    PATH_FILESTAT_SET_SIZE,
373    PATH_FILESTAT_SET_TIMES,
374    FD_FILESTAT_GET,
375    FD_FILESTAT_SET_SIZE,
376    FD_FILESTAT_SET_TIMES,
377    PATH_SYMLINK,
378    PATH_REMOVE_DIRECTORY,
379    PATH_UNLINK_FILE,
380    POLL_FD_READWRITE,
381    SOCK_SHUTDOWN
382);
383
384// This implementation, wherever possible, delegates directly to the Snapshot1 implementation,
385// performing the no-op type conversions along the way.
386#[wiggle::async_trait]
387impl wasi_unstable::WasiUnstable for WasiCtx {
388    async fn args_get(
389        &mut self,
390        memory: &mut GuestMemory<'_>,
391        argv: GuestPtr<GuestPtr<u8>>,
392        argv_buf: GuestPtr<u8>,
393    ) -> Result<(), Error> {
394        Snapshot1::args_get(self, memory, argv, argv_buf).await?;
395        Ok(())
396    }
397
398    async fn args_sizes_get(
399        &mut self,
400        memory: &mut GuestMemory<'_>,
401    ) -> Result<(types::Size, types::Size), Error> {
402        let s = Snapshot1::args_sizes_get(self, memory).await?;
403        Ok(s)
404    }
405
406    async fn environ_get(
407        &mut self,
408        memory: &mut GuestMemory<'_>,
409        environ: GuestPtr<GuestPtr<u8>>,
410        environ_buf: GuestPtr<u8>,
411    ) -> Result<(), Error> {
412        Snapshot1::environ_get(self, memory, environ, environ_buf).await?;
413        Ok(())
414    }
415
416    async fn environ_sizes_get(
417        &mut self,
418        memory: &mut GuestMemory<'_>,
419    ) -> Result<(types::Size, types::Size), Error> {
420        let s = Snapshot1::environ_sizes_get(self, memory).await?;
421        Ok(s)
422    }
423
424    async fn clock_res_get(
425        &mut self,
426        memory: &mut GuestMemory<'_>,
427        id: types::Clockid,
428    ) -> Result<types::Timestamp, Error> {
429        let t = Snapshot1::clock_res_get(self, memory, id.into()).await?;
430        Ok(t)
431    }
432
433    async fn clock_time_get(
434        &mut self,
435        memory: &mut GuestMemory<'_>,
436        id: types::Clockid,
437        precision: types::Timestamp,
438    ) -> Result<types::Timestamp, Error> {
439        let t = Snapshot1::clock_time_get(self, memory, id.into(), precision).await?;
440        Ok(t)
441    }
442
443    async fn fd_advise(
444        &mut self,
445        memory: &mut GuestMemory<'_>,
446        fd: types::Fd,
447        offset: types::Filesize,
448        len: types::Filesize,
449        advice: types::Advice,
450    ) -> Result<(), Error> {
451        Snapshot1::fd_advise(self, memory, fd.into(), offset, len, advice.into()).await?;
452        Ok(())
453    }
454
455    async fn fd_allocate(
456        &mut self,
457        memory: &mut GuestMemory<'_>,
458        fd: types::Fd,
459        offset: types::Filesize,
460        len: types::Filesize,
461    ) -> Result<(), Error> {
462        Snapshot1::fd_allocate(self, memory, fd.into(), offset, len).await?;
463        Ok(())
464    }
465
466    async fn fd_close(&mut self, memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {
467        Snapshot1::fd_close(self, memory, fd.into()).await?;
468        Ok(())
469    }
470
471    async fn fd_datasync(
472        &mut self,
473        memory: &mut GuestMemory<'_>,
474        fd: types::Fd,
475    ) -> Result<(), Error> {
476        Snapshot1::fd_datasync(self, memory, fd.into()).await?;
477        Ok(())
478    }
479
480    async fn fd_fdstat_get(
481        &mut self,
482        memory: &mut GuestMemory<'_>,
483        fd: types::Fd,
484    ) -> Result<types::Fdstat, Error> {
485        Ok(Snapshot1::fd_fdstat_get(self, memory, fd.into())
486            .await?
487            .into())
488    }
489
490    async fn fd_fdstat_set_flags(
491        &mut self,
492        memory: &mut GuestMemory<'_>,
493        fd: types::Fd,
494        flags: types::Fdflags,
495    ) -> Result<(), Error> {
496        Snapshot1::fd_fdstat_set_flags(self, memory, fd.into(), flags.into()).await?;
497        Ok(())
498    }
499
500    async fn fd_fdstat_set_rights(
501        &mut self,
502        memory: &mut GuestMemory<'_>,
503        fd: types::Fd,
504        fs_rights_base: types::Rights,
505        fs_rights_inheriting: types::Rights,
506    ) -> Result<(), Error> {
507        Snapshot1::fd_fdstat_set_rights(
508            self,
509            memory,
510            fd.into(),
511            fs_rights_base.into(),
512            fs_rights_inheriting.into(),
513        )
514        .await?;
515        Ok(())
516    }
517
518    async fn fd_filestat_get(
519        &mut self,
520        memory: &mut GuestMemory<'_>,
521        fd: types::Fd,
522    ) -> Result<types::Filestat, Error> {
523        Ok(Snapshot1::fd_filestat_get(self, memory, fd.into())
524            .await?
525            .into())
526    }
527
528    async fn fd_filestat_set_size(
529        &mut self,
530        memory: &mut GuestMemory<'_>,
531        fd: types::Fd,
532        size: types::Filesize,
533    ) -> Result<(), Error> {
534        Snapshot1::fd_filestat_set_size(self, memory, fd.into(), size).await?;
535        Ok(())
536    }
537
538    async fn fd_filestat_set_times(
539        &mut self,
540        memory: &mut GuestMemory<'_>,
541        fd: types::Fd,
542        atim: types::Timestamp,
543        mtim: types::Timestamp,
544        fst_flags: types::Fstflags,
545    ) -> Result<(), Error> {
546        Snapshot1::fd_filestat_set_times(self, memory, fd.into(), atim, mtim, fst_flags.into())
547            .await?;
548        Ok(())
549    }
550
551    // NOTE on fd_read, fd_pread, fd_write, fd_pwrite implementations:
552    // these cast their pointers from preview0 vectors to preview1 vectors and
553    // this only works because the representation didn't change between preview0
554    // and preview1.
555
556    async fn fd_read(
557        &mut self,
558        memory: &mut GuestMemory<'_>,
559        fd: types::Fd,
560        iovs: types::IovecArray,
561    ) -> Result<types::Size, Error> {
562        Ok(Snapshot1::fd_read(self, memory, fd.into(), iovs.cast()).await?)
563    }
564
565    async fn fd_pread(
566        &mut self,
567        memory: &mut GuestMemory<'_>,
568        fd: types::Fd,
569        iovs: types::IovecArray,
570        offset: types::Filesize,
571    ) -> Result<types::Size, Error> {
572        Ok(Snapshot1::fd_pread(self, memory, fd.into(), iovs.cast(), offset).await?)
573    }
574
575    async fn fd_write(
576        &mut self,
577        memory: &mut GuestMemory<'_>,
578        fd: types::Fd,
579        ciovs: types::CiovecArray,
580    ) -> Result<types::Size, Error> {
581        Ok(Snapshot1::fd_write(self, memory, fd.into(), ciovs.cast()).await?)
582    }
583
584    async fn fd_pwrite(
585        &mut self,
586        memory: &mut GuestMemory<'_>,
587        fd: types::Fd,
588        ciovs: types::CiovecArray,
589        offset: types::Filesize,
590    ) -> Result<types::Size, Error> {
591        Ok(Snapshot1::fd_pwrite(self, memory, fd.into(), ciovs.cast(), offset).await?)
592    }
593
594    async fn fd_prestat_get(
595        &mut self,
596        memory: &mut GuestMemory<'_>,
597        fd: types::Fd,
598    ) -> Result<types::Prestat, Error> {
599        Ok(Snapshot1::fd_prestat_get(self, memory, fd.into())
600            .await?
601            .into())
602    }
603
604    async fn fd_prestat_dir_name(
605        &mut self,
606        memory: &mut GuestMemory<'_>,
607        fd: types::Fd,
608        path: GuestPtr<u8>,
609        path_max_len: types::Size,
610    ) -> Result<(), Error> {
611        Snapshot1::fd_prestat_dir_name(self, memory, fd.into(), path, path_max_len).await?;
612        Ok(())
613    }
614
615    async fn fd_renumber(
616        &mut self,
617        memory: &mut GuestMemory<'_>,
618        from: types::Fd,
619        to: types::Fd,
620    ) -> Result<(), Error> {
621        Snapshot1::fd_renumber(self, memory, from.into(), to.into()).await?;
622        Ok(())
623    }
624
625    async fn fd_seek(
626        &mut self,
627        memory: &mut GuestMemory<'_>,
628        fd: types::Fd,
629        offset: types::Filedelta,
630        whence: types::Whence,
631    ) -> Result<types::Filesize, Error> {
632        Ok(Snapshot1::fd_seek(self, memory, fd.into(), offset, whence.into()).await?)
633    }
634
635    async fn fd_sync(&mut self, memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {
636        Snapshot1::fd_sync(self, memory, fd.into()).await?;
637        Ok(())
638    }
639
640    async fn fd_tell(
641        &mut self,
642        memory: &mut GuestMemory<'_>,
643        fd: types::Fd,
644    ) -> Result<types::Filesize, Error> {
645        Ok(Snapshot1::fd_tell(self, memory, fd.into()).await?)
646    }
647
648    async fn fd_readdir(
649        &mut self,
650        memory: &mut GuestMemory<'_>,
651        fd: types::Fd,
652        buf: GuestPtr<u8>,
653        buf_len: types::Size,
654        cookie: types::Dircookie,
655    ) -> Result<types::Size, Error> {
656        Ok(Snapshot1::fd_readdir(self, memory, fd.into(), buf, buf_len, cookie).await?)
657    }
658
659    async fn path_create_directory(
660        &mut self,
661        memory: &mut GuestMemory<'_>,
662        dirfd: types::Fd,
663        path: GuestPtr<str>,
664    ) -> Result<(), Error> {
665        Snapshot1::path_create_directory(self, memory, dirfd.into(), path).await?;
666        Ok(())
667    }
668
669    async fn path_filestat_get(
670        &mut self,
671        memory: &mut GuestMemory<'_>,
672        dirfd: types::Fd,
673        flags: types::Lookupflags,
674        path: GuestPtr<str>,
675    ) -> Result<types::Filestat, Error> {
676        Ok(
677            Snapshot1::path_filestat_get(self, memory, dirfd.into(), flags.into(), path)
678                .await?
679                .into(),
680        )
681    }
682
683    async fn path_filestat_set_times(
684        &mut self,
685        memory: &mut GuestMemory<'_>,
686        dirfd: types::Fd,
687        flags: types::Lookupflags,
688        path: GuestPtr<str>,
689        atim: types::Timestamp,
690        mtim: types::Timestamp,
691        fst_flags: types::Fstflags,
692    ) -> Result<(), Error> {
693        Snapshot1::path_filestat_set_times(
694            self,
695            memory,
696            dirfd.into(),
697            flags.into(),
698            path,
699            atim,
700            mtim,
701            fst_flags.into(),
702        )
703        .await?;
704        Ok(())
705    }
706
707    async fn path_link(
708        &mut self,
709        memory: &mut GuestMemory<'_>,
710        src_fd: types::Fd,
711        src_flags: types::Lookupflags,
712        src_path: GuestPtr<str>,
713        target_fd: types::Fd,
714        target_path: GuestPtr<str>,
715    ) -> Result<(), Error> {
716        Snapshot1::path_link(
717            self,
718            memory,
719            src_fd.into(),
720            src_flags.into(),
721            src_path,
722            target_fd.into(),
723            target_path,
724        )
725        .await?;
726        Ok(())
727    }
728
729    async fn path_open(
730        &mut self,
731        memory: &mut GuestMemory<'_>,
732        dirfd: types::Fd,
733        dirflags: types::Lookupflags,
734        path: GuestPtr<str>,
735        oflags: types::Oflags,
736        fs_rights_base: types::Rights,
737        fs_rights_inheriting: types::Rights,
738        fdflags: types::Fdflags,
739    ) -> Result<types::Fd, Error> {
740        Ok(Snapshot1::path_open(
741            self,
742            memory,
743            dirfd.into(),
744            dirflags.into(),
745            path,
746            oflags.into(),
747            fs_rights_base.into(),
748            fs_rights_inheriting.into(),
749            fdflags.into(),
750        )
751        .await?
752        .into())
753    }
754
755    async fn path_readlink(
756        &mut self,
757        memory: &mut GuestMemory<'_>,
758        dirfd: types::Fd,
759        path: GuestPtr<str>,
760        buf: GuestPtr<u8>,
761        buf_len: types::Size,
762    ) -> Result<types::Size, Error> {
763        Ok(Snapshot1::path_readlink(self, memory, dirfd.into(), path, buf, buf_len).await?)
764    }
765
766    async fn path_remove_directory(
767        &mut self,
768        memory: &mut GuestMemory<'_>,
769        dirfd: types::Fd,
770        path: GuestPtr<str>,
771    ) -> Result<(), Error> {
772        Snapshot1::path_remove_directory(self, memory, dirfd.into(), path).await?;
773        Ok(())
774    }
775
776    async fn path_rename(
777        &mut self,
778        memory: &mut GuestMemory<'_>,
779        src_fd: types::Fd,
780        src_path: GuestPtr<str>,
781        dest_fd: types::Fd,
782        dest_path: GuestPtr<str>,
783    ) -> Result<(), Error> {
784        Snapshot1::path_rename(
785            self,
786            memory,
787            src_fd.into(),
788            src_path,
789            dest_fd.into(),
790            dest_path,
791        )
792        .await?;
793        Ok(())
794    }
795
796    async fn path_symlink(
797        &mut self,
798        memory: &mut GuestMemory<'_>,
799        src_path: GuestPtr<str>,
800        dirfd: types::Fd,
801        dest_path: GuestPtr<str>,
802    ) -> Result<(), Error> {
803        Snapshot1::path_symlink(self, memory, src_path, dirfd.into(), dest_path).await?;
804        Ok(())
805    }
806
807    async fn path_unlink_file(
808        &mut self,
809        memory: &mut GuestMemory<'_>,
810        dirfd: types::Fd,
811        path: GuestPtr<str>,
812    ) -> Result<(), Error> {
813        Snapshot1::path_unlink_file(self, memory, dirfd.into(), path).await?;
814        Ok(())
815    }
816
817    // NOTE on poll_oneoff implementation:
818    // Like fd_write and friends, the arguments and return values are behind GuestPtrs,
819    // so they are not values we can convert and pass to the poll_oneoff in Snapshot1.
820    // Instead, we have copied the implementation of these functions from the Snapshot1 code.
821    // The implementations are identical, but the `types::` in scope locally is different.
822    // The bodies of these functions is mostly about converting the GuestPtr and types::-based
823    // representation to use the Poll abstraction.
824    async fn poll_oneoff(
825        &mut self,
826        memory: &mut GuestMemory<'_>,
827        subs: GuestPtr<types::Subscription>,
828        events: GuestPtr<types::Event>,
829        nsubscriptions: types::Size,
830    ) -> Result<types::Size, Error> {
831        if nsubscriptions == 0 {
832            return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));
833        }
834
835        // Special-case a `poll_oneoff` which is just sleeping on a single
836        // relative timer event, such as what WASI libc uses to implement sleep
837        // functions. This supports all clock IDs, because POSIX says that
838        // `clock_settime` doesn't effect relative sleeps.
839        if nsubscriptions == 1 {
840            let sub = memory.read(subs)?;
841            if let types::SubscriptionU::Clock(clocksub) = sub.u {
842                if !clocksub
843                    .flags
844                    .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
845                {
846                    self.sched
847                        .sleep(Duration::from_nanos(clocksub.timeout))
848                        .await?;
849                    memory.write(
850                        events,
851                        types::Event {
852                            userdata: sub.userdata,
853                            error: types::Errno::Success,
854                            type_: types::Eventtype::Clock,
855                            fd_readwrite: fd_readwrite_empty(),
856                        },
857                    )?;
858                    return Ok(1);
859                }
860            }
861        }
862
863        let table = &self.table;
864        let mut sub_fds: HashSet<types::Fd> = HashSet::new();
865        // We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside
866        let mut reads: Vec<(u32, Userdata)> = Vec::new();
867        let mut writes: Vec<(u32, Userdata)> = Vec::new();
868        let mut poll = Poll::new();
869
870        let subs = subs.as_array(nsubscriptions);
871        for sub_elem in subs.iter() {
872            let sub_ptr = sub_elem?;
873            let sub = memory.read(sub_ptr)?;
874            match sub.u {
875                types::SubscriptionU::Clock(clocksub) => match clocksub.id {
876                    types::Clockid::Monotonic => {
877                        let clock = self.clocks.monotonic()?;
878                        let precision = Duration::from_nanos(clocksub.precision);
879                        let duration = Duration::from_nanos(clocksub.timeout);
880                        let start = if clocksub
881                            .flags
882                            .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
883                        {
884                            clock.creation_time
885                        } else {
886                            clock.abs_clock.now(precision)
887                        };
888                        let deadline = start
889                            .checked_add(duration)
890                            .ok_or_else(|| Error::overflow().context("deadline"))?;
891                        poll.subscribe_monotonic_clock(
892                            &*clock.abs_clock,
893                            deadline,
894                            precision,
895                            sub.userdata.into(),
896                        )
897                    }
898                    _ => Err(Error::invalid_argument()
899                        .context("timer subscriptions only support monotonic timer"))?,
900                },
901                types::SubscriptionU::FdRead(readsub) => {
902                    let fd = readsub.file_descriptor;
903                    if sub_fds.contains(&fd) {
904                        return Err(Error::invalid_argument()
905                            .context("Fd can be subscribed to at most once per poll"));
906                    } else {
907                        sub_fds.insert(fd);
908                    }
909                    table.get_file(u32::from(fd))?;
910                    reads.push((u32::from(fd), sub.userdata.into()));
911                }
912                types::SubscriptionU::FdWrite(writesub) => {
913                    let fd = writesub.file_descriptor;
914                    if sub_fds.contains(&fd) {
915                        return Err(Error::invalid_argument()
916                            .context("Fd can be subscribed to at most once per poll"));
917                    } else {
918                        sub_fds.insert(fd);
919                    }
920                    table.get_file(u32::from(fd))?;
921                    writes.push((u32::from(fd), sub.userdata.into()));
922                }
923            }
924        }
925
926        self.sched.poll_oneoff(&mut poll).await?;
927
928        let results = poll.results();
929        let num_results = results.len();
930        assert!(
931            num_results <= nsubscriptions as usize,
932            "results exceeds subscriptions"
933        );
934        let events = events.as_array(
935            num_results
936                .try_into()
937                .expect("not greater than nsubscriptions"),
938        );
939        for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) {
940            let event_ptr = event_elem?;
941            let userdata: types::Userdata = userdata.into();
942            memory.write(
943                event_ptr,
944                match result {
945                    SubscriptionResult::Read(r) => {
946                        let type_ = types::Eventtype::FdRead;
947                        match r {
948                            Ok((nbytes, flags)) => types::Event {
949                                userdata,
950                                error: types::Errno::Success,
951                                type_,
952                                fd_readwrite: types::EventFdReadwrite {
953                                    nbytes,
954                                    flags: types::Eventrwflags::from(&flags),
955                                },
956                            },
957                            Err(e) => types::Event {
958                                userdata,
959                                error: types::Errno::from(e.downcast().map_err(Error::trap)?),
960                                type_,
961                                fd_readwrite: fd_readwrite_empty(),
962                            },
963                        }
964                    }
965                    SubscriptionResult::Write(r) => {
966                        let type_ = types::Eventtype::FdWrite;
967                        match r {
968                            Ok((nbytes, flags)) => types::Event {
969                                userdata,
970                                error: types::Errno::Success,
971                                type_,
972                                fd_readwrite: types::EventFdReadwrite {
973                                    nbytes,
974                                    flags: types::Eventrwflags::from(&flags),
975                                },
976                            },
977                            Err(e) => types::Event {
978                                userdata,
979                                error: types::Errno::from(e.downcast().map_err(Error::trap)?),
980                                type_,
981                                fd_readwrite: fd_readwrite_empty(),
982                            },
983                        }
984                    }
985                    SubscriptionResult::MonotonicClock(r) => {
986                        let type_ = types::Eventtype::Clock;
987                        types::Event {
988                            userdata,
989                            error: match r {
990                                Ok(()) => types::Errno::Success,
991                                Err(e) => types::Errno::from(e.downcast().map_err(Error::trap)?),
992                            },
993                            type_,
994                            fd_readwrite: fd_readwrite_empty(),
995                        }
996                    }
997                },
998            )?;
999        }
1000
1001        Ok(num_results.try_into().expect("results fit into memory"))
1002    }
1003
1004    async fn proc_exit(
1005        &mut self,
1006        memory: &mut GuestMemory<'_>,
1007        status: types::Exitcode,
1008    ) -> anyhow::Error {
1009        Snapshot1::proc_exit(self, memory, status).await
1010    }
1011
1012    async fn proc_raise(
1013        &mut self,
1014        _memory: &mut GuestMemory<'_>,
1015        _sig: types::Signal,
1016    ) -> Result<(), Error> {
1017        Err(Error::trap(anyhow::Error::msg("proc_raise unsupported")))
1018    }
1019
1020    async fn sched_yield(&mut self, memory: &mut GuestMemory<'_>) -> Result<(), Error> {
1021        Snapshot1::sched_yield(self, memory).await?;
1022        Ok(())
1023    }
1024
1025    async fn random_get(
1026        &mut self,
1027        memory: &mut GuestMemory<'_>,
1028        buf: GuestPtr<u8>,
1029        buf_len: types::Size,
1030    ) -> Result<(), Error> {
1031        Snapshot1::random_get(self, memory, buf, buf_len).await?;
1032        Ok(())
1033    }
1034
1035    async fn sock_recv(
1036        &mut self,
1037        _memory: &mut GuestMemory<'_>,
1038        _fd: types::Fd,
1039        _ri_data: types::IovecArray,
1040        _ri_flags: types::Riflags,
1041    ) -> Result<(types::Size, types::Roflags), Error> {
1042        Err(Error::trap(anyhow::Error::msg("sock_recv unsupported")))
1043    }
1044
1045    async fn sock_send(
1046        &mut self,
1047        _memory: &mut GuestMemory<'_>,
1048        _fd: types::Fd,
1049        _si_data: types::CiovecArray,
1050        _si_flags: types::Siflags,
1051    ) -> Result<types::Size, Error> {
1052        Err(Error::trap(anyhow::Error::msg("sock_send unsupported")))
1053    }
1054
1055    async fn sock_shutdown(
1056        &mut self,
1057        _memory: &mut GuestMemory<'_>,
1058        _fd: types::Fd,
1059        _how: types::Sdflags,
1060    ) -> Result<(), Error> {
1061        Err(Error::trap(anyhow::Error::msg("sock_shutdown unsupported")))
1062    }
1063}
1064
1065impl From<&RwEventFlags> for types::Eventrwflags {
1066    fn from(flags: &RwEventFlags) -> types::Eventrwflags {
1067        let mut out = types::Eventrwflags::empty();
1068        if flags.contains(RwEventFlags::HANGUP) {
1069            out = out | types::Eventrwflags::FD_READWRITE_HANGUP;
1070        }
1071        out
1072    }
1073}
1074
1075fn fd_readwrite_empty() -> types::EventFdReadwrite {
1076    types::EventFdReadwrite {
1077        nbytes: 0,
1078        flags: types::Eventrwflags::empty(),
1079    }
1080}