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