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