Path: blob/main/crates/wasi-common/src/snapshots/preview_1.rs
3124 views
use crate::{1EnvError, I32Exit, 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}4243impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {44async fn args_get(45&mut self,46memory: &mut GuestMemory<'_>,47argv: GuestPtr<GuestPtr<u8>>,48argv_buf: GuestPtr<u8>,49) -> Result<(), Error> {50self.args.write_to_guest(memory, argv_buf, argv)51}5253async fn args_sizes_get(54&mut self,55_memory: &mut GuestMemory<'_>,56) -> Result<(types::Size, types::Size), Error> {57Ok((self.args.number_elements(), self.args.cumulative_size()))58}5960async fn environ_get(61&mut self,62memory: &mut GuestMemory<'_>,63environ: GuestPtr<GuestPtr<u8>>,64environ_buf: GuestPtr<u8>,65) -> Result<(), Error> {66self.env.write_to_guest(memory, environ_buf, environ)67}6869async fn environ_sizes_get(70&mut self,71_memory: &mut GuestMemory<'_>,72) -> Result<(types::Size, types::Size), Error> {73Ok((self.env.number_elements(), self.env.cumulative_size()))74}7576async fn clock_res_get(77&mut self,78_memory: &mut GuestMemory<'_>,79id: types::Clockid,80) -> Result<types::Timestamp, Error> {81let resolution = match id {82types::Clockid::Realtime => Ok(self.clocks.system()?.resolution()),83types::Clockid::Monotonic => Ok(self.clocks.monotonic()?.abs_clock.resolution()),84types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {85Err(Error::badf().context("process and thread clocks are not supported"))86}87}?;88Ok(resolution.as_nanos().try_into()?)89}9091async fn clock_time_get(92&mut self,93_memory: &mut GuestMemory<'_>,94id: types::Clockid,95precision: types::Timestamp,96) -> Result<types::Timestamp, Error> {97let precision = Duration::from_nanos(precision);98match id {99types::Clockid::Realtime => {100let now = self.clocks.system()?.now(precision).into_std();101let d = now102.duration_since(std::time::SystemTime::UNIX_EPOCH)103.map_err(|_| Error::trap(EnvError::msg("current time before unix epoch")))?;104Ok(d.as_nanos().try_into()?)105}106types::Clockid::Monotonic => {107let clock = self.clocks.monotonic()?;108let now = clock.abs_clock.now(precision);109let d = now.duration_since(clock.creation_time);110Ok(d.as_nanos().try_into()?)111}112types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {113Err(Error::badf().context("process and thread clocks are not supported"))114}115}116}117118async fn fd_advise(119&mut self,120_memory: &mut GuestMemory<'_>,121fd: types::Fd,122offset: types::Filesize,123len: types::Filesize,124advice: types::Advice,125) -> Result<(), Error> {126self.table()127.get_file(u32::from(fd))?128.file129.advise(offset, len, advice.into())130.await?;131Ok(())132}133134async fn fd_allocate(135&mut self,136_memory: &mut GuestMemory<'_>,137fd: types::Fd,138_offset: types::Filesize,139_len: types::Filesize,140) -> Result<(), Error> {141// Check if fd is a file, and has rights, just to reject those cases142// with the errors expected:143let _ = self.table().get_file(u32::from(fd))?;144// This operation from cloudabi is linux-specific, isn't even145// supported across all linux filesystems, and has no support on macos146// or windows. Rather than ship spotty support, it has been removed147// from preview 2, and we are no longer supporting it in preview 1 as148// well.149Err(Error::not_supported())150}151152async fn fd_close(153&mut self,154_memory: &mut GuestMemory<'_>,155fd: types::Fd,156) -> Result<(), Error> {157let table = self.table();158let fd = u32::from(fd);159160// Fail fast: If not present in table, Badf161if !table.contains_key(fd) {162return Err(Error::badf().context("key not in table"));163}164// fd_close must close either a File or a Dir handle165if table.is::<FileEntry>(fd) {166let _ = table.delete::<FileEntry>(fd);167} else if table.is::<DirEntry>(fd) {168let _ = table.delete::<DirEntry>(fd);169} else {170return Err(Error::badf().context("key does not refer to file or directory"));171}172173Ok(())174}175176async fn fd_datasync(177&mut self,178_memory: &mut GuestMemory<'_>,179fd: types::Fd,180) -> Result<(), Error> {181self.table()182.get_file(u32::from(fd))?183.file184.datasync()185.await?;186Ok(())187}188189async fn fd_fdstat_get(190&mut self,191_memory: &mut GuestMemory<'_>,192fd: types::Fd,193) -> Result<types::Fdstat, Error> {194let table = self.table();195let fd = u32::from(fd);196if table.is::<FileEntry>(fd) {197let file_entry: Arc<FileEntry> = table.get(fd)?;198let fdstat = file_entry.get_fdstat().await?;199Ok(types::Fdstat::from(&fdstat))200} else if table.is::<DirEntry>(fd) {201let _dir_entry: Arc<DirEntry> = table.get(fd)?;202let dir_fdstat = types::Fdstat {203fs_filetype: types::Filetype::Directory,204fs_rights_base: directory_base_rights(),205fs_rights_inheriting: directory_inheriting_rights(),206fs_flags: types::Fdflags::empty(),207};208Ok(dir_fdstat)209} else {210Err(Error::badf())211}212}213214async fn fd_fdstat_set_flags(215&mut self,216_memory: &mut GuestMemory<'_>,217fd: types::Fd,218flags: types::Fdflags,219) -> Result<(), Error> {220if let Some(table) = self.table_mut() {221table222.get_file_mut(u32::from(fd))?223.file224.set_fdflags(FdFlags::from(flags))225.await226} else {227log::warn!(228"`fd_fdstat_set_flags` does not work with wasi-threads enabled; see https://github.com/bytecodealliance/wasmtime/issues/5643"229);230Err(Error::not_supported())231}232}233234async fn fd_fdstat_set_rights(235&mut self,236_memory: &mut GuestMemory<'_>,237fd: types::Fd,238_fs_rights_base: types::Rights,239_fs_rights_inheriting: types::Rights,240) -> Result<(), Error> {241let table = self.table();242let fd = u32::from(fd);243if table.is::<FileEntry>(fd) {244let _file_entry: Arc<FileEntry> = table.get(fd)?;245Err(Error::not_supported())246} else if table.is::<DirEntry>(fd) {247let _dir_entry: Arc<DirEntry> = table.get(fd)?;248Err(Error::not_supported())249} else {250Err(Error::badf())251}252}253254async fn fd_filestat_get(255&mut self,256_memory: &mut GuestMemory<'_>,257fd: types::Fd,258) -> Result<types::Filestat, Error> {259let table = self.table();260let fd = u32::from(fd);261if table.is::<FileEntry>(fd) {262let filestat = table.get_file(fd)?.file.get_filestat().await?;263Ok(filestat.into())264} else if table.is::<DirEntry>(fd) {265let filestat = table.get_dir(fd)?.dir.get_filestat().await?;266Ok(filestat.into())267} else {268Err(Error::badf())269}270}271272async fn fd_filestat_set_size(273&mut self,274_memory: &mut GuestMemory<'_>,275fd: types::Fd,276size: types::Filesize,277) -> Result<(), Error> {278self.table()279.get_file(u32::from(fd))?280.file281.set_filestat_size(size)282.await?;283Ok(())284}285286async fn fd_filestat_set_times(287&mut self,288_memory: &mut GuestMemory<'_>,289fd: types::Fd,290atim: types::Timestamp,291mtim: types::Timestamp,292fst_flags: types::Fstflags,293) -> Result<(), Error> {294let fd = u32::from(fd);295let table = self.table();296// Validate flags297let set_atim = fst_flags.contains(types::Fstflags::ATIM);298let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);299let set_mtim = fst_flags.contains(types::Fstflags::MTIM);300let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);301302let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;303let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;304305if table.is::<FileEntry>(fd) {306table307.get_file(fd)308.expect("checked that entry is file")309.file310.set_times(atim, mtim)311.await312} else if table.is::<DirEntry>(fd) {313table314.get_dir(fd)315.expect("checked that entry is dir")316.dir317.set_times(".", atim, mtim, false)318.await319} else {320Err(Error::badf())321}322}323324async fn fd_read(325&mut self,326memory: &mut GuestMemory<'_>,327fd: types::Fd,328iovs: types::IovecArray,329) -> Result<types::Size, Error> {330let f = self.table().get_file(u32::from(fd))?;331// Access mode check normalizes error returned (windows would prefer ACCES here)332if !f.access_mode.contains(FileAccessMode::READ) {333Err(types::Errno::Badf)?334}335let f = &f.file;336337let iovs: Vec<wiggle::GuestPtr<[u8]>> = iovs338.iter()339.map(|iov_ptr| {340let iov_ptr = iov_ptr?;341let iov: types::Iovec = memory.read(iov_ptr)?;342Ok(iov.buf.as_array(iov.buf_len))343})344.collect::<Result<_, Error>>()?;345346// If the first iov structure is from shared memory we can safely assume347// all the rest will be. We then read into memory based on the memory's348// shared-ness:349// - if not shared, we copy directly into the Wasm memory350// - if shared, we use an intermediate buffer; this avoids Rust unsafety351// due to holding on to a `&mut [u8]` of Wasm memory when we cannot352// guarantee the `&mut` exclusivity--other threads could be modifying353// the data as this functions writes to it. Though likely there is no354// issue with OS writing to io structs in multi-threaded scenarios,355// since we do not know here if `&dyn WasiFile` does anything else356// (e.g., read), we cautiously incur some performance overhead by357// copying twice.358let is_shared_memory = memory.is_shared_memory();359let bytes_read: u64 = if is_shared_memory {360// For shared memory, read into an intermediate buffer. Only the361// first iov will be filled and even then the read is capped by the362// `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.363let iov = iovs.into_iter().next();364if let Some(iov) = iov {365let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];366let bytes_read = f.read_vectored(&mut [IoSliceMut::new(&mut buffer)]).await?;367let iov = iov368.get_range(0..bytes_read.try_into()?)369.expect("it should always be possible to slice the iov smaller");370memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;371bytes_read372} else {373return Ok(0);374}375} else {376// Convert the first unsafe guest slice into a safe one--Wiggle377// can only track mutable borrows for an entire region, and converting378// all guest pointers to slices would cause a runtime borrow-checking379// error. As read is allowed to return less than the requested amount,380// it's valid (though not as efficient) for us to only perform the381// read of the first buffer.382let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {383Some(iov) => memory.as_slice_mut(iov)?.unwrap(),384None => return Ok(0),385};386387// Read directly into the Wasm memory.388f.read_vectored(&mut [IoSliceMut::new(guest_slice)]).await?389};390391Ok(types::Size::try_from(bytes_read)?)392}393394async fn fd_pread(395&mut self,396memory: &mut GuestMemory<'_>,397fd: types::Fd,398iovs: types::IovecArray,399offset: types::Filesize,400) -> Result<types::Size, Error> {401let f = self.table().get_file(u32::from(fd))?;402// Access mode check normalizes error returned (windows would prefer ACCES here)403if !f.access_mode.contains(FileAccessMode::READ) {404Err(types::Errno::Badf)?405}406let f = &f.file;407408let iovs: Vec<wiggle::GuestPtr<[u8]>> = iovs409.iter()410.map(|iov_ptr| {411let iov_ptr = iov_ptr?;412let iov: types::Iovec = memory.read(iov_ptr)?;413Ok(iov.buf.as_array(iov.buf_len))414})415.collect::<Result<_, Error>>()?;416417// If the first iov structure is from shared memory we can safely assume418// all the rest will be. We then read into memory based on the memory's419// shared-ness:420// - if not shared, we copy directly into the Wasm memory421// - if shared, we use an intermediate buffer; this avoids Rust unsafety422// due to holding on to a `&mut [u8]` of Wasm memory when we cannot423// guarantee the `&mut` exclusivity--other threads could be modifying424// the data as this functions writes to it. Though likely there is no425// issue with OS writing to io structs in multi-threaded scenarios,426// since we do not know here if `&dyn WasiFile` does anything else427// (e.g., read), we cautiously incur some performance overhead by428// copying twice.429let is_shared_memory = memory.is_shared_memory();430let bytes_read: u64 = if is_shared_memory {431// For shared memory, read into an intermediate buffer. Only the432// first iov will be filled and even then the read is capped by the433// `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.434let iov = iovs.into_iter().next();435if let Some(iov) = iov {436let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];437let bytes_read = f438.read_vectored_at(&mut [IoSliceMut::new(&mut buffer)], offset)439.await?;440let iov = iov441.get_range(0..bytes_read.try_into()?)442.expect("it should always be possible to slice the iov smaller");443memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;444bytes_read445} else {446return Ok(0);447}448} else {449// Convert unsafe guest slices to safe ones.450let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {451Some(iov) => memory.as_slice_mut(iov)?.unwrap(),452None => return Ok(0),453};454455// Read directly into the Wasm memory.456f.read_vectored_at(&mut [IoSliceMut::new(guest_slice)], offset)457.await?458};459460Ok(types::Size::try_from(bytes_read)?)461}462463async fn fd_write(464&mut self,465memory: &mut GuestMemory<'_>,466fd: types::Fd,467ciovs: types::CiovecArray,468) -> Result<types::Size, Error> {469let f = self.table().get_file(u32::from(fd))?;470// Access mode check normalizes error returned (windows would prefer ACCES here)471if !f.access_mode.contains(FileAccessMode::WRITE) {472Err(types::Errno::Badf)?473}474let f = &f.file;475476let guest_slices: Vec<Cow<[u8]>> = ciovs477.iter()478.map(|iov_ptr| {479let iov_ptr = iov_ptr?;480let iov: types::Ciovec = memory.read(iov_ptr)?;481Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)482})483.collect::<Result<_, Error>>()?;484485let ioslices: Vec<IoSlice> = guest_slices486.iter()487.map(|s| IoSlice::new(s.deref()))488.collect();489let bytes_written = f.write_vectored(&ioslices).await?;490491Ok(types::Size::try_from(bytes_written)?)492}493494async fn fd_pwrite(495&mut self,496memory: &mut GuestMemory<'_>,497fd: types::Fd,498ciovs: types::CiovecArray,499offset: types::Filesize,500) -> Result<types::Size, Error> {501let f = self.table().get_file(u32::from(fd))?;502// Access mode check normalizes error returned (windows would prefer ACCES here)503if !f.access_mode.contains(FileAccessMode::WRITE) {504Err(types::Errno::Badf)?505}506let f = &f.file;507508let guest_slices: Vec<Cow<[u8]>> = ciovs509.iter()510.map(|iov_ptr| {511let iov_ptr = iov_ptr?;512let iov: types::Ciovec = memory.read(iov_ptr)?;513Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)514})515.collect::<Result<_, Error>>()?;516517let ioslices: Vec<IoSlice> = guest_slices518.iter()519.map(|s| IoSlice::new(s.deref()))520.collect();521let bytes_written = f.write_vectored_at(&ioslices, offset).await?;522523Ok(types::Size::try_from(bytes_written)?)524}525526async fn fd_prestat_get(527&mut self,528_memory: &mut GuestMemory<'_>,529fd: types::Fd,530) -> Result<types::Prestat, Error> {531let table = self.table();532let dir_entry: Arc<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::badf())?;533if let Some(preopen) = dir_entry.preopen_path() {534let path_str = preopen.to_str().ok_or_else(|| Error::not_supported())?;535let pr_name_len = u32::try_from(path_str.as_bytes().len())?;536Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len }))537} else {538Err(Error::not_supported().context("file is not a preopen"))539}540}541542async fn fd_prestat_dir_name(543&mut self,544memory: &mut GuestMemory<'_>,545fd: types::Fd,546path: GuestPtr<u8>,547path_max_len: types::Size,548) -> Result<(), Error> {549let table = self.table();550let dir_entry: Arc<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?;551if let Some(preopen) = dir_entry.preopen_path() {552let path_bytes = preopen553.to_str()554.ok_or_else(|| Error::not_supported())?555.as_bytes();556let path_len = path_bytes.len();557if path_len > path_max_len as usize {558return Err(Error::name_too_long());559}560let path = path.as_array(path_len as u32);561memory.copy_from_slice(path_bytes, path)?;562Ok(())563} else {564Err(Error::not_supported())565}566}567async fn fd_renumber(568&mut self,569_memory: &mut GuestMemory<'_>,570from: types::Fd,571to: types::Fd,572) -> Result<(), Error> {573let table = self.table();574let from = u32::from(from);575let to = u32::from(to);576if !table.contains_key(from) {577return Err(Error::badf());578}579if !table.contains_key(to) {580return Err(Error::badf());581}582table.renumber(from, to)583}584585async fn fd_seek(586&mut self,587_memory: &mut GuestMemory<'_>,588fd: types::Fd,589offset: types::Filedelta,590whence: types::Whence,591) -> Result<types::Filesize, Error> {592use std::io::SeekFrom;593let whence = match whence {594types::Whence::Cur => SeekFrom::Current(offset),595types::Whence::End => SeekFrom::End(offset),596types::Whence::Set => {597SeekFrom::Start(offset.try_into().map_err(|_| Error::invalid_argument())?)598}599};600let newoffset = self601.table()602.get_file(u32::from(fd))?603.file604.seek(whence)605.await?;606Ok(newoffset)607}608609async fn fd_sync(&mut self, _memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {610self.table().get_file(u32::from(fd))?.file.sync().await?;611Ok(())612}613614async fn fd_tell(615&mut self,616_memory: &mut GuestMemory<'_>,617fd: types::Fd,618) -> Result<types::Filesize, Error> {619let offset = self620.table()621.get_file(u32::from(fd))?622.file623.seek(std::io::SeekFrom::Current(0))624.await?;625Ok(offset)626}627628async fn fd_readdir(629&mut self,630memory: &mut GuestMemory<'_>,631fd: types::Fd,632mut buf: GuestPtr<u8>,633buf_len: types::Size,634cookie: types::Dircookie,635) -> Result<types::Size, Error> {636let mut bufused = 0;637for entity in self638.table()639.get_dir(u32::from(fd))?640.dir641.readdir(ReaddirCursor::from(cookie))642.await?643{644let entity = entity?;645let dirent_raw = dirent_bytes(types::Dirent::try_from(&entity)?);646let dirent_len: types::Size = dirent_raw.len().try_into()?;647let name_raw = entity.name.as_bytes();648let name_len: types::Size = name_raw.len().try_into()?;649650// Copy as many bytes of the dirent as we can, up to the end of the buffer651let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused);652let raw = buf.as_array(dirent_copy_len);653memory.copy_from_slice(&dirent_raw[..dirent_copy_len as usize], raw)?;654655// If the dirent struct wasn't compiled entirely, return that we filled the buffer, which656// tells libc that we're not at EOF.657if dirent_copy_len < dirent_len {658return Ok(buf_len);659}660661buf = buf.add(dirent_copy_len)?;662bufused += dirent_copy_len;663664// Copy as many bytes of the name as we can, up to the end of the buffer665let name_copy_len = std::cmp::min(name_len, buf_len - bufused);666let raw = buf.as_array(name_copy_len);667memory.copy_from_slice(&name_raw[..name_copy_len as usize], raw)?;668669// If the dirent struct wasn't copied entirely, return that we filled the buffer, which670// tells libc that we're not at EOF671672if name_copy_len < name_len {673return Ok(buf_len);674}675676buf = buf.add(name_copy_len)?;677bufused += name_copy_len;678}679Ok(bufused)680}681682async fn path_create_directory(683&mut self,684memory: &mut GuestMemory<'_>,685dirfd: types::Fd,686path: GuestPtr<str>,687) -> Result<(), Error> {688self.table()689.get_dir(u32::from(dirfd))?690.dir691.create_dir(memory.as_cow_str(path)?.deref())692.await693}694695async fn path_filestat_get(696&mut self,697memory: &mut GuestMemory<'_>,698dirfd: types::Fd,699flags: types::Lookupflags,700path: GuestPtr<str>,701) -> Result<types::Filestat, Error> {702let filestat = self703.table()704.get_dir(u32::from(dirfd))?705.dir706.get_path_filestat(707memory.as_cow_str(path)?.deref(),708flags.contains(types::Lookupflags::SYMLINK_FOLLOW),709)710.await?;711Ok(types::Filestat::from(filestat))712}713714async fn path_filestat_set_times(715&mut self,716memory: &mut GuestMemory<'_>,717dirfd: types::Fd,718flags: types::Lookupflags,719path: GuestPtr<str>,720atim: types::Timestamp,721mtim: types::Timestamp,722fst_flags: types::Fstflags,723) -> Result<(), Error> {724let set_atim = fst_flags.contains(types::Fstflags::ATIM);725let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);726let set_mtim = fst_flags.contains(types::Fstflags::MTIM);727let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);728729let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;730let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;731self.table()732.get_dir(u32::from(dirfd))?733.dir734.set_times(735memory.as_cow_str(path)?.deref(),736atim,737mtim,738flags.contains(types::Lookupflags::SYMLINK_FOLLOW),739)740.await741}742743async fn path_link(744&mut self,745memory: &mut GuestMemory<'_>,746src_fd: types::Fd,747src_flags: types::Lookupflags,748src_path: GuestPtr<str>,749target_fd: types::Fd,750target_path: GuestPtr<str>,751) -> Result<(), Error> {752let table = self.table();753let src_dir = table.get_dir(u32::from(src_fd))?;754let target_dir = table.get_dir(u32::from(target_fd))?;755let symlink_follow = src_flags.contains(types::Lookupflags::SYMLINK_FOLLOW);756if symlink_follow {757return Err(Error::invalid_argument()758.context("symlink following on path_link is not supported"));759}760761src_dir762.dir763.hard_link(764memory.as_cow_str(src_path)?.deref(),765target_dir.dir.deref(),766memory.as_cow_str(target_path)?.deref(),767)768.await769}770771async fn path_open(772&mut self,773memory: &mut GuestMemory<'_>,774dirfd: types::Fd,775dirflags: types::Lookupflags,776path: GuestPtr<str>,777oflags: types::Oflags,778fs_rights_base: types::Rights,779_fs_rights_inheriting: types::Rights,780fdflags: types::Fdflags,781) -> Result<types::Fd, Error> {782let table = self.table();783let dirfd = u32::from(dirfd);784if table.is::<FileEntry>(dirfd) {785return Err(Error::not_dir());786}787let dir_entry = table.get_dir(dirfd)?;788789let symlink_follow = dirflags.contains(types::Lookupflags::SYMLINK_FOLLOW);790791let oflags = OFlags::from(&oflags);792let fdflags = FdFlags::from(fdflags);793let path = memory.as_cow_str(path)?;794795let read = fs_rights_base.contains(types::Rights::FD_READ);796let write = fs_rights_base.contains(types::Rights::FD_WRITE);797let access_mode = if read {798FileAccessMode::READ799} else {800FileAccessMode::empty()801} | if write {802FileAccessMode::WRITE803} else {804FileAccessMode::empty()805};806807let file = dir_entry808.dir809.open_file(symlink_follow, path.deref(), oflags, read, write, fdflags)810.await?;811drop(dir_entry);812813let fd = match file {814// Paper over a divergence between Windows and POSIX, where815// POSIX returns EISDIR if you open a directory with the816// WRITE flag: https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html#:~:text=EISDIR817#[cfg(windows)]818OpenResult::Dir(_) if write => {819return Err(types::Errno::Isdir.into());820}821OpenResult::File(file) => table.push(Arc::new(FileEntry::new(file, access_mode)))?,822OpenResult::Dir(child_dir) => table.push(Arc::new(DirEntry::new(None, child_dir)))?,823};824Ok(types::Fd::from(fd))825}826827async fn path_readlink(828&mut self,829memory: &mut GuestMemory<'_>,830dirfd: types::Fd,831path: GuestPtr<str>,832buf: GuestPtr<u8>,833buf_len: types::Size,834) -> Result<types::Size, Error> {835let link = self836.table()837.get_dir(u32::from(dirfd))?838.dir839.read_link(memory.as_cow_str(path)?.deref())840.await?841.into_os_string()842.into_string()843.map_err(|_| Error::illegal_byte_sequence().context("link contents"))?;844let link_bytes = link.as_bytes();845// Like posix readlink(2), silently truncate links when they are larger than the846// destination buffer:847let link_len = std::cmp::min(link_bytes.len(), buf_len as usize);848let buf = buf.as_array(link_len as u32);849memory.copy_from_slice(&link_bytes[..link_len], buf)?;850Ok(link_len as types::Size)851}852853async fn path_remove_directory(854&mut self,855memory: &mut GuestMemory<'_>,856dirfd: types::Fd,857path: GuestPtr<str>,858) -> Result<(), Error> {859self.table()860.get_dir(u32::from(dirfd))?861.dir862.remove_dir(memory.as_cow_str(path)?.deref())863.await864}865866async fn path_rename(867&mut self,868memory: &mut GuestMemory<'_>,869src_fd: types::Fd,870src_path: GuestPtr<str>,871dest_fd: types::Fd,872dest_path: GuestPtr<str>,873) -> Result<(), Error> {874let table = self.table();875let src_dir = table.get_dir(u32::from(src_fd))?;876let dest_dir = table.get_dir(u32::from(dest_fd))?;877src_dir878.dir879.rename(880memory.as_cow_str(src_path)?.deref(),881dest_dir.dir.deref(),882memory.as_cow_str(dest_path)?.deref(),883)884.await885}886887async fn path_symlink(888&mut self,889memory: &mut GuestMemory<'_>,890src_path: GuestPtr<str>,891dirfd: types::Fd,892dest_path: GuestPtr<str>,893) -> Result<(), Error> {894self.table()895.get_dir(u32::from(dirfd))?896.dir897.symlink(898memory.as_cow_str(src_path)?.deref(),899memory.as_cow_str(dest_path)?.deref(),900)901.await902}903904async fn path_unlink_file(905&mut self,906memory: &mut GuestMemory<'_>,907dirfd: types::Fd,908path: GuestPtr<str>,909) -> Result<(), Error> {910self.table()911.get_dir(u32::from(dirfd))?912.dir913.unlink_file(memory.as_cow_str(path)?.deref())914.await915}916917async fn poll_oneoff(918&mut self,919memory: &mut GuestMemory<'_>,920subs: GuestPtr<types::Subscription>,921events: GuestPtr<types::Event>,922nsubscriptions: types::Size,923) -> Result<types::Size, Error> {924if nsubscriptions == 0 {925return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));926}927928// Special-case a `poll_oneoff` which is just sleeping on a single929// relative timer event, such as what WASI libc uses to implement sleep930// functions. This supports all clock IDs, because POSIX says that931// `clock_settime` doesn't effect relative sleeps.932if nsubscriptions == 1 {933let sub = memory.read(subs)?;934if let types::SubscriptionU::Clock(clocksub) = sub.u {935if !clocksub936.flags937.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)938{939self.sched940.sleep(Duration::from_nanos(clocksub.timeout))941.await?;942memory.write(943events,944types::Event {945userdata: sub.userdata,946error: types::Errno::Success,947type_: types::Eventtype::Clock,948fd_readwrite: fd_readwrite_empty(),949},950)?;951return Ok(1);952}953}954}955956let table = &self.table;957// We need these refmuts to outlive Poll, which will hold the &mut dyn WasiFile inside958let mut read_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();959let mut write_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();960961let mut poll = Poll::new();962963let subs = subs.as_array(nsubscriptions);964for sub_elem in subs.iter() {965let sub_ptr = sub_elem?;966let sub = memory.read(sub_ptr)?;967match sub.u {968types::SubscriptionU::Clock(clocksub) => match clocksub.id {969types::Clockid::Monotonic => {970let clock = self.clocks.monotonic()?;971let precision = Duration::from_nanos(clocksub.precision);972let duration = Duration::from_nanos(clocksub.timeout);973let start = if clocksub974.flags975.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)976{977clock.creation_time978} else {979clock.abs_clock.now(precision)980};981let deadline = start982.checked_add(duration)983.ok_or_else(|| Error::overflow().context("deadline"))?;984poll.subscribe_monotonic_clock(985&*clock.abs_clock,986deadline,987precision,988sub.userdata.into(),989)990}991types::Clockid::Realtime => {992// POSIX specifies that functions like `nanosleep` and others use the993// `REALTIME` clock. But it also says that `clock_settime` has no effect994// on threads waiting in these functions. MONOTONIC should always have995// resolution at least as good as REALTIME, so we can translate a996// non-absolute `REALTIME` request into a `MONOTONIC` request.997let clock = self.clocks.monotonic()?;998let precision = Duration::from_nanos(clocksub.precision);999let duration = Duration::from_nanos(clocksub.timeout);1000let deadline = if clocksub1001.flags1002.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)1003{1004return Err(Error::not_supported());1005} else {1006clock1007.abs_clock1008.now(precision)1009.checked_add(duration)1010.ok_or_else(|| Error::overflow().context("deadline"))?1011};1012poll.subscribe_monotonic_clock(1013&*clock.abs_clock,1014deadline,1015precision,1016sub.userdata.into(),1017)1018}1019_ => Err(Error::invalid_argument()1020.context("timer subscriptions only support monotonic timer"))?,1021},1022types::SubscriptionU::FdRead(readsub) => {1023let fd = readsub.file_descriptor;1024let file_ref = table.get_file(u32::from(fd))?;1025read_refs.push((file_ref, Some(sub.userdata.into())));1026}1027types::SubscriptionU::FdWrite(writesub) => {1028let fd = writesub.file_descriptor;1029let file_ref = table.get_file(u32::from(fd))?;1030write_refs.push((file_ref, Some(sub.userdata.into())));1031}1032}1033}10341035let mut read_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();1036for (file_lock, userdata) in read_refs.iter_mut() {1037read_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));1038}10391040for (f, ud) in read_mut_refs.iter_mut() {1041poll.subscribe_read(*f, *ud);1042}10431044let mut write_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();1045for (file_lock, userdata) in write_refs.iter_mut() {1046write_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));1047}10481049for (f, ud) in write_mut_refs.iter_mut() {1050poll.subscribe_write(*f, *ud);1051}10521053self.sched.poll_oneoff(&mut poll).await?;10541055let results = poll.results();1056let num_results = results.len();1057assert!(1058num_results <= nsubscriptions as usize,1059"results exceeds subscriptions"1060);1061let events = events.as_array(1062num_results1063.try_into()1064.expect("not greater than nsubscriptions"),1065);1066for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) {1067let event_ptr = event_elem?;1068let userdata: types::Userdata = userdata.into();1069memory.write(1070event_ptr,1071match result {1072SubscriptionResult::Read(r) => {1073let type_ = types::Eventtype::FdRead;1074match r {1075Ok((nbytes, flags)) => types::Event {1076userdata,1077error: types::Errno::Success,1078type_,1079fd_readwrite: types::EventFdReadwrite {1080nbytes,1081flags: types::Eventrwflags::from(&flags),1082},1083},1084Err(e) => types::Event {1085userdata,1086error: e.downcast().map_err(Error::trap)?,1087type_,1088fd_readwrite: fd_readwrite_empty(),1089},1090}1091}1092SubscriptionResult::Write(r) => {1093let type_ = types::Eventtype::FdWrite;1094match r {1095Ok((nbytes, flags)) => types::Event {1096userdata,1097error: types::Errno::Success,1098type_,1099fd_readwrite: types::EventFdReadwrite {1100nbytes,1101flags: types::Eventrwflags::from(&flags),1102},1103},1104Err(e) => types::Event {1105userdata,1106error: e.downcast().map_err(Error::trap)?,1107type_,1108fd_readwrite: fd_readwrite_empty(),1109},1110}1111}1112SubscriptionResult::MonotonicClock(r) => {1113let type_ = types::Eventtype::Clock;1114types::Event {1115userdata,1116error: match r {1117Ok(()) => types::Errno::Success,1118Err(e) => e.downcast().map_err(Error::trap)?,1119},1120type_,1121fd_readwrite: fd_readwrite_empty(),1122}1123}1124},1125)?;1126}11271128Ok(num_results.try_into().expect("results fit into memory"))1129}11301131async fn proc_exit(1132&mut self,1133_memory: &mut GuestMemory<'_>,1134status: types::Exitcode,1135) -> EnvError {1136// Check that the status is within WASI's range.1137if status < 126 {1138I32Exit(status as i32).into()1139} else {1140EnvError::msg("exit with invalid exit status outside of [0..126)")1141}1142}11431144async fn proc_raise(1145&mut self,1146_memory: &mut GuestMemory<'_>,1147_sig: types::Signal,1148) -> Result<(), Error> {1149Err(Error::trap(EnvError::msg("proc_raise unsupported")))1150}11511152async fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), Error> {1153self.sched.sched_yield().await1154}11551156async fn random_get(1157&mut self,1158memory: &mut GuestMemory<'_>,1159buf: GuestPtr<u8>,1160buf_len: types::Size,1161) -> Result<(), Error> {1162let buf = buf.as_array(buf_len);1163if memory.is_shared_memory() {1164// If the Wasm memory is shared, copy to an intermediate buffer to1165// avoid Rust unsafety (i.e., the called function could rely on1166// `&mut [u8]`'s exclusive ownership which is not guaranteed due to1167// potential access from other threads).1168let mut copied: u32 = 0;1169while copied < buf.len() {1170let len = (buf.len() - copied).min(MAX_SHARED_BUFFER_SIZE as u32);1171let mut tmp = vec![0; len as usize];1172self.random.lock().unwrap().try_fill_bytes(&mut tmp)?;1173let dest = buf.get_range(copied..copied + len).unwrap();1174memory.copy_from_slice(&tmp, dest)?;1175copied += len;1176}1177} else {1178// If the Wasm memory is non-shared, copy directly into the linear1179// memory.1180let mem = &mut memory.as_slice_mut(buf)?.unwrap();1181self.random.lock().unwrap().try_fill_bytes(mem)?;1182}1183Ok(())1184}11851186async fn sock_accept(1187&mut self,1188_memory: &mut GuestMemory<'_>,1189fd: types::Fd,1190flags: types::Fdflags,1191) -> Result<types::Fd, Error> {1192let table = self.table();1193let f = table.get_file(u32::from(fd))?;1194let file = f.file.sock_accept(FdFlags::from(flags)).await?;1195let fd = table.push(Arc::new(FileEntry::new(file, FileAccessMode::all())))?;1196Ok(types::Fd::from(fd))1197}11981199async fn sock_recv(1200&mut self,1201memory: &mut GuestMemory<'_>,1202fd: types::Fd,1203ri_data: types::IovecArray,1204ri_flags: types::Riflags,1205) -> Result<(types::Size, types::Roflags), Error> {1206let f = self.table().get_file(u32::from(fd))?;12071208let iovs: Vec<wiggle::GuestPtr<[u8]>> = ri_data1209.iter()1210.map(|iov_ptr| {1211let iov_ptr = iov_ptr?;1212let iov: types::Iovec = memory.read(iov_ptr)?;1213Ok(iov.buf.as_array(iov.buf_len))1214})1215.collect::<Result<_, Error>>()?;12161217// If the first iov structure is from shared memory we can safely assume1218// all the rest will be. We then read into memory based on the memory's1219// shared-ness:1220// - if not shared, we copy directly into the Wasm memory1221// - if shared, we use an intermediate buffer; this avoids Rust unsafety1222// due to holding on to a `&mut [u8]` of Wasm memory when we cannot1223// guarantee the `&mut` exclusivity--other threads could be modifying1224// the data as this functions writes to it. Though likely there is no1225// issue with OS writing to io structs in multi-threaded scenarios,1226// since we do not know here if `&dyn WasiFile` does anything else1227// (e.g., read), we cautiously incur some performance overhead by1228// copying twice.1229let is_shared_memory = memory.is_shared_memory();1230let (bytes_read, ro_flags) = if is_shared_memory {1231// For shared memory, read into an intermediate buffer. Only the1232// first iov will be filled and even then the read is capped by the1233// `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call.1234let iov = iovs.into_iter().next();1235if let Some(iov) = iov {1236let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];1237let (bytes_read, ro_flags) = f1238.file1239.sock_recv(&mut [IoSliceMut::new(&mut buffer)], RiFlags::from(ri_flags))1240.await?;1241let iov = iov1242.get_range(0..bytes_read.try_into()?)1243.expect("it should always be possible to slice the iov smaller");1244memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;1245(bytes_read, ro_flags)1246} else {1247return Ok((0, RoFlags::empty().into()));1248}1249} else {1250// Convert all of the unsafe guest slices to safe ones--this uses1251// Wiggle's internal borrow checker to ensure no overlaps. We assume1252// here that, because the memory is not shared, there are no other1253// threads to access it while it is written to.1254let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {1255Some(iov) => memory.as_slice_mut(iov)?.unwrap(),1256None => &mut [],1257};12581259// Read directly into the Wasm memory.1260f.file1261.sock_recv(&mut [IoSliceMut::new(guest_slice)], RiFlags::from(ri_flags))1262.await?1263};12641265Ok((types::Size::try_from(bytes_read)?, ro_flags.into()))1266}12671268async fn sock_send(1269&mut self,1270memory: &mut GuestMemory<'_>,1271fd: types::Fd,1272si_data: types::CiovecArray,1273_si_flags: types::Siflags,1274) -> Result<types::Size, Error> {1275let f = self.table().get_file(u32::from(fd))?;12761277let guest_slices: Vec<Cow<[u8]>> = si_data1278.iter()1279.map(|iov_ptr| {1280let iov_ptr = iov_ptr?;1281let iov: types::Ciovec = memory.read(iov_ptr)?;1282Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)1283})1284.collect::<Result<_, Error>>()?;12851286let ioslices: Vec<IoSlice> = guest_slices1287.iter()1288.map(|s| IoSlice::new(s.deref()))1289.collect();1290let bytes_written = f.file.sock_send(&ioslices, SiFlags::empty()).await?;12911292Ok(types::Size::try_from(bytes_written)?)1293}12941295async fn sock_shutdown(1296&mut self,1297_memory: &mut GuestMemory<'_>,1298fd: types::Fd,1299how: types::Sdflags,1300) -> Result<(), Error> {1301let f = self.table().get_file(u32::from(fd))?;13021303f.file.sock_shutdown(SdFlags::from(how)).await1304}1305}13061307impl From<types::Advice> for Advice {1308fn from(advice: types::Advice) -> Advice {1309match advice {1310types::Advice::Normal => Advice::Normal,1311types::Advice::Sequential => Advice::Sequential,1312types::Advice::Random => Advice::Random,1313types::Advice::Willneed => Advice::WillNeed,1314types::Advice::Dontneed => Advice::DontNeed,1315types::Advice::Noreuse => Advice::NoReuse,1316}1317}1318}13191320impl From<&FdStat> for types::Fdstat {1321fn from(fdstat: &FdStat) -> types::Fdstat {1322let mut fs_rights_base = types::Rights::empty();1323if fdstat.access_mode.contains(FileAccessMode::READ) {1324fs_rights_base |= types::Rights::FD_READ;1325}1326if fdstat.access_mode.contains(FileAccessMode::WRITE) {1327fs_rights_base |= types::Rights::FD_WRITE;1328}1329types::Fdstat {1330fs_filetype: types::Filetype::from(&fdstat.filetype),1331fs_rights_base,1332fs_rights_inheriting: types::Rights::empty(),1333fs_flags: types::Fdflags::from(fdstat.flags),1334}1335}1336}13371338impl From<&FileType> for types::Filetype {1339fn from(ft: &FileType) -> types::Filetype {1340match ft {1341FileType::Directory => types::Filetype::Directory,1342FileType::BlockDevice => types::Filetype::BlockDevice,1343FileType::CharacterDevice => types::Filetype::CharacterDevice,1344FileType::RegularFile => types::Filetype::RegularFile,1345FileType::SocketDgram => types::Filetype::SocketDgram,1346FileType::SocketStream => types::Filetype::SocketStream,1347FileType::SymbolicLink => types::Filetype::SymbolicLink,1348FileType::Unknown => types::Filetype::Unknown,1349FileType::Pipe => types::Filetype::Unknown,1350}1351}1352}13531354macro_rules! convert_flags {1355($from:ty, $to:ty, $($flag:ident),+) => {1356impl From<$from> for $to {1357fn from(f: $from) -> $to {1358let mut out = <$to>::empty();1359$(1360if f.contains(<$from>::$flag) {1361out |= <$to>::$flag;1362}1363)+1364out1365}1366}1367}1368}13691370macro_rules! convert_flags_bidirectional {1371($from:ty, $to:ty, $($rest:tt)*) => {1372convert_flags!($from, $to, $($rest)*);1373convert_flags!($to, $from, $($rest)*);1374}1375}13761377convert_flags_bidirectional!(1378FdFlags,1379types::Fdflags,1380APPEND,1381DSYNC,1382NONBLOCK,1383RSYNC,1384SYNC1385);13861387convert_flags_bidirectional!(RiFlags, types::Riflags, RECV_PEEK, RECV_WAITALL);13881389convert_flags_bidirectional!(RoFlags, types::Roflags, RECV_DATA_TRUNCATED);13901391convert_flags_bidirectional!(SdFlags, types::Sdflags, RD, WR);13921393impl From<&types::Oflags> for OFlags {1394fn from(oflags: &types::Oflags) -> OFlags {1395let mut out = OFlags::empty();1396if oflags.contains(types::Oflags::CREAT) {1397out = out | OFlags::CREATE;1398}1399if oflags.contains(types::Oflags::DIRECTORY) {1400out = out | OFlags::DIRECTORY;1401}1402if oflags.contains(types::Oflags::EXCL) {1403out = out | OFlags::EXCLUSIVE;1404}1405if oflags.contains(types::Oflags::TRUNC) {1406out = out | OFlags::TRUNCATE;1407}1408out1409}1410}14111412impl From<&OFlags> for types::Oflags {1413fn from(oflags: &OFlags) -> types::Oflags {1414let mut out = types::Oflags::empty();1415if oflags.contains(OFlags::CREATE) {1416out = out | types::Oflags::CREAT;1417}1418if oflags.contains(OFlags::DIRECTORY) {1419out = out | types::Oflags::DIRECTORY;1420}1421if oflags.contains(OFlags::EXCLUSIVE) {1422out = out | types::Oflags::EXCL;1423}1424if oflags.contains(OFlags::TRUNCATE) {1425out = out | types::Oflags::TRUNC;1426}1427out1428}1429}1430impl From<Filestat> for types::Filestat {1431fn from(stat: Filestat) -> types::Filestat {1432types::Filestat {1433dev: stat.device_id,1434ino: stat.inode,1435filetype: types::Filetype::from(&stat.filetype),1436nlink: stat.nlink,1437size: stat.size,1438atim: stat1439.atim1440.map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)1441.unwrap_or(0),1442mtim: stat1443.mtim1444.map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)1445.unwrap_or(0),1446ctim: stat1447.ctim1448.map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)1449.unwrap_or(0),1450}1451}1452}14531454impl TryFrom<&ReaddirEntity> for types::Dirent {1455type Error = Error;1456fn try_from(e: &ReaddirEntity) -> Result<types::Dirent, Error> {1457Ok(types::Dirent {1458d_ino: e.inode,1459d_namlen: e.name.as_bytes().len().try_into()?,1460d_type: types::Filetype::from(&e.filetype),1461d_next: e.next.into(),1462})1463}1464}14651466fn dirent_bytes(dirent: types::Dirent) -> Vec<u8> {1467use wiggle::GuestType;1468assert_eq!(1469types::Dirent::guest_size(),1470std::mem::size_of::<types::Dirent>() as u32,1471"Dirent guest repr and host repr should match"1472);1473assert_eq!(14741,1475std::mem::size_of_val(&dirent.d_type),1476"Dirent member d_type should be endian-invariant"1477);1478let size = types::Dirent::guest_size()1479.try_into()1480.expect("Dirent is smaller than 2^32");1481let mut bytes = Vec::with_capacity(size);1482bytes.resize(size, 0);1483let ptr = bytes.as_mut_ptr().cast::<types::Dirent>();1484let guest_dirent = types::Dirent {1485d_ino: dirent.d_ino.to_le(),1486d_namlen: dirent.d_namlen.to_le(),1487d_type: dirent.d_type, // endian-invariant1488d_next: dirent.d_next.to_le(),1489};1490unsafe { ptr.write_unaligned(guest_dirent) };1491bytes1492}14931494impl From<&RwEventFlags> for types::Eventrwflags {1495fn from(flags: &RwEventFlags) -> types::Eventrwflags {1496let mut out = types::Eventrwflags::empty();1497if flags.contains(RwEventFlags::HANGUP) {1498out = out | types::Eventrwflags::FD_READWRITE_HANGUP;1499}1500out1501}1502}15031504fn fd_readwrite_empty() -> types::EventFdReadwrite {1505types::EventFdReadwrite {1506nbytes: 0,1507flags: types::Eventrwflags::empty(),1508}1509}15101511fn systimespec(1512set: bool,1513ts: types::Timestamp,1514now: bool,1515) -> Result<Option<SystemTimeSpec>, Error> {1516if set && now {1517Err(Error::invalid_argument())1518} else if set {1519Ok(Some(SystemTimeSpec::Absolute(1520SystemClock::UNIX_EPOCH + Duration::from_nanos(ts),1521)))1522} else if now {1523Ok(Some(SystemTimeSpec::SymbolicNow))1524} else {1525Ok(None)1526}1527}15281529// This is the default subset of base Rights reported for directories prior to1530// https://github.com/bytecodealliance/wasmtime/pull/6265. Some1531// implementations still expect this set of rights to be reported.1532pub(crate) fn directory_base_rights() -> types::Rights {1533types::Rights::PATH_CREATE_DIRECTORY1534| types::Rights::PATH_CREATE_FILE1535| types::Rights::PATH_LINK_SOURCE1536| types::Rights::PATH_LINK_TARGET1537| types::Rights::PATH_OPEN1538| types::Rights::FD_READDIR1539| types::Rights::PATH_READLINK1540| types::Rights::PATH_RENAME_SOURCE1541| types::Rights::PATH_RENAME_TARGET1542| types::Rights::PATH_SYMLINK1543| types::Rights::PATH_REMOVE_DIRECTORY1544| types::Rights::PATH_UNLINK_FILE1545| types::Rights::PATH_FILESTAT_GET1546| types::Rights::PATH_FILESTAT_SET_TIMES1547| types::Rights::FD_FILESTAT_GET1548| types::Rights::FD_FILESTAT_SET_TIMES1549}15501551// This is the default subset of inheriting Rights reported for directories1552// prior to https://github.com/bytecodealliance/wasmtime/pull/6265. Some1553// implementations still expect this set of rights to be reported.1554pub(crate) fn directory_inheriting_rights() -> types::Rights {1555types::Rights::FD_DATASYNC1556| types::Rights::FD_READ1557| types::Rights::FD_SEEK1558| types::Rights::FD_FDSTAT_SET_FLAGS1559| types::Rights::FD_SYNC1560| types::Rights::FD_TELL1561| types::Rights::FD_WRITE1562| types::Rights::FD_ADVISE1563| types::Rights::FD_ALLOCATE1564| types::Rights::FD_FILESTAT_GET1565| types::Rights::FD_FILESTAT_SET_SIZE1566| types::Rights::FD_FILESTAT_SET_TIMES1567| types::Rights::POLL_FD_READWRITE1568| directory_base_rights()1569}157015711572