Skip to main content

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(ref 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(ref 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        if !table.contains_key(to) {
582            return Err(Error::badf());
583        }
584        table.renumber(from, to)
585    }
586
587    async fn fd_seek(
588        &mut self,
589        _memory: &mut GuestMemory<'_>,
590        fd: types::Fd,
591        offset: types::Filedelta,
592        whence: types::Whence,
593    ) -> Result<types::Filesize, Error> {
594        use std::io::SeekFrom;
595        let whence = match whence {
596            types::Whence::Cur => SeekFrom::Current(offset),
597            types::Whence::End => SeekFrom::End(offset),
598            types::Whence::Set => {
599                SeekFrom::Start(offset.try_into().map_err(|_| Error::invalid_argument())?)
600            }
601        };
602        let newoffset = self
603            .table()
604            .get_file(u32::from(fd))?
605            .file
606            .seek(whence)
607            .await?;
608        Ok(newoffset)
609    }
610
611    async fn fd_sync(&mut self, _memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {
612        self.table().get_file(u32::from(fd))?.file.sync().await?;
613        Ok(())
614    }
615
616    async fn fd_tell(
617        &mut self,
618        _memory: &mut GuestMemory<'_>,
619        fd: types::Fd,
620    ) -> Result<types::Filesize, Error> {
621        let offset = self
622            .table()
623            .get_file(u32::from(fd))?
624            .file
625            .seek(std::io::SeekFrom::Current(0))
626            .await?;
627        Ok(offset)
628    }
629
630    async fn fd_readdir(
631        &mut self,
632        memory: &mut GuestMemory<'_>,
633        fd: types::Fd,
634        mut buf: GuestPtr<u8>,
635        buf_len: types::Size,
636        cookie: types::Dircookie,
637    ) -> Result<types::Size, Error> {
638        let mut bufused = 0;
639        for entity in self
640            .table()
641            .get_dir(u32::from(fd))?
642            .dir
643            .readdir(ReaddirCursor::from(cookie))
644            .await?
645        {
646            let entity = entity?;
647            let dirent_raw = dirent_bytes(types::Dirent::try_from(&entity)?);
648            let dirent_len: types::Size = dirent_raw.len().try_into()?;
649            let name_raw = entity.name.as_bytes();
650            let name_len: types::Size = name_raw.len().try_into()?;
651
652            // Copy as many bytes of the dirent as we can, up to the end of the buffer
653            let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused);
654            let raw = buf.as_array(dirent_copy_len);
655            memory.copy_from_slice(&dirent_raw[..dirent_copy_len as usize], raw)?;
656
657            // If the dirent struct wasn't compiled entirely, return that we filled the buffer, which
658            // tells libc that we're not at EOF.
659            if dirent_copy_len < dirent_len {
660                return Ok(buf_len);
661            }
662
663            buf = buf.add(dirent_copy_len)?;
664            bufused += dirent_copy_len;
665
666            // Copy as many bytes of the name as we can, up to the end of the buffer
667            let name_copy_len = std::cmp::min(name_len, buf_len - bufused);
668            let raw = buf.as_array(name_copy_len);
669            memory.copy_from_slice(&name_raw[..name_copy_len as usize], raw)?;
670
671            // If the dirent struct wasn't copied entirely, return that we filled the buffer, which
672            // tells libc that we're not at EOF
673
674            if name_copy_len < name_len {
675                return Ok(buf_len);
676            }
677
678            buf = buf.add(name_copy_len)?;
679            bufused += name_copy_len;
680        }
681        Ok(bufused)
682    }
683
684    async fn path_create_directory(
685        &mut self,
686        memory: &mut GuestMemory<'_>,
687        dirfd: types::Fd,
688        path: GuestPtr<str>,
689    ) -> Result<(), Error> {
690        self.table()
691            .get_dir(u32::from(dirfd))?
692            .dir
693            .create_dir(memory.as_cow_str(path)?.deref())
694            .await
695    }
696
697    async fn path_filestat_get(
698        &mut self,
699        memory: &mut GuestMemory<'_>,
700        dirfd: types::Fd,
701        flags: types::Lookupflags,
702        path: GuestPtr<str>,
703    ) -> Result<types::Filestat, Error> {
704        let filestat = self
705            .table()
706            .get_dir(u32::from(dirfd))?
707            .dir
708            .get_path_filestat(
709                memory.as_cow_str(path)?.deref(),
710                flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
711            )
712            .await?;
713        Ok(types::Filestat::from(filestat))
714    }
715
716    async fn path_filestat_set_times(
717        &mut self,
718        memory: &mut GuestMemory<'_>,
719        dirfd: types::Fd,
720        flags: types::Lookupflags,
721        path: GuestPtr<str>,
722        atim: types::Timestamp,
723        mtim: types::Timestamp,
724        fst_flags: types::Fstflags,
725    ) -> Result<(), Error> {
726        let set_atim = fst_flags.contains(types::Fstflags::ATIM);
727        let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);
728        let set_mtim = fst_flags.contains(types::Fstflags::MTIM);
729        let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);
730
731        let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;
732        let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;
733        self.table()
734            .get_dir(u32::from(dirfd))?
735            .dir
736            .set_times(
737                memory.as_cow_str(path)?.deref(),
738                atim,
739                mtim,
740                flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
741            )
742            .await
743    }
744
745    async fn path_link(
746        &mut self,
747        memory: &mut GuestMemory<'_>,
748        src_fd: types::Fd,
749        src_flags: types::Lookupflags,
750        src_path: GuestPtr<str>,
751        target_fd: types::Fd,
752        target_path: GuestPtr<str>,
753    ) -> Result<(), Error> {
754        let table = self.table();
755        let src_dir = table.get_dir(u32::from(src_fd))?;
756        let target_dir = table.get_dir(u32::from(target_fd))?;
757        let symlink_follow = src_flags.contains(types::Lookupflags::SYMLINK_FOLLOW);
758        if symlink_follow {
759            return Err(Error::invalid_argument()
760                .context("symlink following on path_link is not supported"));
761        }
762
763        src_dir
764            .dir
765            .hard_link(
766                memory.as_cow_str(src_path)?.deref(),
767                target_dir.dir.deref(),
768                memory.as_cow_str(target_path)?.deref(),
769            )
770            .await
771    }
772
773    async fn path_open(
774        &mut self,
775        memory: &mut GuestMemory<'_>,
776        dirfd: types::Fd,
777        dirflags: types::Lookupflags,
778        path: GuestPtr<str>,
779        oflags: types::Oflags,
780        fs_rights_base: types::Rights,
781        _fs_rights_inheriting: types::Rights,
782        fdflags: types::Fdflags,
783    ) -> Result<types::Fd, Error> {
784        let table = self.table();
785        let dirfd = u32::from(dirfd);
786        if table.is::<FileEntry>(dirfd) {
787            return Err(Error::not_dir());
788        }
789        let dir_entry = table.get_dir(dirfd)?;
790
791        let symlink_follow = dirflags.contains(types::Lookupflags::SYMLINK_FOLLOW);
792
793        let oflags = OFlags::from(&oflags);
794        let fdflags = FdFlags::from(fdflags);
795        let path = memory.as_cow_str(path)?;
796
797        let read = fs_rights_base.contains(types::Rights::FD_READ);
798        let write = fs_rights_base.contains(types::Rights::FD_WRITE);
799        let access_mode = if read {
800            FileAccessMode::READ
801        } else {
802            FileAccessMode::empty()
803        } | if write {
804            FileAccessMode::WRITE
805        } else {
806            FileAccessMode::empty()
807        };
808
809        let file = dir_entry
810            .dir
811            .open_file(symlink_follow, path.deref(), oflags, read, write, fdflags)
812            .await?;
813        drop(dir_entry);
814
815        let fd = match file {
816            OpenResult::File(file) => table.push(Arc::new(FileEntry::new(file, access_mode)))?,
817            OpenResult::Dir(child_dir) => table.push(Arc::new(DirEntry::new(None, child_dir)))?,
818        };
819        Ok(types::Fd::from(fd))
820    }
821
822    async fn path_readlink(
823        &mut self,
824        memory: &mut GuestMemory<'_>,
825        dirfd: types::Fd,
826        path: GuestPtr<str>,
827        buf: GuestPtr<u8>,
828        buf_len: types::Size,
829    ) -> Result<types::Size, Error> {
830        let link = self
831            .table()
832            .get_dir(u32::from(dirfd))?
833            .dir
834            .read_link(memory.as_cow_str(path)?.deref())
835            .await?
836            .into_os_string()
837            .into_string()
838            .map_err(|_| Error::illegal_byte_sequence().context("link contents"))?;
839        let link_bytes = link.as_bytes();
840        // Like posix readlink(2), silently truncate links when they are larger than the
841        // destination buffer:
842        let link_len = std::cmp::min(link_bytes.len(), buf_len as usize);
843        let buf = buf.as_array(link_len as u32);
844        memory.copy_from_slice(&link_bytes[..link_len], buf)?;
845        Ok(link_len as types::Size)
846    }
847
848    async fn path_remove_directory(
849        &mut self,
850        memory: &mut GuestMemory<'_>,
851        dirfd: types::Fd,
852        path: GuestPtr<str>,
853    ) -> Result<(), Error> {
854        self.table()
855            .get_dir(u32::from(dirfd))?
856            .dir
857            .remove_dir(memory.as_cow_str(path)?.deref())
858            .await
859    }
860
861    async fn path_rename(
862        &mut self,
863        memory: &mut GuestMemory<'_>,
864        src_fd: types::Fd,
865        src_path: GuestPtr<str>,
866        dest_fd: types::Fd,
867        dest_path: GuestPtr<str>,
868    ) -> Result<(), Error> {
869        let table = self.table();
870        let src_dir = table.get_dir(u32::from(src_fd))?;
871        let dest_dir = table.get_dir(u32::from(dest_fd))?;
872        src_dir
873            .dir
874            .rename(
875                memory.as_cow_str(src_path)?.deref(),
876                dest_dir.dir.deref(),
877                memory.as_cow_str(dest_path)?.deref(),
878            )
879            .await
880    }
881
882    async fn path_symlink(
883        &mut self,
884        memory: &mut GuestMemory<'_>,
885        src_path: GuestPtr<str>,
886        dirfd: types::Fd,
887        dest_path: GuestPtr<str>,
888    ) -> Result<(), Error> {
889        self.table()
890            .get_dir(u32::from(dirfd))?
891            .dir
892            .symlink(
893                memory.as_cow_str(src_path)?.deref(),
894                memory.as_cow_str(dest_path)?.deref(),
895            )
896            .await
897    }
898
899    async fn path_unlink_file(
900        &mut self,
901        memory: &mut GuestMemory<'_>,
902        dirfd: types::Fd,
903        path: GuestPtr<str>,
904    ) -> Result<(), Error> {
905        self.table()
906            .get_dir(u32::from(dirfd))?
907            .dir
908            .unlink_file(memory.as_cow_str(path)?.deref())
909            .await
910    }
911
912    async fn poll_oneoff(
913        &mut self,
914        memory: &mut GuestMemory<'_>,
915        subs: GuestPtr<types::Subscription>,
916        events: GuestPtr<types::Event>,
917        nsubscriptions: types::Size,
918    ) -> Result<types::Size, Error> {
919        if nsubscriptions == 0 {
920            return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));
921        }
922
923        // Special-case a `poll_oneoff` which is just sleeping on a single
924        // relative timer event, such as what WASI libc uses to implement sleep
925        // functions. This supports all clock IDs, because POSIX says that
926        // `clock_settime` doesn't effect relative sleeps.
927        if nsubscriptions == 1 {
928            let sub = memory.read(subs)?;
929            if let types::SubscriptionU::Clock(clocksub) = sub.u {
930                if !clocksub
931                    .flags
932                    .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
933                {
934                    self.sched
935                        .sleep(Duration::from_nanos(clocksub.timeout))
936                        .await?;
937                    memory.write(
938                        events,
939                        types::Event {
940                            userdata: sub.userdata,
941                            error: types::Errno::Success,
942                            type_: types::Eventtype::Clock,
943                            fd_readwrite: fd_readwrite_empty(),
944                        },
945                    )?;
946                    return Ok(1);
947                }
948            }
949        }
950
951        let table = &self.table;
952        // We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside
953        let mut read_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();
954        let mut write_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();
955
956        let mut poll = Poll::new();
957
958        let subs = subs.as_array(nsubscriptions);
959        for sub_elem in subs.iter() {
960            let sub_ptr = sub_elem?;
961            let sub = memory.read(sub_ptr)?;
962            match sub.u {
963                types::SubscriptionU::Clock(clocksub) => match clocksub.id {
964                    types::Clockid::Monotonic => {
965                        let clock = self.clocks.monotonic()?;
966                        let precision = Duration::from_nanos(clocksub.precision);
967                        let duration = Duration::from_nanos(clocksub.timeout);
968                        let start = if clocksub
969                            .flags
970                            .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
971                        {
972                            clock.creation_time
973                        } else {
974                            clock.abs_clock.now(precision)
975                        };
976                        let deadline = start
977                            .checked_add(duration)
978                            .ok_or_else(|| Error::overflow().context("deadline"))?;
979                        poll.subscribe_monotonic_clock(
980                            &*clock.abs_clock,
981                            deadline,
982                            precision,
983                            sub.userdata.into(),
984                        )
985                    }
986                    types::Clockid::Realtime => {
987                        // POSIX specifies that functions like `nanosleep` and others use the
988                        // `REALTIME` clock. But it also says that `clock_settime` has no effect
989                        // on threads waiting in these functions. MONOTONIC should always have
990                        // resolution at least as good as REALTIME, so we can translate a
991                        // non-absolute `REALTIME` request into a `MONOTONIC` request.
992                        let clock = self.clocks.monotonic()?;
993                        let precision = Duration::from_nanos(clocksub.precision);
994                        let duration = Duration::from_nanos(clocksub.timeout);
995                        let deadline = if clocksub
996                            .flags
997                            .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
998                        {
999                            return Err(Error::not_supported());
1000                        } else {
1001                            clock
1002                                .abs_clock
1003                                .now(precision)
1004                                .checked_add(duration)
1005                                .ok_or_else(|| Error::overflow().context("deadline"))?
1006                        };
1007                        poll.subscribe_monotonic_clock(
1008                            &*clock.abs_clock,
1009                            deadline,
1010                            precision,
1011                            sub.userdata.into(),
1012                        )
1013                    }
1014                    _ => Err(Error::invalid_argument()
1015                        .context("timer subscriptions only support monotonic timer"))?,
1016                },
1017                types::SubscriptionU::FdRead(readsub) => {
1018                    let fd = readsub.file_descriptor;
1019                    let file_ref = table.get_file(u32::from(fd))?;
1020                    read_refs.push((file_ref, Some(sub.userdata.into())));
1021                }
1022                types::SubscriptionU::FdWrite(writesub) => {
1023                    let fd = writesub.file_descriptor;
1024                    let file_ref = table.get_file(u32::from(fd))?;
1025                    write_refs.push((file_ref, Some(sub.userdata.into())));
1026                }
1027            }
1028        }
1029
1030        let mut read_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
1031        for (file_lock, userdata) in read_refs.iter_mut() {
1032            read_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));
1033        }
1034
1035        for (f, ud) in read_mut_refs.iter_mut() {
1036            poll.subscribe_read(*f, *ud);
1037        }
1038
1039        let mut write_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
1040        for (file_lock, userdata) in write_refs.iter_mut() {
1041            write_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));
1042        }
1043
1044        for (f, ud) in write_mut_refs.iter_mut() {
1045            poll.subscribe_write(*f, *ud);
1046        }
1047
1048        self.sched.poll_oneoff(&mut poll).await?;
1049
1050        let results = poll.results();
1051        let num_results = results.len();
1052        assert!(
1053            num_results <= nsubscriptions as usize,
1054            "results exceeds subscriptions"
1055        );
1056        let events = events.as_array(
1057            num_results
1058                .try_into()
1059                .expect("not greater than nsubscriptions"),
1060        );
1061        for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) {
1062            let event_ptr = event_elem?;
1063            let userdata: types::Userdata = userdata.into();
1064            memory.write(
1065                event_ptr,
1066                match result {
1067                    SubscriptionResult::Read(r) => {
1068                        let type_ = types::Eventtype::FdRead;
1069                        match r {
1070                            Ok((nbytes, flags)) => types::Event {
1071                                userdata,
1072                                error: types::Errno::Success,
1073                                type_,
1074                                fd_readwrite: types::EventFdReadwrite {
1075                                    nbytes,
1076                                    flags: types::Eventrwflags::from(&flags),
1077                                },
1078                            },
1079                            Err(e) => types::Event {
1080                                userdata,
1081                                error: e.downcast().map_err(Error::trap)?,
1082                                type_,
1083                                fd_readwrite: fd_readwrite_empty(),
1084                            },
1085                        }
1086                    }
1087                    SubscriptionResult::Write(r) => {
1088                        let type_ = types::Eventtype::FdWrite;
1089                        match r {
1090                            Ok((nbytes, flags)) => types::Event {
1091                                userdata,
1092                                error: types::Errno::Success,
1093                                type_,
1094                                fd_readwrite: types::EventFdReadwrite {
1095                                    nbytes,
1096                                    flags: types::Eventrwflags::from(&flags),
1097                                },
1098                            },
1099                            Err(e) => types::Event {
1100                                userdata,
1101                                error: e.downcast().map_err(Error::trap)?,
1102                                type_,
1103                                fd_readwrite: fd_readwrite_empty(),
1104                            },
1105                        }
1106                    }
1107                    SubscriptionResult::MonotonicClock(r) => {
1108                        let type_ = types::Eventtype::Clock;
1109                        types::Event {
1110                            userdata,
1111                            error: match r {
1112                                Ok(()) => types::Errno::Success,
1113                                Err(e) => e.downcast().map_err(Error::trap)?,
1114                            },
1115                            type_,
1116                            fd_readwrite: fd_readwrite_empty(),
1117                        }
1118                    }
1119                },
1120            )?;
1121        }
1122
1123        Ok(num_results.try_into().expect("results fit into memory"))
1124    }
1125
1126    async fn proc_exit(
1127        &mut self,
1128        _memory: &mut GuestMemory<'_>,
1129        status: types::Exitcode,
1130    ) -> anyhow::Error {
1131        // Check that the status is within WASI's range.
1132        if status < 126 {
1133            I32Exit(status as i32).into()
1134        } else {
1135            anyhow::Error::msg("exit with invalid exit status outside of [0..126)")
1136        }
1137    }
1138
1139    async fn proc_raise(
1140        &mut self,
1141        _memory: &mut GuestMemory<'_>,
1142        _sig: types::Signal,
1143    ) -> Result<(), Error> {
1144        Err(Error::trap(anyhow::Error::msg("proc_raise unsupported")))
1145    }
1146
1147    async fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), Error> {
1148        self.sched.sched_yield().await
1149    }
1150
1151    async fn random_get(
1152        &mut self,
1153        memory: &mut GuestMemory<'_>,
1154        buf: GuestPtr<u8>,
1155        buf_len: types::Size,
1156    ) -> Result<(), Error> {
1157        let buf = buf.as_array(buf_len);
1158        if memory.is_shared_memory() {
1159            // If the Wasm memory is shared, copy to an intermediate buffer to
1160            // avoid Rust unsafety (i.e., the called function could rely on
1161            // `&mut [u8]`'s exclusive ownership which is not guaranteed due to
1162            // potential access from other threads).
1163            let mut copied: u32 = 0;
1164            while copied < buf.len() {
1165                let len = (buf.len() - copied).min(MAX_SHARED_BUFFER_SIZE as u32);
1166                let mut tmp = vec![0; len as usize];
1167                self.random.lock().unwrap().try_fill_bytes(&mut tmp)?;
1168                let dest = buf.get_range(copied..copied + len).unwrap();
1169                memory.copy_from_slice(&tmp, dest)?;
1170                copied += len;
1171            }
1172        } else {
1173            // If the Wasm memory is non-shared, copy directly into the linear
1174            // memory.
1175            let mem = &mut memory.as_slice_mut(buf)?.unwrap();
1176            self.random.lock().unwrap().try_fill_bytes(mem)?;
1177        }
1178        Ok(())
1179    }
1180
1181    async fn sock_accept(
1182        &mut self,
1183        _memory: &mut GuestMemory<'_>,
1184        fd: types::Fd,
1185        flags: types::Fdflags,
1186    ) -> Result<types::Fd, Error> {
1187        let table = self.table();
1188        let f = table.get_file(u32::from(fd))?;
1189        let file = f.file.sock_accept(FdFlags::from(flags)).await?;
1190        let fd = table.push(Arc::new(FileEntry::new(file, FileAccessMode::all())))?;
1191        Ok(types::Fd::from(fd))
1192    }
1193
1194    async fn sock_recv(
1195        &mut self,
1196        memory: &mut GuestMemory<'_>,
1197        fd: types::Fd,
1198        ri_data: types::IovecArray,
1199        ri_flags: types::Riflags,
1200    ) -> Result<(types::Size, types::Roflags), Error> {
1201        let f = self.table().get_file(u32::from(fd))?;
1202
1203        let iovs: Vec<wiggle::GuestPtr<[u8]>> = ri_data
1204            .iter()
1205            .map(|iov_ptr| {
1206                let iov_ptr = iov_ptr?;
1207                let iov: types::Iovec = memory.read(iov_ptr)?;
1208                Ok(iov.buf.as_array(iov.buf_len))
1209            })
1210            .collect::<Result<_, Error>>()?;
1211
1212        // If the first iov structure is from shared memory we can safely assume
1213        // all the rest will be. We then read into memory based on the memory's
1214        // shared-ness:
1215        // - if not shared, we copy directly into the Wasm memory
1216        // - if shared, we use an intermediate buffer; this avoids Rust unsafety
1217        //   due to holding on to a `&mut [u8]` of Wasm memory when we cannot
1218        //   guarantee the `&mut` exclusivity--other threads could be modifying
1219        //   the data as this functions writes to it. Though likely there is no
1220        //   issue with OS writing to io structs in multi-threaded scenarios,
1221        //   since we do not know here if `&dyn WasiFile` does anything else
1222        //   (e.g., read), we cautiously incur some performance overhead by
1223        //   copying twice.
1224        let is_shared_memory = memory.is_shared_memory();
1225        let (bytes_read, ro_flags) = if is_shared_memory {
1226            // For shared memory, read into an intermediate buffer. Only the
1227            // first iov will be filled and even then the read is capped by the
1228            // `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.
1229            let iov = iovs.into_iter().next();
1230            if let Some(iov) = iov {
1231                let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];
1232                let (bytes_read, ro_flags) = f
1233                    .file
1234                    .sock_recv(&mut [IoSliceMut::new(&mut buffer)], RiFlags::from(ri_flags))
1235                    .await?;
1236                let iov = iov
1237                    .get_range(0..bytes_read.try_into()?)
1238                    .expect("it should always be possible to slice the iov smaller");
1239                memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;
1240                (bytes_read, ro_flags)
1241            } else {
1242                return Ok((0, RoFlags::empty().into()));
1243            }
1244        } else {
1245            // Convert all of the unsafe guest slices to safe ones--this uses
1246            // Wiggle's internal borrow checker to ensure no overlaps. We assume
1247            // here that, because the memory is not shared, there are no other
1248            // threads to access it while it is written to.
1249            let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {
1250                Some(iov) => memory.as_slice_mut(iov)?.unwrap(),
1251                None => &mut [],
1252            };
1253
1254            // Read directly into the Wasm memory.
1255            f.file
1256                .sock_recv(&mut [IoSliceMut::new(guest_slice)], RiFlags::from(ri_flags))
1257                .await?
1258        };
1259
1260        Ok((types::Size::try_from(bytes_read)?, ro_flags.into()))
1261    }
1262
1263    async fn sock_send(
1264        &mut self,
1265        memory: &mut GuestMemory<'_>,
1266        fd: types::Fd,
1267        si_data: types::CiovecArray,
1268        _si_flags: types::Siflags,
1269    ) -> Result<types::Size, Error> {
1270        let f = self.table().get_file(u32::from(fd))?;
1271
1272        let guest_slices: Vec<Cow<[u8]>> = si_data
1273            .iter()
1274            .map(|iov_ptr| {
1275                let iov_ptr = iov_ptr?;
1276                let iov: types::Ciovec = memory.read(iov_ptr)?;
1277                Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)
1278            })
1279            .collect::<Result<_, Error>>()?;
1280
1281        let ioslices: Vec<IoSlice> = guest_slices
1282            .iter()
1283            .map(|s| IoSlice::new(s.deref()))
1284            .collect();
1285        let bytes_written = f.file.sock_send(&ioslices, SiFlags::empty()).await?;
1286
1287        Ok(types::Size::try_from(bytes_written)?)
1288    }
1289
1290    async fn sock_shutdown(
1291        &mut self,
1292        _memory: &mut GuestMemory<'_>,
1293        fd: types::Fd,
1294        how: types::Sdflags,
1295    ) -> Result<(), Error> {
1296        let f = self.table().get_file(u32::from(fd))?;
1297
1298        f.file.sock_shutdown(SdFlags::from(how)).await
1299    }
1300}
1301
1302impl From<types::Advice> for Advice {
1303    fn from(advice: types::Advice) -> Advice {
1304        match advice {
1305            types::Advice::Normal => Advice::Normal,
1306            types::Advice::Sequential => Advice::Sequential,
1307            types::Advice::Random => Advice::Random,
1308            types::Advice::Willneed => Advice::WillNeed,
1309            types::Advice::Dontneed => Advice::DontNeed,
1310            types::Advice::Noreuse => Advice::NoReuse,
1311        }
1312    }
1313}
1314
1315impl From<&FdStat> for types::Fdstat {
1316    fn from(fdstat: &FdStat) -> types::Fdstat {
1317        let mut fs_rights_base = types::Rights::empty();
1318        if fdstat.access_mode.contains(FileAccessMode::READ) {
1319            fs_rights_base |= types::Rights::FD_READ;
1320        }
1321        if fdstat.access_mode.contains(FileAccessMode::WRITE) {
1322            fs_rights_base |= types::Rights::FD_WRITE;
1323        }
1324        types::Fdstat {
1325            fs_filetype: types::Filetype::from(&fdstat.filetype),
1326            fs_rights_base,
1327            fs_rights_inheriting: types::Rights::empty(),
1328            fs_flags: types::Fdflags::from(fdstat.flags),
1329        }
1330    }
1331}
1332
1333impl From<&FileType> for types::Filetype {
1334    fn from(ft: &FileType) -> types::Filetype {
1335        match ft {
1336            FileType::Directory => types::Filetype::Directory,
1337            FileType::BlockDevice => types::Filetype::BlockDevice,
1338            FileType::CharacterDevice => types::Filetype::CharacterDevice,
1339            FileType::RegularFile => types::Filetype::RegularFile,
1340            FileType::SocketDgram => types::Filetype::SocketDgram,
1341            FileType::SocketStream => types::Filetype::SocketStream,
1342            FileType::SymbolicLink => types::Filetype::SymbolicLink,
1343            FileType::Unknown => types::Filetype::Unknown,
1344            FileType::Pipe => types::Filetype::Unknown,
1345        }
1346    }
1347}
1348
1349macro_rules! convert_flags {
1350    ($from:ty, $to:ty, $($flag:ident),+) => {
1351        impl From<$from> for $to {
1352            fn from(f: $from) -> $to {
1353                let mut out = <$to>::empty();
1354                $(
1355                    if f.contains(<$from>::$flag) {
1356                        out |= <$to>::$flag;
1357                    }
1358                )+
1359                out
1360            }
1361        }
1362    }
1363}
1364
1365macro_rules! convert_flags_bidirectional {
1366    ($from:ty, $to:ty, $($rest:tt)*) => {
1367        convert_flags!($from, $to, $($rest)*);
1368        convert_flags!($to, $from, $($rest)*);
1369    }
1370}
1371
1372convert_flags_bidirectional!(
1373    FdFlags,
1374    types::Fdflags,
1375    APPEND,
1376    DSYNC,
1377    NONBLOCK,
1378    RSYNC,
1379    SYNC
1380);
1381
1382convert_flags_bidirectional!(RiFlags, types::Riflags, RECV_PEEK, RECV_WAITALL);
1383
1384convert_flags_bidirectional!(RoFlags, types::Roflags, RECV_DATA_TRUNCATED);
1385
1386convert_flags_bidirectional!(SdFlags, types::Sdflags, RD, WR);
1387
1388impl From<&types::Oflags> for OFlags {
1389    fn from(oflags: &types::Oflags) -> OFlags {
1390        let mut out = OFlags::empty();
1391        if oflags.contains(types::Oflags::CREAT) {
1392            out = out | OFlags::CREATE;
1393        }
1394        if oflags.contains(types::Oflags::DIRECTORY) {
1395            out = out | OFlags::DIRECTORY;
1396        }
1397        if oflags.contains(types::Oflags::EXCL) {
1398            out = out | OFlags::EXCLUSIVE;
1399        }
1400        if oflags.contains(types::Oflags::TRUNC) {
1401            out = out | OFlags::TRUNCATE;
1402        }
1403        out
1404    }
1405}
1406
1407impl From<&OFlags> for types::Oflags {
1408    fn from(oflags: &OFlags) -> types::Oflags {
1409        let mut out = types::Oflags::empty();
1410        if oflags.contains(OFlags::CREATE) {
1411            out = out | types::Oflags::CREAT;
1412        }
1413        if oflags.contains(OFlags::DIRECTORY) {
1414            out = out | types::Oflags::DIRECTORY;
1415        }
1416        if oflags.contains(OFlags::EXCLUSIVE) {
1417            out = out | types::Oflags::EXCL;
1418        }
1419        if oflags.contains(OFlags::TRUNCATE) {
1420            out = out | types::Oflags::TRUNC;
1421        }
1422        out
1423    }
1424}
1425impl From<Filestat> for types::Filestat {
1426    fn from(stat: Filestat) -> types::Filestat {
1427        types::Filestat {
1428            dev: stat.device_id,
1429            ino: stat.inode,
1430            filetype: types::Filetype::from(&stat.filetype),
1431            nlink: stat.nlink,
1432            size: stat.size,
1433            atim: stat
1434                .atim
1435                .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1436                .unwrap_or(0),
1437            mtim: stat
1438                .mtim
1439                .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1440                .unwrap_or(0),
1441            ctim: stat
1442                .ctim
1443                .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1444                .unwrap_or(0),
1445        }
1446    }
1447}
1448
1449impl TryFrom<&ReaddirEntity> for types::Dirent {
1450    type Error = Error;
1451    fn try_from(e: &ReaddirEntity) -> Result<types::Dirent, Error> {
1452        Ok(types::Dirent {
1453            d_ino: e.inode,
1454            d_namlen: e.name.as_bytes().len().try_into()?,
1455            d_type: types::Filetype::from(&e.filetype),
1456            d_next: e.next.into(),
1457        })
1458    }
1459}
1460
1461fn dirent_bytes(dirent: types::Dirent) -> Vec<u8> {
1462    use wiggle::GuestType;
1463    assert_eq!(
1464        types::Dirent::guest_size(),
1465        std::mem::size_of::<types::Dirent>() as u32,
1466        "Dirent guest repr and host repr should match"
1467    );
1468    assert_eq!(
1469        1,
1470        std::mem::size_of_val(&dirent.d_type),
1471        "Dirent member d_type should be endian-invariant"
1472    );
1473    let size = types::Dirent::guest_size()
1474        .try_into()
1475        .expect("Dirent is smaller than 2^32");
1476    let mut bytes = Vec::with_capacity(size);
1477    bytes.resize(size, 0);
1478    let ptr = bytes.as_mut_ptr().cast::<types::Dirent>();
1479    let guest_dirent = types::Dirent {
1480        d_ino: dirent.d_ino.to_le(),
1481        d_namlen: dirent.d_namlen.to_le(),
1482        d_type: dirent.d_type, // endian-invariant
1483        d_next: dirent.d_next.to_le(),
1484    };
1485    unsafe { ptr.write_unaligned(guest_dirent) };
1486    bytes
1487}
1488
1489impl From<&RwEventFlags> for types::Eventrwflags {
1490    fn from(flags: &RwEventFlags) -> types::Eventrwflags {
1491        let mut out = types::Eventrwflags::empty();
1492        if flags.contains(RwEventFlags::HANGUP) {
1493            out = out | types::Eventrwflags::FD_READWRITE_HANGUP;
1494        }
1495        out
1496    }
1497}
1498
1499fn fd_readwrite_empty() -> types::EventFdReadwrite {
1500    types::EventFdReadwrite {
1501        nbytes: 0,
1502        flags: types::Eventrwflags::empty(),
1503    }
1504}
1505
1506fn systimespec(
1507    set: bool,
1508    ts: types::Timestamp,
1509    now: bool,
1510) -> Result<Option<SystemTimeSpec>, Error> {
1511    if set && now {
1512        Err(Error::invalid_argument())
1513    } else if set {
1514        Ok(Some(SystemTimeSpec::Absolute(
1515            SystemClock::UNIX_EPOCH + Duration::from_nanos(ts),
1516        )))
1517    } else if now {
1518        Ok(Some(SystemTimeSpec::SymbolicNow))
1519    } else {
1520        Ok(None)
1521    }
1522}
1523
1524// This is the default subset of base Rights reported for directories prior to
1525// https://github.com/bytecodealliance/wasmtime/pull/6265. Some
1526// implementations still expect this set of rights to be reported.
1527pub(crate) fn directory_base_rights() -> types::Rights {
1528    types::Rights::PATH_CREATE_DIRECTORY
1529        | types::Rights::PATH_CREATE_FILE
1530        | types::Rights::PATH_LINK_SOURCE
1531        | types::Rights::PATH_LINK_TARGET
1532        | types::Rights::PATH_OPEN
1533        | types::Rights::FD_READDIR
1534        | types::Rights::PATH_READLINK
1535        | types::Rights::PATH_RENAME_SOURCE
1536        | types::Rights::PATH_RENAME_TARGET
1537        | types::Rights::PATH_SYMLINK
1538        | types::Rights::PATH_REMOVE_DIRECTORY
1539        | types::Rights::PATH_UNLINK_FILE
1540        | types::Rights::PATH_FILESTAT_GET
1541        | types::Rights::PATH_FILESTAT_SET_TIMES
1542        | types::Rights::FD_FILESTAT_GET
1543        | types::Rights::FD_FILESTAT_SET_TIMES
1544}
1545
1546// This is the default subset of inheriting Rights reported for directories
1547// prior to https://github.com/bytecodealliance/wasmtime/pull/6265. Some
1548// implementations still expect this set of rights to be reported.
1549pub(crate) fn directory_inheriting_rights() -> types::Rights {
1550    types::Rights::FD_DATASYNC
1551        | types::Rights::FD_READ
1552        | types::Rights::FD_SEEK
1553        | types::Rights::FD_FDSTAT_SET_FLAGS
1554        | types::Rights::FD_SYNC
1555        | types::Rights::FD_TELL
1556        | types::Rights::FD_WRITE
1557        | types::Rights::FD_ADVISE
1558        | types::Rights::FD_ALLOCATE
1559        | types::Rights::FD_FILESTAT_GET
1560        | types::Rights::FD_FILESTAT_SET_SIZE
1561        | types::Rights::FD_FILESTAT_SET_TIMES
1562        | types::Rights::POLL_FD_READWRITE
1563        | directory_base_rights()
1564}