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