1use crate::cli::WasiCliView as _;
67use crate::clocks::WasiClocksView as _;
68use crate::filesystem::WasiFilesystemView as _;
69use crate::p2::bindings::{
70 cli::{
71 stderr::Host as _, stdin::Host as _, stdout::Host as _, terminal_input, terminal_output,
72 terminal_stderr::Host as _, terminal_stdin::Host as _, terminal_stdout::Host as _,
73 },
74 clocks::{monotonic_clock, wall_clock},
75 filesystem::{preopens::Host as _, types as filesystem},
76};
77use crate::p2::{FsError, IsATTY};
78use crate::{ResourceTable, WasiCtx, WasiCtxView, WasiView};
79use anyhow::{Context, bail};
80use std::collections::{BTreeMap, BTreeSet, HashSet, btree_map};
81use std::mem::{self, size_of, size_of_val};
82use std::slice;
83use std::sync::Arc;
84use std::sync::atomic::{AtomicU64, Ordering};
85use system_interface::fs::FileIoExt;
86use wasmtime::component::Resource;
87use wasmtime_wasi_io::{
88 bindings::wasi::io::streams,
89 streams::{StreamError, StreamResult},
90};
91use wiggle::tracing::instrument;
92use wiggle::{GuestError, GuestMemory, GuestPtr, GuestType};
93
94use crate::p2::bindings::cli::environment::Host as _;
96use crate::p2::bindings::filesystem::types::HostDescriptor as _;
97use crate::p2::bindings::random::random::Host as _;
98use wasmtime_wasi_io::bindings::wasi::io::poll::Host as _;
99
100pub struct WasiP1Ctx {
145 table: ResourceTable,
146 wasi: WasiCtx,
147 adapter: WasiP1Adapter,
148}
149
150impl WasiP1Ctx {
151 pub(crate) fn new(wasi: WasiCtx) -> Self {
152 Self {
153 table: ResourceTable::new(),
154 wasi,
155 adapter: WasiP1Adapter::new(),
156 }
157 }
158}
159
160impl WasiView for WasiP1Ctx {
161 fn ctx(&mut self) -> WasiCtxView<'_> {
162 WasiCtxView {
163 ctx: &mut self.wasi,
164 table: &mut self.table,
165 }
166 }
167}
168
169#[derive(Debug)]
170struct File {
171 fd: Resource<filesystem::Descriptor>,
173
174 position: Arc<AtomicU64>,
176
177 append: bool,
179
180 blocking_mode: BlockingMode,
184}
185
186#[derive(Clone, Copy, Debug)]
193enum BlockingMode {
194 Blocking,
195 NonBlocking,
196}
197impl BlockingMode {
198 fn from_fdflags(flags: &types::Fdflags) -> Self {
199 if flags.contains(types::Fdflags::NONBLOCK) {
200 BlockingMode::NonBlocking
201 } else {
202 BlockingMode::Blocking
203 }
204 }
205 async fn read(
206 &self,
207 host: &mut impl streams::HostInputStream,
208 input_stream: Resource<streams::InputStream>,
209 max_size: usize,
210 ) -> Result<Vec<u8>, types::Error> {
211 let max_size = max_size.try_into().unwrap_or(u64::MAX);
212 match streams::HostInputStream::blocking_read(host, input_stream, max_size).await {
213 Ok(r) if r.is_empty() => Err(types::Errno::Intr.into()),
214 Ok(r) => Ok(r),
215 Err(StreamError::Closed) => Ok(Vec::new()),
216 Err(e) => Err(e.into()),
217 }
218 }
219 async fn write(
220 &self,
221 memory: &mut GuestMemory<'_>,
222 host: &mut impl streams::HostOutputStream,
223 output_stream: Resource<streams::OutputStream>,
224 bytes: GuestPtr<[u8]>,
225 ) -> StreamResult<usize> {
226 use streams::HostOutputStream as Streams;
227
228 let bytes = memory
229 .as_cow(bytes)
230 .map_err(|e| StreamError::Trap(e.into()))?;
231 let mut bytes = &bytes[..];
232
233 let total = bytes.len();
234 while !bytes.is_empty() {
235 let len = bytes.len().min(4096);
237 let (chunk, rest) = bytes.split_at(len);
238 bytes = rest;
239
240 Streams::blocking_write_and_flush(host, output_stream.borrowed(), Vec::from(chunk))
241 .await?
242 }
243
244 Ok(total)
245 }
246}
247
248#[derive(Debug)]
249enum Descriptor {
250 Stdin {
251 stream: Resource<streams::InputStream>,
252 isatty: IsATTY,
253 },
254 Stdout {
255 stream: Resource<streams::OutputStream>,
256 isatty: IsATTY,
257 },
258 Stderr {
259 stream: Resource<streams::OutputStream>,
260 isatty: IsATTY,
261 },
262 Directory {
264 fd: Resource<filesystem::Descriptor>,
265 preopen_path: Option<String>,
268 },
269 File(File),
271}
272
273#[derive(Debug, Default)]
274struct WasiP1Adapter {
275 descriptors: Option<Descriptors>,
276}
277
278#[derive(Debug, Default)]
279struct Descriptors {
280 used: BTreeMap<u32, Descriptor>,
281 free: BTreeSet<u32>,
282}
283
284impl Descriptors {
285 fn new(host: &mut WasiP1Ctx) -> Result<Self, types::Error> {
287 let mut descriptors = Self::default();
288 descriptors.push(Descriptor::Stdin {
289 stream: host
290 .cli()
291 .get_stdin()
292 .context("failed to call `get-stdin`")
293 .map_err(types::Error::trap)?,
294 isatty: if let Some(term_in) = host
295 .cli()
296 .get_terminal_stdin()
297 .context("failed to call `get-terminal-stdin`")
298 .map_err(types::Error::trap)?
299 {
300 terminal_input::HostTerminalInput::drop(&mut host.cli(), term_in)
301 .context("failed to call `drop-terminal-input`")
302 .map_err(types::Error::trap)?;
303 IsATTY::Yes
304 } else {
305 IsATTY::No
306 },
307 })?;
308 descriptors.push(Descriptor::Stdout {
309 stream: host
310 .cli()
311 .get_stdout()
312 .context("failed to call `get-stdout`")
313 .map_err(types::Error::trap)?,
314 isatty: if let Some(term_out) = host
315 .cli()
316 .get_terminal_stdout()
317 .context("failed to call `get-terminal-stdout`")
318 .map_err(types::Error::trap)?
319 {
320 terminal_output::HostTerminalOutput::drop(&mut host.cli(), term_out)
321 .context("failed to call `drop-terminal-output`")
322 .map_err(types::Error::trap)?;
323 IsATTY::Yes
324 } else {
325 IsATTY::No
326 },
327 })?;
328 descriptors.push(Descriptor::Stderr {
329 stream: host
330 .cli()
331 .get_stderr()
332 .context("failed to call `get-stderr`")
333 .map_err(types::Error::trap)?,
334 isatty: if let Some(term_out) = host
335 .cli()
336 .get_terminal_stderr()
337 .context("failed to call `get-terminal-stderr`")
338 .map_err(types::Error::trap)?
339 {
340 terminal_output::HostTerminalOutput::drop(&mut host.cli(), term_out)
341 .context("failed to call `drop-terminal-output`")
342 .map_err(types::Error::trap)?;
343 IsATTY::Yes
344 } else {
345 IsATTY::No
346 },
347 })?;
348
349 for dir in host
350 .filesystem()
351 .get_directories()
352 .context("failed to call `get-directories`")
353 .map_err(types::Error::trap)?
354 {
355 descriptors.push(Descriptor::Directory {
356 fd: dir.0,
357 preopen_path: Some(dir.1),
358 })?;
359 }
360 Ok(descriptors)
361 }
362
363 fn unused(&self) -> Result<u32> {
365 match self.used.last_key_value() {
366 Some((fd, _)) => {
367 if let Some(fd) = fd.checked_add(1) {
368 return Ok(fd);
369 }
370 if self.used.len() == u32::MAX as usize {
371 return Err(types::Errno::Loop.into());
372 }
373 Ok((0..u32::MAX)
375 .rev()
376 .find(|fd| !self.used.contains_key(fd))
377 .expect("failed to find an unused file descriptor"))
378 }
379 None => Ok(0),
380 }
381 }
382
383 fn push(&mut self, desc: Descriptor) -> Result<u32> {
387 let fd = if let Some(fd) = self.free.pop_last() {
388 fd
389 } else {
390 self.unused()?
391 };
392 assert!(self.used.insert(fd, desc).is_none());
393 Ok(fd)
394 }
395}
396
397impl WasiP1Adapter {
398 fn new() -> Self {
399 Self::default()
400 }
401}
402
403struct Transaction<'a> {
413 view: &'a mut WasiP1Ctx,
414 descriptors: Descriptors,
415}
416
417impl Drop for Transaction<'_> {
418 fn drop(&mut self) {
420 let descriptors = mem::take(&mut self.descriptors);
421 self.view.adapter.descriptors = Some(descriptors);
422 }
423}
424
425impl Transaction<'_> {
426 fn get_descriptor(&self, fd: types::Fd) -> Result<&Descriptor> {
432 let fd = fd.into();
433 let desc = self.descriptors.used.get(&fd).ok_or(types::Errno::Badf)?;
434 Ok(desc)
435 }
436
437 fn get_file(&self, fd: types::Fd) -> Result<&File> {
440 let fd = fd.into();
441 match self.descriptors.used.get(&fd) {
442 Some(Descriptor::File(file)) => Ok(file),
443 _ => Err(types::Errno::Badf.into()),
444 }
445 }
446
447 fn get_file_mut(&mut self, fd: types::Fd) -> Result<&mut File> {
450 let fd = fd.into();
451 match self.descriptors.used.get_mut(&fd) {
452 Some(Descriptor::File(file)) => Ok(file),
453 _ => Err(types::Errno::Badf.into()),
454 }
455 }
456
457 fn get_seekable(&self, fd: types::Fd) -> Result<&File> {
464 let fd = fd.into();
465 match self.descriptors.used.get(&fd) {
466 Some(Descriptor::File(file)) => Ok(file),
467 Some(
468 Descriptor::Stdin { .. } | Descriptor::Stdout { .. } | Descriptor::Stderr { .. },
469 ) => {
470 Err(types::Errno::Spipe.into())
472 }
473 _ => Err(types::Errno::Badf.into()),
474 }
475 }
476
477 fn get_fd(&self, fd: types::Fd) -> Result<Resource<filesystem::Descriptor>> {
479 match self.get_descriptor(fd)? {
480 Descriptor::File(File { fd, .. }) => Ok(fd.borrowed()),
481 Descriptor::Directory { fd, .. } => Ok(fd.borrowed()),
482 Descriptor::Stdin { .. } | Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => {
483 Err(types::Errno::Badf.into())
484 }
485 }
486 }
487
488 fn get_file_fd(&self, fd: types::Fd) -> Result<Resource<filesystem::Descriptor>> {
491 self.get_file(fd).map(|File { fd, .. }| fd.borrowed())
492 }
493
494 fn get_dir_fd(&self, fd: types::Fd) -> Result<Resource<filesystem::Descriptor>> {
497 let fd = fd.into();
498 match self.descriptors.used.get(&fd) {
499 Some(Descriptor::Directory { fd, .. }) => Ok(fd.borrowed()),
500 _ => Err(types::Errno::Badf.into()),
501 }
502 }
503}
504
505impl WasiP1Ctx {
506 fn transact(&mut self) -> Result<Transaction<'_>, types::Error> {
509 let descriptors = if let Some(descriptors) = self.adapter.descriptors.take() {
510 descriptors
511 } else {
512 Descriptors::new(self)?
513 };
514 Ok(Transaction {
515 view: self,
516 descriptors,
517 })
518 }
519
520 fn get_fd(&mut self, fd: types::Fd) -> Result<Resource<filesystem::Descriptor>, types::Error> {
523 let st = self.transact()?;
524 let fd = st.get_fd(fd)?;
525 Ok(fd)
526 }
527
528 fn get_file_fd(
532 &mut self,
533 fd: types::Fd,
534 ) -> Result<Resource<filesystem::Descriptor>, types::Error> {
535 let st = self.transact()?;
536 let fd = st.get_file_fd(fd)?;
537 Ok(fd)
538 }
539
540 fn get_dir_fd(
545 &mut self,
546 fd: types::Fd,
547 ) -> Result<Resource<filesystem::Descriptor>, types::Error> {
548 let st = self.transact()?;
549 let fd = st.get_dir_fd(fd)?;
550 Ok(fd)
551 }
552
553 async fn fd_write_impl(
555 &mut self,
556 memory: &mut GuestMemory<'_>,
557 fd: types::Fd,
558 ciovs: types::CiovecArray,
559 write: FdWrite,
560 ) -> Result<types::Size, types::Error> {
561 let t = self.transact()?;
562 let desc = t.get_descriptor(fd)?;
563 match desc {
564 Descriptor::File(File {
565 fd,
566 append,
567 position,
568 blocking_mode: _,
575 }) => {
576 let fd = fd.borrowed();
577 let position = position.clone();
578 let pos = position.load(Ordering::Relaxed);
579 let append = *append;
580 drop(t);
581 let f = self.table.get(&fd)?.file()?;
582 let buf = first_non_empty_ciovec(memory, ciovs)?;
583
584 let do_write = move |f: &cap_std::fs::File, buf: &[u8]| match (append, write) {
585 (true, _) => f.append(&buf),
589 (false, FdWrite::At(pos)) => f.write_at(&buf, pos),
590 (false, FdWrite::AtCur) => f.write_at(&buf, pos),
591 };
592
593 let nwritten = match f.as_blocking_file() {
594 Some(f) => do_write(f, &memory.as_cow(buf)?),
597 None => {
601 let buf = memory.to_vec(buf)?;
602 f.run_blocking(move |f| do_write(f, &buf)).await
603 }
604 };
605
606 let nwritten = nwritten.map_err(|e| StreamError::LastOperationFailed(e.into()))?;
607
608 if let FdWrite::AtCur = write {
612 if append {
613 let len = self.filesystem().stat(fd).await?;
614 position.store(len.size, Ordering::Relaxed);
615 } else {
616 let pos = pos
617 .checked_add(nwritten as u64)
618 .ok_or(types::Errno::Overflow)?;
619 position.store(pos, Ordering::Relaxed);
620 }
621 }
622 Ok(nwritten.try_into()?)
623 }
624 Descriptor::Stdout { stream, .. } | Descriptor::Stderr { stream, .. } => {
625 match write {
626 FdWrite::At(_) => return Err(types::Errno::Spipe.into()),
628 FdWrite::AtCur => {}
630 }
631 let stream = stream.borrowed();
632 drop(t);
633 let buf = first_non_empty_ciovec(memory, ciovs)?;
634 let n = BlockingMode::Blocking
635 .write(memory, &mut self.table, stream, buf)
636 .await?
637 .try_into()?;
638 Ok(n)
639 }
640 _ => Err(types::Errno::Badf.into()),
641 }
642 }
643}
644
645#[derive(Copy, Clone)]
646enum FdWrite {
647 At(u64),
648 AtCur,
649}
650
651pub fn add_to_linker_async<T: Send + 'static>(
719 linker: &mut wasmtime::Linker<T>,
720 f: impl Fn(&mut T) -> &mut WasiP1Ctx + Copy + Send + Sync + 'static,
721) -> anyhow::Result<()> {
722 crate::p1::wasi_snapshot_preview1::add_to_linker(linker, f)
723}
724
725pub fn add_to_linker_sync<T: Send + 'static>(
793 linker: &mut wasmtime::Linker<T>,
794 f: impl Fn(&mut T) -> &mut WasiP1Ctx + Copy + Send + Sync + 'static,
795) -> anyhow::Result<()> {
796 sync::add_wasi_snapshot_preview1_to_linker(linker, f)
797}
798
799wiggle::from_witx!({
804 witx: ["witx/p1/wasi_snapshot_preview1.witx"],
805 async: {
806 wasi_snapshot_preview1::{
807 fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size,
808 fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write,
809 fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get,
810 path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory,
811 path_rename, path_symlink, path_unlink_file
812 }
813 },
814 errors: { errno => trappable Error },
815});
816
817pub(crate) mod sync {
818 use anyhow::Result;
819 use std::future::Future;
820
821 wiggle::wasmtime_integration!({
822 witx: ["witx/p1/wasi_snapshot_preview1.witx"],
823 target: super,
824 block_on[in_tokio]: {
825 wasi_snapshot_preview1::{
826 fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size,
827 fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write,
828 fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get,
829 path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory,
830 path_rename, path_symlink, path_unlink_file
831 }
832 },
833 errors: { errno => trappable Error },
834 });
835
836 fn in_tokio<F: Future>(future: F) -> Result<F::Output> {
839 Ok(crate::runtime::in_tokio(future))
840 }
841}
842
843impl wiggle::GuestErrorType for types::Errno {
844 fn success() -> Self {
845 Self::Success
846 }
847}
848
849impl From<StreamError> for types::Error {
850 fn from(err: StreamError) -> Self {
851 match err {
852 StreamError::Closed => types::Errno::Io.into(),
853 StreamError::LastOperationFailed(e) => match e.downcast::<std::io::Error>() {
854 Ok(err) => filesystem::ErrorCode::from(err).into(),
855 Err(e) => {
856 tracing::debug!("dropping error {e:?}");
857 types::Errno::Io.into()
858 }
859 },
860 StreamError::Trap(e) => types::Error::trap(e),
861 }
862 }
863}
864
865impl From<FsError> for types::Error {
866 fn from(err: FsError) -> Self {
867 match err.downcast() {
868 Ok(code) => code.into(),
869 Err(e) => types::Error::trap(e),
870 }
871 }
872}
873
874fn systimespec(set: bool, ts: types::Timestamp, now: bool) -> Result<filesystem::NewTimestamp> {
875 if set && now {
876 Err(types::Errno::Inval.into())
877 } else if set {
878 Ok(filesystem::NewTimestamp::Timestamp(filesystem::Datetime {
879 seconds: ts / 1_000_000_000,
880 nanoseconds: (ts % 1_000_000_000) as _,
881 }))
882 } else if now {
883 Ok(filesystem::NewTimestamp::Now)
884 } else {
885 Ok(filesystem::NewTimestamp::NoChange)
886 }
887}
888
889impl TryFrom<wall_clock::Datetime> for types::Timestamp {
890 type Error = types::Errno;
891
892 fn try_from(
893 wall_clock::Datetime {
894 seconds,
895 nanoseconds,
896 }: wall_clock::Datetime,
897 ) -> Result<Self, Self::Error> {
898 types::Timestamp::from(seconds)
899 .checked_mul(1_000_000_000)
900 .and_then(|ns| ns.checked_add(nanoseconds.into()))
901 .ok_or(types::Errno::Overflow)
902 }
903}
904
905impl From<types::Lookupflags> for filesystem::PathFlags {
906 fn from(flags: types::Lookupflags) -> Self {
907 if flags.contains(types::Lookupflags::SYMLINK_FOLLOW) {
908 filesystem::PathFlags::SYMLINK_FOLLOW
909 } else {
910 filesystem::PathFlags::empty()
911 }
912 }
913}
914
915impl From<types::Oflags> for filesystem::OpenFlags {
916 fn from(flags: types::Oflags) -> Self {
917 let mut out = filesystem::OpenFlags::empty();
918 if flags.contains(types::Oflags::CREAT) {
919 out |= filesystem::OpenFlags::CREATE;
920 }
921 if flags.contains(types::Oflags::DIRECTORY) {
922 out |= filesystem::OpenFlags::DIRECTORY;
923 }
924 if flags.contains(types::Oflags::EXCL) {
925 out |= filesystem::OpenFlags::EXCLUSIVE;
926 }
927 if flags.contains(types::Oflags::TRUNC) {
928 out |= filesystem::OpenFlags::TRUNCATE;
929 }
930 out
931 }
932}
933
934impl From<types::Advice> for filesystem::Advice {
935 fn from(advice: types::Advice) -> Self {
936 match advice {
937 types::Advice::Normal => filesystem::Advice::Normal,
938 types::Advice::Sequential => filesystem::Advice::Sequential,
939 types::Advice::Random => filesystem::Advice::Random,
940 types::Advice::Willneed => filesystem::Advice::WillNeed,
941 types::Advice::Dontneed => filesystem::Advice::DontNeed,
942 types::Advice::Noreuse => filesystem::Advice::NoReuse,
943 }
944 }
945}
946
947impl TryFrom<filesystem::DescriptorType> for types::Filetype {
948 type Error = anyhow::Error;
949
950 fn try_from(ty: filesystem::DescriptorType) -> Result<Self, Self::Error> {
951 match ty {
952 filesystem::DescriptorType::RegularFile => Ok(types::Filetype::RegularFile),
953 filesystem::DescriptorType::Directory => Ok(types::Filetype::Directory),
954 filesystem::DescriptorType::BlockDevice => Ok(types::Filetype::BlockDevice),
955 filesystem::DescriptorType::CharacterDevice => Ok(types::Filetype::CharacterDevice),
956 filesystem::DescriptorType::Fifo => Ok(types::Filetype::Unknown),
958 filesystem::DescriptorType::Socket => {
961 bail!("sockets are not currently supported")
962 }
963 filesystem::DescriptorType::SymbolicLink => Ok(types::Filetype::SymbolicLink),
964 filesystem::DescriptorType::Unknown => Ok(types::Filetype::Unknown),
965 }
966 }
967}
968
969impl From<IsATTY> for types::Filetype {
970 fn from(isatty: IsATTY) -> Self {
971 match isatty {
972 IsATTY::Yes => types::Filetype::CharacterDevice,
973 IsATTY::No => types::Filetype::Unknown,
974 }
975 }
976}
977
978impl From<filesystem::ErrorCode> for types::Errno {
979 fn from(code: filesystem::ErrorCode) -> Self {
980 match code {
981 filesystem::ErrorCode::Access => types::Errno::Acces,
982 filesystem::ErrorCode::WouldBlock => types::Errno::Again,
983 filesystem::ErrorCode::Already => types::Errno::Already,
984 filesystem::ErrorCode::BadDescriptor => types::Errno::Badf,
985 filesystem::ErrorCode::Busy => types::Errno::Busy,
986 filesystem::ErrorCode::Deadlock => types::Errno::Deadlk,
987 filesystem::ErrorCode::Quota => types::Errno::Dquot,
988 filesystem::ErrorCode::Exist => types::Errno::Exist,
989 filesystem::ErrorCode::FileTooLarge => types::Errno::Fbig,
990 filesystem::ErrorCode::IllegalByteSequence => types::Errno::Ilseq,
991 filesystem::ErrorCode::InProgress => types::Errno::Inprogress,
992 filesystem::ErrorCode::Interrupted => types::Errno::Intr,
993 filesystem::ErrorCode::Invalid => types::Errno::Inval,
994 filesystem::ErrorCode::Io => types::Errno::Io,
995 filesystem::ErrorCode::IsDirectory => types::Errno::Isdir,
996 filesystem::ErrorCode::Loop => types::Errno::Loop,
997 filesystem::ErrorCode::TooManyLinks => types::Errno::Mlink,
998 filesystem::ErrorCode::MessageSize => types::Errno::Msgsize,
999 filesystem::ErrorCode::NameTooLong => types::Errno::Nametoolong,
1000 filesystem::ErrorCode::NoDevice => types::Errno::Nodev,
1001 filesystem::ErrorCode::NoEntry => types::Errno::Noent,
1002 filesystem::ErrorCode::NoLock => types::Errno::Nolck,
1003 filesystem::ErrorCode::InsufficientMemory => types::Errno::Nomem,
1004 filesystem::ErrorCode::InsufficientSpace => types::Errno::Nospc,
1005 filesystem::ErrorCode::Unsupported => types::Errno::Notsup,
1006 filesystem::ErrorCode::NotDirectory => types::Errno::Notdir,
1007 filesystem::ErrorCode::NotEmpty => types::Errno::Notempty,
1008 filesystem::ErrorCode::NotRecoverable => types::Errno::Notrecoverable,
1009 filesystem::ErrorCode::NoTty => types::Errno::Notty,
1010 filesystem::ErrorCode::NoSuchDevice => types::Errno::Nxio,
1011 filesystem::ErrorCode::Overflow => types::Errno::Overflow,
1012 filesystem::ErrorCode::NotPermitted => types::Errno::Perm,
1013 filesystem::ErrorCode::Pipe => types::Errno::Pipe,
1014 filesystem::ErrorCode::ReadOnly => types::Errno::Rofs,
1015 filesystem::ErrorCode::InvalidSeek => types::Errno::Spipe,
1016 filesystem::ErrorCode::TextFileBusy => types::Errno::Txtbsy,
1017 filesystem::ErrorCode::CrossDevice => types::Errno::Xdev,
1018 }
1019 }
1020}
1021
1022impl From<std::num::TryFromIntError> for types::Error {
1023 fn from(_: std::num::TryFromIntError) -> Self {
1024 types::Errno::Overflow.into()
1025 }
1026}
1027
1028impl From<GuestError> for types::Error {
1029 fn from(err: GuestError) -> Self {
1030 use wiggle::GuestError::*;
1031 match err {
1032 InvalidFlagValue { .. } => types::Errno::Inval.into(),
1033 InvalidEnumValue { .. } => types::Errno::Inval.into(),
1034 PtrOverflow { .. } | PtrOutOfBounds { .. } | PtrNotAligned { .. } => {
1045 types::Error::trap(err.into())
1046 }
1047 InvalidUtf8 { .. } => types::Errno::Ilseq.into(),
1048 TryFromIntError { .. } => types::Errno::Overflow.into(),
1049 SliceLengthsDiffer { .. } => types::Errno::Fault.into(),
1050 InFunc { err, .. } => types::Error::from(*err),
1051 }
1052 }
1053}
1054
1055impl From<filesystem::ErrorCode> for types::Error {
1056 fn from(code: filesystem::ErrorCode) -> Self {
1057 types::Errno::from(code).into()
1058 }
1059}
1060
1061impl From<wasmtime::component::ResourceTableError> for types::Error {
1062 fn from(err: wasmtime::component::ResourceTableError) -> Self {
1063 types::Error::trap(err.into())
1064 }
1065}
1066
1067type Result<T, E = types::Error> = std::result::Result<T, E>;
1068
1069fn write_bytes(
1070 memory: &mut GuestMemory<'_>,
1071 ptr: GuestPtr<u8>,
1072 buf: &[u8],
1073) -> Result<GuestPtr<u8>, types::Error> {
1074 let len = u32::try_from(buf.len())?;
1077
1078 memory.copy_from_slice(buf, ptr.as_array(len))?;
1079 let next = ptr.add(len)?;
1080 Ok(next)
1081}
1082
1083fn write_byte(memory: &mut GuestMemory<'_>, ptr: GuestPtr<u8>, byte: u8) -> Result<GuestPtr<u8>> {
1084 memory.write(ptr, byte)?;
1085 let next = ptr.add(1)?;
1086 Ok(next)
1087}
1088
1089fn read_string<'a>(memory: &'a GuestMemory<'_>, ptr: GuestPtr<str>) -> Result<String> {
1090 Ok(memory.as_cow_str(ptr)?.into_owned())
1091}
1092
1093fn first_non_empty_ciovec(
1096 memory: &GuestMemory<'_>,
1097 ciovs: types::CiovecArray,
1098) -> Result<GuestPtr<[u8]>> {
1099 for iov in ciovs.iter() {
1100 let iov = memory.read(iov?)?;
1101 if iov.buf_len == 0 {
1102 continue;
1103 }
1104 return Ok(iov.buf.as_array(iov.buf_len));
1105 }
1106 Ok(GuestPtr::new((0, 0)))
1107}
1108
1109fn first_non_empty_iovec(
1112 memory: &GuestMemory<'_>,
1113 iovs: types::IovecArray,
1114) -> Result<GuestPtr<[u8]>> {
1115 for iov in iovs.iter() {
1116 let iov = memory.read(iov?)?;
1117 if iov.buf_len == 0 {
1118 continue;
1119 }
1120 return Ok(iov.buf.as_array(iov.buf_len));
1121 }
1122 Ok(GuestPtr::new((0, 0)))
1123}
1124
1125#[async_trait::async_trait]
1126impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx {
1130 #[instrument(skip(self, memory))]
1131 fn args_get(
1132 &mut self,
1133 memory: &mut GuestMemory<'_>,
1134 argv: GuestPtr<GuestPtr<u8>>,
1135 argv_buf: GuestPtr<u8>,
1136 ) -> Result<(), types::Error> {
1137 self.cli()
1138 .get_arguments()
1139 .context("failed to call `get-arguments`")
1140 .map_err(types::Error::trap)?
1141 .into_iter()
1142 .try_fold((argv, argv_buf), |(argv, argv_buf), arg| -> Result<_> {
1143 memory.write(argv, argv_buf)?;
1144 let argv = argv.add(1)?;
1145
1146 let argv_buf = write_bytes(memory, argv_buf, arg.as_bytes())?;
1147 let argv_buf = write_byte(memory, argv_buf, 0)?;
1148
1149 Ok((argv, argv_buf))
1150 })?;
1151 Ok(())
1152 }
1153
1154 #[instrument(skip(self, _memory))]
1155 fn args_sizes_get(
1156 &mut self,
1157 _memory: &mut GuestMemory<'_>,
1158 ) -> Result<(types::Size, types::Size), types::Error> {
1159 let args = self
1160 .cli()
1161 .get_arguments()
1162 .context("failed to call `get-arguments`")
1163 .map_err(types::Error::trap)?;
1164 let num = args.len().try_into().map_err(|_| types::Errno::Overflow)?;
1165 let len = args
1166 .iter()
1167 .map(|buf| buf.len() + 1) .sum::<usize>()
1169 .try_into()
1170 .map_err(|_| types::Errno::Overflow)?;
1171 Ok((num, len))
1172 }
1173
1174 #[instrument(skip(self, memory))]
1175 fn environ_get(
1176 &mut self,
1177 memory: &mut GuestMemory<'_>,
1178 environ: GuestPtr<GuestPtr<u8>>,
1179 environ_buf: GuestPtr<u8>,
1180 ) -> Result<(), types::Error> {
1181 self.cli()
1182 .get_environment()
1183 .context("failed to call `get-environment`")
1184 .map_err(types::Error::trap)?
1185 .into_iter()
1186 .try_fold(
1187 (environ, environ_buf),
1188 |(environ, environ_buf), (k, v)| -> Result<_, types::Error> {
1189 memory.write(environ, environ_buf)?;
1190 let environ = environ.add(1)?;
1191
1192 let environ_buf = write_bytes(memory, environ_buf, k.as_bytes())?;
1193 let environ_buf = write_byte(memory, environ_buf, b'=')?;
1194 let environ_buf = write_bytes(memory, environ_buf, v.as_bytes())?;
1195 let environ_buf = write_byte(memory, environ_buf, 0)?;
1196
1197 Ok((environ, environ_buf))
1198 },
1199 )?;
1200 Ok(())
1201 }
1202
1203 #[instrument(skip(self, _memory))]
1204 fn environ_sizes_get(
1205 &mut self,
1206 _memory: &mut GuestMemory<'_>,
1207 ) -> Result<(types::Size, types::Size), types::Error> {
1208 let environ = self
1209 .cli()
1210 .get_environment()
1211 .context("failed to call `get-environment`")
1212 .map_err(types::Error::trap)?;
1213 let num = environ.len().try_into()?;
1214 let len = environ
1215 .iter()
1216 .map(|(k, v)| k.len() + 1 + v.len() + 1) .sum::<usize>()
1218 .try_into()?;
1219 Ok((num, len))
1220 }
1221
1222 #[instrument(skip(self, _memory))]
1223 fn clock_res_get(
1224 &mut self,
1225 _memory: &mut GuestMemory<'_>,
1226 id: types::Clockid,
1227 ) -> Result<types::Timestamp, types::Error> {
1228 let res = match id {
1229 types::Clockid::Realtime => wall_clock::Host::resolution(&mut self.clocks())
1230 .context("failed to call `wall_clock::resolution`")
1231 .map_err(types::Error::trap)?
1232 .try_into()?,
1233 types::Clockid::Monotonic => monotonic_clock::Host::resolution(&mut self.clocks())
1234 .context("failed to call `monotonic_clock::resolution`")
1235 .map_err(types::Error::trap)?,
1236 types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
1237 return Err(types::Errno::Badf.into());
1238 }
1239 };
1240 Ok(res)
1241 }
1242
1243 #[instrument(skip(self, _memory))]
1244 fn clock_time_get(
1245 &mut self,
1246 _memory: &mut GuestMemory<'_>,
1247 id: types::Clockid,
1248 _precision: types::Timestamp,
1249 ) -> Result<types::Timestamp, types::Error> {
1250 let now = match id {
1251 types::Clockid::Realtime => wall_clock::Host::now(&mut self.clocks())
1252 .context("failed to call `wall_clock::now`")
1253 .map_err(types::Error::trap)?
1254 .try_into()?,
1255 types::Clockid::Monotonic => monotonic_clock::Host::now(&mut self.clocks())
1256 .context("failed to call `monotonic_clock::now`")
1257 .map_err(types::Error::trap)?,
1258 types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
1259 return Err(types::Errno::Badf.into());
1260 }
1261 };
1262 Ok(now)
1263 }
1264
1265 #[instrument(skip(self, _memory))]
1266 async fn fd_advise(
1267 &mut self,
1268 _memory: &mut GuestMemory<'_>,
1269 fd: types::Fd,
1270 offset: types::Filesize,
1271 len: types::Filesize,
1272 advice: types::Advice,
1273 ) -> Result<(), types::Error> {
1274 let fd = self.get_file_fd(fd)?;
1275 self.filesystem()
1276 .advise(fd, offset, len, advice.into())
1277 .await?;
1278 Ok(())
1279 }
1280
1281 #[instrument(skip(self, _memory))]
1284 fn fd_allocate(
1285 &mut self,
1286 _memory: &mut GuestMemory<'_>,
1287 fd: types::Fd,
1288 _offset: types::Filesize,
1289 _len: types::Filesize,
1290 ) -> Result<(), types::Error> {
1291 self.get_file_fd(fd)?;
1292 Err(types::Errno::Notsup.into())
1293 }
1294
1295 #[instrument(skip(self, _memory))]
1298 async fn fd_close(
1299 &mut self,
1300 _memory: &mut GuestMemory<'_>,
1301 fd: types::Fd,
1302 ) -> Result<(), types::Error> {
1303 let desc = {
1304 let fd = fd.into();
1305 let mut st = self.transact()?;
1306 let desc = st.descriptors.used.remove(&fd).ok_or(types::Errno::Badf)?;
1307 st.descriptors.free.insert(fd);
1308 desc
1309 };
1310 match desc {
1311 Descriptor::Stdin { stream, .. } => {
1312 streams::HostInputStream::drop(&mut self.table, stream)
1313 .await
1314 .context("failed to call `drop` on `input-stream`")
1315 }
1316 Descriptor::Stdout { stream, .. } | Descriptor::Stderr { stream, .. } => {
1317 streams::HostOutputStream::drop(&mut self.table, stream)
1318 .await
1319 .context("failed to call `drop` on `output-stream`")
1320 }
1321 Descriptor::File(File { fd, .. }) | Descriptor::Directory { fd, .. } => {
1322 filesystem::HostDescriptor::drop(&mut self.filesystem(), fd)
1323 .context("failed to call `drop`")
1324 }
1325 }
1326 .map_err(types::Error::trap)
1327 }
1328
1329 #[instrument(skip(self, _memory))]
1332 async fn fd_datasync(
1333 &mut self,
1334 _memory: &mut GuestMemory<'_>,
1335 fd: types::Fd,
1336 ) -> Result<(), types::Error> {
1337 let fd = self.get_file_fd(fd)?;
1338 self.filesystem().sync_data(fd).await?;
1339 Ok(())
1340 }
1341
1342 #[instrument(skip(self, _memory))]
1345 async fn fd_fdstat_get(
1346 &mut self,
1347 _memory: &mut GuestMemory<'_>,
1348 fd: types::Fd,
1349 ) -> Result<types::Fdstat, types::Error> {
1350 let (fd, blocking, append) = match self.transact()?.get_descriptor(fd)? {
1351 Descriptor::Stdin { isatty, .. } => {
1352 let fs_rights_base = types::Rights::FD_READ;
1353 return Ok(types::Fdstat {
1354 fs_filetype: (*isatty).into(),
1355 fs_flags: types::Fdflags::empty(),
1356 fs_rights_base,
1357 fs_rights_inheriting: fs_rights_base,
1358 });
1359 }
1360 Descriptor::Stdout { isatty, .. } | Descriptor::Stderr { isatty, .. } => {
1361 let fs_rights_base = types::Rights::FD_WRITE;
1362 return Ok(types::Fdstat {
1363 fs_filetype: (*isatty).into(),
1364 fs_flags: types::Fdflags::empty(),
1365 fs_rights_base,
1366 fs_rights_inheriting: fs_rights_base,
1367 });
1368 }
1369 Descriptor::Directory {
1370 preopen_path: Some(_),
1371 ..
1372 } => {
1373 let fs_rights_base = types::Rights::PATH_CREATE_DIRECTORY
1375 | types::Rights::PATH_CREATE_FILE
1376 | types::Rights::PATH_LINK_SOURCE
1377 | types::Rights::PATH_LINK_TARGET
1378 | types::Rights::PATH_OPEN
1379 | types::Rights::FD_READDIR
1380 | types::Rights::PATH_READLINK
1381 | types::Rights::PATH_RENAME_SOURCE
1382 | types::Rights::PATH_RENAME_TARGET
1383 | types::Rights::PATH_SYMLINK
1384 | types::Rights::PATH_REMOVE_DIRECTORY
1385 | types::Rights::PATH_UNLINK_FILE
1386 | types::Rights::PATH_FILESTAT_GET
1387 | types::Rights::PATH_FILESTAT_SET_TIMES
1388 | types::Rights::FD_FILESTAT_GET
1389 | types::Rights::FD_FILESTAT_SET_TIMES;
1390
1391 let fs_rights_inheriting = fs_rights_base
1392 | types::Rights::FD_DATASYNC
1393 | types::Rights::FD_READ
1394 | types::Rights::FD_SEEK
1395 | types::Rights::FD_FDSTAT_SET_FLAGS
1396 | types::Rights::FD_SYNC
1397 | types::Rights::FD_TELL
1398 | types::Rights::FD_WRITE
1399 | types::Rights::FD_ADVISE
1400 | types::Rights::FD_ALLOCATE
1401 | types::Rights::FD_FILESTAT_GET
1402 | types::Rights::FD_FILESTAT_SET_SIZE
1403 | types::Rights::FD_FILESTAT_SET_TIMES
1404 | types::Rights::POLL_FD_READWRITE;
1405
1406 return Ok(types::Fdstat {
1407 fs_filetype: types::Filetype::Directory,
1408 fs_flags: types::Fdflags::empty(),
1409 fs_rights_base,
1410 fs_rights_inheriting,
1411 });
1412 }
1413 Descriptor::Directory { fd, .. } => (fd.borrowed(), BlockingMode::Blocking, false),
1414 Descriptor::File(File {
1415 fd,
1416 blocking_mode,
1417 append,
1418 ..
1419 }) => (fd.borrowed(), *blocking_mode, *append),
1420 };
1421 let flags = self.filesystem().get_flags(fd.borrowed()).await?;
1422 let fs_filetype = self
1423 .filesystem()
1424 .get_type(fd.borrowed())
1425 .await?
1426 .try_into()
1427 .map_err(types::Error::trap)?;
1428 let mut fs_flags = types::Fdflags::empty();
1429 let mut fs_rights_base = types::Rights::all();
1430 if let types::Filetype::Directory = fs_filetype {
1431 fs_rights_base &= !types::Rights::FD_SEEK;
1432 fs_rights_base &= !types::Rights::FD_FILESTAT_SET_SIZE;
1433 }
1434 if !flags.contains(filesystem::DescriptorFlags::READ) {
1435 fs_rights_base &= !types::Rights::FD_READ;
1436 fs_rights_base &= !types::Rights::FD_READDIR;
1437 }
1438 if !flags.contains(filesystem::DescriptorFlags::WRITE) {
1439 fs_rights_base &= !types::Rights::FD_WRITE;
1440 }
1441 if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
1442 fs_flags |= types::Fdflags::DSYNC;
1443 }
1444 if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
1445 fs_flags |= types::Fdflags::RSYNC;
1446 }
1447 if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
1448 fs_flags |= types::Fdflags::SYNC;
1449 }
1450 if append {
1451 fs_flags |= types::Fdflags::APPEND;
1452 }
1453 if matches!(blocking, BlockingMode::NonBlocking) {
1454 fs_flags |= types::Fdflags::NONBLOCK;
1455 }
1456 Ok(types::Fdstat {
1457 fs_filetype,
1458 fs_flags,
1459 fs_rights_base,
1460 fs_rights_inheriting: fs_rights_base,
1461 })
1462 }
1463
1464 #[instrument(skip(self, _memory))]
1467 fn fd_fdstat_set_flags(
1468 &mut self,
1469 _memory: &mut GuestMemory<'_>,
1470 fd: types::Fd,
1471 flags: types::Fdflags,
1472 ) -> Result<(), types::Error> {
1473 let mut st = self.transact()?;
1474 let File {
1475 append,
1476 blocking_mode,
1477 ..
1478 } = st.get_file_mut(fd)?;
1479
1480 if flags.contains(types::Fdflags::DSYNC)
1482 || flags.contains(types::Fdflags::SYNC)
1483 || flags.contains(types::Fdflags::RSYNC)
1484 {
1485 return Err(types::Errno::Inval.into());
1486 }
1487 *append = flags.contains(types::Fdflags::APPEND);
1488 *blocking_mode = BlockingMode::from_fdflags(&flags);
1489 Ok(())
1490 }
1491
1492 #[instrument(skip(self, _memory))]
1494 fn fd_fdstat_set_rights(
1495 &mut self,
1496 _memory: &mut GuestMemory<'_>,
1497 fd: types::Fd,
1498 _fs_rights_base: types::Rights,
1499 _fs_rights_inheriting: types::Rights,
1500 ) -> Result<(), types::Error> {
1501 self.get_fd(fd)?;
1502 Ok(())
1503 }
1504
1505 #[instrument(skip(self, _memory))]
1507 async fn fd_filestat_get(
1508 &mut self,
1509 _memory: &mut GuestMemory<'_>,
1510 fd: types::Fd,
1511 ) -> Result<types::Filestat, types::Error> {
1512 let t = self.transact()?;
1513 let desc = t.get_descriptor(fd)?;
1514 match desc {
1515 Descriptor::Stdin { isatty, .. }
1516 | Descriptor::Stdout { isatty, .. }
1517 | Descriptor::Stderr { isatty, .. } => Ok(types::Filestat {
1518 dev: 0,
1519 ino: 0,
1520 filetype: (*isatty).into(),
1521 nlink: 0,
1522 size: 0,
1523 atim: 0,
1524 mtim: 0,
1525 ctim: 0,
1526 }),
1527 Descriptor::Directory { fd, .. } | Descriptor::File(File { fd, .. }) => {
1528 let fd = fd.borrowed();
1529 drop(t);
1530 let filesystem::DescriptorStat {
1531 type_,
1532 link_count: nlink,
1533 size,
1534 data_access_timestamp,
1535 data_modification_timestamp,
1536 status_change_timestamp,
1537 } = self.filesystem().stat(fd.borrowed()).await?;
1538 let metadata_hash = self.filesystem().metadata_hash(fd).await?;
1539 let filetype = type_.try_into().map_err(types::Error::trap)?;
1540 let zero = wall_clock::Datetime {
1541 seconds: 0,
1542 nanoseconds: 0,
1543 };
1544 let atim = data_access_timestamp.unwrap_or(zero).try_into()?;
1545 let mtim = data_modification_timestamp.unwrap_or(zero).try_into()?;
1546 let ctim = status_change_timestamp.unwrap_or(zero).try_into()?;
1547 Ok(types::Filestat {
1548 dev: 1,
1549 ino: metadata_hash.lower,
1550 filetype,
1551 nlink,
1552 size,
1553 atim,
1554 mtim,
1555 ctim,
1556 })
1557 }
1558 }
1559 }
1560
1561 #[instrument(skip(self, _memory))]
1564 async fn fd_filestat_set_size(
1565 &mut self,
1566 _memory: &mut GuestMemory<'_>,
1567 fd: types::Fd,
1568 size: types::Filesize,
1569 ) -> Result<(), types::Error> {
1570 let fd = self.get_file_fd(fd)?;
1571 self.filesystem().set_size(fd, size).await?;
1572 Ok(())
1573 }
1574
1575 #[instrument(skip(self, _memory))]
1578 async fn fd_filestat_set_times(
1579 &mut self,
1580 _memory: &mut GuestMemory<'_>,
1581 fd: types::Fd,
1582 atim: types::Timestamp,
1583 mtim: types::Timestamp,
1584 fst_flags: types::Fstflags,
1585 ) -> Result<(), types::Error> {
1586 let atim = systimespec(
1587 fst_flags.contains(types::Fstflags::ATIM),
1588 atim,
1589 fst_flags.contains(types::Fstflags::ATIM_NOW),
1590 )?;
1591 let mtim = systimespec(
1592 fst_flags.contains(types::Fstflags::MTIM),
1593 mtim,
1594 fst_flags.contains(types::Fstflags::MTIM_NOW),
1595 )?;
1596
1597 let fd = self.get_fd(fd)?;
1598 self.filesystem().set_times(fd, atim, mtim).await?;
1599 Ok(())
1600 }
1601
1602 #[instrument(skip(self, memory))]
1605 async fn fd_read(
1606 &mut self,
1607 memory: &mut GuestMemory<'_>,
1608 fd: types::Fd,
1609 iovs: types::IovecArray,
1610 ) -> Result<types::Size, types::Error> {
1611 let t = self.transact()?;
1612 let desc = t.get_descriptor(fd)?;
1613 match desc {
1614 Descriptor::File(File {
1615 fd,
1616 position,
1617 blocking_mode: _,
1620 ..
1621 }) => {
1622 let fd = fd.borrowed();
1623 let position = position.clone();
1624 drop(t);
1625 let pos = position.load(Ordering::Relaxed);
1626 let file = self.table.get(&fd)?.file()?;
1627 let iov = first_non_empty_iovec(memory, iovs)?;
1628 let bytes_read = match (file.as_blocking_file(), memory.as_slice_mut(iov)?) {
1629 (Some(file), Some(mut buf)) => file
1633 .read_at(&mut buf, pos)
1634 .map_err(|e| StreamError::LastOperationFailed(e.into()))?,
1635 (_, buf) => {
1639 drop(buf);
1640 let mut buf = vec![0; iov.len() as usize];
1641 let buf = file
1642 .run_blocking(move |file| -> Result<_, types::Error> {
1643 let bytes_read = file
1644 .read_at(&mut buf, pos)
1645 .map_err(|e| StreamError::LastOperationFailed(e.into()))?;
1646 buf.truncate(bytes_read);
1647 Ok(buf)
1648 })
1649 .await?;
1650 let iov = iov.get_range(0..u32::try_from(buf.len())?).unwrap();
1651 memory.copy_from_slice(&buf, iov)?;
1652 buf.len()
1653 }
1654 };
1655
1656 let pos = pos
1657 .checked_add(bytes_read.try_into()?)
1658 .ok_or(types::Errno::Overflow)?;
1659 position.store(pos, Ordering::Relaxed);
1660
1661 Ok(bytes_read.try_into()?)
1662 }
1663 Descriptor::Stdin { stream, .. } => {
1664 let stream = stream.borrowed();
1665 drop(t);
1666 let buf = first_non_empty_iovec(memory, iovs)?;
1667 let read = BlockingMode::Blocking
1668 .read(&mut self.table, stream, buf.len().try_into()?)
1669 .await?;
1670 if read.len() > buf.len().try_into()? {
1671 return Err(types::Errno::Range.into());
1672 }
1673 let buf = buf.get_range(0..u32::try_from(read.len())?).unwrap();
1674 memory.copy_from_slice(&read, buf)?;
1675 let n = read.len().try_into()?;
1676 Ok(n)
1677 }
1678 _ => return Err(types::Errno::Badf.into()),
1679 }
1680 }
1681
1682 #[instrument(skip(self, memory))]
1685 async fn fd_pread(
1686 &mut self,
1687 memory: &mut GuestMemory<'_>,
1688 fd: types::Fd,
1689 iovs: types::IovecArray,
1690 offset: types::Filesize,
1691 ) -> Result<types::Size, types::Error> {
1692 let t = self.transact()?;
1693 let desc = t.get_descriptor(fd)?;
1694 let (buf, read) = match desc {
1695 Descriptor::File(File {
1696 fd, blocking_mode, ..
1697 }) => {
1698 let fd = fd.borrowed();
1699 let blocking_mode = *blocking_mode;
1700 drop(t);
1701 let buf = first_non_empty_iovec(memory, iovs)?;
1702
1703 let stream = self.filesystem().read_via_stream(fd, offset)?;
1704 let read = blocking_mode
1705 .read(&mut self.table, stream.borrowed(), buf.len().try_into()?)
1706 .await;
1707 streams::HostInputStream::drop(&mut self.table, stream)
1708 .await
1709 .map_err(|e| types::Error::trap(e))?;
1710 (buf, read?)
1711 }
1712 Descriptor::Stdin { .. } => {
1713 return Err(types::Errno::Spipe.into());
1715 }
1716 _ => return Err(types::Errno::Badf.into()),
1717 };
1718 if read.len() > buf.len().try_into()? {
1719 return Err(types::Errno::Range.into());
1720 }
1721 let buf = buf.get_range(0..u32::try_from(read.len())?).unwrap();
1722 memory.copy_from_slice(&read, buf)?;
1723 let n = read.len().try_into()?;
1724 Ok(n)
1725 }
1726
1727 #[instrument(skip(self, memory))]
1730 async fn fd_write(
1731 &mut self,
1732 memory: &mut GuestMemory<'_>,
1733 fd: types::Fd,
1734 ciovs: types::CiovecArray,
1735 ) -> Result<types::Size, types::Error> {
1736 self.fd_write_impl(memory, fd, ciovs, FdWrite::AtCur).await
1737 }
1738
1739 #[instrument(skip(self, memory))]
1742 async fn fd_pwrite(
1743 &mut self,
1744 memory: &mut GuestMemory<'_>,
1745 fd: types::Fd,
1746 ciovs: types::CiovecArray,
1747 offset: types::Filesize,
1748 ) -> Result<types::Size, types::Error> {
1749 self.fd_write_impl(memory, fd, ciovs, FdWrite::At(offset))
1750 .await
1751 }
1752
1753 #[instrument(skip(self, _memory))]
1755 fn fd_prestat_get(
1756 &mut self,
1757 _memory: &mut GuestMemory<'_>,
1758 fd: types::Fd,
1759 ) -> Result<types::Prestat, types::Error> {
1760 if let Descriptor::Directory {
1761 preopen_path: Some(p),
1762 ..
1763 } = self.transact()?.get_descriptor(fd)?
1764 {
1765 let pr_name_len = p.len().try_into()?;
1766 return Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len }));
1767 }
1768 Err(types::Errno::Badf.into()) }
1770
1771 #[instrument(skip(self, memory))]
1773 fn fd_prestat_dir_name(
1774 &mut self,
1775 memory: &mut GuestMemory<'_>,
1776 fd: types::Fd,
1777 path: GuestPtr<u8>,
1778 path_max_len: types::Size,
1779 ) -> Result<(), types::Error> {
1780 let path_max_len = path_max_len.try_into()?;
1781 if let Descriptor::Directory {
1782 preopen_path: Some(p),
1783 ..
1784 } = self.transact()?.get_descriptor(fd)?
1785 {
1786 if p.len() > path_max_len {
1787 return Err(types::Errno::Nametoolong.into());
1788 }
1789 write_bytes(memory, path, p.as_bytes())?;
1790 return Ok(());
1791 }
1792 Err(types::Errno::Notdir.into()) }
1794
1795 #[instrument(skip(self, _memory))]
1797 fn fd_renumber(
1798 &mut self,
1799 _memory: &mut GuestMemory<'_>,
1800 from: types::Fd,
1801 to: types::Fd,
1802 ) -> Result<(), types::Error> {
1803 let mut st = self.transact()?;
1804 let from = from.into();
1805 let btree_map::Entry::Occupied(desc) = st.descriptors.used.entry(from) else {
1806 return Err(types::Errno::Badf.into());
1807 };
1808 let to = to.into();
1809 if from != to {
1810 let desc = desc.remove();
1811 st.descriptors.free.insert(from);
1812 st.descriptors.free.remove(&to);
1813 st.descriptors.used.insert(to, desc);
1814 }
1815 Ok(())
1816 }
1817
1818 #[instrument(skip(self, _memory))]
1821 async fn fd_seek(
1822 &mut self,
1823 _memory: &mut GuestMemory<'_>,
1824 fd: types::Fd,
1825 offset: types::Filedelta,
1826 whence: types::Whence,
1827 ) -> Result<types::Filesize, types::Error> {
1828 let t = self.transact()?;
1829 let File { fd, position, .. } = t.get_seekable(fd)?;
1830 let fd = fd.borrowed();
1831 let position = position.clone();
1832 drop(t);
1833 let pos = match whence {
1834 types::Whence::Set if offset >= 0 => {
1835 offset.try_into().map_err(|_| types::Errno::Inval)?
1836 }
1837 types::Whence::Cur => position
1838 .load(Ordering::Relaxed)
1839 .checked_add_signed(offset)
1840 .ok_or(types::Errno::Inval)?,
1841 types::Whence::End => {
1842 let filesystem::DescriptorStat { size, .. } = self.filesystem().stat(fd).await?;
1843 size.checked_add_signed(offset).ok_or(types::Errno::Inval)?
1844 }
1845 _ => return Err(types::Errno::Inval.into()),
1846 };
1847 position.store(pos, Ordering::Relaxed);
1848 Ok(pos)
1849 }
1850
1851 #[instrument(skip(self, _memory))]
1854 async fn fd_sync(
1855 &mut self,
1856 _memory: &mut GuestMemory<'_>,
1857 fd: types::Fd,
1858 ) -> Result<(), types::Error> {
1859 let fd = self.get_file_fd(fd)?;
1860 self.filesystem().sync(fd).await?;
1861 Ok(())
1862 }
1863
1864 #[instrument(skip(self, _memory))]
1867 fn fd_tell(
1868 &mut self,
1869 _memory: &mut GuestMemory<'_>,
1870 fd: types::Fd,
1871 ) -> Result<types::Filesize, types::Error> {
1872 let pos = self
1873 .transact()?
1874 .get_seekable(fd)
1875 .map(|File { position, .. }| position.load(Ordering::Relaxed))?;
1876 Ok(pos)
1877 }
1878
1879 #[instrument(skip(self, memory))]
1880 async fn fd_readdir(
1881 &mut self,
1882 memory: &mut GuestMemory<'_>,
1883 fd: types::Fd,
1884 buf: GuestPtr<u8>,
1885 buf_len: types::Size,
1886 cookie: types::Dircookie,
1887 ) -> Result<types::Size, types::Error> {
1888 let fd = self.get_dir_fd(fd)?;
1889 let stream = self.filesystem().read_directory(fd.borrowed()).await?;
1890 let dir_metadata_hash = self.filesystem().metadata_hash(fd.borrowed()).await?;
1891 let cookie = cookie.try_into().map_err(|_| types::Errno::Overflow)?;
1892
1893 let head = [
1894 (
1895 types::Dirent {
1896 d_next: 1u64.to_le(),
1897 d_ino: dir_metadata_hash.lower.to_le(),
1898 d_type: types::Filetype::Directory,
1899 d_namlen: 1u32.to_le(),
1900 },
1901 ".".into(),
1902 ),
1903 (
1904 types::Dirent {
1905 d_next: 2u64.to_le(),
1906 d_ino: dir_metadata_hash.lower.to_le(), d_type: types::Filetype::Directory,
1908 d_namlen: 2u32.to_le(),
1909 },
1910 "..".into(),
1911 ),
1912 ];
1913
1914 let mut dir = Vec::new();
1915 for (entry, d_next) in self
1916 .table
1917 .delete(stream)?
1919 .into_iter()
1920 .zip(3u64..)
1921 {
1922 let filesystem::DirectoryEntry { type_, name } = entry?;
1923 let metadata_hash = self
1924 .filesystem()
1925 .metadata_hash_at(fd.borrowed(), filesystem::PathFlags::empty(), name.clone())
1926 .await?;
1927 let d_type = type_.try_into().map_err(types::Error::trap)?;
1928 let d_namlen: u32 = name.len().try_into().map_err(|_| types::Errno::Overflow)?;
1929 dir.push((
1930 types::Dirent {
1931 d_next: d_next.to_le(),
1932 d_ino: metadata_hash.lower.to_le(),
1933 d_type, d_namlen: d_namlen.to_le(),
1935 },
1936 name,
1937 ))
1938 }
1939
1940 const DIRENT_SIZE: u32 = size_of::<types::Dirent>() as _;
1942 assert_eq!(
1943 types::Dirent::guest_size(),
1944 DIRENT_SIZE,
1945 "Dirent guest repr and host repr should match"
1946 );
1947 let mut buf = buf;
1948 let mut cap = buf_len;
1949 for (ref entry, path) in head.into_iter().chain(dir.into_iter()).skip(cookie) {
1950 let mut path = path.into_bytes();
1951 assert_eq!(
1952 1,
1953 size_of_val(&entry.d_type),
1954 "Dirent member d_type should be endian-invariant"
1955 );
1956 let entry_len = cap.min(DIRENT_SIZE);
1957 let entry = entry as *const _ as _;
1958 let entry = unsafe { slice::from_raw_parts(entry, entry_len as _) };
1959 cap = cap.checked_sub(entry_len).unwrap();
1960 buf = write_bytes(memory, buf, entry)?;
1961 if cap == 0 {
1962 return Ok(buf_len);
1963 }
1964
1965 if let Ok(cap) = cap.try_into() {
1966 path.truncate(cap);
1968 }
1969 cap = cap.checked_sub(path.len() as _).unwrap();
1970 buf = write_bytes(memory, buf, &path)?;
1971 if cap == 0 {
1972 return Ok(buf_len);
1973 }
1974 }
1975 Ok(buf_len.checked_sub(cap).unwrap())
1976 }
1977
1978 #[instrument(skip(self, memory))]
1979 async fn path_create_directory(
1980 &mut self,
1981 memory: &mut GuestMemory<'_>,
1982 dirfd: types::Fd,
1983 path: GuestPtr<str>,
1984 ) -> Result<(), types::Error> {
1985 let dirfd = self.get_dir_fd(dirfd)?;
1986 let path = read_string(memory, path)?;
1987 self.filesystem()
1988 .create_directory_at(dirfd.borrowed(), path)
1989 .await?;
1990 Ok(())
1991 }
1992
1993 #[instrument(skip(self, memory))]
1996 async fn path_filestat_get(
1997 &mut self,
1998 memory: &mut GuestMemory<'_>,
1999 dirfd: types::Fd,
2000 flags: types::Lookupflags,
2001 path: GuestPtr<str>,
2002 ) -> Result<types::Filestat, types::Error> {
2003 let dirfd = self.get_dir_fd(dirfd)?;
2004 let path = read_string(memory, path)?;
2005 let filesystem::DescriptorStat {
2006 type_,
2007 link_count: nlink,
2008 size,
2009 data_access_timestamp,
2010 data_modification_timestamp,
2011 status_change_timestamp,
2012 } = self
2013 .filesystem()
2014 .stat_at(dirfd.borrowed(), flags.into(), path.clone())
2015 .await?;
2016 let metadata_hash = self
2017 .filesystem()
2018 .metadata_hash_at(dirfd, flags.into(), path)
2019 .await?;
2020 let filetype = type_.try_into().map_err(types::Error::trap)?;
2021 let zero = wall_clock::Datetime {
2022 seconds: 0,
2023 nanoseconds: 0,
2024 };
2025 let atim = data_access_timestamp.unwrap_or(zero).try_into()?;
2026 let mtim = data_modification_timestamp.unwrap_or(zero).try_into()?;
2027 let ctim = status_change_timestamp.unwrap_or(zero).try_into()?;
2028 Ok(types::Filestat {
2029 dev: 1,
2030 ino: metadata_hash.lower,
2031 filetype,
2032 nlink,
2033 size,
2034 atim,
2035 mtim,
2036 ctim,
2037 })
2038 }
2039
2040 #[instrument(skip(self, memory))]
2043 async fn path_filestat_set_times(
2044 &mut self,
2045 memory: &mut GuestMemory<'_>,
2046 dirfd: types::Fd,
2047 flags: types::Lookupflags,
2048 path: GuestPtr<str>,
2049 atim: types::Timestamp,
2050 mtim: types::Timestamp,
2051 fst_flags: types::Fstflags,
2052 ) -> Result<(), types::Error> {
2053 let atim = systimespec(
2054 fst_flags.contains(types::Fstflags::ATIM),
2055 atim,
2056 fst_flags.contains(types::Fstflags::ATIM_NOW),
2057 )?;
2058 let mtim = systimespec(
2059 fst_flags.contains(types::Fstflags::MTIM),
2060 mtim,
2061 fst_flags.contains(types::Fstflags::MTIM_NOW),
2062 )?;
2063
2064 let dirfd = self.get_dir_fd(dirfd)?;
2065 let path = read_string(memory, path)?;
2066 self.filesystem()
2067 .set_times_at(dirfd, flags.into(), path, atim, mtim)
2068 .await?;
2069 Ok(())
2070 }
2071
2072 #[instrument(skip(self, memory))]
2075 async fn path_link(
2076 &mut self,
2077 memory: &mut GuestMemory<'_>,
2078 src_fd: types::Fd,
2079 src_flags: types::Lookupflags,
2080 src_path: GuestPtr<str>,
2081 target_fd: types::Fd,
2082 target_path: GuestPtr<str>,
2083 ) -> Result<(), types::Error> {
2084 let src_fd = self.get_dir_fd(src_fd)?;
2085 let target_fd = self.get_dir_fd(target_fd)?;
2086 let src_path = read_string(memory, src_path)?;
2087 let target_path = read_string(memory, target_path)?;
2088 self.filesystem()
2089 .link_at(src_fd, src_flags.into(), src_path, target_fd, target_path)
2090 .await?;
2091 Ok(())
2092 }
2093
2094 #[instrument(skip(self, memory))]
2097 async fn path_open(
2098 &mut self,
2099 memory: &mut GuestMemory<'_>,
2100 dirfd: types::Fd,
2101 dirflags: types::Lookupflags,
2102 path: GuestPtr<str>,
2103 oflags: types::Oflags,
2104 fs_rights_base: types::Rights,
2105 _fs_rights_inheriting: types::Rights,
2106 fdflags: types::Fdflags,
2107 ) -> Result<types::Fd, types::Error> {
2108 let path = read_string(memory, path)?;
2109
2110 let mut flags = filesystem::DescriptorFlags::empty();
2111 if fs_rights_base.contains(types::Rights::FD_READ) {
2112 flags |= filesystem::DescriptorFlags::READ;
2113 }
2114 if fs_rights_base.contains(types::Rights::FD_WRITE) {
2115 flags |= filesystem::DescriptorFlags::WRITE;
2116 }
2117 if fdflags.contains(types::Fdflags::SYNC) {
2118 flags |= filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC;
2119 }
2120 if fdflags.contains(types::Fdflags::DSYNC) {
2121 flags |= filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC;
2122 }
2123 if fdflags.contains(types::Fdflags::RSYNC) {
2124 flags |= filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC;
2125 }
2126
2127 let t = self.transact()?;
2128 let dirfd = match t.get_descriptor(dirfd)? {
2129 Descriptor::Directory { fd, .. } => fd.borrowed(),
2130 Descriptor::File(_) => return Err(types::Errno::Notdir.into()),
2131 _ => return Err(types::Errno::Badf.into()),
2132 };
2133 drop(t);
2134 let fd = self
2135 .filesystem()
2136 .open_at(dirfd, dirflags.into(), path, oflags.into(), flags)
2137 .await?;
2138 let mut t = self.transact()?;
2139 let desc = match t.view.table.get(&fd)? {
2140 crate::p2::filesystem::Descriptor::Dir(_) => Descriptor::Directory {
2141 fd,
2142 preopen_path: None,
2143 },
2144 crate::p2::filesystem::Descriptor::File(_) => Descriptor::File(File {
2145 fd,
2146 position: Default::default(),
2147 append: fdflags.contains(types::Fdflags::APPEND),
2148 blocking_mode: BlockingMode::from_fdflags(&fdflags),
2149 }),
2150 };
2151 let fd = t.descriptors.push(desc)?;
2152 Ok(fd.into())
2153 }
2154
2155 #[instrument(skip(self, memory))]
2158 async fn path_readlink(
2159 &mut self,
2160 memory: &mut GuestMemory<'_>,
2161 dirfd: types::Fd,
2162 path: GuestPtr<str>,
2163 buf: GuestPtr<u8>,
2164 buf_len: types::Size,
2165 ) -> Result<types::Size, types::Error> {
2166 let dirfd = self.get_dir_fd(dirfd)?;
2167 let path = read_string(memory, path)?;
2168 let mut path = self
2169 .filesystem()
2170 .readlink_at(dirfd, path)
2171 .await?
2172 .into_bytes();
2173 if let Ok(buf_len) = buf_len.try_into() {
2174 path.truncate(buf_len);
2176 }
2177 let n = path.len().try_into().map_err(|_| types::Errno::Overflow)?;
2178 write_bytes(memory, buf, &path)?;
2179 Ok(n)
2180 }
2181
2182 #[instrument(skip(self, memory))]
2183 async fn path_remove_directory(
2184 &mut self,
2185 memory: &mut GuestMemory<'_>,
2186 dirfd: types::Fd,
2187 path: GuestPtr<str>,
2188 ) -> Result<(), types::Error> {
2189 let dirfd = self.get_dir_fd(dirfd)?;
2190 let path = read_string(memory, path)?;
2191 self.filesystem().remove_directory_at(dirfd, path).await?;
2192 Ok(())
2193 }
2194
2195 #[instrument(skip(self, memory))]
2198 async fn path_rename(
2199 &mut self,
2200 memory: &mut GuestMemory<'_>,
2201 src_fd: types::Fd,
2202 src_path: GuestPtr<str>,
2203 dest_fd: types::Fd,
2204 dest_path: GuestPtr<str>,
2205 ) -> Result<(), types::Error> {
2206 let src_fd = self.get_dir_fd(src_fd)?;
2207 let dest_fd = self.get_dir_fd(dest_fd)?;
2208 let src_path = read_string(memory, src_path)?;
2209 let dest_path = read_string(memory, dest_path)?;
2210 self.filesystem()
2211 .rename_at(src_fd, src_path, dest_fd, dest_path)
2212 .await?;
2213 Ok(())
2214 }
2215
2216 #[instrument(skip(self, memory))]
2217 async fn path_symlink(
2218 &mut self,
2219 memory: &mut GuestMemory<'_>,
2220 src_path: GuestPtr<str>,
2221 dirfd: types::Fd,
2222 dest_path: GuestPtr<str>,
2223 ) -> Result<(), types::Error> {
2224 let dirfd = self.get_dir_fd(dirfd)?;
2225 let src_path = read_string(memory, src_path)?;
2226 let dest_path = read_string(memory, dest_path)?;
2227 self.filesystem()
2228 .symlink_at(dirfd.borrowed(), src_path, dest_path)
2229 .await?;
2230 Ok(())
2231 }
2232
2233 #[instrument(skip(self, memory))]
2234 async fn path_unlink_file(
2235 &mut self,
2236 memory: &mut GuestMemory<'_>,
2237 dirfd: types::Fd,
2238 path: GuestPtr<str>,
2239 ) -> Result<(), types::Error> {
2240 let dirfd = self.get_dir_fd(dirfd)?;
2241 let path = memory.as_cow_str(path)?.into_owned();
2242 self.filesystem()
2243 .unlink_file_at(dirfd.borrowed(), path)
2244 .await?;
2245 Ok(())
2246 }
2247
2248 #[instrument(skip(self, memory))]
2249 async fn poll_oneoff(
2250 &mut self,
2251 memory: &mut GuestMemory<'_>,
2252 subs: GuestPtr<types::Subscription>,
2253 events: GuestPtr<types::Event>,
2254 nsubscriptions: types::Size,
2255 ) -> Result<types::Size, types::Error> {
2256 if nsubscriptions == 0 {
2257 return Err(types::Errno::Inval.into());
2259 }
2260
2261 if nsubscriptions == 1 {
2267 let sub = memory.read(subs)?;
2268 if let types::SubscriptionU::Clock(clocksub) = sub.u {
2269 if !clocksub
2270 .flags
2271 .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
2272 && self.wasi.filesystem.allow_blocking_current_thread
2273 {
2274 std::thread::sleep(std::time::Duration::from_nanos(clocksub.timeout));
2275 memory.write(
2276 events,
2277 types::Event {
2278 userdata: sub.userdata,
2279 error: types::Errno::Success,
2280 type_: types::Eventtype::Clock,
2281 fd_readwrite: types::EventFdReadwrite {
2282 flags: types::Eventrwflags::empty(),
2283 nbytes: 1,
2284 },
2285 },
2286 )?;
2287 return Ok(1);
2288 }
2289 }
2290 }
2291
2292 let subs = subs.as_array(nsubscriptions);
2293 let events = events.as_array(nsubscriptions);
2294
2295 let n = usize::try_from(nsubscriptions).unwrap_or(usize::MAX);
2296 let mut pollables = Vec::with_capacity(n);
2297 for sub in subs.iter() {
2298 let sub = memory.read(sub?)?;
2299 let p = match sub.u {
2300 types::SubscriptionU::Clock(types::SubscriptionClock {
2301 id,
2302 timeout,
2303 flags,
2304 ..
2305 }) => {
2306 let absolute = flags.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME);
2307 let (timeout, absolute) = match id {
2308 types::Clockid::Monotonic => (timeout, absolute),
2309 types::Clockid::Realtime if !absolute => (timeout, false),
2310 types::Clockid::Realtime => {
2311 let now = wall_clock::Host::now(&mut self.clocks())
2312 .context("failed to call `wall_clock::now`")
2313 .map_err(types::Error::trap)?;
2314
2315 let seconds = timeout / 1_000_000_000;
2317 let nanoseconds = timeout % 1_000_000_000;
2318
2319 let timeout = if now.seconds < seconds
2320 || now.seconds == seconds
2321 && u64::from(now.nanoseconds) < nanoseconds
2322 {
2323 now.seconds * 1_000_000_000 + u64::from(now.nanoseconds) - timeout
2326 } else {
2327 0
2328 };
2329 (timeout, false)
2330 }
2331 _ => return Err(types::Errno::Inval.into()),
2332 };
2333 if absolute {
2334 monotonic_clock::Host::subscribe_instant(&mut self.clocks(), timeout)
2335 .context("failed to call `monotonic_clock::subscribe_instant`")
2336 .map_err(types::Error::trap)?
2337 } else {
2338 monotonic_clock::Host::subscribe_duration(&mut self.clocks(), timeout)
2339 .context("failed to call `monotonic_clock::subscribe_duration`")
2340 .map_err(types::Error::trap)?
2341 }
2342 }
2343 types::SubscriptionU::FdRead(types::SubscriptionFdReadwrite {
2344 file_descriptor,
2345 }) => {
2346 let stream = {
2347 let t = self.transact()?;
2348 let desc = t.get_descriptor(file_descriptor)?;
2349 match desc {
2350 Descriptor::Stdin { stream, .. } => stream.borrowed(),
2351 Descriptor::File(File { fd, position, .. }) => {
2352 let pos = position.load(Ordering::Relaxed);
2353 let fd = fd.borrowed();
2354 drop(t);
2355 self.filesystem().read_via_stream(fd, pos)?
2356 }
2357 _ => return Err(types::Errno::Badf.into()),
2359 }
2360 };
2361 streams::HostInputStream::subscribe(&mut self.table, stream)
2362 .context("failed to call `subscribe` on `input-stream`")
2363 .map_err(types::Error::trap)?
2364 }
2365 types::SubscriptionU::FdWrite(types::SubscriptionFdReadwrite {
2366 file_descriptor,
2367 }) => {
2368 let stream = {
2369 let t = self.transact()?;
2370 let desc = t.get_descriptor(file_descriptor)?;
2371 match desc {
2372 Descriptor::Stdout { stream, .. }
2373 | Descriptor::Stderr { stream, .. } => stream.borrowed(),
2374 Descriptor::File(File {
2375 fd,
2376 position,
2377 append,
2378 ..
2379 }) => {
2380 let fd = fd.borrowed();
2381 let position = position.clone();
2382 let append = *append;
2383 drop(t);
2384 if append {
2385 self.filesystem().append_via_stream(fd)?
2386 } else {
2387 let pos = position.load(Ordering::Relaxed);
2388 self.filesystem().write_via_stream(fd, pos)?
2389 }
2390 }
2391 _ => return Err(types::Errno::Badf.into()),
2393 }
2394 };
2395 streams::HostOutputStream::subscribe(&mut self.table, stream)
2396 .context("failed to call `subscribe` on `output-stream`")
2397 .map_err(types::Error::trap)?
2398 }
2399 };
2400 pollables.push(p);
2401 }
2402 let ready: HashSet<_> = self
2403 .table
2404 .poll(pollables)
2405 .await
2406 .context("failed to call `poll-oneoff`")
2407 .map_err(types::Error::trap)?
2408 .into_iter()
2409 .collect();
2410
2411 let mut count: types::Size = 0;
2412 for (sub, event) in (0..)
2413 .zip(subs.iter())
2414 .filter_map(|(idx, sub)| ready.contains(&idx).then_some(sub))
2415 .zip(events.iter())
2416 {
2417 let sub = memory.read(sub?)?;
2418 let event = event?;
2419 let e = match sub.u {
2420 types::SubscriptionU::Clock(..) => types::Event {
2421 userdata: sub.userdata,
2422 error: types::Errno::Success,
2423 type_: types::Eventtype::Clock,
2424 fd_readwrite: types::EventFdReadwrite {
2425 flags: types::Eventrwflags::empty(),
2426 nbytes: 0,
2427 },
2428 },
2429 types::SubscriptionU::FdRead(types::SubscriptionFdReadwrite {
2430 file_descriptor,
2431 }) => {
2432 let t = self.transact()?;
2433 let desc = t.get_descriptor(file_descriptor)?;
2434 match desc {
2435 Descriptor::Stdin { .. } => types::Event {
2436 userdata: sub.userdata,
2437 error: types::Errno::Success,
2438 type_: types::Eventtype::FdRead,
2439 fd_readwrite: types::EventFdReadwrite {
2440 flags: types::Eventrwflags::empty(),
2441 nbytes: 1,
2442 },
2443 },
2444 Descriptor::File(File { fd, position, .. }) => {
2445 let fd = fd.borrowed();
2446 let position = position.clone();
2447 drop(t);
2448 match self.filesystem().stat(fd).await? {
2449 filesystem::DescriptorStat { size, .. } => {
2450 let pos = position.load(Ordering::Relaxed);
2451 let nbytes = size.saturating_sub(pos);
2452 types::Event {
2453 userdata: sub.userdata,
2454 error: types::Errno::Success,
2455 type_: types::Eventtype::FdRead,
2456 fd_readwrite: types::EventFdReadwrite {
2457 flags: if nbytes == 0 {
2458 types::Eventrwflags::FD_READWRITE_HANGUP
2459 } else {
2460 types::Eventrwflags::empty()
2461 },
2462 nbytes: 1,
2463 },
2464 }
2465 }
2466 }
2467 }
2468 _ => return Err(types::Errno::Badf.into()),
2470 }
2471 }
2472 types::SubscriptionU::FdWrite(types::SubscriptionFdReadwrite {
2473 file_descriptor,
2474 }) => {
2475 let t = self.transact()?;
2476 let desc = t.get_descriptor(file_descriptor)?;
2477 match desc {
2478 Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => types::Event {
2479 userdata: sub.userdata,
2480 error: types::Errno::Success,
2481 type_: types::Eventtype::FdWrite,
2482 fd_readwrite: types::EventFdReadwrite {
2483 flags: types::Eventrwflags::empty(),
2484 nbytes: 1,
2485 },
2486 },
2487 Descriptor::File(_) => types::Event {
2488 userdata: sub.userdata,
2489 error: types::Errno::Success,
2490 type_: types::Eventtype::FdWrite,
2491 fd_readwrite: types::EventFdReadwrite {
2492 flags: types::Eventrwflags::empty(),
2493 nbytes: 1,
2494 },
2495 },
2496 _ => return Err(types::Errno::Badf.into()),
2498 }
2499 }
2500 };
2501 memory.write(event, e)?;
2502 count = count
2503 .checked_add(1)
2504 .ok_or_else(|| types::Error::from(types::Errno::Overflow))?
2505 }
2506 Ok(count)
2507 }
2508
2509 #[instrument(skip(self, _memory))]
2510 fn proc_exit(
2511 &mut self,
2512 _memory: &mut GuestMemory<'_>,
2513 status: types::Exitcode,
2514 ) -> anyhow::Error {
2515 if status >= 126 {
2517 return anyhow::Error::msg("exit with invalid exit status outside of [0..126)");
2518 }
2519 crate::I32Exit(status as i32).into()
2520 }
2521
2522 #[instrument(skip(self, _memory))]
2523 fn proc_raise(
2524 &mut self,
2525 _memory: &mut GuestMemory<'_>,
2526 _sig: types::Signal,
2527 ) -> Result<(), types::Error> {
2528 Err(types::Errno::Notsup.into())
2529 }
2530
2531 #[instrument(skip(self, _memory))]
2532 fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), types::Error> {
2533 Ok(())
2535 }
2536
2537 #[instrument(skip(self, memory))]
2538 fn random_get(
2539 &mut self,
2540 memory: &mut GuestMemory<'_>,
2541 buf: GuestPtr<u8>,
2542 buf_len: types::Size,
2543 ) -> Result<(), types::Error> {
2544 let rand = self
2545 .wasi
2546 .random
2547 .get_random_bytes(buf_len.into())
2548 .context("failed to call `get-random-bytes`")
2549 .map_err(types::Error::trap)?;
2550 write_bytes(memory, buf, &rand)?;
2551 Ok(())
2552 }
2553
2554 #[instrument(skip(self, _memory))]
2555 fn sock_accept(
2556 &mut self,
2557 _memory: &mut GuestMemory<'_>,
2558 fd: types::Fd,
2559 flags: types::Fdflags,
2560 ) -> Result<types::Fd, types::Error> {
2561 tracing::warn!("p1 sock_accept is not implemented");
2562 self.transact()?.get_descriptor(fd)?;
2563 Err(types::Errno::Notsock.into())
2564 }
2565
2566 #[instrument(skip(self, _memory))]
2567 fn sock_recv(
2568 &mut self,
2569 _memory: &mut GuestMemory<'_>,
2570 fd: types::Fd,
2571 ri_data: types::IovecArray,
2572 ri_flags: types::Riflags,
2573 ) -> Result<(types::Size, types::Roflags), types::Error> {
2574 tracing::warn!("p1 sock_recv is not implemented");
2575 self.transact()?.get_descriptor(fd)?;
2576 Err(types::Errno::Notsock.into())
2577 }
2578
2579 #[instrument(skip(self, _memory))]
2580 fn sock_send(
2581 &mut self,
2582 _memory: &mut GuestMemory<'_>,
2583 fd: types::Fd,
2584 si_data: types::CiovecArray,
2585 _si_flags: types::Siflags,
2586 ) -> Result<types::Size, types::Error> {
2587 tracing::warn!("p1 sock_send is not implemented");
2588 self.transact()?.get_descriptor(fd)?;
2589 Err(types::Errno::Notsock.into())
2590 }
2591
2592 #[instrument(skip(self, _memory))]
2593 fn sock_shutdown(
2594 &mut self,
2595 _memory: &mut GuestMemory<'_>,
2596 fd: types::Fd,
2597 how: types::Sdflags,
2598 ) -> Result<(), types::Error> {
2599 tracing::warn!("p1 sock_shutdown is not implemented");
2600 self.transact()?.get_descriptor(fd)?;
2601 Err(types::Errno::Notsock.into())
2602 }
2603}
2604
2605trait ResourceExt<T> {
2606 fn borrowed(&self) -> Resource<T>;
2607}
2608
2609impl<T: 'static> ResourceExt<T> for Resource<T> {
2610 fn borrowed(&self) -> Resource<T> {
2611 Resource::new_borrow(self.rep())
2612 }
2613}