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, fd_renumber
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, fd_renumber
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 wasi_unstable::WasiUnstable for WasiP1Ctx {
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    async 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()).await?;
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        self.consume_fuel_for_array(subs_array)?;
522        let mut old_subs = Vec::new();
523        for slot in subs_array.iter() {
524            let slot = slot?;
525            let sub = memory.read(slot)?;
526            old_subs.push(sub.clone());
527            memory.write(
528                slot.cast(),
529                snapshot1_types::Subscription {
530                    userdata: sub.userdata,
531                    u: match sub.u {
532                        types::SubscriptionU::Clock(c) => {
533                            snapshot1_types::SubscriptionU::Clock(c.into())
534                        }
535                        types::SubscriptionU::FdRead(c) => {
536                            snapshot1_types::SubscriptionU::FdRead(c.into())
537                        }
538                        types::SubscriptionU::FdWrite(c) => {
539                            snapshot1_types::SubscriptionU::FdWrite(c.into())
540                        }
541                    },
542                },
543            )?;
544        }
545        let ret = Snapshot1::poll_oneoff(self, memory, subs.cast(), events.cast(), nsubscriptions)
546            .await?;
547        for (sub, slot) in old_subs.into_iter().zip(subs_array.iter()) {
548            memory.write(slot?, sub)?;
549        }
550        Ok(ret)
551    }
552
553    fn proc_exit(
554        &mut self,
555        memory: &mut GuestMemory<'_>,
556        status: types::Exitcode,
557    ) -> wasmtime::Error {
558        Snapshot1::proc_exit(self, memory, status)
559    }
560
561    fn proc_raise(
562        &mut self,
563        memory: &mut GuestMemory<'_>,
564        sig: types::Signal,
565    ) -> Result<(), Error> {
566        Snapshot1::proc_raise(self, memory, sig.into())?;
567        Ok(())
568    }
569
570    fn sched_yield(&mut self, memory: &mut GuestMemory<'_>) -> Result<(), Error> {
571        Snapshot1::sched_yield(self, memory)?;
572        Ok(())
573    }
574
575    fn random_get(
576        &mut self,
577        memory: &mut GuestMemory<'_>,
578        buf: GuestPtr<u8>,
579        buf_len: types::Size,
580    ) -> Result<(), Error> {
581        Snapshot1::random_get(self, memory, buf, buf_len)?;
582        Ok(())
583    }
584
585    fn sock_recv(
586        &mut self,
587        _memory: &mut GuestMemory<'_>,
588        _fd: types::Fd,
589        _ri_data: types::IovecArray,
590        _ri_flags: types::Riflags,
591    ) -> Result<(types::Size, types::Roflags), Error> {
592        Err(Error::trap(wasmtime::Error::msg("sock_recv unsupported")))
593    }
594
595    fn sock_send(
596        &mut self,
597        _memory: &mut GuestMemory<'_>,
598        _fd: types::Fd,
599        _si_data: types::CiovecArray,
600        _si_flags: types::Siflags,
601    ) -> Result<types::Size, Error> {
602        Err(Error::trap(wasmtime::Error::msg("sock_send unsupported")))
603    }
604
605    fn sock_shutdown(
606        &mut self,
607        _memory: &mut GuestMemory<'_>,
608        _fd: types::Fd,
609        _how: types::Sdflags,
610    ) -> Result<(), Error> {
611        Err(Error::trap(wasmtime::Error::msg(
612            "sock_shutdown unsupported",
613        )))
614    }
615}
616
617fn assert_iovec_array_same() {
618    // NB: this isn't enough to assert the types are the same, but it's
619    // something. Additionally p1 and p0 aren't changing any more
620    // and it's been manually verified that these two types are the same, so
621    // it's ok to cast between them.
622    assert_eq!(
623        std::mem::size_of::<types::IovecArray>(),
624        std::mem::size_of::<snapshot1_types::IovecArray>()
625    );
626}
627
628fn assert_ciovec_array_same() {
629    // NB: see above too
630    assert_eq!(
631        std::mem::size_of::<types::CiovecArray>(),
632        std::mem::size_of::<snapshot1_types::CiovecArray>()
633    );
634}
635
636impl From<snapshot1_types::Error> for Error {
637    fn from(error: snapshot1_types::Error) -> Error {
638        match error.downcast() {
639            Ok(errno) => Error::from(types::Errno::from(errno)),
640            Err(trap) => Error::trap(trap),
641        }
642    }
643}
644
645/// Fd is a newtype wrapper around u32. Unwrap and wrap it.
646impl From<types::Fd> for snapshot1_types::Fd {
647    fn from(fd: types::Fd) -> snapshot1_types::Fd {
648        u32::from(fd).into()
649    }
650}
651
652/// Fd is a newtype wrapper around u32. Unwrap and wrap it.
653impl From<snapshot1_types::Fd> for types::Fd {
654    fn from(fd: snapshot1_types::Fd) -> types::Fd {
655        u32::from(fd).into()
656    }
657}
658
659/// Trivial conversion between two c-style enums that have the exact same set of variants.
660/// Could we do something unsafe and not list all these variants out? Probably, but doing
661/// it this way doesn't bother me much. I copy-pasted the list of variants out of the
662/// rendered rustdocs.
663/// LLVM ought to compile these From impls into no-ops, inshallah
664macro_rules! convert_enum {
665    ($from:ty, $to:ty, $($var:ident),+) => {
666        impl From<$from> for $to {
667            fn from(e: $from) -> $to {
668                match e {
669                    $( <$from>::$var => <$to>::$var, )+
670                }
671            }
672        }
673    }
674}
675convert_enum!(
676    snapshot1_types::Errno,
677    types::Errno,
678    Success,
679    TooBig,
680    Acces,
681    Addrinuse,
682    Addrnotavail,
683    Afnosupport,
684    Again,
685    Already,
686    Badf,
687    Badmsg,
688    Busy,
689    Canceled,
690    Child,
691    Connaborted,
692    Connrefused,
693    Connreset,
694    Deadlk,
695    Destaddrreq,
696    Dom,
697    Dquot,
698    Exist,
699    Fault,
700    Fbig,
701    Hostunreach,
702    Idrm,
703    Ilseq,
704    Inprogress,
705    Intr,
706    Inval,
707    Io,
708    Isconn,
709    Isdir,
710    Loop,
711    Mfile,
712    Mlink,
713    Msgsize,
714    Multihop,
715    Nametoolong,
716    Netdown,
717    Netreset,
718    Netunreach,
719    Nfile,
720    Nobufs,
721    Nodev,
722    Noent,
723    Noexec,
724    Nolck,
725    Nolink,
726    Nomem,
727    Nomsg,
728    Noprotoopt,
729    Nospc,
730    Nosys,
731    Notconn,
732    Notdir,
733    Notempty,
734    Notrecoverable,
735    Notsock,
736    Notsup,
737    Notty,
738    Nxio,
739    Overflow,
740    Ownerdead,
741    Perm,
742    Pipe,
743    Proto,
744    Protonosupport,
745    Prototype,
746    Range,
747    Rofs,
748    Spipe,
749    Srch,
750    Stale,
751    Timedout,
752    Txtbsy,
753    Xdev,
754    Notcapable
755);
756convert_enum!(
757    types::Clockid,
758    snapshot1_types::Clockid,
759    Realtime,
760    Monotonic,
761    ProcessCputimeId,
762    ThreadCputimeId
763);
764
765convert_enum!(
766    types::Advice,
767    snapshot1_types::Advice,
768    Normal,
769    Sequential,
770    Random,
771    Willneed,
772    Dontneed,
773    Noreuse
774);
775convert_enum!(
776    snapshot1_types::Filetype,
777    types::Filetype,
778    Directory,
779    BlockDevice,
780    CharacterDevice,
781    RegularFile,
782    SocketDgram,
783    SocketStream,
784    SymbolicLink,
785    Unknown
786);
787convert_enum!(types::Whence, snapshot1_types::Whence, Cur, End, Set);
788
789convert_enum!(
790    types::Signal,
791    snapshot1_types::Signal,
792    None,
793    Hup,
794    Int,
795    Quit,
796    Ill,
797    Trap,
798    Abrt,
799    Bus,
800    Fpe,
801    Kill,
802    Usr1,
803    Segv,
804    Usr2,
805    Pipe,
806    Alrm,
807    Term,
808    Chld,
809    Cont,
810    Stop,
811    Tstp,
812    Ttin,
813    Ttou,
814    Urg,
815    Xcpu,
816    Xfsz,
817    Vtalrm,
818    Prof,
819    Winch,
820    Poll,
821    Pwr,
822    Sys
823);
824
825/// Prestat isn't a c-style enum, its a union where the variant has a payload. Its the only one of
826/// those we need to convert, so write it by hand.
827impl From<snapshot1_types::Prestat> for types::Prestat {
828    fn from(p: snapshot1_types::Prestat) -> types::Prestat {
829        match p {
830            snapshot1_types::Prestat::Dir(d) => types::Prestat::Dir(d.into()),
831        }
832    }
833}
834
835/// Trivial conversion between two structs that have the exact same set of fields,
836/// with recursive descent into the field types.
837macro_rules! convert_struct {
838    ($from:ty, $to:path, $($field:ident),+) => {
839        impl From<$from> for $to {
840            fn from(e: $from) -> $to {
841                $to {
842                    $( $field: e.$field.into(), )+
843                }
844            }
845        }
846    }
847}
848
849convert_struct!(snapshot1_types::PrestatDir, types::PrestatDir, pr_name_len);
850convert_struct!(
851    snapshot1_types::Fdstat,
852    types::Fdstat,
853    fs_filetype,
854    fs_rights_base,
855    fs_rights_inheriting,
856    fs_flags
857);
858convert_struct!(
859    types::SubscriptionClock,
860    snapshot1_types::SubscriptionClock,
861    id,
862    timeout,
863    precision,
864    flags
865);
866convert_struct!(
867    types::SubscriptionFdReadwrite,
868    snapshot1_types::SubscriptionFdReadwrite,
869    file_descriptor
870);
871
872/// Snapshot1 Filestat is incompatible with Snapshot0 Filestat - the nlink
873/// field is u32 on this Filestat, and u64 on theirs. If you've got more than
874/// 2^32 links I don't know what to tell you
875impl From<snapshot1_types::Filestat> for types::Filestat {
876    fn from(f: snapshot1_types::Filestat) -> types::Filestat {
877        types::Filestat {
878            dev: f.dev,
879            ino: f.ino,
880            filetype: f.filetype.into(),
881            nlink: f.nlink.try_into().unwrap_or(u32::MAX),
882            size: f.size,
883            atim: f.atim,
884            mtim: f.mtim,
885            ctim: f.ctim,
886        }
887    }
888}
889
890/// Trivial conversion between two bitflags that have the exact same set of flags.
891macro_rules! convert_flags {
892    ($from:ty, $to:ty, $($flag:ident),+) => {
893        impl From<$from> for $to {
894            fn from(f: $from) -> $to {
895                let mut out = <$to>::empty();
896                $(
897                    if f.contains(<$from>::$flag) {
898                        out |= <$to>::$flag;
899                    }
900                )+
901                out
902            }
903        }
904    }
905}
906
907/// Need to convert in both directions? This saves listing out the flags twice
908macro_rules! convert_flags_bidirectional {
909    ($from:ty, $to:ty, $($flag:tt)*) => {
910        convert_flags!($from, $to, $($flag)*);
911        convert_flags!($to, $from, $($flag)*);
912    }
913}
914
915convert_flags_bidirectional!(
916    snapshot1_types::Fdflags,
917    types::Fdflags,
918    APPEND,
919    DSYNC,
920    NONBLOCK,
921    RSYNC,
922    SYNC
923);
924convert_flags!(
925    types::Lookupflags,
926    snapshot1_types::Lookupflags,
927    SYMLINK_FOLLOW
928);
929convert_flags!(
930    types::Fstflags,
931    snapshot1_types::Fstflags,
932    ATIM,
933    ATIM_NOW,
934    MTIM,
935    MTIM_NOW
936);
937convert_flags!(
938    types::Oflags,
939    snapshot1_types::Oflags,
940    CREAT,
941    DIRECTORY,
942    EXCL,
943    TRUNC
944);
945convert_flags_bidirectional!(
946    types::Rights,
947    snapshot1_types::Rights,
948    FD_DATASYNC,
949    FD_READ,
950    FD_SEEK,
951    FD_FDSTAT_SET_FLAGS,
952    FD_SYNC,
953    FD_TELL,
954    FD_WRITE,
955    FD_ADVISE,
956    FD_ALLOCATE,
957    PATH_CREATE_DIRECTORY,
958    PATH_CREATE_FILE,
959    PATH_LINK_SOURCE,
960    PATH_LINK_TARGET,
961    PATH_OPEN,
962    FD_READDIR,
963    PATH_READLINK,
964    PATH_RENAME_SOURCE,
965    PATH_RENAME_TARGET,
966    PATH_FILESTAT_GET,
967    PATH_FILESTAT_SET_SIZE,
968    PATH_FILESTAT_SET_TIMES,
969    FD_FILESTAT_GET,
970    FD_FILESTAT_SET_SIZE,
971    FD_FILESTAT_SET_TIMES,
972    PATH_SYMLINK,
973    PATH_REMOVE_DIRECTORY,
974    PATH_UNLINK_FILE,
975    POLL_FD_READWRITE,
976    SOCK_SHUTDOWN
977);
978convert_flags!(
979    types::Subclockflags,
980    snapshot1_types::Subclockflags,
981    SUBSCRIPTION_CLOCK_ABSTIME
982);
983
984impl From<GuestError> for types::Error {
985    fn from(err: GuestError) -> Self {
986        snapshot1_types::Error::from(err).into()
987    }
988}