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