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