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