Path: blob/main/crates/wasi-common/src/snapshots/preview_1.rs
1692 views
use crate::{1I32Exit, SystemTimeSpec, WasiCtx,2dir::{DirEntry, OpenResult, ReaddirCursor, ReaddirEntity, TableDirExt},3file::{4Advice, FdFlags, FdStat, FileAccessMode, FileEntry, FileType, Filestat, OFlags, RiFlags,5RoFlags, SdFlags, SiFlags, TableFileExt, WasiFile,6},7sched::{8Poll, Userdata,9subscription::{RwEventFlags, SubscriptionResult},10},11};12use cap_std::time::{Duration, SystemClock};13use std::borrow::Cow;14use std::io::{IoSlice, IoSliceMut};15use std::ops::Deref;16use std::sync::Arc;17use wiggle::GuestMemory;18use wiggle::GuestPtr;1920pub mod error;21use error::{Error, ErrorExt};2223// Limit the size of intermediate buffers when copying to WebAssembly shared24// memory.25pub(crate) const MAX_SHARED_BUFFER_SIZE: usize = 1 << 16;2627wiggle::from_witx!({28witx: ["witx/preview1/wasi_snapshot_preview1.witx"],29errors: { errno => trappable Error },30// Note: not every function actually needs to be async, however, nearly all of them do, and31// keeping that set the same in this macro and the wasmtime_wiggle / lucet_wiggle macros is32// tedious, and there is no cost to having a sync function be async in this case.33async: *,34wasmtime: false,35});3637impl wiggle::GuestErrorType for types::Errno {38fn success() -> Self {39Self::Success40}41}4243#[wiggle::async_trait]44impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {45async fn args_get(46&mut self,47memory: &mut GuestMemory<'_>,48argv: GuestPtr<GuestPtr<u8>>,49argv_buf: GuestPtr<u8>,50) -> Result<(), Error> {51self.args.write_to_guest(memory, argv_buf, argv)52}5354async fn args_sizes_get(55&mut self,56_memory: &mut GuestMemory<'_>,57) -> Result<(types::Size, types::Size), Error> {58Ok((self.args.number_elements(), self.args.cumulative_size()))59}6061async fn environ_get(62&mut self,63memory: &mut GuestMemory<'_>,64environ: GuestPtr<GuestPtr<u8>>,65environ_buf: GuestPtr<u8>,66) -> Result<(), Error> {67self.env.write_to_guest(memory, environ_buf, environ)68}6970async fn environ_sizes_get(71&mut self,72_memory: &mut GuestMemory<'_>,73) -> Result<(types::Size, types::Size), Error> {74Ok((self.env.number_elements(), self.env.cumulative_size()))75}7677async fn clock_res_get(78&mut self,79_memory: &mut GuestMemory<'_>,80id: types::Clockid,81) -> Result<types::Timestamp, Error> {82let resolution = match id {83types::Clockid::Realtime => Ok(self.clocks.system()?.resolution()),84types::Clockid::Monotonic => Ok(self.clocks.monotonic()?.abs_clock.resolution()),85types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {86Err(Error::badf().context("process and thread clocks are not supported"))87}88}?;89Ok(resolution.as_nanos().try_into()?)90}9192async fn clock_time_get(93&mut self,94_memory: &mut GuestMemory<'_>,95id: types::Clockid,96precision: types::Timestamp,97) -> Result<types::Timestamp, Error> {98let precision = Duration::from_nanos(precision);99match id {100types::Clockid::Realtime => {101let now = self.clocks.system()?.now(precision).into_std();102let d = now103.duration_since(std::time::SystemTime::UNIX_EPOCH)104.map_err(|_| {105Error::trap(anyhow::Error::msg("current time before unix epoch"))106})?;107Ok(d.as_nanos().try_into()?)108}109types::Clockid::Monotonic => {110let clock = self.clocks.monotonic()?;111let now = clock.abs_clock.now(precision);112let d = now.duration_since(clock.creation_time);113Ok(d.as_nanos().try_into()?)114}115types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {116Err(Error::badf().context("process and thread clocks are not supported"))117}118}119}120121async fn fd_advise(122&mut self,123_memory: &mut GuestMemory<'_>,124fd: types::Fd,125offset: types::Filesize,126len: types::Filesize,127advice: types::Advice,128) -> Result<(), Error> {129self.table()130.get_file(u32::from(fd))?131.file132.advise(offset, len, advice.into())133.await?;134Ok(())135}136137async fn fd_allocate(138&mut self,139_memory: &mut GuestMemory<'_>,140fd: 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 cases145// with the errors expected:146let _ = self.table().get_file(u32::from(fd))?;147// This operation from cloudabi is linux-specific, isn't even148// supported across all linux filesystems, and has no support on macos149// or windows. Rather than ship spotty support, it has been removed150// from preview 2, and we are no longer supporting it in preview 1 as151// well.152Err(Error::not_supported())153}154155async fn fd_close(156&mut self,157_memory: &mut GuestMemory<'_>,158fd: types::Fd,159) -> Result<(), Error> {160let table = self.table();161let fd = u32::from(fd);162163// Fail fast: If not present in table, Badf164if !table.contains_key(fd) {165return Err(Error::badf().context("key not in table"));166}167// fd_close must close either a File or a Dir handle168if table.is::<FileEntry>(fd) {169let _ = table.delete::<FileEntry>(fd);170} else if table.is::<DirEntry>(fd) {171let _ = table.delete::<DirEntry>(fd);172} else {173return Err(Error::badf().context("key does not refer to file or directory"));174}175176Ok(())177}178179async fn fd_datasync(180&mut self,181_memory: &mut GuestMemory<'_>,182fd: types::Fd,183) -> Result<(), Error> {184self.table()185.get_file(u32::from(fd))?186.file187.datasync()188.await?;189Ok(())190}191192async fn fd_fdstat_get(193&mut self,194_memory: &mut GuestMemory<'_>,195fd: types::Fd,196) -> Result<types::Fdstat, Error> {197let table = self.table();198let fd = u32::from(fd);199if table.is::<FileEntry>(fd) {200let file_entry: Arc<FileEntry> = table.get(fd)?;201let fdstat = file_entry.get_fdstat().await?;202Ok(types::Fdstat::from(&fdstat))203} else if table.is::<DirEntry>(fd) {204let _dir_entry: Arc<DirEntry> = table.get(fd)?;205let dir_fdstat = types::Fdstat {206fs_filetype: types::Filetype::Directory,207fs_rights_base: directory_base_rights(),208fs_rights_inheriting: directory_inheriting_rights(),209fs_flags: types::Fdflags::empty(),210};211Ok(dir_fdstat)212} else {213Err(Error::badf())214}215}216217async fn fd_fdstat_set_flags(218&mut self,219_memory: &mut GuestMemory<'_>,220fd: types::Fd,221flags: types::Fdflags,222) -> Result<(), Error> {223if let Some(table) = self.table_mut() {224table225.get_file_mut(u32::from(fd))?226.file227.set_fdflags(FdFlags::from(flags))228.await229} else {230log::warn!(231"`fd_fdstat_set_flags` does not work with wasi-threads enabled; see https://github.com/bytecodealliance/wasmtime/issues/5643"232);233Err(Error::not_supported())234}235}236237async fn fd_fdstat_set_rights(238&mut self,239_memory: &mut GuestMemory<'_>,240fd: types::Fd,241_fs_rights_base: types::Rights,242_fs_rights_inheriting: types::Rights,243) -> Result<(), Error> {244let table = self.table();245let fd = u32::from(fd);246if table.is::<FileEntry>(fd) {247let _file_entry: Arc<FileEntry> = table.get(fd)?;248Err(Error::not_supported())249} else if table.is::<DirEntry>(fd) {250let _dir_entry: Arc<DirEntry> = table.get(fd)?;251Err(Error::not_supported())252} else {253Err(Error::badf())254}255}256257async fn fd_filestat_get(258&mut self,259_memory: &mut GuestMemory<'_>,260fd: types::Fd,261) -> Result<types::Filestat, Error> {262let table = self.table();263let fd = u32::from(fd);264if table.is::<FileEntry>(fd) {265let filestat = table.get_file(fd)?.file.get_filestat().await?;266Ok(filestat.into())267} else if table.is::<DirEntry>(fd) {268let filestat = table.get_dir(fd)?.dir.get_filestat().await?;269Ok(filestat.into())270} else {271Err(Error::badf())272}273}274275async fn fd_filestat_set_size(276&mut self,277_memory: &mut GuestMemory<'_>,278fd: types::Fd,279size: types::Filesize,280) -> Result<(), Error> {281self.table()282.get_file(u32::from(fd))?283.file284.set_filestat_size(size)285.await?;286Ok(())287}288289async fn fd_filestat_set_times(290&mut self,291_memory: &mut GuestMemory<'_>,292fd: types::Fd,293atim: types::Timestamp,294mtim: types::Timestamp,295fst_flags: types::Fstflags,296) -> Result<(), Error> {297let fd = u32::from(fd);298let table = self.table();299// Validate flags300let set_atim = fst_flags.contains(types::Fstflags::ATIM);301let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);302let set_mtim = fst_flags.contains(types::Fstflags::MTIM);303let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);304305let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;306let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;307308if table.is::<FileEntry>(fd) {309table310.get_file(fd)311.expect("checked that entry is file")312.file313.set_times(atim, mtim)314.await315} else if table.is::<DirEntry>(fd) {316table317.get_dir(fd)318.expect("checked that entry is dir")319.dir320.set_times(".", atim, mtim, false)321.await322} else {323Err(Error::badf())324}325}326327async fn fd_read(328&mut self,329memory: &mut GuestMemory<'_>,330fd: types::Fd,331iovs: types::IovecArray,332) -> Result<types::Size, Error> {333let f = self.table().get_file(u32::from(fd))?;334// Access mode check normalizes error returned (windows would prefer ACCES here)335if !f.access_mode.contains(FileAccessMode::READ) {336Err(types::Errno::Badf)?337}338let f = &f.file;339340let iovs: Vec<wiggle::GuestPtr<[u8]>> = iovs341.iter()342.map(|iov_ptr| {343let iov_ptr = iov_ptr?;344let iov: types::Iovec = memory.read(iov_ptr)?;345Ok(iov.buf.as_array(iov.buf_len))346})347.collect::<Result<_, Error>>()?;348349// If the first iov structure is from shared memory we can safely assume350// all the rest will be. We then read into memory based on the memory's351// shared-ness:352// - if not shared, we copy directly into the Wasm memory353// - if shared, we use an intermediate buffer; this avoids Rust unsafety354// due to holding on to a `&mut [u8]` of Wasm memory when we cannot355// guarantee the `&mut` exclusivity--other threads could be modifying356// the data as this functions writes to it. Though likely there is no357// issue with OS writing to io structs in multi-threaded scenarios,358// since we do not know here if `&dyn WasiFile` does anything else359// (e.g., read), we cautiously incur some performance overhead by360// copying twice.361let is_shared_memory = memory.is_shared_memory();362let bytes_read: u64 = if is_shared_memory {363// For shared memory, read into an intermediate buffer. Only the364// first iov will be filled and even then the read is capped by the365// `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.366let iov = iovs.into_iter().next();367if let Some(iov) = iov {368let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];369let bytes_read = f.read_vectored(&mut [IoSliceMut::new(&mut buffer)]).await?;370let iov = iov371.get_range(0..bytes_read.try_into()?)372.expect("it should always be possible to slice the iov smaller");373memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;374bytes_read375} else {376return Ok(0);377}378} else {379// Convert the first unsafe guest slice into a safe one--Wiggle380// can only track mutable borrows for an entire region, and converting381// all guest pointers to slices would cause a runtime borrow-checking382// 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 the384// read of the first buffer.385let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {386Some(iov) => memory.as_slice_mut(iov)?.unwrap(),387None => return Ok(0),388};389390// Read directly into the Wasm memory.391f.read_vectored(&mut [IoSliceMut::new(guest_slice)]).await?392};393394Ok(types::Size::try_from(bytes_read)?)395}396397async fn fd_pread(398&mut self,399memory: &mut GuestMemory<'_>,400fd: types::Fd,401iovs: types::IovecArray,402offset: types::Filesize,403) -> Result<types::Size, Error> {404let f = self.table().get_file(u32::from(fd))?;405// Access mode check normalizes error returned (windows would prefer ACCES here)406if !f.access_mode.contains(FileAccessMode::READ) {407Err(types::Errno::Badf)?408}409let f = &f.file;410411let iovs: Vec<wiggle::GuestPtr<[u8]>> = iovs412.iter()413.map(|iov_ptr| {414let iov_ptr = iov_ptr?;415let iov: types::Iovec = memory.read(iov_ptr)?;416Ok(iov.buf.as_array(iov.buf_len))417})418.collect::<Result<_, Error>>()?;419420// If the first iov structure is from shared memory we can safely assume421// all the rest will be. We then read into memory based on the memory's422// shared-ness:423// - if not shared, we copy directly into the Wasm memory424// - if shared, we use an intermediate buffer; this avoids Rust unsafety425// due to holding on to a `&mut [u8]` of Wasm memory when we cannot426// guarantee the `&mut` exclusivity--other threads could be modifying427// the data as this functions writes to it. Though likely there is no428// issue with OS writing to io structs in multi-threaded scenarios,429// since we do not know here if `&dyn WasiFile` does anything else430// (e.g., read), we cautiously incur some performance overhead by431// copying twice.432let is_shared_memory = memory.is_shared_memory();433let bytes_read: u64 = if is_shared_memory {434// For shared memory, read into an intermediate buffer. Only the435// first iov will be filled and even then the read is capped by the436// `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.437let iov = iovs.into_iter().next();438if let Some(iov) = iov {439let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];440let bytes_read = f441.read_vectored_at(&mut [IoSliceMut::new(&mut buffer)], offset)442.await?;443let iov = iov444.get_range(0..bytes_read.try_into()?)445.expect("it should always be possible to slice the iov smaller");446memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;447bytes_read448} else {449return Ok(0);450}451} else {452// Convert unsafe guest slices to safe ones.453let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {454Some(iov) => memory.as_slice_mut(iov)?.unwrap(),455None => return Ok(0),456};457458// Read directly into the Wasm memory.459f.read_vectored_at(&mut [IoSliceMut::new(guest_slice)], offset)460.await?461};462463Ok(types::Size::try_from(bytes_read)?)464}465466async fn fd_write(467&mut self,468memory: &mut GuestMemory<'_>,469fd: types::Fd,470ciovs: types::CiovecArray,471) -> Result<types::Size, Error> {472let f = self.table().get_file(u32::from(fd))?;473// Access mode check normalizes error returned (windows would prefer ACCES here)474if !f.access_mode.contains(FileAccessMode::WRITE) {475Err(types::Errno::Badf)?476}477let f = &f.file;478479let guest_slices: Vec<Cow<[u8]>> = ciovs480.iter()481.map(|iov_ptr| {482let iov_ptr = iov_ptr?;483let iov: types::Ciovec = memory.read(iov_ptr)?;484Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)485})486.collect::<Result<_, Error>>()?;487488let ioslices: Vec<IoSlice> = guest_slices489.iter()490.map(|s| IoSlice::new(s.deref()))491.collect();492let bytes_written = f.write_vectored(&ioslices).await?;493494Ok(types::Size::try_from(bytes_written)?)495}496497async fn fd_pwrite(498&mut self,499memory: &mut GuestMemory<'_>,500fd: types::Fd,501ciovs: types::CiovecArray,502offset: types::Filesize,503) -> Result<types::Size, Error> {504let f = self.table().get_file(u32::from(fd))?;505// Access mode check normalizes error returned (windows would prefer ACCES here)506if !f.access_mode.contains(FileAccessMode::WRITE) {507Err(types::Errno::Badf)?508}509let f = &f.file;510511let guest_slices: Vec<Cow<[u8]>> = ciovs512.iter()513.map(|iov_ptr| {514let iov_ptr = iov_ptr?;515let iov: types::Ciovec = memory.read(iov_ptr)?;516Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)517})518.collect::<Result<_, Error>>()?;519520let ioslices: Vec<IoSlice> = guest_slices521.iter()522.map(|s| IoSlice::new(s.deref()))523.collect();524let bytes_written = f.write_vectored_at(&ioslices, offset).await?;525526Ok(types::Size::try_from(bytes_written)?)527}528529async fn fd_prestat_get(530&mut self,531_memory: &mut GuestMemory<'_>,532fd: types::Fd,533) -> Result<types::Prestat, Error> {534let table = self.table();535let dir_entry: Arc<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::badf())?;536if let Some(preopen) = dir_entry.preopen_path() {537let path_str = preopen.to_str().ok_or_else(|| Error::not_supported())?;538let pr_name_len = u32::try_from(path_str.as_bytes().len())?;539Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len }))540} else {541Err(Error::not_supported().context("file is not a preopen"))542}543}544545async fn fd_prestat_dir_name(546&mut self,547memory: &mut GuestMemory<'_>,548fd: types::Fd,549path: GuestPtr<u8>,550path_max_len: types::Size,551) -> Result<(), Error> {552let table = self.table();553let dir_entry: Arc<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?;554if let Some(preopen) = dir_entry.preopen_path() {555let path_bytes = preopen556.to_str()557.ok_or_else(|| Error::not_supported())?558.as_bytes();559let path_len = path_bytes.len();560if path_len > path_max_len as usize {561return Err(Error::name_too_long());562}563let path = path.as_array(path_len as u32);564memory.copy_from_slice(path_bytes, path)?;565Ok(())566} else {567Err(Error::not_supported())568}569}570async fn fd_renumber(571&mut self,572_memory: &mut GuestMemory<'_>,573from: types::Fd,574to: types::Fd,575) -> Result<(), Error> {576let table = self.table();577let from = u32::from(from);578let to = u32::from(to);579if !table.contains_key(from) {580return Err(Error::badf());581}582if !table.contains_key(to) {583return Err(Error::badf());584}585table.renumber(from, to)586}587588async fn fd_seek(589&mut self,590_memory: &mut GuestMemory<'_>,591fd: types::Fd,592offset: types::Filedelta,593whence: types::Whence,594) -> Result<types::Filesize, Error> {595use std::io::SeekFrom;596let whence = match whence {597types::Whence::Cur => SeekFrom::Current(offset),598types::Whence::End => SeekFrom::End(offset),599types::Whence::Set => {600SeekFrom::Start(offset.try_into().map_err(|_| Error::invalid_argument())?)601}602};603let newoffset = self604.table()605.get_file(u32::from(fd))?606.file607.seek(whence)608.await?;609Ok(newoffset)610}611612async fn fd_sync(&mut self, _memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {613self.table().get_file(u32::from(fd))?.file.sync().await?;614Ok(())615}616617async fn fd_tell(618&mut self,619_memory: &mut GuestMemory<'_>,620fd: types::Fd,621) -> Result<types::Filesize, Error> {622let offset = self623.table()624.get_file(u32::from(fd))?625.file626.seek(std::io::SeekFrom::Current(0))627.await?;628Ok(offset)629}630631async fn fd_readdir(632&mut self,633memory: &mut GuestMemory<'_>,634fd: types::Fd,635mut buf: GuestPtr<u8>,636buf_len: types::Size,637cookie: types::Dircookie,638) -> Result<types::Size, Error> {639let mut bufused = 0;640for entity in self641.table()642.get_dir(u32::from(fd))?643.dir644.readdir(ReaddirCursor::from(cookie))645.await?646{647let entity = entity?;648let dirent_raw = dirent_bytes(types::Dirent::try_from(&entity)?);649let dirent_len: types::Size = dirent_raw.len().try_into()?;650let name_raw = entity.name.as_bytes();651let name_len: types::Size = name_raw.len().try_into()?;652653// Copy as many bytes of the dirent as we can, up to the end of the buffer654let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused);655let raw = buf.as_array(dirent_copy_len);656memory.copy_from_slice(&dirent_raw[..dirent_copy_len as usize], raw)?;657658// If the dirent struct wasn't compiled entirely, return that we filled the buffer, which659// tells libc that we're not at EOF.660if dirent_copy_len < dirent_len {661return Ok(buf_len);662}663664buf = buf.add(dirent_copy_len)?;665bufused += dirent_copy_len;666667// Copy as many bytes of the name as we can, up to the end of the buffer668let name_copy_len = std::cmp::min(name_len, buf_len - bufused);669let raw = buf.as_array(name_copy_len);670memory.copy_from_slice(&name_raw[..name_copy_len as usize], raw)?;671672// If the dirent struct wasn't copied entirely, return that we filled the buffer, which673// tells libc that we're not at EOF674675if name_copy_len < name_len {676return Ok(buf_len);677}678679buf = buf.add(name_copy_len)?;680bufused += name_copy_len;681}682Ok(bufused)683}684685async fn path_create_directory(686&mut self,687memory: &mut GuestMemory<'_>,688dirfd: types::Fd,689path: GuestPtr<str>,690) -> Result<(), Error> {691self.table()692.get_dir(u32::from(dirfd))?693.dir694.create_dir(memory.as_cow_str(path)?.deref())695.await696}697698async fn path_filestat_get(699&mut self,700memory: &mut GuestMemory<'_>,701dirfd: types::Fd,702flags: types::Lookupflags,703path: GuestPtr<str>,704) -> Result<types::Filestat, Error> {705let filestat = self706.table()707.get_dir(u32::from(dirfd))?708.dir709.get_path_filestat(710memory.as_cow_str(path)?.deref(),711flags.contains(types::Lookupflags::SYMLINK_FOLLOW),712)713.await?;714Ok(types::Filestat::from(filestat))715}716717async fn path_filestat_set_times(718&mut self,719memory: &mut GuestMemory<'_>,720dirfd: types::Fd,721flags: types::Lookupflags,722path: GuestPtr<str>,723atim: types::Timestamp,724mtim: types::Timestamp,725fst_flags: types::Fstflags,726) -> Result<(), Error> {727let set_atim = fst_flags.contains(types::Fstflags::ATIM);728let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);729let set_mtim = fst_flags.contains(types::Fstflags::MTIM);730let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);731732let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;733let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;734self.table()735.get_dir(u32::from(dirfd))?736.dir737.set_times(738memory.as_cow_str(path)?.deref(),739atim,740mtim,741flags.contains(types::Lookupflags::SYMLINK_FOLLOW),742)743.await744}745746async fn path_link(747&mut self,748memory: &mut GuestMemory<'_>,749src_fd: types::Fd,750src_flags: types::Lookupflags,751src_path: GuestPtr<str>,752target_fd: types::Fd,753target_path: GuestPtr<str>,754) -> Result<(), Error> {755let table = self.table();756let src_dir = table.get_dir(u32::from(src_fd))?;757let target_dir = table.get_dir(u32::from(target_fd))?;758let symlink_follow = src_flags.contains(types::Lookupflags::SYMLINK_FOLLOW);759if symlink_follow {760return Err(Error::invalid_argument()761.context("symlink following on path_link is not supported"));762}763764src_dir765.dir766.hard_link(767memory.as_cow_str(src_path)?.deref(),768target_dir.dir.deref(),769memory.as_cow_str(target_path)?.deref(),770)771.await772}773774async fn path_open(775&mut self,776memory: &mut GuestMemory<'_>,777dirfd: types::Fd,778dirflags: types::Lookupflags,779path: GuestPtr<str>,780oflags: types::Oflags,781fs_rights_base: types::Rights,782_fs_rights_inheriting: types::Rights,783fdflags: types::Fdflags,784) -> Result<types::Fd, Error> {785let table = self.table();786let dirfd = u32::from(dirfd);787if table.is::<FileEntry>(dirfd) {788return Err(Error::not_dir());789}790let dir_entry = table.get_dir(dirfd)?;791792let symlink_follow = dirflags.contains(types::Lookupflags::SYMLINK_FOLLOW);793794let oflags = OFlags::from(&oflags);795let fdflags = FdFlags::from(fdflags);796let path = memory.as_cow_str(path)?;797798let read = fs_rights_base.contains(types::Rights::FD_READ);799let write = fs_rights_base.contains(types::Rights::FD_WRITE);800let access_mode = if read {801FileAccessMode::READ802} else {803FileAccessMode::empty()804} | if write {805FileAccessMode::WRITE806} else {807FileAccessMode::empty()808};809810let file = dir_entry811.dir812.open_file(symlink_follow, path.deref(), oflags, read, write, fdflags)813.await?;814drop(dir_entry);815816let fd = match file {817OpenResult::File(file) => table.push(Arc::new(FileEntry::new(file, access_mode)))?,818OpenResult::Dir(child_dir) => table.push(Arc::new(DirEntry::new(None, child_dir)))?,819};820Ok(types::Fd::from(fd))821}822823async fn path_readlink(824&mut self,825memory: &mut GuestMemory<'_>,826dirfd: types::Fd,827path: GuestPtr<str>,828buf: GuestPtr<u8>,829buf_len: types::Size,830) -> Result<types::Size, Error> {831let link = self832.table()833.get_dir(u32::from(dirfd))?834.dir835.read_link(memory.as_cow_str(path)?.deref())836.await?837.into_os_string()838.into_string()839.map_err(|_| Error::illegal_byte_sequence().context("link contents"))?;840let link_bytes = link.as_bytes();841// Like posix readlink(2), silently truncate links when they are larger than the842// destination buffer:843let link_len = std::cmp::min(link_bytes.len(), buf_len as usize);844let buf = buf.as_array(link_len as u32);845memory.copy_from_slice(&link_bytes[..link_len], buf)?;846Ok(link_len as types::Size)847}848849async fn path_remove_directory(850&mut self,851memory: &mut GuestMemory<'_>,852dirfd: types::Fd,853path: GuestPtr<str>,854) -> Result<(), Error> {855self.table()856.get_dir(u32::from(dirfd))?857.dir858.remove_dir(memory.as_cow_str(path)?.deref())859.await860}861862async fn path_rename(863&mut self,864memory: &mut GuestMemory<'_>,865src_fd: types::Fd,866src_path: GuestPtr<str>,867dest_fd: types::Fd,868dest_path: GuestPtr<str>,869) -> Result<(), Error> {870let table = self.table();871let src_dir = table.get_dir(u32::from(src_fd))?;872let dest_dir = table.get_dir(u32::from(dest_fd))?;873src_dir874.dir875.rename(876memory.as_cow_str(src_path)?.deref(),877dest_dir.dir.deref(),878memory.as_cow_str(dest_path)?.deref(),879)880.await881}882883async fn path_symlink(884&mut self,885memory: &mut GuestMemory<'_>,886src_path: GuestPtr<str>,887dirfd: types::Fd,888dest_path: GuestPtr<str>,889) -> Result<(), Error> {890self.table()891.get_dir(u32::from(dirfd))?892.dir893.symlink(894memory.as_cow_str(src_path)?.deref(),895memory.as_cow_str(dest_path)?.deref(),896)897.await898}899900async fn path_unlink_file(901&mut self,902memory: &mut GuestMemory<'_>,903dirfd: types::Fd,904path: GuestPtr<str>,905) -> Result<(), Error> {906self.table()907.get_dir(u32::from(dirfd))?908.dir909.unlink_file(memory.as_cow_str(path)?.deref())910.await911}912913async fn poll_oneoff(914&mut self,915memory: &mut GuestMemory<'_>,916subs: GuestPtr<types::Subscription>,917events: GuestPtr<types::Event>,918nsubscriptions: types::Size,919) -> Result<types::Size, Error> {920if nsubscriptions == 0 {921return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));922}923924// Special-case a `poll_oneoff` which is just sleeping on a single925// relative timer event, such as what WASI libc uses to implement sleep926// functions. This supports all clock IDs, because POSIX says that927// `clock_settime` doesn't effect relative sleeps.928if nsubscriptions == 1 {929let sub = memory.read(subs)?;930if let types::SubscriptionU::Clock(clocksub) = sub.u {931if !clocksub932.flags933.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)934{935self.sched936.sleep(Duration::from_nanos(clocksub.timeout))937.await?;938memory.write(939events,940types::Event {941userdata: sub.userdata,942error: types::Errno::Success,943type_: types::Eventtype::Clock,944fd_readwrite: fd_readwrite_empty(),945},946)?;947return Ok(1);948}949}950}951952let table = &self.table;953// We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside954let mut read_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();955let mut write_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();956957let mut poll = Poll::new();958959let subs = subs.as_array(nsubscriptions);960for sub_elem in subs.iter() {961let sub_ptr = sub_elem?;962let sub = memory.read(sub_ptr)?;963match sub.u {964types::SubscriptionU::Clock(clocksub) => match clocksub.id {965types::Clockid::Monotonic => {966let clock = self.clocks.monotonic()?;967let precision = Duration::from_nanos(clocksub.precision);968let duration = Duration::from_nanos(clocksub.timeout);969let start = if clocksub970.flags971.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)972{973clock.creation_time974} else {975clock.abs_clock.now(precision)976};977let deadline = start978.checked_add(duration)979.ok_or_else(|| Error::overflow().context("deadline"))?;980poll.subscribe_monotonic_clock(981&*clock.abs_clock,982deadline,983precision,984sub.userdata.into(),985)986}987types::Clockid::Realtime => {988// POSIX specifies that functions like `nanosleep` and others use the989// `REALTIME` clock. But it also says that `clock_settime` has no effect990// on threads waiting in these functions. MONOTONIC should always have991// resolution at least as good as REALTIME, so we can translate a992// non-absolute `REALTIME` request into a `MONOTONIC` request.993let clock = self.clocks.monotonic()?;994let precision = Duration::from_nanos(clocksub.precision);995let duration = Duration::from_nanos(clocksub.timeout);996let deadline = if clocksub997.flags998.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)999{1000return Err(Error::not_supported());1001} else {1002clock1003.abs_clock1004.now(precision)1005.checked_add(duration)1006.ok_or_else(|| Error::overflow().context("deadline"))?1007};1008poll.subscribe_monotonic_clock(1009&*clock.abs_clock,1010deadline,1011precision,1012sub.userdata.into(),1013)1014}1015_ => Err(Error::invalid_argument()1016.context("timer subscriptions only support monotonic timer"))?,1017},1018types::SubscriptionU::FdRead(readsub) => {1019let fd = readsub.file_descriptor;1020let file_ref = table.get_file(u32::from(fd))?;1021read_refs.push((file_ref, Some(sub.userdata.into())));1022}1023types::SubscriptionU::FdWrite(writesub) => {1024let fd = writesub.file_descriptor;1025let file_ref = table.get_file(u32::from(fd))?;1026write_refs.push((file_ref, Some(sub.userdata.into())));1027}1028}1029}10301031let mut read_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();1032for (file_lock, userdata) in read_refs.iter_mut() {1033read_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));1034}10351036for (f, ud) in read_mut_refs.iter_mut() {1037poll.subscribe_read(*f, *ud);1038}10391040let mut write_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();1041for (file_lock, userdata) in write_refs.iter_mut() {1042write_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));1043}10441045for (f, ud) in write_mut_refs.iter_mut() {1046poll.subscribe_write(*f, *ud);1047}10481049self.sched.poll_oneoff(&mut poll).await?;10501051let results = poll.results();1052let num_results = results.len();1053assert!(1054num_results <= nsubscriptions as usize,1055"results exceeds subscriptions"1056);1057let events = events.as_array(1058num_results1059.try_into()1060.expect("not greater than nsubscriptions"),1061);1062for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) {1063let event_ptr = event_elem?;1064let userdata: types::Userdata = userdata.into();1065memory.write(1066event_ptr,1067match result {1068SubscriptionResult::Read(r) => {1069let type_ = types::Eventtype::FdRead;1070match r {1071Ok((nbytes, flags)) => types::Event {1072userdata,1073error: types::Errno::Success,1074type_,1075fd_readwrite: types::EventFdReadwrite {1076nbytes,1077flags: types::Eventrwflags::from(&flags),1078},1079},1080Err(e) => types::Event {1081userdata,1082error: e.downcast().map_err(Error::trap)?,1083type_,1084fd_readwrite: fd_readwrite_empty(),1085},1086}1087}1088SubscriptionResult::Write(r) => {1089let type_ = types::Eventtype::FdWrite;1090match r {1091Ok((nbytes, flags)) => types::Event {1092userdata,1093error: types::Errno::Success,1094type_,1095fd_readwrite: types::EventFdReadwrite {1096nbytes,1097flags: types::Eventrwflags::from(&flags),1098},1099},1100Err(e) => types::Event {1101userdata,1102error: e.downcast().map_err(Error::trap)?,1103type_,1104fd_readwrite: fd_readwrite_empty(),1105},1106}1107}1108SubscriptionResult::MonotonicClock(r) => {1109let type_ = types::Eventtype::Clock;1110types::Event {1111userdata,1112error: match r {1113Ok(()) => types::Errno::Success,1114Err(e) => e.downcast().map_err(Error::trap)?,1115},1116type_,1117fd_readwrite: fd_readwrite_empty(),1118}1119}1120},1121)?;1122}11231124Ok(num_results.try_into().expect("results fit into memory"))1125}11261127async fn proc_exit(1128&mut self,1129_memory: &mut GuestMemory<'_>,1130status: types::Exitcode,1131) -> anyhow::Error {1132// Check that the status is within WASI's range.1133if status < 126 {1134I32Exit(status as i32).into()1135} else {1136anyhow::Error::msg("exit with invalid exit status outside of [0..126)")1137}1138}11391140async fn proc_raise(1141&mut self,1142_memory: &mut GuestMemory<'_>,1143_sig: types::Signal,1144) -> Result<(), Error> {1145Err(Error::trap(anyhow::Error::msg("proc_raise unsupported")))1146}11471148async fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), Error> {1149self.sched.sched_yield().await1150}11511152async fn random_get(1153&mut self,1154memory: &mut GuestMemory<'_>,1155buf: GuestPtr<u8>,1156buf_len: types::Size,1157) -> Result<(), Error> {1158let buf = buf.as_array(buf_len);1159if memory.is_shared_memory() {1160// If the Wasm memory is shared, copy to an intermediate buffer to1161// avoid Rust unsafety (i.e., the called function could rely on1162// `&mut [u8]`'s exclusive ownership which is not guaranteed due to1163// potential access from other threads).1164let mut copied: u32 = 0;1165while copied < buf.len() {1166let len = (buf.len() - copied).min(MAX_SHARED_BUFFER_SIZE as u32);1167let mut tmp = vec![0; len as usize];1168self.random.lock().unwrap().try_fill_bytes(&mut tmp)?;1169let dest = buf.get_range(copied..copied + len).unwrap();1170memory.copy_from_slice(&tmp, dest)?;1171copied += len;1172}1173} else {1174// If the Wasm memory is non-shared, copy directly into the linear1175// memory.1176let mem = &mut memory.as_slice_mut(buf)?.unwrap();1177self.random.lock().unwrap().try_fill_bytes(mem)?;1178}1179Ok(())1180}11811182async fn sock_accept(1183&mut self,1184_memory: &mut GuestMemory<'_>,1185fd: types::Fd,1186flags: types::Fdflags,1187) -> Result<types::Fd, Error> {1188let table = self.table();1189let f = table.get_file(u32::from(fd))?;1190let file = f.file.sock_accept(FdFlags::from(flags)).await?;1191let fd = table.push(Arc::new(FileEntry::new(file, FileAccessMode::all())))?;1192Ok(types::Fd::from(fd))1193}11941195async fn sock_recv(1196&mut self,1197memory: &mut GuestMemory<'_>,1198fd: types::Fd,1199ri_data: types::IovecArray,1200ri_flags: types::Riflags,1201) -> Result<(types::Size, types::Roflags), Error> {1202let f = self.table().get_file(u32::from(fd))?;12031204let iovs: Vec<wiggle::GuestPtr<[u8]>> = ri_data1205.iter()1206.map(|iov_ptr| {1207let iov_ptr = iov_ptr?;1208let iov: types::Iovec = memory.read(iov_ptr)?;1209Ok(iov.buf.as_array(iov.buf_len))1210})1211.collect::<Result<_, Error>>()?;12121213// If the first iov structure is from shared memory we can safely assume1214// all the rest will be. We then read into memory based on the memory's1215// shared-ness:1216// - if not shared, we copy directly into the Wasm memory1217// - if shared, we use an intermediate buffer; this avoids Rust unsafety1218// due to holding on to a `&mut [u8]` of Wasm memory when we cannot1219// guarantee the `&mut` exclusivity--other threads could be modifying1220// the data as this functions writes to it. Though likely there is no1221// issue with OS writing to io structs in multi-threaded scenarios,1222// since we do not know here if `&dyn WasiFile` does anything else1223// (e.g., read), we cautiously incur some performance overhead by1224// copying twice.1225let is_shared_memory = memory.is_shared_memory();1226let (bytes_read, ro_flags) = if is_shared_memory {1227// For shared memory, read into an intermediate buffer. Only the1228// first iov will be filled and even then the read is capped by the1229// `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.1230let iov = iovs.into_iter().next();1231if let Some(iov) = iov {1232let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];1233let (bytes_read, ro_flags) = f1234.file1235.sock_recv(&mut [IoSliceMut::new(&mut buffer)], RiFlags::from(ri_flags))1236.await?;1237let iov = iov1238.get_range(0..bytes_read.try_into()?)1239.expect("it should always be possible to slice the iov smaller");1240memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;1241(bytes_read, ro_flags)1242} else {1243return Ok((0, RoFlags::empty().into()));1244}1245} else {1246// Convert all of the unsafe guest slices to safe ones--this uses1247// Wiggle's internal borrow checker to ensure no overlaps. We assume1248// here that, because the memory is not shared, there are no other1249// threads to access it while it is written to.1250let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {1251Some(iov) => memory.as_slice_mut(iov)?.unwrap(),1252None => &mut [],1253};12541255// Read directly into the Wasm memory.1256f.file1257.sock_recv(&mut [IoSliceMut::new(guest_slice)], RiFlags::from(ri_flags))1258.await?1259};12601261Ok((types::Size::try_from(bytes_read)?, ro_flags.into()))1262}12631264async fn sock_send(1265&mut self,1266memory: &mut GuestMemory<'_>,1267fd: types::Fd,1268si_data: types::CiovecArray,1269_si_flags: types::Siflags,1270) -> Result<types::Size, Error> {1271let f = self.table().get_file(u32::from(fd))?;12721273let guest_slices: Vec<Cow<[u8]>> = si_data1274.iter()1275.map(|iov_ptr| {1276let iov_ptr = iov_ptr?;1277let iov: types::Ciovec = memory.read(iov_ptr)?;1278Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)1279})1280.collect::<Result<_, Error>>()?;12811282let ioslices: Vec<IoSlice> = guest_slices1283.iter()1284.map(|s| IoSlice::new(s.deref()))1285.collect();1286let bytes_written = f.file.sock_send(&ioslices, SiFlags::empty()).await?;12871288Ok(types::Size::try_from(bytes_written)?)1289}12901291async fn sock_shutdown(1292&mut self,1293_memory: &mut GuestMemory<'_>,1294fd: types::Fd,1295how: types::Sdflags,1296) -> Result<(), Error> {1297let f = self.table().get_file(u32::from(fd))?;12981299f.file.sock_shutdown(SdFlags::from(how)).await1300}1301}13021303impl From<types::Advice> for Advice {1304fn from(advice: types::Advice) -> Advice {1305match advice {1306types::Advice::Normal => Advice::Normal,1307types::Advice::Sequential => Advice::Sequential,1308types::Advice::Random => Advice::Random,1309types::Advice::Willneed => Advice::WillNeed,1310types::Advice::Dontneed => Advice::DontNeed,1311types::Advice::Noreuse => Advice::NoReuse,1312}1313}1314}13151316impl From<&FdStat> for types::Fdstat {1317fn from(fdstat: &FdStat) -> types::Fdstat {1318let mut fs_rights_base = types::Rights::empty();1319if fdstat.access_mode.contains(FileAccessMode::READ) {1320fs_rights_base |= types::Rights::FD_READ;1321}1322if fdstat.access_mode.contains(FileAccessMode::WRITE) {1323fs_rights_base |= types::Rights::FD_WRITE;1324}1325types::Fdstat {1326fs_filetype: types::Filetype::from(&fdstat.filetype),1327fs_rights_base,1328fs_rights_inheriting: types::Rights::empty(),1329fs_flags: types::Fdflags::from(fdstat.flags),1330}1331}1332}13331334impl From<&FileType> for types::Filetype {1335fn from(ft: &FileType) -> types::Filetype {1336match ft {1337FileType::Directory => types::Filetype::Directory,1338FileType::BlockDevice => types::Filetype::BlockDevice,1339FileType::CharacterDevice => types::Filetype::CharacterDevice,1340FileType::RegularFile => types::Filetype::RegularFile,1341FileType::SocketDgram => types::Filetype::SocketDgram,1342FileType::SocketStream => types::Filetype::SocketStream,1343FileType::SymbolicLink => types::Filetype::SymbolicLink,1344FileType::Unknown => types::Filetype::Unknown,1345FileType::Pipe => types::Filetype::Unknown,1346}1347}1348}13491350macro_rules! convert_flags {1351($from:ty, $to:ty, $($flag:ident),+) => {1352impl From<$from> for $to {1353fn from(f: $from) -> $to {1354let mut out = <$to>::empty();1355$(1356if f.contains(<$from>::$flag) {1357out |= <$to>::$flag;1358}1359)+1360out1361}1362}1363}1364}13651366macro_rules! convert_flags_bidirectional {1367($from:ty, $to:ty, $($rest:tt)*) => {1368convert_flags!($from, $to, $($rest)*);1369convert_flags!($to, $from, $($rest)*);1370}1371}13721373convert_flags_bidirectional!(1374FdFlags,1375types::Fdflags,1376APPEND,1377DSYNC,1378NONBLOCK,1379RSYNC,1380SYNC1381);13821383convert_flags_bidirectional!(RiFlags, types::Riflags, RECV_PEEK, RECV_WAITALL);13841385convert_flags_bidirectional!(RoFlags, types::Roflags, RECV_DATA_TRUNCATED);13861387convert_flags_bidirectional!(SdFlags, types::Sdflags, RD, WR);13881389impl From<&types::Oflags> for OFlags {1390fn from(oflags: &types::Oflags) -> OFlags {1391let mut out = OFlags::empty();1392if oflags.contains(types::Oflags::CREAT) {1393out = out | OFlags::CREATE;1394}1395if oflags.contains(types::Oflags::DIRECTORY) {1396out = out | OFlags::DIRECTORY;1397}1398if oflags.contains(types::Oflags::EXCL) {1399out = out | OFlags::EXCLUSIVE;1400}1401if oflags.contains(types::Oflags::TRUNC) {1402out = out | OFlags::TRUNCATE;1403}1404out1405}1406}14071408impl From<&OFlags> for types::Oflags {1409fn from(oflags: &OFlags) -> types::Oflags {1410let mut out = types::Oflags::empty();1411if oflags.contains(OFlags::CREATE) {1412out = out | types::Oflags::CREAT;1413}1414if oflags.contains(OFlags::DIRECTORY) {1415out = out | types::Oflags::DIRECTORY;1416}1417if oflags.contains(OFlags::EXCLUSIVE) {1418out = out | types::Oflags::EXCL;1419}1420if oflags.contains(OFlags::TRUNCATE) {1421out = out | types::Oflags::TRUNC;1422}1423out1424}1425}1426impl From<Filestat> for types::Filestat {1427fn from(stat: Filestat) -> types::Filestat {1428types::Filestat {1429dev: stat.device_id,1430ino: stat.inode,1431filetype: types::Filetype::from(&stat.filetype),1432nlink: stat.nlink,1433size: stat.size,1434atim: stat1435.atim1436.map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)1437.unwrap_or(0),1438mtim: stat1439.mtim1440.map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)1441.unwrap_or(0),1442ctim: stat1443.ctim1444.map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)1445.unwrap_or(0),1446}1447}1448}14491450impl TryFrom<&ReaddirEntity> for types::Dirent {1451type Error = Error;1452fn try_from(e: &ReaddirEntity) -> Result<types::Dirent, Error> {1453Ok(types::Dirent {1454d_ino: e.inode,1455d_namlen: e.name.as_bytes().len().try_into()?,1456d_type: types::Filetype::from(&e.filetype),1457d_next: e.next.into(),1458})1459}1460}14611462fn dirent_bytes(dirent: types::Dirent) -> Vec<u8> {1463use wiggle::GuestType;1464assert_eq!(1465types::Dirent::guest_size(),1466std::mem::size_of::<types::Dirent>() as u32,1467"Dirent guest repr and host repr should match"1468);1469assert_eq!(14701,1471std::mem::size_of_val(&dirent.d_type),1472"Dirent member d_type should be endian-invariant"1473);1474let size = types::Dirent::guest_size()1475.try_into()1476.expect("Dirent is smaller than 2^32");1477let mut bytes = Vec::with_capacity(size);1478bytes.resize(size, 0);1479let ptr = bytes.as_mut_ptr().cast::<types::Dirent>();1480let guest_dirent = types::Dirent {1481d_ino: dirent.d_ino.to_le(),1482d_namlen: dirent.d_namlen.to_le(),1483d_type: dirent.d_type, // endian-invariant1484d_next: dirent.d_next.to_le(),1485};1486unsafe { ptr.write_unaligned(guest_dirent) };1487bytes1488}14891490impl From<&RwEventFlags> for types::Eventrwflags {1491fn from(flags: &RwEventFlags) -> types::Eventrwflags {1492let mut out = types::Eventrwflags::empty();1493if flags.contains(RwEventFlags::HANGUP) {1494out = out | types::Eventrwflags::FD_READWRITE_HANGUP;1495}1496out1497}1498}14991500fn fd_readwrite_empty() -> types::EventFdReadwrite {1501types::EventFdReadwrite {1502nbytes: 0,1503flags: types::Eventrwflags::empty(),1504}1505}15061507fn systimespec(1508set: bool,1509ts: types::Timestamp,1510now: bool,1511) -> Result<Option<SystemTimeSpec>, Error> {1512if set && now {1513Err(Error::invalid_argument())1514} else if set {1515Ok(Some(SystemTimeSpec::Absolute(1516SystemClock::UNIX_EPOCH + Duration::from_nanos(ts),1517)))1518} else if now {1519Ok(Some(SystemTimeSpec::SymbolicNow))1520} else {1521Ok(None)1522}1523}15241525// This is the default subset of base Rights reported for directories prior to1526// https://github.com/bytecodealliance/wasmtime/pull/6265. Some1527// implementations still expect this set of rights to be reported.1528pub(crate) fn directory_base_rights() -> types::Rights {1529types::Rights::PATH_CREATE_DIRECTORY1530| types::Rights::PATH_CREATE_FILE1531| types::Rights::PATH_LINK_SOURCE1532| types::Rights::PATH_LINK_TARGET1533| types::Rights::PATH_OPEN1534| types::Rights::FD_READDIR1535| types::Rights::PATH_READLINK1536| types::Rights::PATH_RENAME_SOURCE1537| types::Rights::PATH_RENAME_TARGET1538| types::Rights::PATH_SYMLINK1539| types::Rights::PATH_REMOVE_DIRECTORY1540| types::Rights::PATH_UNLINK_FILE1541| types::Rights::PATH_FILESTAT_GET1542| types::Rights::PATH_FILESTAT_SET_TIMES1543| types::Rights::FD_FILESTAT_GET1544| types::Rights::FD_FILESTAT_SET_TIMES1545}15461547// This is the default subset of inheriting Rights reported for directories1548// prior to https://github.com/bytecodealliance/wasmtime/pull/6265. Some1549// implementations still expect this set of rights to be reported.1550pub(crate) fn directory_inheriting_rights() -> types::Rights {1551types::Rights::FD_DATASYNC1552| types::Rights::FD_READ1553| types::Rights::FD_SEEK1554| types::Rights::FD_FDSTAT_SET_FLAGS1555| types::Rights::FD_SYNC1556| types::Rights::FD_TELL1557| types::Rights::FD_WRITE1558| types::Rights::FD_ADVISE1559| types::Rights::FD_ALLOCATE1560| types::Rights::FD_FILESTAT_GET1561| types::Rights::FD_FILESTAT_SET_SIZE1562| types::Rights::FD_FILESTAT_SET_TIMES1563| types::Rights::POLL_FD_READWRITE1564| directory_base_rights()1565}156615671568