Skip to main content

wasmtime_wasi/
p0.rs

1//! Bindings for WASIp0 aka Preview 0 aka `wasi_unstable`.
2//!
3//! This module is purely here for backwards compatibility in the Wasmtime CLI.
4//! You probably want to use [`p1`](crate::p1) instead.
5
6use crate::p0::types::Error;
7use crate::p1::WasiP1Ctx;
8use crate::p1::types as snapshot1_types;
9use crate::p1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
10use wiggle::{GuestError, GuestMemory, GuestPtr};
11
12pub fn add_to_linker_async<T: Send + 'static>(
13    linker: &mut wasmtime::Linker<T>,
14    f: impl Fn(&mut T) -> &mut WasiP1Ctx + Copy + Send + Sync + 'static,
15) -> wasmtime::Result<()> {
16    wasi_unstable::add_to_linker(linker, f)
17}
18
19pub fn add_to_linker_sync<T: Send + 'static>(
20    linker: &mut wasmtime::Linker<T>,
21    f: impl Fn(&mut T) -> &mut WasiP1Ctx + Copy + Send + Sync + 'static,
22) -> wasmtime::Result<()> {
23    sync::add_wasi_unstable_to_linker(linker, f)
24}
25
26wiggle::from_witx!({
27    witx: ["witx/p0/wasi_unstable.witx"],
28    async: {
29        wasi_unstable::{
30            fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size,
31            fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write,
32            fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get,
33            path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory,
34            path_rename, path_symlink, path_unlink_file
35        }
36    },
37    errors: { errno => trappable Error },
38});
39
40mod sync {
41    use std::future::Future;
42    use wasmtime::Result;
43
44    wiggle::wasmtime_integration!({
45        witx: ["witx/p0/wasi_unstable.witx"],
46        target: super,
47        block_on[in_tokio]: {
48            wasi_unstable::{
49                fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size,
50                fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write,
51                fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get,
52                path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory,
53                path_rename, path_symlink, path_unlink_file
54            }
55        },
56        errors: { errno => trappable Error },
57    });
58
59    // Small wrapper around `in_tokio` to add a `Result` layer which is always
60    // `Ok`
61    fn in_tokio<F: Future>(future: F) -> Result<F::Output> {
62        Ok(crate::runtime::in_tokio(future))
63    }
64}
65
66impl wiggle::GuestErrorType for types::Errno {
67    fn success() -> Self {
68        Self::Success
69    }
70}
71
72impl<T: Snapshot1 + Send> wasi_unstable::WasiUnstable for T {
73    fn set_hostcall_fuel(&mut self, fuel: usize) {
74        Snapshot1::set_hostcall_fuel(self, fuel)
75    }
76    fn args_get(
77        &mut self,
78        memory: &mut GuestMemory<'_>,
79        argv: GuestPtr<GuestPtr<u8>>,
80        argv_buf: GuestPtr<u8>,
81    ) -> Result<(), Error> {
82        Snapshot1::args_get(self, memory, argv, argv_buf)?;
83        Ok(())
84    }
85
86    fn args_sizes_get(
87        &mut self,
88        memory: &mut GuestMemory<'_>,
89    ) -> Result<(types::Size, types::Size), Error> {
90        let s = Snapshot1::args_sizes_get(self, memory)?;
91        Ok(s)
92    }
93
94    fn environ_get(
95        &mut self,
96        memory: &mut GuestMemory<'_>,
97        environ: GuestPtr<GuestPtr<u8>>,
98        environ_buf: GuestPtr<u8>,
99    ) -> Result<(), Error> {
100        Snapshot1::environ_get(self, memory, environ, environ_buf)?;
101        Ok(())
102    }
103
104    fn environ_sizes_get(
105        &mut self,
106        memory: &mut GuestMemory<'_>,
107    ) -> Result<(types::Size, types::Size), Error> {
108        let s = Snapshot1::environ_sizes_get(self, memory)?;
109        Ok(s)
110    }
111
112    fn clock_res_get(
113        &mut self,
114        memory: &mut GuestMemory<'_>,
115        id: types::Clockid,
116    ) -> Result<types::Timestamp, Error> {
117        let t = Snapshot1::clock_res_get(self, memory, id.into())?;
118        Ok(t)
119    }
120
121    fn clock_time_get(
122        &mut self,
123        memory: &mut GuestMemory<'_>,
124        id: types::Clockid,
125        precision: types::Timestamp,
126    ) -> Result<types::Timestamp, Error> {
127        let t = Snapshot1::clock_time_get(self, memory, id.into(), precision)?;
128        Ok(t)
129    }
130
131    async fn fd_advise(
132        &mut self,
133        memory: &mut GuestMemory<'_>,
134        fd: types::Fd,
135        offset: types::Filesize,
136        len: types::Filesize,
137        advice: types::Advice,
138    ) -> Result<(), Error> {
139        Snapshot1::fd_advise(self, memory, fd.into(), offset, len, advice.into()).await?;
140        Ok(())
141    }
142
143    fn fd_allocate(
144        &mut self,
145        memory: &mut GuestMemory<'_>,
146        fd: types::Fd,
147        offset: types::Filesize,
148        len: types::Filesize,
149    ) -> Result<(), Error> {
150        Snapshot1::fd_allocate(self, memory, fd.into(), offset, len)?;
151        Ok(())
152    }
153
154    async fn fd_close(&mut self, memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {
155        Snapshot1::fd_close(self, memory, fd.into()).await?;
156        Ok(())
157    }
158
159    async fn fd_datasync(
160        &mut self,
161        memory: &mut GuestMemory<'_>,
162        fd: types::Fd,
163    ) -> Result<(), Error> {
164        Snapshot1::fd_datasync(self, memory, fd.into()).await?;
165        Ok(())
166    }
167
168    async fn fd_fdstat_get(
169        &mut self,
170        memory: &mut GuestMemory<'_>,
171        fd: types::Fd,
172    ) -> Result<types::Fdstat, Error> {
173        Ok(Snapshot1::fd_fdstat_get(self, memory, fd.into())
174            .await?
175            .into())
176    }
177
178    fn fd_fdstat_set_flags(
179        &mut self,
180        memory: &mut GuestMemory<'_>,
181        fd: types::Fd,
182        flags: types::Fdflags,
183    ) -> Result<(), Error> {
184        Snapshot1::fd_fdstat_set_flags(self, memory, fd.into(), flags.into())?;
185        Ok(())
186    }
187
188    fn fd_fdstat_set_rights(
189        &mut self,
190        memory: &mut GuestMemory<'_>,
191        fd: types::Fd,
192        fs_rights_base: types::Rights,
193        fs_rights_inheriting: types::Rights,
194    ) -> Result<(), Error> {
195        Snapshot1::fd_fdstat_set_rights(
196            self,
197            memory,
198            fd.into(),
199            fs_rights_base.into(),
200            fs_rights_inheriting.into(),
201        )?;
202        Ok(())
203    }
204
205    async fn fd_filestat_get(
206        &mut self,
207        memory: &mut GuestMemory<'_>,
208        fd: types::Fd,
209    ) -> Result<types::Filestat, Error> {
210        Ok(Snapshot1::fd_filestat_get(self, memory, fd.into())
211            .await?
212            .into())
213    }
214
215    async fn fd_filestat_set_size(
216        &mut self,
217        memory: &mut GuestMemory<'_>,
218        fd: types::Fd,
219        size: types::Filesize,
220    ) -> Result<(), Error> {
221        Snapshot1::fd_filestat_set_size(self, memory, fd.into(), size).await?;
222        Ok(())
223    }
224
225    async fn fd_filestat_set_times(
226        &mut self,
227        memory: &mut GuestMemory<'_>,
228        fd: types::Fd,
229        atim: types::Timestamp,
230        mtim: types::Timestamp,
231        fst_flags: types::Fstflags,
232    ) -> Result<(), Error> {
233        Snapshot1::fd_filestat_set_times(self, memory, fd.into(), atim, mtim, fst_flags.into())
234            .await?;
235        Ok(())
236    }
237
238    async fn fd_read(
239        &mut self,
240        memory: &mut GuestMemory<'_>,
241        fd: types::Fd,
242        iovs: types::IovecArray,
243    ) -> Result<types::Size, Error> {
244        assert_iovec_array_same();
245        let result = Snapshot1::fd_read(self, memory, fd.into(), iovs.cast()).await?;
246        Ok(result)
247    }
248
249    async fn fd_pread(
250        &mut self,
251        memory: &mut GuestMemory<'_>,
252        fd: types::Fd,
253        iovs: types::IovecArray,
254        offset: types::Filesize,
255    ) -> Result<types::Size, Error> {
256        assert_iovec_array_same();
257        let result = Snapshot1::fd_pread(self, memory, fd.into(), iovs.cast(), offset).await?;
258        Ok(result)
259    }
260
261    async fn fd_write(
262        &mut self,
263        memory: &mut GuestMemory<'_>,
264        fd: types::Fd,
265        ciovs: types::CiovecArray,
266    ) -> Result<types::Size, Error> {
267        assert_ciovec_array_same();
268        let result = Snapshot1::fd_write(self, memory, fd.into(), ciovs.cast()).await?;
269        Ok(result)
270    }
271
272    async fn fd_pwrite(
273        &mut self,
274        memory: &mut GuestMemory<'_>,
275        fd: types::Fd,
276        ciovs: types::CiovecArray,
277        offset: types::Filesize,
278    ) -> Result<types::Size, Error> {
279        assert_ciovec_array_same();
280        let result = Snapshot1::fd_pwrite(self, memory, fd.into(), ciovs.cast(), offset).await?;
281        Ok(result)
282    }
283
284    fn fd_prestat_get(
285        &mut self,
286        memory: &mut GuestMemory<'_>,
287        fd: types::Fd,
288    ) -> Result<types::Prestat, Error> {
289        Ok(Snapshot1::fd_prestat_get(self, memory, fd.into())?.into())
290    }
291
292    fn fd_prestat_dir_name(
293        &mut self,
294        memory: &mut GuestMemory<'_>,
295        fd: types::Fd,
296        path: GuestPtr<u8>,
297        path_max_len: types::Size,
298    ) -> Result<(), Error> {
299        Snapshot1::fd_prestat_dir_name(self, memory, fd.into(), path, path_max_len)?;
300        Ok(())
301    }
302
303    fn fd_renumber(
304        &mut self,
305        memory: &mut GuestMemory<'_>,
306        from: types::Fd,
307        to: types::Fd,
308    ) -> Result<(), Error> {
309        Snapshot1::fd_renumber(self, memory, from.into(), to.into())?;
310        Ok(())
311    }
312
313    async fn fd_seek(
314        &mut self,
315        memory: &mut GuestMemory<'_>,
316        fd: types::Fd,
317        offset: types::Filedelta,
318        whence: types::Whence,
319    ) -> Result<types::Filesize, Error> {
320        Ok(Snapshot1::fd_seek(self, memory, fd.into(), offset, whence.into()).await?)
321    }
322
323    async fn fd_sync(&mut self, memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {
324        Snapshot1::fd_sync(self, memory, fd.into()).await?;
325        Ok(())
326    }
327
328    fn fd_tell(
329        &mut self,
330        memory: &mut GuestMemory<'_>,
331        fd: types::Fd,
332    ) -> Result<types::Filesize, Error> {
333        Ok(Snapshot1::fd_tell(self, memory, fd.into())?)
334    }
335
336    async fn fd_readdir(
337        &mut self,
338        memory: &mut GuestMemory<'_>,
339        fd: types::Fd,
340        buf: GuestPtr<u8>,
341        buf_len: types::Size,
342        cookie: types::Dircookie,
343    ) -> Result<types::Size, Error> {
344        Ok(Snapshot1::fd_readdir(self, memory, fd.into(), buf, buf_len, cookie).await?)
345    }
346
347    async fn path_create_directory(
348        &mut self,
349        memory: &mut GuestMemory<'_>,
350        dirfd: types::Fd,
351        path: GuestPtr<str>,
352    ) -> Result<(), Error> {
353        Snapshot1::path_create_directory(self, memory, dirfd.into(), path).await?;
354        Ok(())
355    }
356
357    async fn path_filestat_get(
358        &mut self,
359        memory: &mut GuestMemory<'_>,
360        dirfd: types::Fd,
361        flags: types::Lookupflags,
362        path: GuestPtr<str>,
363    ) -> Result<types::Filestat, Error> {
364        Ok(
365            Snapshot1::path_filestat_get(self, memory, dirfd.into(), flags.into(), path)
366                .await?
367                .into(),
368        )
369    }
370
371    async fn path_filestat_set_times(
372        &mut self,
373        memory: &mut GuestMemory<'_>,
374        dirfd: types::Fd,
375        flags: types::Lookupflags,
376        path: GuestPtr<str>,
377        atim: types::Timestamp,
378        mtim: types::Timestamp,
379        fst_flags: types::Fstflags,
380    ) -> Result<(), Error> {
381        Snapshot1::path_filestat_set_times(
382            self,
383            memory,
384            dirfd.into(),
385            flags.into(),
386            path,
387            atim,
388            mtim,
389            fst_flags.into(),
390        )
391        .await?;
392        Ok(())
393    }
394
395    async fn path_link(
396        &mut self,
397        memory: &mut GuestMemory<'_>,
398        src_fd: types::Fd,
399        src_flags: types::Lookupflags,
400        src_path: GuestPtr<str>,
401        target_fd: types::Fd,
402        target_path: GuestPtr<str>,
403    ) -> Result<(), Error> {
404        Snapshot1::path_link(
405            self,
406            memory,
407            src_fd.into(),
408            src_flags.into(),
409            src_path,
410            target_fd.into(),
411            target_path,
412        )
413        .await?;
414        Ok(())
415    }
416
417    async fn path_open(
418        &mut self,
419        memory: &mut GuestMemory<'_>,
420        dirfd: types::Fd,
421        dirflags: types::Lookupflags,
422        path: GuestPtr<str>,
423        oflags: types::Oflags,
424        fs_rights_base: types::Rights,
425        fs_rights_inheriting: types::Rights,
426        fdflags: types::Fdflags,
427    ) -> Result<types::Fd, Error> {
428        Ok(Snapshot1::path_open(
429            self,
430            memory,
431            dirfd.into(),
432            dirflags.into(),
433            path,
434            oflags.into(),
435            fs_rights_base.into(),
436            fs_rights_inheriting.into(),
437            fdflags.into(),
438        )
439        .await?
440        .into())
441    }
442
443    async fn path_readlink(
444        &mut self,
445        memory: &mut GuestMemory<'_>,
446        dirfd: types::Fd,
447        path: GuestPtr<str>,
448        buf: GuestPtr<u8>,
449        buf_len: types::Size,
450    ) -> Result<types::Size, Error> {
451        Ok(Snapshot1::path_readlink(self, memory, dirfd.into(), path, buf, buf_len).await?)
452    }
453
454    async fn path_remove_directory(
455        &mut self,
456        memory: &mut GuestMemory<'_>,
457        dirfd: types::Fd,
458        path: GuestPtr<str>,
459    ) -> Result<(), Error> {
460        Snapshot1::path_remove_directory(self, memory, dirfd.into(), path).await?;
461        Ok(())
462    }
463
464    async fn path_rename(
465        &mut self,
466        memory: &mut GuestMemory<'_>,
467        src_fd: types::Fd,
468        src_path: GuestPtr<str>,
469        dest_fd: types::Fd,
470        dest_path: GuestPtr<str>,
471    ) -> Result<(), Error> {
472        Snapshot1::path_rename(
473            self,
474            memory,
475            src_fd.into(),
476            src_path,
477            dest_fd.into(),
478            dest_path,
479        )
480        .await?;
481        Ok(())
482    }
483
484    async fn path_symlink(
485        &mut self,
486        memory: &mut GuestMemory<'_>,
487        src_path: GuestPtr<str>,
488        dirfd: types::Fd,
489        dest_path: GuestPtr<str>,
490    ) -> Result<(), Error> {
491        Snapshot1::path_symlink(self, memory, src_path, dirfd.into(), dest_path).await?;
492        Ok(())
493    }
494
495    async fn path_unlink_file(
496        &mut self,
497        memory: &mut GuestMemory<'_>,
498        dirfd: types::Fd,
499        path: GuestPtr<str>,
500    ) -> Result<(), Error> {
501        Snapshot1::path_unlink_file(self, memory, dirfd.into(), path).await?;
502        Ok(())
503    }
504
505    // The representation of `SubscriptionClock` is different in p0 and
506    // p1 so a bit of a hack is employed here. The change was to remove a
507    // field from `SubscriptionClock` so to implement this without copying too
508    // much the `subs` field is overwritten with p1-compatible structures
509    // and then the p1 implementation is used. Before returning though
510    // the old values are restored to pretend like we didn't overwrite them.
511    //
512    // Surely no one would pass overlapping pointers to this API right?
513    async fn poll_oneoff(
514        &mut self,
515        memory: &mut GuestMemory<'_>,
516        subs: GuestPtr<types::Subscription>,
517        events: GuestPtr<types::Event>,
518        nsubscriptions: types::Size,
519    ) -> Result<types::Size, Error> {
520        let subs_array = subs.as_array(nsubscriptions);
521        let mut old_subs = Vec::new();
522        for slot in subs_array.iter() {
523            let slot = slot?;
524            let sub = memory.read(slot)?;
525            old_subs.push(sub.clone());
526            memory.write(
527                slot.cast(),
528                snapshot1_types::Subscription {
529                    userdata: sub.userdata,
530                    u: match sub.u {
531                        types::SubscriptionU::Clock(c) => {
532                            snapshot1_types::SubscriptionU::Clock(c.into())
533                        }
534                        types::SubscriptionU::FdRead(c) => {
535                            snapshot1_types::SubscriptionU::FdRead(c.into())
536                        }
537                        types::SubscriptionU::FdWrite(c) => {
538                            snapshot1_types::SubscriptionU::FdWrite(c.into())
539                        }
540                    },
541                },
542            )?;
543        }
544        let ret = Snapshot1::poll_oneoff(self, memory, subs.cast(), events.cast(), nsubscriptions)
545            .await?;
546        for (sub, slot) in old_subs.into_iter().zip(subs_array.iter()) {
547            memory.write(slot?, sub)?;
548        }
549        Ok(ret)
550    }
551
552    fn proc_exit(
553        &mut self,
554        memory: &mut GuestMemory<'_>,
555        status: types::Exitcode,
556    ) -> wasmtime::Error {
557        Snapshot1::proc_exit(self, memory, status)
558    }
559
560    fn proc_raise(
561        &mut self,
562        memory: &mut GuestMemory<'_>,
563        sig: types::Signal,
564    ) -> Result<(), Error> {
565        Snapshot1::proc_raise(self, memory, sig.into())?;
566        Ok(())
567    }
568
569    fn sched_yield(&mut self, memory: &mut GuestMemory<'_>) -> Result<(), Error> {
570        Snapshot1::sched_yield(self, memory)?;
571        Ok(())
572    }
573
574    fn random_get(
575        &mut self,
576        memory: &mut GuestMemory<'_>,
577        buf: GuestPtr<u8>,
578        buf_len: types::Size,
579    ) -> Result<(), Error> {
580        Snapshot1::random_get(self, memory, buf, buf_len)?;
581        Ok(())
582    }
583
584    fn sock_recv(
585        &mut self,
586        _memory: &mut GuestMemory<'_>,
587        _fd: types::Fd,
588        _ri_data: types::IovecArray,
589        _ri_flags: types::Riflags,
590    ) -> Result<(types::Size, types::Roflags), Error> {
591        Err(Error::trap(wasmtime::Error::msg("sock_recv unsupported")))
592    }
593
594    fn sock_send(
595        &mut self,
596        _memory: &mut GuestMemory<'_>,
597        _fd: types::Fd,
598        _si_data: types::CiovecArray,
599        _si_flags: types::Siflags,
600    ) -> Result<types::Size, Error> {
601        Err(Error::trap(wasmtime::Error::msg("sock_send unsupported")))
602    }
603
604    fn sock_shutdown(
605        &mut self,
606        _memory: &mut GuestMemory<'_>,
607        _fd: types::Fd,
608        _how: types::Sdflags,
609    ) -> Result<(), Error> {
610        Err(Error::trap(wasmtime::Error::msg(
611            "sock_shutdown unsupported",
612        )))
613    }
614}
615
616fn assert_iovec_array_same() {
617    // NB: this isn't enough to assert the types are the same, but it's
618    // something. Additionally p1 and p0 aren't changing any more
619    // and it's been manually verified that these two types are the same, so
620    // it's ok to cast between them.
621    assert_eq!(
622        std::mem::size_of::<types::IovecArray>(),
623        std::mem::size_of::<snapshot1_types::IovecArray>()
624    );
625}
626
627fn assert_ciovec_array_same() {
628    // NB: see above too
629    assert_eq!(
630        std::mem::size_of::<types::CiovecArray>(),
631        std::mem::size_of::<snapshot1_types::CiovecArray>()
632    );
633}
634
635impl From<snapshot1_types::Error> for Error {
636    fn from(error: snapshot1_types::Error) -> Error {
637        match error.downcast() {
638            Ok(errno) => Error::from(types::Errno::from(errno)),
639            Err(trap) => Error::trap(trap),
640        }
641    }
642}
643
644/// Fd is a newtype wrapper around u32. Unwrap and wrap it.
645impl From<types::Fd> for snapshot1_types::Fd {
646    fn from(fd: types::Fd) -> snapshot1_types::Fd {
647        u32::from(fd).into()
648    }
649}
650
651/// Fd is a newtype wrapper around u32. Unwrap and wrap it.
652impl From<snapshot1_types::Fd> for types::Fd {
653    fn from(fd: snapshot1_types::Fd) -> types::Fd {
654        u32::from(fd).into()
655    }
656}
657
658/// Trivial conversion between two c-style enums that have the exact same set of variants.
659/// Could we do something unsafe and not list all these variants out? Probably, but doing
660/// it this way doesn't bother me much. I copy-pasted the list of variants out of the
661/// rendered rustdocs.
662/// LLVM ought to compile these From impls into no-ops, inshallah
663macro_rules! convert_enum {
664    ($from:ty, $to:ty, $($var:ident),+) => {
665        impl From<$from> for $to {
666            fn from(e: $from) -> $to {
667                match e {
668                    $( <$from>::$var => <$to>::$var, )+
669                }
670            }
671        }
672    }
673}
674convert_enum!(
675    snapshot1_types::Errno,
676    types::Errno,
677    Success,
678    TooBig,
679    Acces,
680    Addrinuse,
681    Addrnotavail,
682    Afnosupport,
683    Again,
684    Already,
685    Badf,
686    Badmsg,
687    Busy,
688    Canceled,
689    Child,
690    Connaborted,
691    Connrefused,
692    Connreset,
693    Deadlk,
694    Destaddrreq,
695    Dom,
696    Dquot,
697    Exist,
698    Fault,
699    Fbig,
700    Hostunreach,
701    Idrm,
702    Ilseq,
703    Inprogress,
704    Intr,
705    Inval,
706    Io,
707    Isconn,
708    Isdir,
709    Loop,
710    Mfile,
711    Mlink,
712    Msgsize,
713    Multihop,
714    Nametoolong,
715    Netdown,
716    Netreset,
717    Netunreach,
718    Nfile,
719    Nobufs,
720    Nodev,
721    Noent,
722    Noexec,
723    Nolck,
724    Nolink,
725    Nomem,
726    Nomsg,
727    Noprotoopt,
728    Nospc,
729    Nosys,
730    Notconn,
731    Notdir,
732    Notempty,
733    Notrecoverable,
734    Notsock,
735    Notsup,
736    Notty,
737    Nxio,
738    Overflow,
739    Ownerdead,
740    Perm,
741    Pipe,
742    Proto,
743    Protonosupport,
744    Prototype,
745    Range,
746    Rofs,
747    Spipe,
748    Srch,
749    Stale,
750    Timedout,
751    Txtbsy,
752    Xdev,
753    Notcapable
754);
755convert_enum!(
756    types::Clockid,
757    snapshot1_types::Clockid,
758    Realtime,
759    Monotonic,
760    ProcessCputimeId,
761    ThreadCputimeId
762);
763
764convert_enum!(
765    types::Advice,
766    snapshot1_types::Advice,
767    Normal,
768    Sequential,
769    Random,
770    Willneed,
771    Dontneed,
772    Noreuse
773);
774convert_enum!(
775    snapshot1_types::Filetype,
776    types::Filetype,
777    Directory,
778    BlockDevice,
779    CharacterDevice,
780    RegularFile,
781    SocketDgram,
782    SocketStream,
783    SymbolicLink,
784    Unknown
785);
786convert_enum!(types::Whence, snapshot1_types::Whence, Cur, End, Set);
787
788convert_enum!(
789    types::Signal,
790    snapshot1_types::Signal,
791    None,
792    Hup,
793    Int,
794    Quit,
795    Ill,
796    Trap,
797    Abrt,
798    Bus,
799    Fpe,
800    Kill,
801    Usr1,
802    Segv,
803    Usr2,
804    Pipe,
805    Alrm,
806    Term,
807    Chld,
808    Cont,
809    Stop,
810    Tstp,
811    Ttin,
812    Ttou,
813    Urg,
814    Xcpu,
815    Xfsz,
816    Vtalrm,
817    Prof,
818    Winch,
819    Poll,
820    Pwr,
821    Sys
822);
823
824/// Prestat isn't a c-style enum, its a union where the variant has a payload. Its the only one of
825/// those we need to convert, so write it by hand.
826impl From<snapshot1_types::Prestat> for types::Prestat {
827    fn from(p: snapshot1_types::Prestat) -> types::Prestat {
828        match p {
829            snapshot1_types::Prestat::Dir(d) => types::Prestat::Dir(d.into()),
830        }
831    }
832}
833
834/// Trivial conversion between two structs that have the exact same set of fields,
835/// with recursive descent into the field types.
836macro_rules! convert_struct {
837    ($from:ty, $to:path, $($field:ident),+) => {
838        impl From<$from> for $to {
839            fn from(e: $from) -> $to {
840                $to {
841                    $( $field: e.$field.into(), )+
842                }
843            }
844        }
845    }
846}
847
848convert_struct!(snapshot1_types::PrestatDir, types::PrestatDir, pr_name_len);
849convert_struct!(
850    snapshot1_types::Fdstat,
851    types::Fdstat,
852    fs_filetype,
853    fs_rights_base,
854    fs_rights_inheriting,
855    fs_flags
856);
857convert_struct!(
858    types::SubscriptionClock,
859    snapshot1_types::SubscriptionClock,
860    id,
861    timeout,
862    precision,
863    flags
864);
865convert_struct!(
866    types::SubscriptionFdReadwrite,
867    snapshot1_types::SubscriptionFdReadwrite,
868    file_descriptor
869);
870
871/// Snapshot1 Filestat is incompatible with Snapshot0 Filestat - the nlink
872/// field is u32 on this Filestat, and u64 on theirs. If you've got more than
873/// 2^32 links I don't know what to tell you
874impl From<snapshot1_types::Filestat> for types::Filestat {
875    fn from(f: snapshot1_types::Filestat) -> types::Filestat {
876        types::Filestat {
877            dev: f.dev,
878            ino: f.ino,
879            filetype: f.filetype.into(),
880            nlink: f.nlink.try_into().unwrap_or(u32::MAX),
881            size: f.size,
882            atim: f.atim,
883            mtim: f.mtim,
884            ctim: f.ctim,
885        }
886    }
887}
888
889/// Trivial conversion between two bitflags that have the exact same set of flags.
890macro_rules! convert_flags {
891    ($from:ty, $to:ty, $($flag:ident),+) => {
892        impl From<$from> for $to {
893            fn from(f: $from) -> $to {
894                let mut out = <$to>::empty();
895                $(
896                    if f.contains(<$from>::$flag) {
897                        out |= <$to>::$flag;
898                    }
899                )+
900                out
901            }
902        }
903    }
904}
905
906/// Need to convert in both directions? This saves listing out the flags twice
907macro_rules! convert_flags_bidirectional {
908    ($from:ty, $to:ty, $($flag:tt)*) => {
909        convert_flags!($from, $to, $($flag)*);
910        convert_flags!($to, $from, $($flag)*);
911    }
912}
913
914convert_flags_bidirectional!(
915    snapshot1_types::Fdflags,
916    types::Fdflags,
917    APPEND,
918    DSYNC,
919    NONBLOCK,
920    RSYNC,
921    SYNC
922);
923convert_flags!(
924    types::Lookupflags,
925    snapshot1_types::Lookupflags,
926    SYMLINK_FOLLOW
927);
928convert_flags!(
929    types::Fstflags,
930    snapshot1_types::Fstflags,
931    ATIM,
932    ATIM_NOW,
933    MTIM,
934    MTIM_NOW
935);
936convert_flags!(
937    types::Oflags,
938    snapshot1_types::Oflags,
939    CREAT,
940    DIRECTORY,
941    EXCL,
942    TRUNC
943);
944convert_flags_bidirectional!(
945    types::Rights,
946    snapshot1_types::Rights,
947    FD_DATASYNC,
948    FD_READ,
949    FD_SEEK,
950    FD_FDSTAT_SET_FLAGS,
951    FD_SYNC,
952    FD_TELL,
953    FD_WRITE,
954    FD_ADVISE,
955    FD_ALLOCATE,
956    PATH_CREATE_DIRECTORY,
957    PATH_CREATE_FILE,
958    PATH_LINK_SOURCE,
959    PATH_LINK_TARGET,
960    PATH_OPEN,
961    FD_READDIR,
962    PATH_READLINK,
963    PATH_RENAME_SOURCE,
964    PATH_RENAME_TARGET,
965    PATH_FILESTAT_GET,
966    PATH_FILESTAT_SET_SIZE,
967    PATH_FILESTAT_SET_TIMES,
968    FD_FILESTAT_GET,
969    FD_FILESTAT_SET_SIZE,
970    FD_FILESTAT_SET_TIMES,
971    PATH_SYMLINK,
972    PATH_REMOVE_DIRECTORY,
973    PATH_UNLINK_FILE,
974    POLL_FD_READWRITE,
975    SOCK_SHUTDOWN
976);
977convert_flags!(
978    types::Subclockflags,
979    snapshot1_types::Subclockflags,
980    SUBSCRIPTION_CLOCK_ABSTIME
981);
982
983impl From<GuestError> for types::Error {
984    fn from(err: GuestError) -> Self {
985        snapshot1_types::Error::from(err).into()
986    }
987}