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