wasi_common/snapshots/
preview_1.rs

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