wasi_common/snapshots/
preview_1.rs

1use crate::{
2    I32Exit, SystemTimeSpec, WasiCtx,
3    dir::{DirEntry, OpenResult, ReaddirCursor, ReaddirEntity, TableDirExt},
4    file::{
5        Advice, FdFlags, FdStat, FileAccessMode, FileEntry, FileType, Filestat, OFlags, RiFlags,
6        RoFlags, SdFlags, SiFlags, TableFileExt, WasiFile,
7    },
8    sched::{
9        Poll, Userdata,
10        subscription::{RwEventFlags, SubscriptionResult},
11    },
12};
13use cap_std::time::{Duration, SystemClock};
14use std::borrow::Cow;
15use std::io::{IoSlice, IoSliceMut};
16use std::ops::Deref;
17use std::sync::Arc;
18use wiggle::GuestMemory;
19use wiggle::GuestPtr;
20
21pub mod error;
22use error::{Error, ErrorExt};
23
24// Limit the size of intermediate buffers when copying to WebAssembly shared
25// memory.
26pub(crate) const MAX_SHARED_BUFFER_SIZE: usize = 1 << 16;
27
28wiggle::from_witx!({
29    witx: ["witx/preview1/wasi_snapshot_preview1.witx"],
30    errors: { errno => trappable Error },
31    // Note: not every function actually needs to be async, however, nearly all of them do, and
32    // keeping that set the same in this macro and the wasmtime_wiggle / lucet_wiggle macros is
33    // tedious, and there is no cost to having a sync function be async in this case.
34    async: *,
35    wasmtime: false,
36});
37
38impl wiggle::GuestErrorType for types::Errno {
39    fn success() -> Self {
40        Self::Success
41    }
42}
43
44impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
45    async fn args_get(
46        &mut self,
47        memory: &mut GuestMemory<'_>,
48        argv: GuestPtr<GuestPtr<u8>>,
49        argv_buf: GuestPtr<u8>,
50    ) -> Result<(), Error> {
51        self.args.write_to_guest(memory, argv_buf, argv)
52    }
53
54    async fn args_sizes_get(
55        &mut self,
56        _memory: &mut GuestMemory<'_>,
57    ) -> Result<(types::Size, types::Size), Error> {
58        Ok((self.args.number_elements(), self.args.cumulative_size()))
59    }
60
61    async fn environ_get(
62        &mut self,
63        memory: &mut GuestMemory<'_>,
64        environ: GuestPtr<GuestPtr<u8>>,
65        environ_buf: GuestPtr<u8>,
66    ) -> Result<(), Error> {
67        self.env.write_to_guest(memory, environ_buf, environ)
68    }
69
70    async fn environ_sizes_get(
71        &mut self,
72        _memory: &mut GuestMemory<'_>,
73    ) -> Result<(types::Size, types::Size), Error> {
74        Ok((self.env.number_elements(), self.env.cumulative_size()))
75    }
76
77    async fn clock_res_get(
78        &mut self,
79        _memory: &mut GuestMemory<'_>,
80        id: types::Clockid,
81    ) -> Result<types::Timestamp, Error> {
82        let resolution = match id {
83            types::Clockid::Realtime => Ok(self.clocks.system()?.resolution()),
84            types::Clockid::Monotonic => Ok(self.clocks.monotonic()?.abs_clock.resolution()),
85            types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
86                Err(Error::badf().context("process and thread clocks are not supported"))
87            }
88        }?;
89        Ok(resolution.as_nanos().try_into()?)
90    }
91
92    async fn clock_time_get(
93        &mut self,
94        _memory: &mut GuestMemory<'_>,
95        id: types::Clockid,
96        precision: types::Timestamp,
97    ) -> Result<types::Timestamp, Error> {
98        let precision = Duration::from_nanos(precision);
99        match id {
100            types::Clockid::Realtime => {
101                let now = self.clocks.system()?.now(precision).into_std();
102                let d = now
103                    .duration_since(std::time::SystemTime::UNIX_EPOCH)
104                    .map_err(|_| {
105                        Error::trap(anyhow::Error::msg("current time before unix epoch"))
106                    })?;
107                Ok(d.as_nanos().try_into()?)
108            }
109            types::Clockid::Monotonic => {
110                let clock = self.clocks.monotonic()?;
111                let now = clock.abs_clock.now(precision);
112                let d = now.duration_since(clock.creation_time);
113                Ok(d.as_nanos().try_into()?)
114            }
115            types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
116                Err(Error::badf().context("process and thread clocks are not supported"))
117            }
118        }
119    }
120
121    async fn fd_advise(
122        &mut self,
123        _memory: &mut GuestMemory<'_>,
124        fd: types::Fd,
125        offset: types::Filesize,
126        len: types::Filesize,
127        advice: types::Advice,
128    ) -> Result<(), Error> {
129        self.table()
130            .get_file(u32::from(fd))?
131            .file
132            .advise(offset, len, advice.into())
133            .await?;
134        Ok(())
135    }
136
137    async fn fd_allocate(
138        &mut self,
139        _memory: &mut GuestMemory<'_>,
140        fd: types::Fd,
141        _offset: types::Filesize,
142        _len: types::Filesize,
143    ) -> Result<(), Error> {
144        // Check if fd is a file, and has rights, just to reject those cases
145        // with the errors expected:
146        let _ = self.table().get_file(u32::from(fd))?;
147        // This operation from cloudabi is linux-specific, isn't even
148        // supported across all linux filesystems, and has no support on macos
149        // or windows. Rather than ship spotty support, it has been removed
150        // from preview 2, and we are no longer supporting it in preview 1 as
151        // well.
152        Err(Error::not_supported())
153    }
154
155    async fn fd_close(
156        &mut self,
157        _memory: &mut GuestMemory<'_>,
158        fd: types::Fd,
159    ) -> Result<(), Error> {
160        let table = self.table();
161        let fd = u32::from(fd);
162
163        // Fail fast: If not present in table, Badf
164        if !table.contains_key(fd) {
165            return Err(Error::badf().context("key not in table"));
166        }
167        // fd_close must close either a File or a Dir handle
168        if table.is::<FileEntry>(fd) {
169            let _ = table.delete::<FileEntry>(fd);
170        } else if table.is::<DirEntry>(fd) {
171            let _ = table.delete::<DirEntry>(fd);
172        } else {
173            return Err(Error::badf().context("key does not refer to file or directory"));
174        }
175
176        Ok(())
177    }
178
179    async fn fd_datasync(
180        &mut self,
181        _memory: &mut GuestMemory<'_>,
182        fd: types::Fd,
183    ) -> Result<(), Error> {
184        self.table()
185            .get_file(u32::from(fd))?
186            .file
187            .datasync()
188            .await?;
189        Ok(())
190    }
191
192    async fn fd_fdstat_get(
193        &mut self,
194        _memory: &mut GuestMemory<'_>,
195        fd: types::Fd,
196    ) -> Result<types::Fdstat, Error> {
197        let table = self.table();
198        let fd = u32::from(fd);
199        if table.is::<FileEntry>(fd) {
200            let file_entry: Arc<FileEntry> = table.get(fd)?;
201            let fdstat = file_entry.get_fdstat().await?;
202            Ok(types::Fdstat::from(&fdstat))
203        } else if table.is::<DirEntry>(fd) {
204            let _dir_entry: Arc<DirEntry> = table.get(fd)?;
205            let dir_fdstat = types::Fdstat {
206                fs_filetype: types::Filetype::Directory,
207                fs_rights_base: directory_base_rights(),
208                fs_rights_inheriting: directory_inheriting_rights(),
209                fs_flags: types::Fdflags::empty(),
210            };
211            Ok(dir_fdstat)
212        } else {
213            Err(Error::badf())
214        }
215    }
216
217    async fn fd_fdstat_set_flags(
218        &mut self,
219        _memory: &mut GuestMemory<'_>,
220        fd: types::Fd,
221        flags: types::Fdflags,
222    ) -> Result<(), Error> {
223        if let Some(table) = self.table_mut() {
224            table
225                .get_file_mut(u32::from(fd))?
226                .file
227                .set_fdflags(FdFlags::from(flags))
228                .await
229        } else {
230            log::warn!(
231                "`fd_fdstat_set_flags` does not work with wasi-threads enabled; see https://github.com/bytecodealliance/wasmtime/issues/5643"
232            );
233            Err(Error::not_supported())
234        }
235    }
236
237    async fn fd_fdstat_set_rights(
238        &mut self,
239        _memory: &mut GuestMemory<'_>,
240        fd: types::Fd,
241        _fs_rights_base: types::Rights,
242        _fs_rights_inheriting: types::Rights,
243    ) -> Result<(), Error> {
244        let table = self.table();
245        let fd = u32::from(fd);
246        if table.is::<FileEntry>(fd) {
247            let _file_entry: Arc<FileEntry> = table.get(fd)?;
248            Err(Error::not_supported())
249        } else if table.is::<DirEntry>(fd) {
250            let _dir_entry: Arc<DirEntry> = table.get(fd)?;
251            Err(Error::not_supported())
252        } else {
253            Err(Error::badf())
254        }
255    }
256
257    async fn fd_filestat_get(
258        &mut self,
259        _memory: &mut GuestMemory<'_>,
260        fd: types::Fd,
261    ) -> Result<types::Filestat, Error> {
262        let table = self.table();
263        let fd = u32::from(fd);
264        if table.is::<FileEntry>(fd) {
265            let filestat = table.get_file(fd)?.file.get_filestat().await?;
266            Ok(filestat.into())
267        } else if table.is::<DirEntry>(fd) {
268            let filestat = table.get_dir(fd)?.dir.get_filestat().await?;
269            Ok(filestat.into())
270        } else {
271            Err(Error::badf())
272        }
273    }
274
275    async fn fd_filestat_set_size(
276        &mut self,
277        _memory: &mut GuestMemory<'_>,
278        fd: types::Fd,
279        size: types::Filesize,
280    ) -> Result<(), Error> {
281        self.table()
282            .get_file(u32::from(fd))?
283            .file
284            .set_filestat_size(size)
285            .await?;
286        Ok(())
287    }
288
289    async fn fd_filestat_set_times(
290        &mut self,
291        _memory: &mut GuestMemory<'_>,
292        fd: types::Fd,
293        atim: types::Timestamp,
294        mtim: types::Timestamp,
295        fst_flags: types::Fstflags,
296    ) -> Result<(), Error> {
297        let fd = u32::from(fd);
298        let table = self.table();
299        // Validate flags
300        let set_atim = fst_flags.contains(types::Fstflags::ATIM);
301        let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);
302        let set_mtim = fst_flags.contains(types::Fstflags::MTIM);
303        let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);
304
305        let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;
306        let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;
307
308        if table.is::<FileEntry>(fd) {
309            table
310                .get_file(fd)
311                .expect("checked that entry is file")
312                .file
313                .set_times(atim, mtim)
314                .await
315        } else if table.is::<DirEntry>(fd) {
316            table
317                .get_dir(fd)
318                .expect("checked that entry is dir")
319                .dir
320                .set_times(".", atim, mtim, false)
321                .await
322        } else {
323            Err(Error::badf())
324        }
325    }
326
327    async fn fd_read(
328        &mut self,
329        memory: &mut GuestMemory<'_>,
330        fd: types::Fd,
331        iovs: types::IovecArray,
332    ) -> Result<types::Size, Error> {
333        let f = self.table().get_file(u32::from(fd))?;
334        // Access mode check normalizes error returned (windows would prefer ACCES here)
335        if !f.access_mode.contains(FileAccessMode::READ) {
336            Err(types::Errno::Badf)?
337        }
338        let f = &f.file;
339
340        let iovs: Vec<wiggle::GuestPtr<[u8]>> = iovs
341            .iter()
342            .map(|iov_ptr| {
343                let iov_ptr = iov_ptr?;
344                let iov: types::Iovec = memory.read(iov_ptr)?;
345                Ok(iov.buf.as_array(iov.buf_len))
346            })
347            .collect::<Result<_, Error>>()?;
348
349        // If the first iov structure is from shared memory we can safely assume
350        // all the rest will be. We then read into memory based on the memory's
351        // shared-ness:
352        // - if not shared, we copy directly into the Wasm memory
353        // - if shared, we use an intermediate buffer; this avoids Rust unsafety
354        //   due to holding on to a `&mut [u8]` of Wasm memory when we cannot
355        //   guarantee the `&mut` exclusivity--other threads could be modifying
356        //   the data as this functions writes to it. Though likely there is no
357        //   issue with OS writing to io structs in multi-threaded scenarios,
358        //   since we do not know here if `&dyn WasiFile` does anything else
359        //   (e.g., read), we cautiously incur some performance overhead by
360        //   copying twice.
361        let is_shared_memory = memory.is_shared_memory();
362        let bytes_read: u64 = if is_shared_memory {
363            // For shared memory, read into an intermediate buffer. Only the
364            // first iov will be filled and even then the read is capped by the
365            // `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.
366            let iov = iovs.into_iter().next();
367            if let Some(iov) = iov {
368                let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];
369                let bytes_read = f.read_vectored(&mut [IoSliceMut::new(&mut buffer)]).await?;
370                let iov = iov
371                    .get_range(0..bytes_read.try_into()?)
372                    .expect("it should always be possible to slice the iov smaller");
373                memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;
374                bytes_read
375            } else {
376                return Ok(0);
377            }
378        } else {
379            // Convert the first unsafe guest slice into a safe one--Wiggle
380            // can only track mutable borrows for an entire region, and converting
381            // all guest pointers to slices would cause a runtime borrow-checking
382            // error. As read is allowed to return less than the requested amount,
383            // it's valid (though not as efficient) for us to only perform the
384            // read of the first buffer.
385            let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {
386                Some(iov) => memory.as_slice_mut(iov)?.unwrap(),
387                None => return Ok(0),
388            };
389
390            // Read directly into the Wasm memory.
391            f.read_vectored(&mut [IoSliceMut::new(guest_slice)]).await?
392        };
393
394        Ok(types::Size::try_from(bytes_read)?)
395    }
396
397    async fn fd_pread(
398        &mut self,
399        memory: &mut GuestMemory<'_>,
400        fd: types::Fd,
401        iovs: types::IovecArray,
402        offset: types::Filesize,
403    ) -> Result<types::Size, Error> {
404        let f = self.table().get_file(u32::from(fd))?;
405        // Access mode check normalizes error returned (windows would prefer ACCES here)
406        if !f.access_mode.contains(FileAccessMode::READ) {
407            Err(types::Errno::Badf)?
408        }
409        let f = &f.file;
410
411        let iovs: Vec<wiggle::GuestPtr<[u8]>> = iovs
412            .iter()
413            .map(|iov_ptr| {
414                let iov_ptr = iov_ptr?;
415                let iov: types::Iovec = memory.read(iov_ptr)?;
416                Ok(iov.buf.as_array(iov.buf_len))
417            })
418            .collect::<Result<_, Error>>()?;
419
420        // If the first iov structure is from shared memory we can safely assume
421        // all the rest will be. We then read into memory based on the memory's
422        // shared-ness:
423        // - if not shared, we copy directly into the Wasm memory
424        // - if shared, we use an intermediate buffer; this avoids Rust unsafety
425        //   due to holding on to a `&mut [u8]` of Wasm memory when we cannot
426        //   guarantee the `&mut` exclusivity--other threads could be modifying
427        //   the data as this functions writes to it. Though likely there is no
428        //   issue with OS writing to io structs in multi-threaded scenarios,
429        //   since we do not know here if `&dyn WasiFile` does anything else
430        //   (e.g., read), we cautiously incur some performance overhead by
431        //   copying twice.
432        let is_shared_memory = memory.is_shared_memory();
433        let bytes_read: u64 = if is_shared_memory {
434            // For shared memory, read into an intermediate buffer. Only the
435            // first iov will be filled and even then the read is capped by the
436            // `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.
437            let iov = iovs.into_iter().next();
438            if let Some(iov) = iov {
439                let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];
440                let bytes_read = f
441                    .read_vectored_at(&mut [IoSliceMut::new(&mut buffer)], offset)
442                    .await?;
443                let iov = iov
444                    .get_range(0..bytes_read.try_into()?)
445                    .expect("it should always be possible to slice the iov smaller");
446                memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;
447                bytes_read
448            } else {
449                return Ok(0);
450            }
451        } else {
452            // Convert unsafe guest slices to safe ones.
453            let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {
454                Some(iov) => memory.as_slice_mut(iov)?.unwrap(),
455                None => return Ok(0),
456            };
457
458            // Read directly into the Wasm memory.
459            f.read_vectored_at(&mut [IoSliceMut::new(guest_slice)], offset)
460                .await?
461        };
462
463        Ok(types::Size::try_from(bytes_read)?)
464    }
465
466    async fn fd_write(
467        &mut self,
468        memory: &mut GuestMemory<'_>,
469        fd: types::Fd,
470        ciovs: types::CiovecArray,
471    ) -> Result<types::Size, Error> {
472        let f = self.table().get_file(u32::from(fd))?;
473        // Access mode check normalizes error returned (windows would prefer ACCES here)
474        if !f.access_mode.contains(FileAccessMode::WRITE) {
475            Err(types::Errno::Badf)?
476        }
477        let f = &f.file;
478
479        let guest_slices: Vec<Cow<[u8]>> = ciovs
480            .iter()
481            .map(|iov_ptr| {
482                let iov_ptr = iov_ptr?;
483                let iov: types::Ciovec = memory.read(iov_ptr)?;
484                Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)
485            })
486            .collect::<Result<_, Error>>()?;
487
488        let ioslices: Vec<IoSlice> = guest_slices
489            .iter()
490            .map(|s| IoSlice::new(s.deref()))
491            .collect();
492        let bytes_written = f.write_vectored(&ioslices).await?;
493
494        Ok(types::Size::try_from(bytes_written)?)
495    }
496
497    async fn fd_pwrite(
498        &mut self,
499        memory: &mut GuestMemory<'_>,
500        fd: types::Fd,
501        ciovs: types::CiovecArray,
502        offset: types::Filesize,
503    ) -> Result<types::Size, Error> {
504        let f = self.table().get_file(u32::from(fd))?;
505        // Access mode check normalizes error returned (windows would prefer ACCES here)
506        if !f.access_mode.contains(FileAccessMode::WRITE) {
507            Err(types::Errno::Badf)?
508        }
509        let f = &f.file;
510
511        let guest_slices: Vec<Cow<[u8]>> = ciovs
512            .iter()
513            .map(|iov_ptr| {
514                let iov_ptr = iov_ptr?;
515                let iov: types::Ciovec = memory.read(iov_ptr)?;
516                Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)
517            })
518            .collect::<Result<_, Error>>()?;
519
520        let ioslices: Vec<IoSlice> = guest_slices
521            .iter()
522            .map(|s| IoSlice::new(s.deref()))
523            .collect();
524        let bytes_written = f.write_vectored_at(&ioslices, offset).await?;
525
526        Ok(types::Size::try_from(bytes_written)?)
527    }
528
529    async fn fd_prestat_get(
530        &mut self,
531        _memory: &mut GuestMemory<'_>,
532        fd: types::Fd,
533    ) -> Result<types::Prestat, Error> {
534        let table = self.table();
535        let dir_entry: Arc<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::badf())?;
536        if let Some(preopen) = dir_entry.preopen_path() {
537            let path_str = preopen.to_str().ok_or_else(|| Error::not_supported())?;
538            let pr_name_len = u32::try_from(path_str.as_bytes().len())?;
539            Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len }))
540        } else {
541            Err(Error::not_supported().context("file is not a preopen"))
542        }
543    }
544
545    async fn fd_prestat_dir_name(
546        &mut self,
547        memory: &mut GuestMemory<'_>,
548        fd: types::Fd,
549        path: GuestPtr<u8>,
550        path_max_len: types::Size,
551    ) -> Result<(), Error> {
552        let table = self.table();
553        let dir_entry: Arc<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?;
554        if let Some(preopen) = dir_entry.preopen_path() {
555            let path_bytes = preopen
556                .to_str()
557                .ok_or_else(|| Error::not_supported())?
558                .as_bytes();
559            let path_len = path_bytes.len();
560            if path_len > path_max_len as usize {
561                return Err(Error::name_too_long());
562            }
563            let path = path.as_array(path_len as u32);
564            memory.copy_from_slice(path_bytes, path)?;
565            Ok(())
566        } else {
567            Err(Error::not_supported())
568        }
569    }
570    async fn fd_renumber(
571        &mut self,
572        _memory: &mut GuestMemory<'_>,
573        from: types::Fd,
574        to: types::Fd,
575    ) -> Result<(), Error> {
576        let table = self.table();
577        let from = u32::from(from);
578        let to = u32::from(to);
579        if !table.contains_key(from) {
580            return Err(Error::badf());
581        }
582        if !table.contains_key(to) {
583            return Err(Error::badf());
584        }
585        table.renumber(from, to)
586    }
587
588    async fn fd_seek(
589        &mut self,
590        _memory: &mut GuestMemory<'_>,
591        fd: types::Fd,
592        offset: types::Filedelta,
593        whence: types::Whence,
594    ) -> Result<types::Filesize, Error> {
595        use std::io::SeekFrom;
596        let whence = match whence {
597            types::Whence::Cur => SeekFrom::Current(offset),
598            types::Whence::End => SeekFrom::End(offset),
599            types::Whence::Set => {
600                SeekFrom::Start(offset.try_into().map_err(|_| Error::invalid_argument())?)
601            }
602        };
603        let newoffset = self
604            .table()
605            .get_file(u32::from(fd))?
606            .file
607            .seek(whence)
608            .await?;
609        Ok(newoffset)
610    }
611
612    async fn fd_sync(&mut self, _memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {
613        self.table().get_file(u32::from(fd))?.file.sync().await?;
614        Ok(())
615    }
616
617    async fn fd_tell(
618        &mut self,
619        _memory: &mut GuestMemory<'_>,
620        fd: types::Fd,
621    ) -> Result<types::Filesize, Error> {
622        let offset = self
623            .table()
624            .get_file(u32::from(fd))?
625            .file
626            .seek(std::io::SeekFrom::Current(0))
627            .await?;
628        Ok(offset)
629    }
630
631    async fn fd_readdir(
632        &mut self,
633        memory: &mut GuestMemory<'_>,
634        fd: types::Fd,
635        mut buf: GuestPtr<u8>,
636        buf_len: types::Size,
637        cookie: types::Dircookie,
638    ) -> Result<types::Size, Error> {
639        let mut bufused = 0;
640        for entity in self
641            .table()
642            .get_dir(u32::from(fd))?
643            .dir
644            .readdir(ReaddirCursor::from(cookie))
645            .await?
646        {
647            let entity = entity?;
648            let dirent_raw = dirent_bytes(types::Dirent::try_from(&entity)?);
649            let dirent_len: types::Size = dirent_raw.len().try_into()?;
650            let name_raw = entity.name.as_bytes();
651            let name_len: types::Size = name_raw.len().try_into()?;
652
653            // Copy as many bytes of the dirent as we can, up to the end of the buffer
654            let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused);
655            let raw = buf.as_array(dirent_copy_len);
656            memory.copy_from_slice(&dirent_raw[..dirent_copy_len as usize], raw)?;
657
658            // If the dirent struct wasn't compiled entirely, return that we filled the buffer, which
659            // tells libc that we're not at EOF.
660            if dirent_copy_len < dirent_len {
661                return Ok(buf_len);
662            }
663
664            buf = buf.add(dirent_copy_len)?;
665            bufused += dirent_copy_len;
666
667            // Copy as many bytes of the name as we can, up to the end of the buffer
668            let name_copy_len = std::cmp::min(name_len, buf_len - bufused);
669            let raw = buf.as_array(name_copy_len);
670            memory.copy_from_slice(&name_raw[..name_copy_len as usize], raw)?;
671
672            // If the dirent struct wasn't copied entirely, return that we filled the buffer, which
673            // tells libc that we're not at EOF
674
675            if name_copy_len < name_len {
676                return Ok(buf_len);
677            }
678
679            buf = buf.add(name_copy_len)?;
680            bufused += name_copy_len;
681        }
682        Ok(bufused)
683    }
684
685    async fn path_create_directory(
686        &mut self,
687        memory: &mut GuestMemory<'_>,
688        dirfd: types::Fd,
689        path: GuestPtr<str>,
690    ) -> Result<(), Error> {
691        self.table()
692            .get_dir(u32::from(dirfd))?
693            .dir
694            .create_dir(memory.as_cow_str(path)?.deref())
695            .await
696    }
697
698    async fn path_filestat_get(
699        &mut self,
700        memory: &mut GuestMemory<'_>,
701        dirfd: types::Fd,
702        flags: types::Lookupflags,
703        path: GuestPtr<str>,
704    ) -> Result<types::Filestat, Error> {
705        let filestat = self
706            .table()
707            .get_dir(u32::from(dirfd))?
708            .dir
709            .get_path_filestat(
710                memory.as_cow_str(path)?.deref(),
711                flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
712            )
713            .await?;
714        Ok(types::Filestat::from(filestat))
715    }
716
717    async fn path_filestat_set_times(
718        &mut self,
719        memory: &mut GuestMemory<'_>,
720        dirfd: types::Fd,
721        flags: types::Lookupflags,
722        path: GuestPtr<str>,
723        atim: types::Timestamp,
724        mtim: types::Timestamp,
725        fst_flags: types::Fstflags,
726    ) -> Result<(), Error> {
727        let set_atim = fst_flags.contains(types::Fstflags::ATIM);
728        let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);
729        let set_mtim = fst_flags.contains(types::Fstflags::MTIM);
730        let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);
731
732        let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;
733        let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;
734        self.table()
735            .get_dir(u32::from(dirfd))?
736            .dir
737            .set_times(
738                memory.as_cow_str(path)?.deref(),
739                atim,
740                mtim,
741                flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
742            )
743            .await
744    }
745
746    async fn path_link(
747        &mut self,
748        memory: &mut GuestMemory<'_>,
749        src_fd: types::Fd,
750        src_flags: types::Lookupflags,
751        src_path: GuestPtr<str>,
752        target_fd: types::Fd,
753        target_path: GuestPtr<str>,
754    ) -> Result<(), Error> {
755        let table = self.table();
756        let src_dir = table.get_dir(u32::from(src_fd))?;
757        let target_dir = table.get_dir(u32::from(target_fd))?;
758        let symlink_follow = src_flags.contains(types::Lookupflags::SYMLINK_FOLLOW);
759        if symlink_follow {
760            return Err(Error::invalid_argument()
761                .context("symlink following on path_link is not supported"));
762        }
763
764        src_dir
765            .dir
766            .hard_link(
767                memory.as_cow_str(src_path)?.deref(),
768                target_dir.dir.deref(),
769                memory.as_cow_str(target_path)?.deref(),
770            )
771            .await
772    }
773
774    async fn path_open(
775        &mut self,
776        memory: &mut GuestMemory<'_>,
777        dirfd: types::Fd,
778        dirflags: types::Lookupflags,
779        path: GuestPtr<str>,
780        oflags: types::Oflags,
781        fs_rights_base: types::Rights,
782        _fs_rights_inheriting: types::Rights,
783        fdflags: types::Fdflags,
784    ) -> Result<types::Fd, Error> {
785        let table = self.table();
786        let dirfd = u32::from(dirfd);
787        if table.is::<FileEntry>(dirfd) {
788            return Err(Error::not_dir());
789        }
790        let dir_entry = table.get_dir(dirfd)?;
791
792        let symlink_follow = dirflags.contains(types::Lookupflags::SYMLINK_FOLLOW);
793
794        let oflags = OFlags::from(&oflags);
795        let fdflags = FdFlags::from(fdflags);
796        let path = memory.as_cow_str(path)?;
797
798        let read = fs_rights_base.contains(types::Rights::FD_READ);
799        let write = fs_rights_base.contains(types::Rights::FD_WRITE);
800        let access_mode = if read {
801            FileAccessMode::READ
802        } else {
803            FileAccessMode::empty()
804        } | if write {
805            FileAccessMode::WRITE
806        } else {
807            FileAccessMode::empty()
808        };
809
810        let file = dir_entry
811            .dir
812            .open_file(symlink_follow, path.deref(), oflags, read, write, fdflags)
813            .await?;
814        drop(dir_entry);
815
816        let fd = match file {
817            OpenResult::File(file) => table.push(Arc::new(FileEntry::new(file, access_mode)))?,
818            OpenResult::Dir(child_dir) => table.push(Arc::new(DirEntry::new(None, child_dir)))?,
819        };
820        Ok(types::Fd::from(fd))
821    }
822
823    async fn path_readlink(
824        &mut self,
825        memory: &mut GuestMemory<'_>,
826        dirfd: types::Fd,
827        path: GuestPtr<str>,
828        buf: GuestPtr<u8>,
829        buf_len: types::Size,
830    ) -> Result<types::Size, Error> {
831        let link = self
832            .table()
833            .get_dir(u32::from(dirfd))?
834            .dir
835            .read_link(memory.as_cow_str(path)?.deref())
836            .await?
837            .into_os_string()
838            .into_string()
839            .map_err(|_| Error::illegal_byte_sequence().context("link contents"))?;
840        let link_bytes = link.as_bytes();
841        // Like posix readlink(2), silently truncate links when they are larger than the
842        // destination buffer:
843        let link_len = std::cmp::min(link_bytes.len(), buf_len as usize);
844        let buf = buf.as_array(link_len as u32);
845        memory.copy_from_slice(&link_bytes[..link_len], buf)?;
846        Ok(link_len as types::Size)
847    }
848
849    async fn path_remove_directory(
850        &mut self,
851        memory: &mut GuestMemory<'_>,
852        dirfd: types::Fd,
853        path: GuestPtr<str>,
854    ) -> Result<(), Error> {
855        self.table()
856            .get_dir(u32::from(dirfd))?
857            .dir
858            .remove_dir(memory.as_cow_str(path)?.deref())
859            .await
860    }
861
862    async fn path_rename(
863        &mut self,
864        memory: &mut GuestMemory<'_>,
865        src_fd: types::Fd,
866        src_path: GuestPtr<str>,
867        dest_fd: types::Fd,
868        dest_path: GuestPtr<str>,
869    ) -> Result<(), Error> {
870        let table = self.table();
871        let src_dir = table.get_dir(u32::from(src_fd))?;
872        let dest_dir = table.get_dir(u32::from(dest_fd))?;
873        src_dir
874            .dir
875            .rename(
876                memory.as_cow_str(src_path)?.deref(),
877                dest_dir.dir.deref(),
878                memory.as_cow_str(dest_path)?.deref(),
879            )
880            .await
881    }
882
883    async fn path_symlink(
884        &mut self,
885        memory: &mut GuestMemory<'_>,
886        src_path: GuestPtr<str>,
887        dirfd: types::Fd,
888        dest_path: GuestPtr<str>,
889    ) -> Result<(), Error> {
890        self.table()
891            .get_dir(u32::from(dirfd))?
892            .dir
893            .symlink(
894                memory.as_cow_str(src_path)?.deref(),
895                memory.as_cow_str(dest_path)?.deref(),
896            )
897            .await
898    }
899
900    async fn path_unlink_file(
901        &mut self,
902        memory: &mut GuestMemory<'_>,
903        dirfd: types::Fd,
904        path: GuestPtr<str>,
905    ) -> Result<(), Error> {
906        self.table()
907            .get_dir(u32::from(dirfd))?
908            .dir
909            .unlink_file(memory.as_cow_str(path)?.deref())
910            .await
911    }
912
913    async fn poll_oneoff(
914        &mut self,
915        memory: &mut GuestMemory<'_>,
916        subs: GuestPtr<types::Subscription>,
917        events: GuestPtr<types::Event>,
918        nsubscriptions: types::Size,
919    ) -> Result<types::Size, Error> {
920        if nsubscriptions == 0 {
921            return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));
922        }
923
924        // Special-case a `poll_oneoff` which is just sleeping on a single
925        // relative timer event, such as what WASI libc uses to implement sleep
926        // functions. This supports all clock IDs, because POSIX says that
927        // `clock_settime` doesn't effect relative sleeps.
928        if nsubscriptions == 1 {
929            let sub = memory.read(subs)?;
930            if let types::SubscriptionU::Clock(clocksub) = sub.u {
931                if !clocksub
932                    .flags
933                    .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
934                {
935                    self.sched
936                        .sleep(Duration::from_nanos(clocksub.timeout))
937                        .await?;
938                    memory.write(
939                        events,
940                        types::Event {
941                            userdata: sub.userdata,
942                            error: types::Errno::Success,
943                            type_: types::Eventtype::Clock,
944                            fd_readwrite: fd_readwrite_empty(),
945                        },
946                    )?;
947                    return Ok(1);
948                }
949            }
950        }
951
952        let table = &self.table;
953        // We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside
954        let mut read_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();
955        let mut write_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();
956
957        let mut poll = Poll::new();
958
959        let subs = subs.as_array(nsubscriptions);
960        for sub_elem in subs.iter() {
961            let sub_ptr = sub_elem?;
962            let sub = memory.read(sub_ptr)?;
963            match sub.u {
964                types::SubscriptionU::Clock(clocksub) => match clocksub.id {
965                    types::Clockid::Monotonic => {
966                        let clock = self.clocks.monotonic()?;
967                        let precision = Duration::from_nanos(clocksub.precision);
968                        let duration = Duration::from_nanos(clocksub.timeout);
969                        let start = if clocksub
970                            .flags
971                            .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
972                        {
973                            clock.creation_time
974                        } else {
975                            clock.abs_clock.now(precision)
976                        };
977                        let deadline = start
978                            .checked_add(duration)
979                            .ok_or_else(|| Error::overflow().context("deadline"))?;
980                        poll.subscribe_monotonic_clock(
981                            &*clock.abs_clock,
982                            deadline,
983                            precision,
984                            sub.userdata.into(),
985                        )
986                    }
987                    types::Clockid::Realtime => {
988                        // POSIX specifies that functions like `nanosleep` and others use the
989                        // `REALTIME` clock. But it also says that `clock_settime` has no effect
990                        // on threads waiting in these functions. MONOTONIC should always have
991                        // resolution at least as good as REALTIME, so we can translate a
992                        // non-absolute `REALTIME` request into a `MONOTONIC` request.
993                        let clock = self.clocks.monotonic()?;
994                        let precision = Duration::from_nanos(clocksub.precision);
995                        let duration = Duration::from_nanos(clocksub.timeout);
996                        let deadline = if clocksub
997                            .flags
998                            .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
999                        {
1000                            return Err(Error::not_supported());
1001                        } else {
1002                            clock
1003                                .abs_clock
1004                                .now(precision)
1005                                .checked_add(duration)
1006                                .ok_or_else(|| Error::overflow().context("deadline"))?
1007                        };
1008                        poll.subscribe_monotonic_clock(
1009                            &*clock.abs_clock,
1010                            deadline,
1011                            precision,
1012                            sub.userdata.into(),
1013                        )
1014                    }
1015                    _ => Err(Error::invalid_argument()
1016                        .context("timer subscriptions only support monotonic timer"))?,
1017                },
1018                types::SubscriptionU::FdRead(readsub) => {
1019                    let fd = readsub.file_descriptor;
1020                    let file_ref = table.get_file(u32::from(fd))?;
1021                    read_refs.push((file_ref, Some(sub.userdata.into())));
1022                }
1023                types::SubscriptionU::FdWrite(writesub) => {
1024                    let fd = writesub.file_descriptor;
1025                    let file_ref = table.get_file(u32::from(fd))?;
1026                    write_refs.push((file_ref, Some(sub.userdata.into())));
1027                }
1028            }
1029        }
1030
1031        let mut read_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
1032        for (file_lock, userdata) in read_refs.iter_mut() {
1033            read_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));
1034        }
1035
1036        for (f, ud) in read_mut_refs.iter_mut() {
1037            poll.subscribe_read(*f, *ud);
1038        }
1039
1040        let mut write_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
1041        for (file_lock, userdata) in write_refs.iter_mut() {
1042            write_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));
1043        }
1044
1045        for (f, ud) in write_mut_refs.iter_mut() {
1046            poll.subscribe_write(*f, *ud);
1047        }
1048
1049        self.sched.poll_oneoff(&mut poll).await?;
1050
1051        let results = poll.results();
1052        let num_results = results.len();
1053        assert!(
1054            num_results <= nsubscriptions as usize,
1055            "results exceeds subscriptions"
1056        );
1057        let events = events.as_array(
1058            num_results
1059                .try_into()
1060                .expect("not greater than nsubscriptions"),
1061        );
1062        for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) {
1063            let event_ptr = event_elem?;
1064            let userdata: types::Userdata = userdata.into();
1065            memory.write(
1066                event_ptr,
1067                match result {
1068                    SubscriptionResult::Read(r) => {
1069                        let type_ = types::Eventtype::FdRead;
1070                        match r {
1071                            Ok((nbytes, flags)) => types::Event {
1072                                userdata,
1073                                error: types::Errno::Success,
1074                                type_,
1075                                fd_readwrite: types::EventFdReadwrite {
1076                                    nbytes,
1077                                    flags: types::Eventrwflags::from(&flags),
1078                                },
1079                            },
1080                            Err(e) => types::Event {
1081                                userdata,
1082                                error: e.downcast().map_err(Error::trap)?,
1083                                type_,
1084                                fd_readwrite: fd_readwrite_empty(),
1085                            },
1086                        }
1087                    }
1088                    SubscriptionResult::Write(r) => {
1089                        let type_ = types::Eventtype::FdWrite;
1090                        match r {
1091                            Ok((nbytes, flags)) => types::Event {
1092                                userdata,
1093                                error: types::Errno::Success,
1094                                type_,
1095                                fd_readwrite: types::EventFdReadwrite {
1096                                    nbytes,
1097                                    flags: types::Eventrwflags::from(&flags),
1098                                },
1099                            },
1100                            Err(e) => types::Event {
1101                                userdata,
1102                                error: e.downcast().map_err(Error::trap)?,
1103                                type_,
1104                                fd_readwrite: fd_readwrite_empty(),
1105                            },
1106                        }
1107                    }
1108                    SubscriptionResult::MonotonicClock(r) => {
1109                        let type_ = types::Eventtype::Clock;
1110                        types::Event {
1111                            userdata,
1112                            error: match r {
1113                                Ok(()) => types::Errno::Success,
1114                                Err(e) => e.downcast().map_err(Error::trap)?,
1115                            },
1116                            type_,
1117                            fd_readwrite: fd_readwrite_empty(),
1118                        }
1119                    }
1120                },
1121            )?;
1122        }
1123
1124        Ok(num_results.try_into().expect("results fit into memory"))
1125    }
1126
1127    async fn proc_exit(
1128        &mut self,
1129        _memory: &mut GuestMemory<'_>,
1130        status: types::Exitcode,
1131    ) -> anyhow::Error {
1132        // Check that the status is within WASI's range.
1133        if status < 126 {
1134            I32Exit(status as i32).into()
1135        } else {
1136            anyhow::Error::msg("exit with invalid exit status outside of [0..126)")
1137        }
1138    }
1139
1140    async fn proc_raise(
1141        &mut self,
1142        _memory: &mut GuestMemory<'_>,
1143        _sig: types::Signal,
1144    ) -> Result<(), Error> {
1145        Err(Error::trap(anyhow::Error::msg("proc_raise unsupported")))
1146    }
1147
1148    async fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), Error> {
1149        self.sched.sched_yield().await
1150    }
1151
1152    async fn random_get(
1153        &mut self,
1154        memory: &mut GuestMemory<'_>,
1155        buf: GuestPtr<u8>,
1156        buf_len: types::Size,
1157    ) -> Result<(), Error> {
1158        let buf = buf.as_array(buf_len);
1159        if memory.is_shared_memory() {
1160            // If the Wasm memory is shared, copy to an intermediate buffer to
1161            // avoid Rust unsafety (i.e., the called function could rely on
1162            // `&mut [u8]`'s exclusive ownership which is not guaranteed due to
1163            // potential access from other threads).
1164            let mut copied: u32 = 0;
1165            while copied < buf.len() {
1166                let len = (buf.len() - copied).min(MAX_SHARED_BUFFER_SIZE as u32);
1167                let mut tmp = vec![0; len as usize];
1168                self.random.lock().unwrap().try_fill_bytes(&mut tmp)?;
1169                let dest = buf.get_range(copied..copied + len).unwrap();
1170                memory.copy_from_slice(&tmp, dest)?;
1171                copied += len;
1172            }
1173        } else {
1174            // If the Wasm memory is non-shared, copy directly into the linear
1175            // memory.
1176            let mem = &mut memory.as_slice_mut(buf)?.unwrap();
1177            self.random.lock().unwrap().try_fill_bytes(mem)?;
1178        }
1179        Ok(())
1180    }
1181
1182    async fn sock_accept(
1183        &mut self,
1184        _memory: &mut GuestMemory<'_>,
1185        fd: types::Fd,
1186        flags: types::Fdflags,
1187    ) -> Result<types::Fd, Error> {
1188        let table = self.table();
1189        let f = table.get_file(u32::from(fd))?;
1190        let file = f.file.sock_accept(FdFlags::from(flags)).await?;
1191        let fd = table.push(Arc::new(FileEntry::new(file, FileAccessMode::all())))?;
1192        Ok(types::Fd::from(fd))
1193    }
1194
1195    async fn sock_recv(
1196        &mut self,
1197        memory: &mut GuestMemory<'_>,
1198        fd: types::Fd,
1199        ri_data: types::IovecArray,
1200        ri_flags: types::Riflags,
1201    ) -> Result<(types::Size, types::Roflags), Error> {
1202        let f = self.table().get_file(u32::from(fd))?;
1203
1204        let iovs: Vec<wiggle::GuestPtr<[u8]>> = ri_data
1205            .iter()
1206            .map(|iov_ptr| {
1207                let iov_ptr = iov_ptr?;
1208                let iov: types::Iovec = memory.read(iov_ptr)?;
1209                Ok(iov.buf.as_array(iov.buf_len))
1210            })
1211            .collect::<Result<_, Error>>()?;
1212
1213        // If the first iov structure is from shared memory we can safely assume
1214        // all the rest will be. We then read into memory based on the memory's
1215        // shared-ness:
1216        // - if not shared, we copy directly into the Wasm memory
1217        // - if shared, we use an intermediate buffer; this avoids Rust unsafety
1218        //   due to holding on to a `&mut [u8]` of Wasm memory when we cannot
1219        //   guarantee the `&mut` exclusivity--other threads could be modifying
1220        //   the data as this functions writes to it. Though likely there is no
1221        //   issue with OS writing to io structs in multi-threaded scenarios,
1222        //   since we do not know here if `&dyn WasiFile` does anything else
1223        //   (e.g., read), we cautiously incur some performance overhead by
1224        //   copying twice.
1225        let is_shared_memory = memory.is_shared_memory();
1226        let (bytes_read, ro_flags) = if is_shared_memory {
1227            // For shared memory, read into an intermediate buffer. Only the
1228            // first iov will be filled and even then the read is capped by the
1229            // `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.
1230            let iov = iovs.into_iter().next();
1231            if let Some(iov) = iov {
1232                let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];
1233                let (bytes_read, ro_flags) = f
1234                    .file
1235                    .sock_recv(&mut [IoSliceMut::new(&mut buffer)], RiFlags::from(ri_flags))
1236                    .await?;
1237                let iov = iov
1238                    .get_range(0..bytes_read.try_into()?)
1239                    .expect("it should always be possible to slice the iov smaller");
1240                memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;
1241                (bytes_read, ro_flags)
1242            } else {
1243                return Ok((0, RoFlags::empty().into()));
1244            }
1245        } else {
1246            // Convert all of the unsafe guest slices to safe ones--this uses
1247            // Wiggle's internal borrow checker to ensure no overlaps. We assume
1248            // here that, because the memory is not shared, there are no other
1249            // threads to access it while it is written to.
1250            let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {
1251                Some(iov) => memory.as_slice_mut(iov)?.unwrap(),
1252                None => &mut [],
1253            };
1254
1255            // Read directly into the Wasm memory.
1256            f.file
1257                .sock_recv(&mut [IoSliceMut::new(guest_slice)], RiFlags::from(ri_flags))
1258                .await?
1259        };
1260
1261        Ok((types::Size::try_from(bytes_read)?, ro_flags.into()))
1262    }
1263
1264    async fn sock_send(
1265        &mut self,
1266        memory: &mut GuestMemory<'_>,
1267        fd: types::Fd,
1268        si_data: types::CiovecArray,
1269        _si_flags: types::Siflags,
1270    ) -> Result<types::Size, Error> {
1271        let f = self.table().get_file(u32::from(fd))?;
1272
1273        let guest_slices: Vec<Cow<[u8]>> = si_data
1274            .iter()
1275            .map(|iov_ptr| {
1276                let iov_ptr = iov_ptr?;
1277                let iov: types::Ciovec = memory.read(iov_ptr)?;
1278                Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)
1279            })
1280            .collect::<Result<_, Error>>()?;
1281
1282        let ioslices: Vec<IoSlice> = guest_slices
1283            .iter()
1284            .map(|s| IoSlice::new(s.deref()))
1285            .collect();
1286        let bytes_written = f.file.sock_send(&ioslices, SiFlags::empty()).await?;
1287
1288        Ok(types::Size::try_from(bytes_written)?)
1289    }
1290
1291    async fn sock_shutdown(
1292        &mut self,
1293        _memory: &mut GuestMemory<'_>,
1294        fd: types::Fd,
1295        how: types::Sdflags,
1296    ) -> Result<(), Error> {
1297        let f = self.table().get_file(u32::from(fd))?;
1298
1299        f.file.sock_shutdown(SdFlags::from(how)).await
1300    }
1301}
1302
1303impl From<types::Advice> for Advice {
1304    fn from(advice: types::Advice) -> Advice {
1305        match advice {
1306            types::Advice::Normal => Advice::Normal,
1307            types::Advice::Sequential => Advice::Sequential,
1308            types::Advice::Random => Advice::Random,
1309            types::Advice::Willneed => Advice::WillNeed,
1310            types::Advice::Dontneed => Advice::DontNeed,
1311            types::Advice::Noreuse => Advice::NoReuse,
1312        }
1313    }
1314}
1315
1316impl From<&FdStat> for types::Fdstat {
1317    fn from(fdstat: &FdStat) -> types::Fdstat {
1318        let mut fs_rights_base = types::Rights::empty();
1319        if fdstat.access_mode.contains(FileAccessMode::READ) {
1320            fs_rights_base |= types::Rights::FD_READ;
1321        }
1322        if fdstat.access_mode.contains(FileAccessMode::WRITE) {
1323            fs_rights_base |= types::Rights::FD_WRITE;
1324        }
1325        types::Fdstat {
1326            fs_filetype: types::Filetype::from(&fdstat.filetype),
1327            fs_rights_base,
1328            fs_rights_inheriting: types::Rights::empty(),
1329            fs_flags: types::Fdflags::from(fdstat.flags),
1330        }
1331    }
1332}
1333
1334impl From<&FileType> for types::Filetype {
1335    fn from(ft: &FileType) -> types::Filetype {
1336        match ft {
1337            FileType::Directory => types::Filetype::Directory,
1338            FileType::BlockDevice => types::Filetype::BlockDevice,
1339            FileType::CharacterDevice => types::Filetype::CharacterDevice,
1340            FileType::RegularFile => types::Filetype::RegularFile,
1341            FileType::SocketDgram => types::Filetype::SocketDgram,
1342            FileType::SocketStream => types::Filetype::SocketStream,
1343            FileType::SymbolicLink => types::Filetype::SymbolicLink,
1344            FileType::Unknown => types::Filetype::Unknown,
1345            FileType::Pipe => types::Filetype::Unknown,
1346        }
1347    }
1348}
1349
1350macro_rules! convert_flags {
1351    ($from:ty, $to:ty, $($flag:ident),+) => {
1352        impl From<$from> for $to {
1353            fn from(f: $from) -> $to {
1354                let mut out = <$to>::empty();
1355                $(
1356                    if f.contains(<$from>::$flag) {
1357                        out |= <$to>::$flag;
1358                    }
1359                )+
1360                out
1361            }
1362        }
1363    }
1364}
1365
1366macro_rules! convert_flags_bidirectional {
1367    ($from:ty, $to:ty, $($rest:tt)*) => {
1368        convert_flags!($from, $to, $($rest)*);
1369        convert_flags!($to, $from, $($rest)*);
1370    }
1371}
1372
1373convert_flags_bidirectional!(
1374    FdFlags,
1375    types::Fdflags,
1376    APPEND,
1377    DSYNC,
1378    NONBLOCK,
1379    RSYNC,
1380    SYNC
1381);
1382
1383convert_flags_bidirectional!(RiFlags, types::Riflags, RECV_PEEK, RECV_WAITALL);
1384
1385convert_flags_bidirectional!(RoFlags, types::Roflags, RECV_DATA_TRUNCATED);
1386
1387convert_flags_bidirectional!(SdFlags, types::Sdflags, RD, WR);
1388
1389impl From<&types::Oflags> for OFlags {
1390    fn from(oflags: &types::Oflags) -> OFlags {
1391        let mut out = OFlags::empty();
1392        if oflags.contains(types::Oflags::CREAT) {
1393            out = out | OFlags::CREATE;
1394        }
1395        if oflags.contains(types::Oflags::DIRECTORY) {
1396            out = out | OFlags::DIRECTORY;
1397        }
1398        if oflags.contains(types::Oflags::EXCL) {
1399            out = out | OFlags::EXCLUSIVE;
1400        }
1401        if oflags.contains(types::Oflags::TRUNC) {
1402            out = out | OFlags::TRUNCATE;
1403        }
1404        out
1405    }
1406}
1407
1408impl From<&OFlags> for types::Oflags {
1409    fn from(oflags: &OFlags) -> types::Oflags {
1410        let mut out = types::Oflags::empty();
1411        if oflags.contains(OFlags::CREATE) {
1412            out = out | types::Oflags::CREAT;
1413        }
1414        if oflags.contains(OFlags::DIRECTORY) {
1415            out = out | types::Oflags::DIRECTORY;
1416        }
1417        if oflags.contains(OFlags::EXCLUSIVE) {
1418            out = out | types::Oflags::EXCL;
1419        }
1420        if oflags.contains(OFlags::TRUNCATE) {
1421            out = out | types::Oflags::TRUNC;
1422        }
1423        out
1424    }
1425}
1426impl From<Filestat> for types::Filestat {
1427    fn from(stat: Filestat) -> types::Filestat {
1428        types::Filestat {
1429            dev: stat.device_id,
1430            ino: stat.inode,
1431            filetype: types::Filetype::from(&stat.filetype),
1432            nlink: stat.nlink,
1433            size: stat.size,
1434            atim: stat
1435                .atim
1436                .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1437                .unwrap_or(0),
1438            mtim: stat
1439                .mtim
1440                .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1441                .unwrap_or(0),
1442            ctim: stat
1443                .ctim
1444                .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1445                .unwrap_or(0),
1446        }
1447    }
1448}
1449
1450impl TryFrom<&ReaddirEntity> for types::Dirent {
1451    type Error = Error;
1452    fn try_from(e: &ReaddirEntity) -> Result<types::Dirent, Error> {
1453        Ok(types::Dirent {
1454            d_ino: e.inode,
1455            d_namlen: e.name.as_bytes().len().try_into()?,
1456            d_type: types::Filetype::from(&e.filetype),
1457            d_next: e.next.into(),
1458        })
1459    }
1460}
1461
1462fn dirent_bytes(dirent: types::Dirent) -> Vec<u8> {
1463    use wiggle::GuestType;
1464    assert_eq!(
1465        types::Dirent::guest_size(),
1466        std::mem::size_of::<types::Dirent>() as u32,
1467        "Dirent guest repr and host repr should match"
1468    );
1469    assert_eq!(
1470        1,
1471        std::mem::size_of_val(&dirent.d_type),
1472        "Dirent member d_type should be endian-invariant"
1473    );
1474    let size = types::Dirent::guest_size()
1475        .try_into()
1476        .expect("Dirent is smaller than 2^32");
1477    let mut bytes = Vec::with_capacity(size);
1478    bytes.resize(size, 0);
1479    let ptr = bytes.as_mut_ptr().cast::<types::Dirent>();
1480    let guest_dirent = types::Dirent {
1481        d_ino: dirent.d_ino.to_le(),
1482        d_namlen: dirent.d_namlen.to_le(),
1483        d_type: dirent.d_type, // endian-invariant
1484        d_next: dirent.d_next.to_le(),
1485    };
1486    unsafe { ptr.write_unaligned(guest_dirent) };
1487    bytes
1488}
1489
1490impl From<&RwEventFlags> for types::Eventrwflags {
1491    fn from(flags: &RwEventFlags) -> types::Eventrwflags {
1492        let mut out = types::Eventrwflags::empty();
1493        if flags.contains(RwEventFlags::HANGUP) {
1494            out = out | types::Eventrwflags::FD_READWRITE_HANGUP;
1495        }
1496        out
1497    }
1498}
1499
1500fn fd_readwrite_empty() -> types::EventFdReadwrite {
1501    types::EventFdReadwrite {
1502        nbytes: 0,
1503        flags: types::Eventrwflags::empty(),
1504    }
1505}
1506
1507fn systimespec(
1508    set: bool,
1509    ts: types::Timestamp,
1510    now: bool,
1511) -> Result<Option<SystemTimeSpec>, Error> {
1512    if set && now {
1513        Err(Error::invalid_argument())
1514    } else if set {
1515        Ok(Some(SystemTimeSpec::Absolute(
1516            SystemClock::UNIX_EPOCH + Duration::from_nanos(ts),
1517        )))
1518    } else if now {
1519        Ok(Some(SystemTimeSpec::SymbolicNow))
1520    } else {
1521        Ok(None)
1522    }
1523}
1524
1525// This is the default subset of base Rights reported for directories prior to
1526// https://github.com/bytecodealliance/wasmtime/pull/6265. Some
1527// implementations still expect this set of rights to be reported.
1528pub(crate) fn directory_base_rights() -> types::Rights {
1529    types::Rights::PATH_CREATE_DIRECTORY
1530        | types::Rights::PATH_CREATE_FILE
1531        | types::Rights::PATH_LINK_SOURCE
1532        | types::Rights::PATH_LINK_TARGET
1533        | types::Rights::PATH_OPEN
1534        | types::Rights::FD_READDIR
1535        | types::Rights::PATH_READLINK
1536        | types::Rights::PATH_RENAME_SOURCE
1537        | types::Rights::PATH_RENAME_TARGET
1538        | types::Rights::PATH_SYMLINK
1539        | types::Rights::PATH_REMOVE_DIRECTORY
1540        | types::Rights::PATH_UNLINK_FILE
1541        | types::Rights::PATH_FILESTAT_GET
1542        | types::Rights::PATH_FILESTAT_SET_TIMES
1543        | types::Rights::FD_FILESTAT_GET
1544        | types::Rights::FD_FILESTAT_SET_TIMES
1545}
1546
1547// This is the default subset of inheriting Rights reported for directories
1548// prior to https://github.com/bytecodealliance/wasmtime/pull/6265. Some
1549// implementations still expect this set of rights to be reported.
1550pub(crate) fn directory_inheriting_rights() -> types::Rights {
1551    types::Rights::FD_DATASYNC
1552        | types::Rights::FD_READ
1553        | types::Rights::FD_SEEK
1554        | types::Rights::FD_FDSTAT_SET_FLAGS
1555        | types::Rights::FD_SYNC
1556        | types::Rights::FD_TELL
1557        | types::Rights::FD_WRITE
1558        | types::Rights::FD_ADVISE
1559        | types::Rights::FD_ALLOCATE
1560        | types::Rights::FD_FILESTAT_GET
1561        | types::Rights::FD_FILESTAT_SET_SIZE
1562        | types::Rights::FD_FILESTAT_SET_TIMES
1563        | types::Rights::POLL_FD_READWRITE
1564        | directory_base_rights()
1565}