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
1163impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx {
1167 #[instrument(skip(self, memory))]
1168 fn args_get(
1169 &mut self,
1170 memory: &mut GuestMemory<'_>,
1171 argv: GuestPtr<GuestPtr<u8>>,
1172 argv_buf: GuestPtr<u8>,
1173 ) -> Result<(), types::Error> {
1174 self.cli()
1175 .get_arguments()
1176 .context("failed to call `get-arguments`")
1177 .map_err(types::Error::trap)?
1178 .into_iter()
1179 .try_fold((argv, argv_buf), |(argv, argv_buf), arg| -> Result<_> {
1180 memory.write(argv, argv_buf)?;
1181 let argv = argv.add(1)?;
1182
1183 let argv_buf = write_bytes(memory, argv_buf, arg.as_bytes())?;
1184 let argv_buf = write_byte(memory, argv_buf, 0)?;
1185
1186 Ok((argv, argv_buf))
1187 })?;
1188 Ok(())
1189 }
1190
1191 #[instrument(skip(self, _memory))]
1192 fn args_sizes_get(
1193 &mut self,
1194 _memory: &mut GuestMemory<'_>,
1195 ) -> Result<(types::Size, types::Size), types::Error> {
1196 let args = self
1197 .cli()
1198 .get_arguments()
1199 .context("failed to call `get-arguments`")
1200 .map_err(types::Error::trap)?;
1201 let num = args.len().try_into().map_err(|_| types::Errno::Overflow)?;
1202 let len = args
1203 .iter()
1204 .map(|buf| buf.len() + 1) .sum::<usize>()
1206 .try_into()
1207 .map_err(|_| types::Errno::Overflow)?;
1208 Ok((num, len))
1209 }
1210
1211 #[instrument(skip(self, memory))]
1212 fn environ_get(
1213 &mut self,
1214 memory: &mut GuestMemory<'_>,
1215 environ: GuestPtr<GuestPtr<u8>>,
1216 environ_buf: GuestPtr<u8>,
1217 ) -> Result<(), types::Error> {
1218 self.cli()
1219 .get_environment()
1220 .context("failed to call `get-environment`")
1221 .map_err(types::Error::trap)?
1222 .into_iter()
1223 .try_fold(
1224 (environ, environ_buf),
1225 |(environ, environ_buf), (k, v)| -> Result<_, types::Error> {
1226 memory.write(environ, environ_buf)?;
1227 let environ = environ.add(1)?;
1228
1229 let environ_buf = write_bytes(memory, environ_buf, k.as_bytes())?;
1230 let environ_buf = write_byte(memory, environ_buf, b'=')?;
1231 let environ_buf = write_bytes(memory, environ_buf, v.as_bytes())?;
1232 let environ_buf = write_byte(memory, environ_buf, 0)?;
1233
1234 Ok((environ, environ_buf))
1235 },
1236 )?;
1237 Ok(())
1238 }
1239
1240 #[instrument(skip(self, _memory))]
1241 fn environ_sizes_get(
1242 &mut self,
1243 _memory: &mut GuestMemory<'_>,
1244 ) -> Result<(types::Size, types::Size), types::Error> {
1245 let environ = self
1246 .cli()
1247 .get_environment()
1248 .context("failed to call `get-environment`")
1249 .map_err(types::Error::trap)?;
1250 let num = environ.len().try_into()?;
1251 let len = environ
1252 .iter()
1253 .map(|(k, v)| k.len() + 1 + v.len() + 1) .sum::<usize>()
1255 .try_into()?;
1256 Ok((num, len))
1257 }
1258
1259 #[instrument(skip(self, _memory))]
1260 fn clock_res_get(
1261 &mut self,
1262 _memory: &mut GuestMemory<'_>,
1263 id: types::Clockid,
1264 ) -> Result<types::Timestamp, types::Error> {
1265 let res = match id {
1266 types::Clockid::Realtime => wall_clock::Host::resolution(&mut self.clocks())
1267 .context("failed to call `wall_clock::resolution`")
1268 .map_err(types::Error::trap)?
1269 .try_into()?,
1270 types::Clockid::Monotonic => monotonic_clock::Host::resolution(&mut self.clocks())
1271 .context("failed to call `monotonic_clock::resolution`")
1272 .map_err(types::Error::trap)?,
1273 types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
1274 return Err(types::Errno::Badf.into());
1275 }
1276 };
1277 Ok(res)
1278 }
1279
1280 #[instrument(skip(self, _memory))]
1281 fn clock_time_get(
1282 &mut self,
1283 _memory: &mut GuestMemory<'_>,
1284 id: types::Clockid,
1285 _precision: types::Timestamp,
1286 ) -> Result<types::Timestamp, types::Error> {
1287 let now = match id {
1288 types::Clockid::Realtime => wall_clock::Host::now(&mut self.clocks())
1289 .context("failed to call `wall_clock::now`")
1290 .map_err(types::Error::trap)?
1291 .try_into()?,
1292 types::Clockid::Monotonic => monotonic_clock::Host::now(&mut self.clocks())
1293 .context("failed to call `monotonic_clock::now`")
1294 .map_err(types::Error::trap)?,
1295 types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
1296 return Err(types::Errno::Badf.into());
1297 }
1298 };
1299 Ok(now)
1300 }
1301
1302 #[instrument(skip(self, _memory))]
1303 async fn fd_advise(
1304 &mut self,
1305 _memory: &mut GuestMemory<'_>,
1306 fd: types::Fd,
1307 offset: types::Filesize,
1308 len: types::Filesize,
1309 advice: types::Advice,
1310 ) -> Result<(), types::Error> {
1311 let fd = self.get_file_fd(fd)?;
1312 self.filesystem()
1313 .advise(fd, offset, len, advice.into())
1314 .await?;
1315 Ok(())
1316 }
1317
1318 #[instrument(skip(self, _memory))]
1321 fn fd_allocate(
1322 &mut self,
1323 _memory: &mut GuestMemory<'_>,
1324 fd: types::Fd,
1325 _offset: types::Filesize,
1326 _len: types::Filesize,
1327 ) -> Result<(), types::Error> {
1328 self.get_file_fd(fd)?;
1329 Err(types::Errno::Notsup.into())
1330 }
1331
1332 #[instrument(skip(self, _memory))]
1335 async fn fd_close(
1336 &mut self,
1337 _memory: &mut GuestMemory<'_>,
1338 fd: types::Fd,
1339 ) -> Result<(), types::Error> {
1340 let desc = {
1341 let fd = fd.into();
1342 let mut st = self.transact()?;
1343 let desc = st.descriptors.used.remove(&fd).ok_or(types::Errno::Badf)?;
1344 st.descriptors.free.insert(fd);
1345 desc
1346 };
1347 match desc {
1348 Descriptor::Stdin { stream, .. } => {
1349 streams::HostInputStream::drop(&mut self.table, stream)
1350 .await
1351 .context("failed to call `drop` on `input-stream`")
1352 }
1353 Descriptor::Stdout { stream, .. } | Descriptor::Stderr { stream, .. } => {
1354 streams::HostOutputStream::drop(&mut self.table, stream)
1355 .await
1356 .context("failed to call `drop` on `output-stream`")
1357 }
1358 Descriptor::File(File { fd, .. }) | Descriptor::Directory { fd, .. } => {
1359 filesystem::HostDescriptor::drop(&mut self.filesystem(), fd)
1360 .context("failed to call `drop`")
1361 }
1362 }
1363 .map_err(types::Error::trap)
1364 }
1365
1366 #[instrument(skip(self, _memory))]
1369 async fn fd_datasync(
1370 &mut self,
1371 _memory: &mut GuestMemory<'_>,
1372 fd: types::Fd,
1373 ) -> Result<(), types::Error> {
1374 let fd = self.get_file_fd(fd)?;
1375 self.filesystem().sync_data(fd).await?;
1376 Ok(())
1377 }
1378
1379 #[instrument(skip(self, _memory))]
1382 async fn fd_fdstat_get(
1383 &mut self,
1384 _memory: &mut GuestMemory<'_>,
1385 fd: types::Fd,
1386 ) -> Result<types::Fdstat, types::Error> {
1387 let (fd, blocking, append) = match self.transact()?.get_descriptor(fd)? {
1388 Descriptor::Stdin { isatty, .. } => {
1389 let fs_rights_base = types::Rights::FD_READ;
1390 return Ok(types::Fdstat {
1391 fs_filetype: (*isatty).into(),
1392 fs_flags: types::Fdflags::empty(),
1393 fs_rights_base,
1394 fs_rights_inheriting: fs_rights_base,
1395 });
1396 }
1397 Descriptor::Stdout { isatty, .. } | Descriptor::Stderr { isatty, .. } => {
1398 let fs_rights_base = types::Rights::FD_WRITE;
1399 return Ok(types::Fdstat {
1400 fs_filetype: (*isatty).into(),
1401 fs_flags: types::Fdflags::empty(),
1402 fs_rights_base,
1403 fs_rights_inheriting: fs_rights_base,
1404 });
1405 }
1406 Descriptor::Directory {
1407 preopen_path: Some(_),
1408 ..
1409 } => {
1410 let fs_rights_base = types::Rights::PATH_CREATE_DIRECTORY
1412 | types::Rights::PATH_CREATE_FILE
1413 | types::Rights::PATH_LINK_SOURCE
1414 | types::Rights::PATH_LINK_TARGET
1415 | types::Rights::PATH_OPEN
1416 | types::Rights::FD_READDIR
1417 | types::Rights::PATH_READLINK
1418 | types::Rights::PATH_RENAME_SOURCE
1419 | types::Rights::PATH_RENAME_TARGET
1420 | types::Rights::PATH_SYMLINK
1421 | types::Rights::PATH_REMOVE_DIRECTORY
1422 | types::Rights::PATH_UNLINK_FILE
1423 | types::Rights::PATH_FILESTAT_GET
1424 | types::Rights::PATH_FILESTAT_SET_TIMES
1425 | types::Rights::FD_FILESTAT_GET
1426 | types::Rights::FD_FILESTAT_SET_TIMES;
1427
1428 let fs_rights_inheriting = fs_rights_base
1429 | types::Rights::FD_DATASYNC
1430 | types::Rights::FD_READ
1431 | types::Rights::FD_SEEK
1432 | types::Rights::FD_FDSTAT_SET_FLAGS
1433 | types::Rights::FD_SYNC
1434 | types::Rights::FD_TELL
1435 | types::Rights::FD_WRITE
1436 | types::Rights::FD_ADVISE
1437 | types::Rights::FD_ALLOCATE
1438 | types::Rights::FD_FILESTAT_GET
1439 | types::Rights::FD_FILESTAT_SET_SIZE
1440 | types::Rights::FD_FILESTAT_SET_TIMES
1441 | types::Rights::POLL_FD_READWRITE;
1442
1443 return Ok(types::Fdstat {
1444 fs_filetype: types::Filetype::Directory,
1445 fs_flags: types::Fdflags::empty(),
1446 fs_rights_base,
1447 fs_rights_inheriting,
1448 });
1449 }
1450 Descriptor::Directory { fd, .. } => (fd.borrowed(), BlockingMode::Blocking, false),
1451 Descriptor::File(File {
1452 fd,
1453 blocking_mode,
1454 append,
1455 ..
1456 }) => (fd.borrowed(), *blocking_mode, *append),
1457 };
1458 let flags = self.filesystem().get_flags(fd.borrowed()).await?;
1459 let fs_filetype = self
1460 .filesystem()
1461 .get_type(fd.borrowed())
1462 .await?
1463 .try_into()
1464 .map_err(types::Error::trap)?;
1465 let mut fs_flags = types::Fdflags::empty();
1466 let mut fs_rights_base = types::Rights::all();
1467 if let types::Filetype::Directory = fs_filetype {
1468 fs_rights_base &= !types::Rights::FD_SEEK;
1469 fs_rights_base &= !types::Rights::FD_FILESTAT_SET_SIZE;
1470 fs_rights_base &= !types::Rights::PATH_FILESTAT_SET_SIZE;
1471 }
1472 if !flags.contains(filesystem::DescriptorFlags::READ) {
1473 fs_rights_base &= !types::Rights::FD_READ;
1474 fs_rights_base &= !types::Rights::FD_READDIR;
1475 }
1476 if !flags.contains(filesystem::DescriptorFlags::WRITE) {
1477 fs_rights_base &= !types::Rights::FD_WRITE;
1478 }
1479 if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
1480 fs_flags |= types::Fdflags::DSYNC;
1481 }
1482 if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
1483 fs_flags |= types::Fdflags::RSYNC;
1484 }
1485 if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
1486 fs_flags |= types::Fdflags::SYNC;
1487 }
1488 if append {
1489 fs_flags |= types::Fdflags::APPEND;
1490 }
1491 if matches!(blocking, BlockingMode::NonBlocking) {
1492 fs_flags |= types::Fdflags::NONBLOCK;
1493 }
1494 Ok(types::Fdstat {
1495 fs_filetype,
1496 fs_flags,
1497 fs_rights_base,
1498 fs_rights_inheriting: fs_rights_base,
1499 })
1500 }
1501
1502 #[instrument(skip(self, _memory))]
1505 fn fd_fdstat_set_flags(
1506 &mut self,
1507 _memory: &mut GuestMemory<'_>,
1508 fd: types::Fd,
1509 flags: types::Fdflags,
1510 ) -> Result<(), types::Error> {
1511 let mut st = self.transact()?;
1512 let File {
1513 append,
1514 blocking_mode,
1515 ..
1516 } = st.get_file_mut(fd)?;
1517
1518 if flags.contains(types::Fdflags::DSYNC)
1520 || flags.contains(types::Fdflags::SYNC)
1521 || flags.contains(types::Fdflags::RSYNC)
1522 {
1523 return Err(types::Errno::Inval.into());
1524 }
1525 *append = flags.contains(types::Fdflags::APPEND);
1526 *blocking_mode = BlockingMode::from_fdflags(&flags);
1527 Ok(())
1528 }
1529
1530 #[instrument(skip(self, _memory))]
1532 fn fd_fdstat_set_rights(
1533 &mut self,
1534 _memory: &mut GuestMemory<'_>,
1535 fd: types::Fd,
1536 _fs_rights_base: types::Rights,
1537 _fs_rights_inheriting: types::Rights,
1538 ) -> Result<(), types::Error> {
1539 self.get_fd(fd)?;
1540 Err(types::Errno::Notsup.into())
1541 }
1542
1543 #[instrument(skip(self, _memory))]
1545 async fn fd_filestat_get(
1546 &mut self,
1547 _memory: &mut GuestMemory<'_>,
1548 fd: types::Fd,
1549 ) -> Result<types::Filestat, types::Error> {
1550 let t = self.transact()?;
1551 let desc = t.get_descriptor(fd)?;
1552 match desc {
1553 Descriptor::Stdin { isatty, .. }
1554 | Descriptor::Stdout { isatty, .. }
1555 | Descriptor::Stderr { isatty, .. } => Ok(types::Filestat {
1556 dev: 0,
1557 ino: 0,
1558 filetype: (*isatty).into(),
1559 nlink: 0,
1560 size: 0,
1561 atim: 0,
1562 mtim: 0,
1563 ctim: 0,
1564 }),
1565 Descriptor::Directory { fd, .. } | Descriptor::File(File { fd, .. }) => {
1566 let fd = fd.borrowed();
1567 drop(t);
1568 let filesystem::DescriptorStat {
1569 type_,
1570 link_count: nlink,
1571 size,
1572 data_access_timestamp,
1573 data_modification_timestamp,
1574 status_change_timestamp,
1575 } = self.filesystem().stat(fd.borrowed()).await?;
1576 let metadata_hash = self.filesystem().metadata_hash(fd).await?;
1577 let filetype = type_.try_into().map_err(types::Error::trap)?;
1578 let zero = wall_clock::Datetime {
1579 seconds: 0,
1580 nanoseconds: 0,
1581 };
1582 let atim = data_access_timestamp.unwrap_or(zero).try_into()?;
1583 let mtim = data_modification_timestamp.unwrap_or(zero).try_into()?;
1584 let ctim = status_change_timestamp.unwrap_or(zero).try_into()?;
1585 Ok(types::Filestat {
1586 dev: 1,
1587 ino: metadata_hash.lower,
1588 filetype,
1589 nlink,
1590 size,
1591 atim,
1592 mtim,
1593 ctim,
1594 })
1595 }
1596 }
1597 }
1598
1599 #[instrument(skip(self, _memory))]
1602 async fn fd_filestat_set_size(
1603 &mut self,
1604 _memory: &mut GuestMemory<'_>,
1605 fd: types::Fd,
1606 size: types::Filesize,
1607 ) -> Result<(), types::Error> {
1608 let fd = self.get_file_fd(fd)?;
1609 self.filesystem().set_size(fd, size).await?;
1610 Ok(())
1611 }
1612
1613 #[instrument(skip(self, _memory))]
1616 async fn fd_filestat_set_times(
1617 &mut self,
1618 _memory: &mut GuestMemory<'_>,
1619 fd: types::Fd,
1620 atim: types::Timestamp,
1621 mtim: types::Timestamp,
1622 fst_flags: types::Fstflags,
1623 ) -> Result<(), types::Error> {
1624 let atim = systimespec(
1625 fst_flags.contains(types::Fstflags::ATIM),
1626 atim,
1627 fst_flags.contains(types::Fstflags::ATIM_NOW),
1628 )?;
1629 let mtim = systimespec(
1630 fst_flags.contains(types::Fstflags::MTIM),
1631 mtim,
1632 fst_flags.contains(types::Fstflags::MTIM_NOW),
1633 )?;
1634
1635 let fd = self.get_fd(fd)?;
1636 self.filesystem().set_times(fd, atim, mtim).await?;
1637 Ok(())
1638 }
1639
1640 #[instrument(skip(self, memory))]
1643 async fn fd_read(
1644 &mut self,
1645 memory: &mut GuestMemory<'_>,
1646 fd: types::Fd,
1647 iovs: types::IovecArray,
1648 ) -> Result<types::Size, types::Error> {
1649 let t = self.transact()?;
1650 let desc = t.get_descriptor(fd)?;
1651 match desc {
1652 Descriptor::File(File {
1653 fd,
1654 position,
1655 blocking_mode: _,
1658 ..
1659 }) => {
1660 let fd = fd.borrowed();
1661 let position = position.clone();
1662 drop(t);
1663 let pos = position.load(Ordering::Relaxed);
1664 let file = self.table.get(&fd)?.file()?;
1665 let iov = first_non_empty_iovec(memory, iovs)?;
1666 let bytes_read = match (file.as_blocking_file(), memory.as_slice_mut(iov)?) {
1667 (Some(file), Some(mut buf)) => file
1671 .read_at(&mut buf, pos)
1672 .map_err(|e| StreamError::LastOperationFailed(e.into()))?,
1673 (_, buf) => {
1677 drop(buf);
1678 let mut buf = vec![0; iov.len() as usize];
1679 let buf = file
1680 .run_blocking(move |file| -> Result<_, types::Error> {
1681 let bytes_read = file
1682 .read_at(&mut buf, pos)
1683 .map_err(|e| StreamError::LastOperationFailed(e.into()))?;
1684 buf.truncate(bytes_read);
1685 Ok(buf)
1686 })
1687 .await?;
1688 let iov = iov.get_range(0..u32::try_from(buf.len())?).unwrap();
1689 memory.copy_from_slice(&buf, iov)?;
1690 buf.len()
1691 }
1692 };
1693
1694 let pos = pos
1695 .checked_add(bytes_read.try_into()?)
1696 .ok_or(types::Errno::Overflow)?;
1697 position.store(pos, Ordering::Relaxed);
1698
1699 Ok(bytes_read.try_into()?)
1700 }
1701 Descriptor::Stdin { stream, .. } => {
1702 let stream = stream.borrowed();
1703 drop(t);
1704 let buf = first_non_empty_iovec(memory, iovs)?;
1705 let read = BlockingMode::Blocking
1706 .read(&mut self.table, stream, buf.len().try_into()?)
1707 .await?;
1708 if read.len() > buf.len().try_into()? {
1709 return Err(types::Errno::Range.into());
1710 }
1711 let buf = buf.get_range(0..u32::try_from(read.len())?).unwrap();
1712 memory.copy_from_slice(&read, buf)?;
1713 let n = read.len().try_into()?;
1714 Ok(n)
1715 }
1716 _ => return Err(types::Errno::Badf.into()),
1717 }
1718 }
1719
1720 #[instrument(skip(self, memory))]
1723 async fn fd_pread(
1724 &mut self,
1725 memory: &mut GuestMemory<'_>,
1726 fd: types::Fd,
1727 iovs: types::IovecArray,
1728 offset: types::Filesize,
1729 ) -> Result<types::Size, types::Error> {
1730 let t = self.transact()?;
1731 let desc = t.get_descriptor(fd)?;
1732 let (buf, read) = match desc {
1733 Descriptor::File(File {
1734 fd, blocking_mode, ..
1735 }) => {
1736 let fd = fd.borrowed();
1737 let blocking_mode = *blocking_mode;
1738 drop(t);
1739 let buf = first_non_empty_iovec(memory, iovs)?;
1740
1741 let stream = self.filesystem().read_via_stream(fd, offset)?;
1742 let read = blocking_mode
1743 .read(&mut self.table, stream.borrowed(), buf.len().try_into()?)
1744 .await;
1745 streams::HostInputStream::drop(&mut self.table, stream)
1746 .await
1747 .map_err(|e| types::Error::trap(e))?;
1748 (buf, read?)
1749 }
1750 Descriptor::Stdin { .. } => {
1751 return Err(types::Errno::Spipe.into());
1753 }
1754 _ => return Err(types::Errno::Badf.into()),
1755 };
1756 if read.len() > buf.len().try_into()? {
1757 return Err(types::Errno::Range.into());
1758 }
1759 let buf = buf.get_range(0..u32::try_from(read.len())?).unwrap();
1760 memory.copy_from_slice(&read, buf)?;
1761 let n = read.len().try_into()?;
1762 Ok(n)
1763 }
1764
1765 #[instrument(skip(self, memory))]
1768 async fn fd_write(
1769 &mut self,
1770 memory: &mut GuestMemory<'_>,
1771 fd: types::Fd,
1772 ciovs: types::CiovecArray,
1773 ) -> Result<types::Size, types::Error> {
1774 self.fd_write_impl(memory, fd, ciovs, FdWrite::AtCur).await
1775 }
1776
1777 #[instrument(skip(self, memory))]
1780 async fn fd_pwrite(
1781 &mut self,
1782 memory: &mut GuestMemory<'_>,
1783 fd: types::Fd,
1784 ciovs: types::CiovecArray,
1785 offset: types::Filesize,
1786 ) -> Result<types::Size, types::Error> {
1787 self.fd_write_impl(memory, fd, ciovs, FdWrite::At(offset))
1788 .await
1789 }
1790
1791 #[instrument(skip(self, _memory))]
1793 fn fd_prestat_get(
1794 &mut self,
1795 _memory: &mut GuestMemory<'_>,
1796 fd: types::Fd,
1797 ) -> Result<types::Prestat, types::Error> {
1798 if let Descriptor::Directory {
1799 preopen_path: Some(p),
1800 ..
1801 } = self.transact()?.get_descriptor(fd)?
1802 {
1803 let pr_name_len = p.len().try_into()?;
1804 return Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len }));
1805 }
1806 Err(types::Errno::Badf.into()) }
1808
1809 #[instrument(skip(self, memory))]
1811 fn fd_prestat_dir_name(
1812 &mut self,
1813 memory: &mut GuestMemory<'_>,
1814 fd: types::Fd,
1815 path: GuestPtr<u8>,
1816 path_max_len: types::Size,
1817 ) -> Result<(), types::Error> {
1818 let path_max_len = path_max_len.try_into()?;
1819 if let Descriptor::Directory {
1820 preopen_path: Some(p),
1821 ..
1822 } = self.transact()?.get_descriptor(fd)?
1823 {
1824 if p.len() > path_max_len {
1825 return Err(types::Errno::Nametoolong.into());
1826 }
1827 write_bytes(memory, path, p.as_bytes())?;
1828 return Ok(());
1829 }
1830 Err(types::Errno::Notdir.into()) }
1832
1833 #[instrument(skip(self, _memory))]
1835 fn fd_renumber(
1836 &mut self,
1837 _memory: &mut GuestMemory<'_>,
1838 from: types::Fd,
1839 to: types::Fd,
1840 ) -> Result<(), types::Error> {
1841 let mut st = self.transact()?;
1842 let from = from.into();
1843 let to = to.into();
1844 if !st.descriptors.used.contains_key(&to) {
1845 return Err(types::Errno::Badf.into());
1846 }
1847 let btree_map::Entry::Occupied(desc) = st.descriptors.used.entry(from) else {
1848 return Err(types::Errno::Badf.into());
1849 };
1850 if from != to {
1851 let desc = desc.remove();
1852 st.descriptors.free.insert(from);
1853 st.descriptors.free.remove(&to);
1854 st.descriptors.used.insert(to, desc);
1855 }
1856 Ok(())
1857 }
1858
1859 #[instrument(skip(self, _memory))]
1862 async fn fd_seek(
1863 &mut self,
1864 _memory: &mut GuestMemory<'_>,
1865 fd: types::Fd,
1866 offset: types::Filedelta,
1867 whence: types::Whence,
1868 ) -> Result<types::Filesize, types::Error> {
1869 let t = self.transact()?;
1870 let File { fd, position, .. } = t.get_seekable(fd)?;
1871 let fd = fd.borrowed();
1872 let position = position.clone();
1873 drop(t);
1874 let pos = match whence {
1875 types::Whence::Set if offset >= 0 => {
1876 offset.try_into().map_err(|_| types::Errno::Inval)?
1877 }
1878 types::Whence::Cur => position
1879 .load(Ordering::Relaxed)
1880 .checked_add_signed(offset)
1881 .ok_or(types::Errno::Inval)?,
1882 types::Whence::End => {
1883 let filesystem::DescriptorStat { size, .. } = self.filesystem().stat(fd).await?;
1884 size.checked_add_signed(offset).ok_or(types::Errno::Inval)?
1885 }
1886 _ => return Err(types::Errno::Inval.into()),
1887 };
1888 position.store(pos, Ordering::Relaxed);
1889 Ok(pos)
1890 }
1891
1892 #[instrument(skip(self, _memory))]
1895 async fn fd_sync(
1896 &mut self,
1897 _memory: &mut GuestMemory<'_>,
1898 fd: types::Fd,
1899 ) -> Result<(), types::Error> {
1900 let fd = self.get_file_fd(fd)?;
1901 self.filesystem().sync(fd).await?;
1902 Ok(())
1903 }
1904
1905 #[instrument(skip(self, _memory))]
1908 fn fd_tell(
1909 &mut self,
1910 _memory: &mut GuestMemory<'_>,
1911 fd: types::Fd,
1912 ) -> Result<types::Filesize, types::Error> {
1913 let pos = self
1914 .transact()?
1915 .get_seekable(fd)
1916 .map(|File { position, .. }| position.load(Ordering::Relaxed))?;
1917 Ok(pos)
1918 }
1919
1920 #[instrument(skip(self, memory))]
1921 async fn fd_readdir(
1922 &mut self,
1923 memory: &mut GuestMemory<'_>,
1924 fd: types::Fd,
1925 buf: GuestPtr<u8>,
1926 buf_len: types::Size,
1927 cookie: types::Dircookie,
1928 ) -> Result<types::Size, types::Error> {
1929 let fd = self.get_dir_fd(fd)?;
1930 let stream = self.filesystem().read_directory(fd.borrowed()).await?;
1931 let dir_metadata_hash = self.filesystem().metadata_hash(fd.borrowed()).await?;
1932 let cookie = cookie.try_into().map_err(|_| types::Errno::Overflow)?;
1933
1934 let head = [
1935 (
1936 types::Dirent {
1937 d_next: 1u64.to_le(),
1938 d_ino: dir_metadata_hash.lower.to_le(),
1939 d_type: types::Filetype::Directory,
1940 d_namlen: 1u32.to_le(),
1941 },
1942 ".".into(),
1943 ),
1944 (
1945 types::Dirent {
1946 d_next: 2u64.to_le(),
1947 d_ino: dir_metadata_hash.lower.to_le(), d_type: types::Filetype::Directory,
1949 d_namlen: 2u32.to_le(),
1950 },
1951 "..".into(),
1952 ),
1953 ];
1954
1955 let mut dir = Vec::new();
1956 for (entry, d_next) in self
1957 .table
1958 .delete(stream)?
1960 .into_iter()
1961 .zip(3u64..)
1962 {
1963 let filesystem::DirectoryEntry { type_, name } = entry?;
1964 let metadata_hash = self
1965 .filesystem()
1966 .metadata_hash_at(fd.borrowed(), filesystem::PathFlags::empty(), name.clone())
1967 .await?;
1968 let d_type = type_.try_into().map_err(types::Error::trap)?;
1969 let d_namlen: u32 = name.len().try_into().map_err(|_| types::Errno::Overflow)?;
1970 dir.push((
1971 types::Dirent {
1972 d_next: d_next.to_le(),
1973 d_ino: metadata_hash.lower.to_le(),
1974 d_type, d_namlen: d_namlen.to_le(),
1976 },
1977 name,
1978 ))
1979 }
1980
1981 const DIRENT_SIZE: u32 = size_of::<types::Dirent>() as _;
1983 assert_eq!(
1984 types::Dirent::guest_size(),
1985 DIRENT_SIZE,
1986 "Dirent guest repr and host repr should match"
1987 );
1988 let mut buf = buf;
1989 let mut cap = buf_len;
1990 for (ref entry, path) in head.into_iter().chain(dir.into_iter()).skip(cookie) {
1991 let mut path = path.into_bytes();
1992 assert_eq!(
1993 1,
1994 size_of_val(&entry.d_type),
1995 "Dirent member d_type should be endian-invariant"
1996 );
1997 let entry_len = cap.min(DIRENT_SIZE);
1998 let entry = entry as *const _ as _;
1999 let entry = unsafe { slice::from_raw_parts(entry, entry_len as _) };
2000 cap = cap.checked_sub(entry_len).unwrap();
2001 buf = write_bytes(memory, buf, entry)?;
2002 if cap == 0 {
2003 return Ok(buf_len);
2004 }
2005
2006 if let Ok(cap) = cap.try_into() {
2007 path.truncate(cap);
2009 }
2010 cap = cap.checked_sub(path.len() as _).unwrap();
2011 buf = write_bytes(memory, buf, &path)?;
2012 if cap == 0 {
2013 return Ok(buf_len);
2014 }
2015 }
2016 Ok(buf_len.checked_sub(cap).unwrap())
2017 }
2018
2019 #[instrument(skip(self, memory))]
2020 async fn path_create_directory(
2021 &mut self,
2022 memory: &mut GuestMemory<'_>,
2023 dirfd: types::Fd,
2024 path: GuestPtr<str>,
2025 ) -> Result<(), types::Error> {
2026 let dirfd = self.get_dir_fd(dirfd)?;
2027 let path = read_string(memory, path)?;
2028 self.filesystem()
2029 .create_directory_at(dirfd.borrowed(), path)
2030 .await?;
2031 Ok(())
2032 }
2033
2034 #[instrument(skip(self, memory))]
2037 async fn path_filestat_get(
2038 &mut self,
2039 memory: &mut GuestMemory<'_>,
2040 dirfd: types::Fd,
2041 flags: types::Lookupflags,
2042 path: GuestPtr<str>,
2043 ) -> Result<types::Filestat, types::Error> {
2044 let dirfd = self.get_dir_fd(dirfd)?;
2045 let path = read_string(memory, path)?;
2046 let filesystem::DescriptorStat {
2047 type_,
2048 link_count: nlink,
2049 size,
2050 data_access_timestamp,
2051 data_modification_timestamp,
2052 status_change_timestamp,
2053 } = self
2054 .filesystem()
2055 .stat_at(dirfd.borrowed(), flags.into(), path.clone())
2056 .await?;
2057 let metadata_hash = self
2058 .filesystem()
2059 .metadata_hash_at(dirfd, flags.into(), path)
2060 .await?;
2061 let filetype = type_.try_into().map_err(types::Error::trap)?;
2062 let zero = wall_clock::Datetime {
2063 seconds: 0,
2064 nanoseconds: 0,
2065 };
2066 let atim = data_access_timestamp.unwrap_or(zero).try_into()?;
2067 let mtim = data_modification_timestamp.unwrap_or(zero).try_into()?;
2068 let ctim = status_change_timestamp.unwrap_or(zero).try_into()?;
2069 Ok(types::Filestat {
2070 dev: 1,
2071 ino: metadata_hash.lower,
2072 filetype,
2073 nlink,
2074 size,
2075 atim,
2076 mtim,
2077 ctim,
2078 })
2079 }
2080
2081 #[instrument(skip(self, memory))]
2084 async fn path_filestat_set_times(
2085 &mut self,
2086 memory: &mut GuestMemory<'_>,
2087 dirfd: types::Fd,
2088 flags: types::Lookupflags,
2089 path: GuestPtr<str>,
2090 atim: types::Timestamp,
2091 mtim: types::Timestamp,
2092 fst_flags: types::Fstflags,
2093 ) -> Result<(), types::Error> {
2094 let atim = systimespec(
2095 fst_flags.contains(types::Fstflags::ATIM),
2096 atim,
2097 fst_flags.contains(types::Fstflags::ATIM_NOW),
2098 )?;
2099 let mtim = systimespec(
2100 fst_flags.contains(types::Fstflags::MTIM),
2101 mtim,
2102 fst_flags.contains(types::Fstflags::MTIM_NOW),
2103 )?;
2104
2105 let dirfd = self.get_dir_fd(dirfd)?;
2106 let path = read_string(memory, path)?;
2107 self.filesystem()
2108 .set_times_at(dirfd, flags.into(), path, atim, mtim)
2109 .await?;
2110 Ok(())
2111 }
2112
2113 #[instrument(skip(self, memory))]
2116 async fn path_link(
2117 &mut self,
2118 memory: &mut GuestMemory<'_>,
2119 src_fd: types::Fd,
2120 src_flags: types::Lookupflags,
2121 src_path: GuestPtr<str>,
2122 target_fd: types::Fd,
2123 target_path: GuestPtr<str>,
2124 ) -> Result<(), types::Error> {
2125 let src_fd = self.get_dir_fd(src_fd)?;
2126 let target_fd = self.get_dir_fd(target_fd)?;
2127 let src_path = read_string(memory, src_path)?;
2128 let target_path = read_string(memory, target_path)?;
2129 self.filesystem()
2130 .link_at(src_fd, src_flags.into(), src_path, target_fd, target_path)
2131 .await?;
2132 Ok(())
2133 }
2134
2135 #[instrument(skip(self, memory))]
2138 async fn path_open(
2139 &mut self,
2140 memory: &mut GuestMemory<'_>,
2141 dirfd: types::Fd,
2142 dirflags: types::Lookupflags,
2143 path: GuestPtr<str>,
2144 oflags: types::Oflags,
2145 fs_rights_base: types::Rights,
2146 _fs_rights_inheriting: types::Rights,
2147 fdflags: types::Fdflags,
2148 ) -> Result<types::Fd, types::Error> {
2149 let path = read_string(memory, path)?;
2150
2151 let mut flags = filesystem::DescriptorFlags::empty();
2152 if fs_rights_base.contains(types::Rights::FD_READ) {
2153 flags |= filesystem::DescriptorFlags::READ;
2154 }
2155 if fs_rights_base.contains(types::Rights::FD_WRITE) {
2156 flags |= filesystem::DescriptorFlags::WRITE;
2157 }
2158 if fdflags.contains(types::Fdflags::SYNC) {
2159 flags |= filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC;
2160 }
2161 if fdflags.contains(types::Fdflags::DSYNC) {
2162 flags |= filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC;
2163 }
2164 if fdflags.contains(types::Fdflags::RSYNC) {
2165 flags |= filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC;
2166 }
2167
2168 let t = self.transact()?;
2169 let dirfd = match t.get_descriptor(dirfd)? {
2170 Descriptor::Directory { fd, .. } => fd.borrowed(),
2171 Descriptor::File(_) => return Err(types::Errno::Notdir.into()),
2172 _ => return Err(types::Errno::Badf.into()),
2173 };
2174 drop(t);
2175 let fd = self
2176 .filesystem()
2177 .open_at(dirfd, dirflags.into(), path, oflags.into(), flags)
2178 .await?;
2179 let mut t = self.transact()?;
2180 let desc = match t.view.table.get(&fd)? {
2181 crate::filesystem::Descriptor::Dir(_) => Descriptor::Directory {
2182 fd,
2183 preopen_path: None,
2184 },
2185 crate::filesystem::Descriptor::File(_) => Descriptor::File(File {
2186 fd,
2187 position: Default::default(),
2188 append: fdflags.contains(types::Fdflags::APPEND),
2189 blocking_mode: BlockingMode::from_fdflags(&fdflags),
2190 }),
2191 };
2192 let fd = t.descriptors.push(desc)?;
2193 Ok(fd.into())
2194 }
2195
2196 #[instrument(skip(self, memory))]
2199 async fn path_readlink(
2200 &mut self,
2201 memory: &mut GuestMemory<'_>,
2202 dirfd: types::Fd,
2203 path: GuestPtr<str>,
2204 buf: GuestPtr<u8>,
2205 buf_len: types::Size,
2206 ) -> Result<types::Size, types::Error> {
2207 let dirfd = self.get_dir_fd(dirfd)?;
2208 let path = read_string(memory, path)?;
2209 let mut path = self
2210 .filesystem()
2211 .readlink_at(dirfd, path)
2212 .await?
2213 .into_bytes();
2214 if let Ok(buf_len) = buf_len.try_into() {
2215 path.truncate(buf_len);
2217 }
2218 let n = path.len().try_into().map_err(|_| types::Errno::Overflow)?;
2219 write_bytes(memory, buf, &path)?;
2220 Ok(n)
2221 }
2222
2223 #[instrument(skip(self, memory))]
2224 async fn path_remove_directory(
2225 &mut self,
2226 memory: &mut GuestMemory<'_>,
2227 dirfd: types::Fd,
2228 path: GuestPtr<str>,
2229 ) -> Result<(), types::Error> {
2230 let dirfd = self.get_dir_fd(dirfd)?;
2231 let path = read_string(memory, path)?;
2232 self.filesystem().remove_directory_at(dirfd, path).await?;
2233 Ok(())
2234 }
2235
2236 #[instrument(skip(self, memory))]
2239 async fn path_rename(
2240 &mut self,
2241 memory: &mut GuestMemory<'_>,
2242 src_fd: types::Fd,
2243 src_path: GuestPtr<str>,
2244 dest_fd: types::Fd,
2245 dest_path: GuestPtr<str>,
2246 ) -> Result<(), types::Error> {
2247 let src_fd = self.get_dir_fd(src_fd)?;
2248 let dest_fd = self.get_dir_fd(dest_fd)?;
2249 let src_path = read_string(memory, src_path)?;
2250 let dest_path = read_string(memory, dest_path)?;
2251 self.filesystem()
2252 .rename_at(src_fd, src_path, dest_fd, dest_path)
2253 .await?;
2254 Ok(())
2255 }
2256
2257 #[instrument(skip(self, memory))]
2258 async fn path_symlink(
2259 &mut self,
2260 memory: &mut GuestMemory<'_>,
2261 src_path: GuestPtr<str>,
2262 dirfd: types::Fd,
2263 dest_path: GuestPtr<str>,
2264 ) -> Result<(), types::Error> {
2265 let dirfd = self.get_dir_fd(dirfd)?;
2266 let src_path = read_string(memory, src_path)?;
2267 let dest_path = read_string(memory, dest_path)?;
2268 self.filesystem()
2269 .symlink_at(dirfd.borrowed(), src_path, dest_path)
2270 .await?;
2271 Ok(())
2272 }
2273
2274 #[instrument(skip(self, memory))]
2275 async fn path_unlink_file(
2276 &mut self,
2277 memory: &mut GuestMemory<'_>,
2278 dirfd: types::Fd,
2279 path: GuestPtr<str>,
2280 ) -> Result<(), types::Error> {
2281 let dirfd = self.get_dir_fd(dirfd)?;
2282 let path = memory.as_cow_str(path)?.into_owned();
2283 self.filesystem()
2284 .unlink_file_at(dirfd.borrowed(), path)
2285 .await?;
2286 Ok(())
2287 }
2288
2289 #[instrument(skip(self, memory))]
2290 async fn poll_oneoff(
2291 &mut self,
2292 memory: &mut GuestMemory<'_>,
2293 subs: GuestPtr<types::Subscription>,
2294 events: GuestPtr<types::Event>,
2295 nsubscriptions: types::Size,
2296 ) -> Result<types::Size, types::Error> {
2297 if nsubscriptions == 0 {
2298 return Err(types::Errno::Inval.into());
2300 }
2301
2302 if nsubscriptions == 1 {
2308 let sub = memory.read(subs)?;
2309 if let types::SubscriptionU::Clock(clocksub) = sub.u {
2310 if !clocksub
2311 .flags
2312 .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
2313 && self.wasi.filesystem.allow_blocking_current_thread
2314 {
2315 std::thread::sleep(std::time::Duration::from_nanos(clocksub.timeout));
2316 memory.write(
2317 events,
2318 types::Event {
2319 userdata: sub.userdata,
2320 error: types::Errno::Success,
2321 type_: types::Eventtype::Clock,
2322 fd_readwrite: types::EventFdReadwrite {
2323 flags: types::Eventrwflags::empty(),
2324 nbytes: 1,
2325 },
2326 },
2327 )?;
2328 return Ok(1);
2329 }
2330 }
2331 }
2332
2333 let subs = subs.as_array(nsubscriptions);
2334 let events = events.as_array(nsubscriptions);
2335
2336 let n = usize::try_from(nsubscriptions).unwrap_or(usize::MAX);
2337 let mut pollables = Vec::with_capacity(n);
2338 for sub in subs.iter() {
2339 let sub = memory.read(sub?)?;
2340 let p = match sub.u {
2341 types::SubscriptionU::Clock(types::SubscriptionClock {
2342 id,
2343 timeout,
2344 flags,
2345 ..
2346 }) => {
2347 let absolute = flags.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME);
2348 let (timeout, absolute) = match id {
2349 types::Clockid::Monotonic => (timeout, absolute),
2350 types::Clockid::Realtime if !absolute => (timeout, false),
2351 types::Clockid::Realtime => {
2352 let now = wall_clock::Host::now(&mut self.clocks())
2353 .context("failed to call `wall_clock::now`")
2354 .map_err(types::Error::trap)?;
2355
2356 let seconds = timeout / 1_000_000_000;
2358 let nanoseconds = timeout % 1_000_000_000;
2359
2360 let timeout = if now.seconds < seconds
2361 || now.seconds == seconds
2362 && u64::from(now.nanoseconds) < nanoseconds
2363 {
2364 now.seconds * 1_000_000_000 + u64::from(now.nanoseconds) - timeout
2367 } else {
2368 0
2369 };
2370 (timeout, false)
2371 }
2372 _ => return Err(types::Errno::Inval.into()),
2373 };
2374 if absolute {
2375 monotonic_clock::Host::subscribe_instant(&mut self.clocks(), timeout)
2376 .context("failed to call `monotonic_clock::subscribe_instant`")
2377 .map_err(types::Error::trap)?
2378 } else {
2379 monotonic_clock::Host::subscribe_duration(&mut self.clocks(), timeout)
2380 .context("failed to call `monotonic_clock::subscribe_duration`")
2381 .map_err(types::Error::trap)?
2382 }
2383 }
2384 types::SubscriptionU::FdRead(types::SubscriptionFdReadwrite {
2385 file_descriptor,
2386 }) => {
2387 let stream = {
2388 let t = self.transact()?;
2389 let desc = t.get_descriptor(file_descriptor)?;
2390 match desc {
2391 Descriptor::Stdin { stream, .. } => stream.borrowed(),
2392 Descriptor::File(File { fd, position, .. }) => {
2393 let pos = position.load(Ordering::Relaxed);
2394 let fd = fd.borrowed();
2395 drop(t);
2396 self.filesystem().read_via_stream(fd, pos)?
2397 }
2398 _ => return Err(types::Errno::Badf.into()),
2400 }
2401 };
2402 streams::HostInputStream::subscribe(&mut self.table, stream)
2403 .context("failed to call `subscribe` on `input-stream`")
2404 .map_err(types::Error::trap)?
2405 }
2406 types::SubscriptionU::FdWrite(types::SubscriptionFdReadwrite {
2407 file_descriptor,
2408 }) => {
2409 let stream = {
2410 let t = self.transact()?;
2411 let desc = t.get_descriptor(file_descriptor)?;
2412 match desc {
2413 Descriptor::Stdout { stream, .. }
2414 | Descriptor::Stderr { stream, .. } => stream.borrowed(),
2415 Descriptor::File(File {
2416 fd,
2417 position,
2418 append,
2419 ..
2420 }) => {
2421 let fd = fd.borrowed();
2422 let position = position.clone();
2423 let append = *append;
2424 drop(t);
2425 if append {
2426 self.filesystem().append_via_stream(fd)?
2427 } else {
2428 let pos = position.load(Ordering::Relaxed);
2429 self.filesystem().write_via_stream(fd, pos)?
2430 }
2431 }
2432 _ => return Err(types::Errno::Badf.into()),
2434 }
2435 };
2436 streams::HostOutputStream::subscribe(&mut self.table, stream)
2437 .context("failed to call `subscribe` on `output-stream`")
2438 .map_err(types::Error::trap)?
2439 }
2440 };
2441 pollables.push(p);
2442 }
2443 let ready: HashSet<_> = self
2444 .table
2445 .poll(pollables)
2446 .await
2447 .context("failed to call `poll-oneoff`")
2448 .map_err(types::Error::trap)?
2449 .into_iter()
2450 .collect();
2451
2452 let mut count: types::Size = 0;
2453 for (sub, event) in (0..)
2454 .zip(subs.iter())
2455 .filter_map(|(idx, sub)| ready.contains(&idx).then_some(sub))
2456 .zip(events.iter())
2457 {
2458 let sub = memory.read(sub?)?;
2459 let event = event?;
2460 let e = match sub.u {
2461 types::SubscriptionU::Clock(..) => types::Event {
2462 userdata: sub.userdata,
2463 error: types::Errno::Success,
2464 type_: types::Eventtype::Clock,
2465 fd_readwrite: types::EventFdReadwrite {
2466 flags: types::Eventrwflags::empty(),
2467 nbytes: 0,
2468 },
2469 },
2470 types::SubscriptionU::FdRead(types::SubscriptionFdReadwrite {
2471 file_descriptor,
2472 }) => {
2473 let t = self.transact()?;
2474 let desc = t.get_descriptor(file_descriptor)?;
2475 match desc {
2476 Descriptor::Stdin { .. } => types::Event {
2477 userdata: sub.userdata,
2478 error: types::Errno::Success,
2479 type_: types::Eventtype::FdRead,
2480 fd_readwrite: types::EventFdReadwrite {
2481 flags: types::Eventrwflags::empty(),
2482 nbytes: 1,
2483 },
2484 },
2485 Descriptor::File(File { fd, position, .. }) => {
2486 let fd = fd.borrowed();
2487 let position = position.clone();
2488 drop(t);
2489 match self.filesystem().stat(fd).await? {
2490 filesystem::DescriptorStat { size, .. } => {
2491 let pos = position.load(Ordering::Relaxed);
2492 let nbytes = size.saturating_sub(pos);
2493 types::Event {
2494 userdata: sub.userdata,
2495 error: types::Errno::Success,
2496 type_: types::Eventtype::FdRead,
2497 fd_readwrite: types::EventFdReadwrite {
2498 flags: if nbytes == 0 {
2499 types::Eventrwflags::FD_READWRITE_HANGUP
2500 } else {
2501 types::Eventrwflags::empty()
2502 },
2503 nbytes: 1,
2504 },
2505 }
2506 }
2507 }
2508 }
2509 _ => return Err(types::Errno::Badf.into()),
2511 }
2512 }
2513 types::SubscriptionU::FdWrite(types::SubscriptionFdReadwrite {
2514 file_descriptor,
2515 }) => {
2516 let t = self.transact()?;
2517 let desc = t.get_descriptor(file_descriptor)?;
2518 match desc {
2519 Descriptor::Stdout { .. } | Descriptor::Stderr { .. } => types::Event {
2520 userdata: sub.userdata,
2521 error: types::Errno::Success,
2522 type_: types::Eventtype::FdWrite,
2523 fd_readwrite: types::EventFdReadwrite {
2524 flags: types::Eventrwflags::empty(),
2525 nbytes: 1,
2526 },
2527 },
2528 Descriptor::File(_) => types::Event {
2529 userdata: sub.userdata,
2530 error: types::Errno::Success,
2531 type_: types::Eventtype::FdWrite,
2532 fd_readwrite: types::EventFdReadwrite {
2533 flags: types::Eventrwflags::empty(),
2534 nbytes: 1,
2535 },
2536 },
2537 _ => return Err(types::Errno::Badf.into()),
2539 }
2540 }
2541 };
2542 memory.write(event, e)?;
2543 count = count
2544 .checked_add(1)
2545 .ok_or_else(|| types::Error::from(types::Errno::Overflow))?
2546 }
2547 Ok(count)
2548 }
2549
2550 #[instrument(skip(self, _memory))]
2551 fn proc_exit(
2552 &mut self,
2553 _memory: &mut GuestMemory<'_>,
2554 status: types::Exitcode,
2555 ) -> anyhow::Error {
2556 if status >= 126 {
2558 return anyhow::Error::msg("exit with invalid exit status outside of [0..126)");
2559 }
2560 crate::I32Exit(status as i32).into()
2561 }
2562
2563 #[instrument(skip(self, _memory))]
2564 fn proc_raise(
2565 &mut self,
2566 _memory: &mut GuestMemory<'_>,
2567 _sig: types::Signal,
2568 ) -> Result<(), types::Error> {
2569 Err(types::Errno::Notsup.into())
2570 }
2571
2572 #[instrument(skip(self, _memory))]
2573 fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), types::Error> {
2574 Ok(())
2576 }
2577
2578 #[instrument(skip(self, memory))]
2579 fn random_get(
2580 &mut self,
2581 memory: &mut GuestMemory<'_>,
2582 buf: GuestPtr<u8>,
2583 buf_len: types::Size,
2584 ) -> Result<(), types::Error> {
2585 let rand = self
2586 .wasi
2587 .random
2588 .get_random_bytes(buf_len.into())
2589 .context("failed to call `get-random-bytes`")
2590 .map_err(types::Error::trap)?;
2591 write_bytes(memory, buf, &rand)?;
2592 Ok(())
2593 }
2594
2595 #[instrument(skip(self, _memory))]
2596 fn sock_accept(
2597 &mut self,
2598 _memory: &mut GuestMemory<'_>,
2599 fd: types::Fd,
2600 flags: types::Fdflags,
2601 ) -> Result<types::Fd, types::Error> {
2602 tracing::warn!("p1 sock_accept is not implemented");
2603 self.transact()?.get_descriptor(fd)?;
2604 Err(types::Errno::Notsock.into())
2605 }
2606
2607 #[instrument(skip(self, _memory))]
2608 fn sock_recv(
2609 &mut self,
2610 _memory: &mut GuestMemory<'_>,
2611 fd: types::Fd,
2612 ri_data: types::IovecArray,
2613 ri_flags: types::Riflags,
2614 ) -> Result<(types::Size, types::Roflags), types::Error> {
2615 tracing::warn!("p1 sock_recv is not implemented");
2616 self.transact()?.get_descriptor(fd)?;
2617 Err(types::Errno::Notsock.into())
2618 }
2619
2620 #[instrument(skip(self, _memory))]
2621 fn sock_send(
2622 &mut self,
2623 _memory: &mut GuestMemory<'_>,
2624 fd: types::Fd,
2625 si_data: types::CiovecArray,
2626 _si_flags: types::Siflags,
2627 ) -> Result<types::Size, types::Error> {
2628 tracing::warn!("p1 sock_send is not implemented");
2629 self.transact()?.get_descriptor(fd)?;
2630 Err(types::Errno::Notsock.into())
2631 }
2632
2633 #[instrument(skip(self, _memory))]
2634 fn sock_shutdown(
2635 &mut self,
2636 _memory: &mut GuestMemory<'_>,
2637 fd: types::Fd,
2638 how: types::Sdflags,
2639 ) -> Result<(), types::Error> {
2640 tracing::warn!("p1 sock_shutdown is not implemented");
2641 self.transact()?.get_descriptor(fd)?;
2642 Err(types::Errno::Notsock.into())
2643 }
2644}
2645
2646trait ResourceExt<T> {
2647 fn borrowed(&self) -> Resource<T>;
2648}
2649
2650impl<T: 'static> ResourceExt<T> for Resource<T> {
2651 fn borrowed(&self) -> Resource<T> {
2652 Resource::new_borrow(self.rep())
2653 }
2654}