wasi_common/snapshots/
preview_1.rs

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