wasi_common/snapshots/
preview_1.rs

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