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