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