1use crate::{
2 I32Exit, SystemTimeSpec, WasiCtx,
3 dir::{DirEntry, OpenResult, ReaddirCursor, ReaddirEntity, TableDirExt},
4 file::{
5 Advice, FdFlags, FdStat, FileAccessMode, FileEntry, FileType, Filestat, OFlags, RiFlags,
6 RoFlags, SdFlags, SiFlags, TableFileExt, WasiFile,
7 },
8 sched::{
9 Poll, Userdata,
10 subscription::{RwEventFlags, SubscriptionResult},
11 },
12};
13use cap_std::time::{Duration, SystemClock};
14use std::borrow::Cow;
15use std::io::{IoSlice, IoSliceMut};
16use std::ops::Deref;
17use std::sync::Arc;
18use wiggle::GuestMemory;
19use wiggle::GuestPtr;
20
21pub mod error;
22use error::{Error, ErrorExt};
23
24pub(crate) const MAX_SHARED_BUFFER_SIZE: usize = 1 << 16;
27
28wiggle::from_witx!({
29 witx: ["witx/preview1/wasi_snapshot_preview1.witx"],
30 errors: { errno => trappable Error },
31 async: *,
35 wasmtime: false,
36});
37
38impl wiggle::GuestErrorType for types::Errno {
39 fn success() -> Self {
40 Self::Success
41 }
42}
43
44impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
45 async fn args_get(
46 &mut self,
47 memory: &mut GuestMemory<'_>,
48 argv: GuestPtr<GuestPtr<u8>>,
49 argv_buf: GuestPtr<u8>,
50 ) -> Result<(), Error> {
51 self.args.write_to_guest(memory, argv_buf, argv)
52 }
53
54 async fn args_sizes_get(
55 &mut self,
56 _memory: &mut GuestMemory<'_>,
57 ) -> Result<(types::Size, types::Size), Error> {
58 Ok((self.args.number_elements(), self.args.cumulative_size()))
59 }
60
61 async fn environ_get(
62 &mut self,
63 memory: &mut GuestMemory<'_>,
64 environ: GuestPtr<GuestPtr<u8>>,
65 environ_buf: GuestPtr<u8>,
66 ) -> Result<(), Error> {
67 self.env.write_to_guest(memory, environ_buf, environ)
68 }
69
70 async fn environ_sizes_get(
71 &mut self,
72 _memory: &mut GuestMemory<'_>,
73 ) -> Result<(types::Size, types::Size), Error> {
74 Ok((self.env.number_elements(), self.env.cumulative_size()))
75 }
76
77 async fn clock_res_get(
78 &mut self,
79 _memory: &mut GuestMemory<'_>,
80 id: types::Clockid,
81 ) -> Result<types::Timestamp, Error> {
82 let resolution = match id {
83 types::Clockid::Realtime => Ok(self.clocks.system()?.resolution()),
84 types::Clockid::Monotonic => Ok(self.clocks.monotonic()?.abs_clock.resolution()),
85 types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
86 Err(Error::badf().context("process and thread clocks are not supported"))
87 }
88 }?;
89 Ok(resolution.as_nanos().try_into()?)
90 }
91
92 async fn clock_time_get(
93 &mut self,
94 _memory: &mut GuestMemory<'_>,
95 id: types::Clockid,
96 precision: types::Timestamp,
97 ) -> Result<types::Timestamp, Error> {
98 let precision = Duration::from_nanos(precision);
99 match id {
100 types::Clockid::Realtime => {
101 let now = self.clocks.system()?.now(precision).into_std();
102 let d = now
103 .duration_since(std::time::SystemTime::UNIX_EPOCH)
104 .map_err(|_| {
105 Error::trap(anyhow::Error::msg("current time before unix epoch"))
106 })?;
107 Ok(d.as_nanos().try_into()?)
108 }
109 types::Clockid::Monotonic => {
110 let clock = self.clocks.monotonic()?;
111 let now = clock.abs_clock.now(precision);
112 let d = now.duration_since(clock.creation_time);
113 Ok(d.as_nanos().try_into()?)
114 }
115 types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
116 Err(Error::badf().context("process and thread clocks are not supported"))
117 }
118 }
119 }
120
121 async fn fd_advise(
122 &mut self,
123 _memory: &mut GuestMemory<'_>,
124 fd: types::Fd,
125 offset: types::Filesize,
126 len: types::Filesize,
127 advice: types::Advice,
128 ) -> Result<(), Error> {
129 self.table()
130 .get_file(u32::from(fd))?
131 .file
132 .advise(offset, len, advice.into())
133 .await?;
134 Ok(())
135 }
136
137 async fn fd_allocate(
138 &mut self,
139 _memory: &mut GuestMemory<'_>,
140 fd: types::Fd,
141 _offset: types::Filesize,
142 _len: types::Filesize,
143 ) -> Result<(), Error> {
144 let _ = self.table().get_file(u32::from(fd))?;
147 Err(Error::not_supported())
153 }
154
155 async fn fd_close(
156 &mut self,
157 _memory: &mut GuestMemory<'_>,
158 fd: types::Fd,
159 ) -> Result<(), Error> {
160 let table = self.table();
161 let fd = u32::from(fd);
162
163 if !table.contains_key(fd) {
165 return Err(Error::badf().context("key not in table"));
166 }
167 if table.is::<FileEntry>(fd) {
169 let _ = table.delete::<FileEntry>(fd);
170 } else if table.is::<DirEntry>(fd) {
171 let _ = table.delete::<DirEntry>(fd);
172 } else {
173 return Err(Error::badf().context("key does not refer to file or directory"));
174 }
175
176 Ok(())
177 }
178
179 async fn fd_datasync(
180 &mut self,
181 _memory: &mut GuestMemory<'_>,
182 fd: types::Fd,
183 ) -> Result<(), Error> {
184 self.table()
185 .get_file(u32::from(fd))?
186 .file
187 .datasync()
188 .await?;
189 Ok(())
190 }
191
192 async fn fd_fdstat_get(
193 &mut self,
194 _memory: &mut GuestMemory<'_>,
195 fd: types::Fd,
196 ) -> Result<types::Fdstat, Error> {
197 let table = self.table();
198 let fd = u32::from(fd);
199 if table.is::<FileEntry>(fd) {
200 let file_entry: Arc<FileEntry> = table.get(fd)?;
201 let fdstat = file_entry.get_fdstat().await?;
202 Ok(types::Fdstat::from(&fdstat))
203 } else if table.is::<DirEntry>(fd) {
204 let _dir_entry: Arc<DirEntry> = table.get(fd)?;
205 let dir_fdstat = types::Fdstat {
206 fs_filetype: types::Filetype::Directory,
207 fs_rights_base: directory_base_rights(),
208 fs_rights_inheriting: directory_inheriting_rights(),
209 fs_flags: types::Fdflags::empty(),
210 };
211 Ok(dir_fdstat)
212 } else {
213 Err(Error::badf())
214 }
215 }
216
217 async fn fd_fdstat_set_flags(
218 &mut self,
219 _memory: &mut GuestMemory<'_>,
220 fd: types::Fd,
221 flags: types::Fdflags,
222 ) -> Result<(), Error> {
223 if let Some(table) = self.table_mut() {
224 table
225 .get_file_mut(u32::from(fd))?
226 .file
227 .set_fdflags(FdFlags::from(flags))
228 .await
229 } else {
230 log::warn!(
231 "`fd_fdstat_set_flags` does not work with wasi-threads enabled; see https://github.com/bytecodealliance/wasmtime/issues/5643"
232 );
233 Err(Error::not_supported())
234 }
235 }
236
237 async fn fd_fdstat_set_rights(
238 &mut self,
239 _memory: &mut GuestMemory<'_>,
240 fd: types::Fd,
241 _fs_rights_base: types::Rights,
242 _fs_rights_inheriting: types::Rights,
243 ) -> Result<(), Error> {
244 let table = self.table();
245 let fd = u32::from(fd);
246 if table.is::<FileEntry>(fd) {
247 let _file_entry: Arc<FileEntry> = table.get(fd)?;
248 Err(Error::not_supported())
249 } else if table.is::<DirEntry>(fd) {
250 let _dir_entry: Arc<DirEntry> = table.get(fd)?;
251 Err(Error::not_supported())
252 } else {
253 Err(Error::badf())
254 }
255 }
256
257 async fn fd_filestat_get(
258 &mut self,
259 _memory: &mut GuestMemory<'_>,
260 fd: types::Fd,
261 ) -> Result<types::Filestat, Error> {
262 let table = self.table();
263 let fd = u32::from(fd);
264 if table.is::<FileEntry>(fd) {
265 let filestat = table.get_file(fd)?.file.get_filestat().await?;
266 Ok(filestat.into())
267 } else if table.is::<DirEntry>(fd) {
268 let filestat = table.get_dir(fd)?.dir.get_filestat().await?;
269 Ok(filestat.into())
270 } else {
271 Err(Error::badf())
272 }
273 }
274
275 async fn fd_filestat_set_size(
276 &mut self,
277 _memory: &mut GuestMemory<'_>,
278 fd: types::Fd,
279 size: types::Filesize,
280 ) -> Result<(), Error> {
281 self.table()
282 .get_file(u32::from(fd))?
283 .file
284 .set_filestat_size(size)
285 .await?;
286 Ok(())
287 }
288
289 async fn fd_filestat_set_times(
290 &mut self,
291 _memory: &mut GuestMemory<'_>,
292 fd: types::Fd,
293 atim: types::Timestamp,
294 mtim: types::Timestamp,
295 fst_flags: types::Fstflags,
296 ) -> Result<(), Error> {
297 let fd = u32::from(fd);
298 let table = self.table();
299 let set_atim = fst_flags.contains(types::Fstflags::ATIM);
301 let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);
302 let set_mtim = fst_flags.contains(types::Fstflags::MTIM);
303 let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);
304
305 let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;
306 let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;
307
308 if table.is::<FileEntry>(fd) {
309 table
310 .get_file(fd)
311 .expect("checked that entry is file")
312 .file
313 .set_times(atim, mtim)
314 .await
315 } else if table.is::<DirEntry>(fd) {
316 table
317 .get_dir(fd)
318 .expect("checked that entry is dir")
319 .dir
320 .set_times(".", atim, mtim, false)
321 .await
322 } else {
323 Err(Error::badf())
324 }
325 }
326
327 async fn fd_read(
328 &mut self,
329 memory: &mut GuestMemory<'_>,
330 fd: types::Fd,
331 iovs: types::IovecArray,
332 ) -> Result<types::Size, Error> {
333 let f = self.table().get_file(u32::from(fd))?;
334 if !f.access_mode.contains(FileAccessMode::READ) {
336 Err(types::Errno::Badf)?
337 }
338 let f = &f.file;
339
340 let iovs: Vec<wiggle::GuestPtr<[u8]>> = iovs
341 .iter()
342 .map(|iov_ptr| {
343 let iov_ptr = iov_ptr?;
344 let iov: types::Iovec = memory.read(iov_ptr)?;
345 Ok(iov.buf.as_array(iov.buf_len))
346 })
347 .collect::<Result<_, Error>>()?;
348
349 let is_shared_memory = memory.is_shared_memory();
362 let bytes_read: u64 = if is_shared_memory {
363 let iov = iovs.into_iter().next();
367 if let Some(iov) = iov {
368 let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];
369 let bytes_read = f.read_vectored(&mut [IoSliceMut::new(&mut buffer)]).await?;
370 let iov = iov
371 .get_range(0..bytes_read.try_into()?)
372 .expect("it should always be possible to slice the iov smaller");
373 memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;
374 bytes_read
375 } else {
376 return Ok(0);
377 }
378 } else {
379 let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {
386 Some(iov) => memory.as_slice_mut(iov)?.unwrap(),
387 None => return Ok(0),
388 };
389
390 f.read_vectored(&mut [IoSliceMut::new(guest_slice)]).await?
392 };
393
394 Ok(types::Size::try_from(bytes_read)?)
395 }
396
397 async fn fd_pread(
398 &mut self,
399 memory: &mut GuestMemory<'_>,
400 fd: types::Fd,
401 iovs: types::IovecArray,
402 offset: types::Filesize,
403 ) -> Result<types::Size, Error> {
404 let f = self.table().get_file(u32::from(fd))?;
405 if !f.access_mode.contains(FileAccessMode::READ) {
407 Err(types::Errno::Badf)?
408 }
409 let f = &f.file;
410
411 let iovs: Vec<wiggle::GuestPtr<[u8]>> = iovs
412 .iter()
413 .map(|iov_ptr| {
414 let iov_ptr = iov_ptr?;
415 let iov: types::Iovec = memory.read(iov_ptr)?;
416 Ok(iov.buf.as_array(iov.buf_len))
417 })
418 .collect::<Result<_, Error>>()?;
419
420 let is_shared_memory = memory.is_shared_memory();
433 let bytes_read: u64 = if is_shared_memory {
434 let iov = iovs.into_iter().next();
438 if let Some(iov) = iov {
439 let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];
440 let bytes_read = f
441 .read_vectored_at(&mut [IoSliceMut::new(&mut buffer)], offset)
442 .await?;
443 let iov = iov
444 .get_range(0..bytes_read.try_into()?)
445 .expect("it should always be possible to slice the iov smaller");
446 memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;
447 bytes_read
448 } else {
449 return Ok(0);
450 }
451 } else {
452 let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {
454 Some(iov) => memory.as_slice_mut(iov)?.unwrap(),
455 None => return Ok(0),
456 };
457
458 f.read_vectored_at(&mut [IoSliceMut::new(guest_slice)], offset)
460 .await?
461 };
462
463 Ok(types::Size::try_from(bytes_read)?)
464 }
465
466 async fn fd_write(
467 &mut self,
468 memory: &mut GuestMemory<'_>,
469 fd: types::Fd,
470 ciovs: types::CiovecArray,
471 ) -> Result<types::Size, Error> {
472 let f = self.table().get_file(u32::from(fd))?;
473 if !f.access_mode.contains(FileAccessMode::WRITE) {
475 Err(types::Errno::Badf)?
476 }
477 let f = &f.file;
478
479 let guest_slices: Vec<Cow<[u8]>> = ciovs
480 .iter()
481 .map(|iov_ptr| {
482 let iov_ptr = iov_ptr?;
483 let iov: types::Ciovec = memory.read(iov_ptr)?;
484 Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)
485 })
486 .collect::<Result<_, Error>>()?;
487
488 let ioslices: Vec<IoSlice> = guest_slices
489 .iter()
490 .map(|s| IoSlice::new(s.deref()))
491 .collect();
492 let bytes_written = f.write_vectored(&ioslices).await?;
493
494 Ok(types::Size::try_from(bytes_written)?)
495 }
496
497 async fn fd_pwrite(
498 &mut self,
499 memory: &mut GuestMemory<'_>,
500 fd: types::Fd,
501 ciovs: types::CiovecArray,
502 offset: types::Filesize,
503 ) -> Result<types::Size, Error> {
504 let f = self.table().get_file(u32::from(fd))?;
505 if !f.access_mode.contains(FileAccessMode::WRITE) {
507 Err(types::Errno::Badf)?
508 }
509 let f = &f.file;
510
511 let guest_slices: Vec<Cow<[u8]>> = ciovs
512 .iter()
513 .map(|iov_ptr| {
514 let iov_ptr = iov_ptr?;
515 let iov: types::Ciovec = memory.read(iov_ptr)?;
516 Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)
517 })
518 .collect::<Result<_, Error>>()?;
519
520 let ioslices: Vec<IoSlice> = guest_slices
521 .iter()
522 .map(|s| IoSlice::new(s.deref()))
523 .collect();
524 let bytes_written = f.write_vectored_at(&ioslices, offset).await?;
525
526 Ok(types::Size::try_from(bytes_written)?)
527 }
528
529 async fn fd_prestat_get(
530 &mut self,
531 _memory: &mut GuestMemory<'_>,
532 fd: types::Fd,
533 ) -> Result<types::Prestat, Error> {
534 let table = self.table();
535 let dir_entry: Arc<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::badf())?;
536 if let Some(preopen) = dir_entry.preopen_path() {
537 let path_str = preopen.to_str().ok_or_else(|| Error::not_supported())?;
538 let pr_name_len = u32::try_from(path_str.as_bytes().len())?;
539 Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len }))
540 } else {
541 Err(Error::not_supported().context("file is not a preopen"))
542 }
543 }
544
545 async fn fd_prestat_dir_name(
546 &mut self,
547 memory: &mut GuestMemory<'_>,
548 fd: types::Fd,
549 path: GuestPtr<u8>,
550 path_max_len: types::Size,
551 ) -> Result<(), Error> {
552 let table = self.table();
553 let dir_entry: Arc<DirEntry> = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?;
554 if let Some(preopen) = dir_entry.preopen_path() {
555 let path_bytes = preopen
556 .to_str()
557 .ok_or_else(|| Error::not_supported())?
558 .as_bytes();
559 let path_len = path_bytes.len();
560 if path_len > path_max_len as usize {
561 return Err(Error::name_too_long());
562 }
563 let path = path.as_array(path_len as u32);
564 memory.copy_from_slice(path_bytes, path)?;
565 Ok(())
566 } else {
567 Err(Error::not_supported())
568 }
569 }
570 async fn fd_renumber(
571 &mut self,
572 _memory: &mut GuestMemory<'_>,
573 from: types::Fd,
574 to: types::Fd,
575 ) -> Result<(), Error> {
576 let table = self.table();
577 let from = u32::from(from);
578 let to = u32::from(to);
579 if !table.contains_key(from) {
580 return Err(Error::badf());
581 }
582 if !table.contains_key(to) {
583 return Err(Error::badf());
584 }
585 table.renumber(from, to)
586 }
587
588 async fn fd_seek(
589 &mut self,
590 _memory: &mut GuestMemory<'_>,
591 fd: types::Fd,
592 offset: types::Filedelta,
593 whence: types::Whence,
594 ) -> Result<types::Filesize, Error> {
595 use std::io::SeekFrom;
596 let whence = match whence {
597 types::Whence::Cur => SeekFrom::Current(offset),
598 types::Whence::End => SeekFrom::End(offset),
599 types::Whence::Set => {
600 SeekFrom::Start(offset.try_into().map_err(|_| Error::invalid_argument())?)
601 }
602 };
603 let newoffset = self
604 .table()
605 .get_file(u32::from(fd))?
606 .file
607 .seek(whence)
608 .await?;
609 Ok(newoffset)
610 }
611
612 async fn fd_sync(&mut self, _memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> {
613 self.table().get_file(u32::from(fd))?.file.sync().await?;
614 Ok(())
615 }
616
617 async fn fd_tell(
618 &mut self,
619 _memory: &mut GuestMemory<'_>,
620 fd: types::Fd,
621 ) -> Result<types::Filesize, Error> {
622 let offset = self
623 .table()
624 .get_file(u32::from(fd))?
625 .file
626 .seek(std::io::SeekFrom::Current(0))
627 .await?;
628 Ok(offset)
629 }
630
631 async fn fd_readdir(
632 &mut self,
633 memory: &mut GuestMemory<'_>,
634 fd: types::Fd,
635 mut buf: GuestPtr<u8>,
636 buf_len: types::Size,
637 cookie: types::Dircookie,
638 ) -> Result<types::Size, Error> {
639 let mut bufused = 0;
640 for entity in self
641 .table()
642 .get_dir(u32::from(fd))?
643 .dir
644 .readdir(ReaddirCursor::from(cookie))
645 .await?
646 {
647 let entity = entity?;
648 let dirent_raw = dirent_bytes(types::Dirent::try_from(&entity)?);
649 let dirent_len: types::Size = dirent_raw.len().try_into()?;
650 let name_raw = entity.name.as_bytes();
651 let name_len: types::Size = name_raw.len().try_into()?;
652
653 let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused);
655 let raw = buf.as_array(dirent_copy_len);
656 memory.copy_from_slice(&dirent_raw[..dirent_copy_len as usize], raw)?;
657
658 if dirent_copy_len < dirent_len {
661 return Ok(buf_len);
662 }
663
664 buf = buf.add(dirent_copy_len)?;
665 bufused += dirent_copy_len;
666
667 let name_copy_len = std::cmp::min(name_len, buf_len - bufused);
669 let raw = buf.as_array(name_copy_len);
670 memory.copy_from_slice(&name_raw[..name_copy_len as usize], raw)?;
671
672 if name_copy_len < name_len {
676 return Ok(buf_len);
677 }
678
679 buf = buf.add(name_copy_len)?;
680 bufused += name_copy_len;
681 }
682 Ok(bufused)
683 }
684
685 async fn path_create_directory(
686 &mut self,
687 memory: &mut GuestMemory<'_>,
688 dirfd: types::Fd,
689 path: GuestPtr<str>,
690 ) -> Result<(), Error> {
691 self.table()
692 .get_dir(u32::from(dirfd))?
693 .dir
694 .create_dir(memory.as_cow_str(path)?.deref())
695 .await
696 }
697
698 async fn path_filestat_get(
699 &mut self,
700 memory: &mut GuestMemory<'_>,
701 dirfd: types::Fd,
702 flags: types::Lookupflags,
703 path: GuestPtr<str>,
704 ) -> Result<types::Filestat, Error> {
705 let filestat = self
706 .table()
707 .get_dir(u32::from(dirfd))?
708 .dir
709 .get_path_filestat(
710 memory.as_cow_str(path)?.deref(),
711 flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
712 )
713 .await?;
714 Ok(types::Filestat::from(filestat))
715 }
716
717 async fn path_filestat_set_times(
718 &mut self,
719 memory: &mut GuestMemory<'_>,
720 dirfd: types::Fd,
721 flags: types::Lookupflags,
722 path: GuestPtr<str>,
723 atim: types::Timestamp,
724 mtim: types::Timestamp,
725 fst_flags: types::Fstflags,
726 ) -> Result<(), Error> {
727 let set_atim = fst_flags.contains(types::Fstflags::ATIM);
728 let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW);
729 let set_mtim = fst_flags.contains(types::Fstflags::MTIM);
730 let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW);
731
732 let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?;
733 let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?;
734 self.table()
735 .get_dir(u32::from(dirfd))?
736 .dir
737 .set_times(
738 memory.as_cow_str(path)?.deref(),
739 atim,
740 mtim,
741 flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
742 )
743 .await
744 }
745
746 async fn path_link(
747 &mut self,
748 memory: &mut GuestMemory<'_>,
749 src_fd: types::Fd,
750 src_flags: types::Lookupflags,
751 src_path: GuestPtr<str>,
752 target_fd: types::Fd,
753 target_path: GuestPtr<str>,
754 ) -> Result<(), Error> {
755 let table = self.table();
756 let src_dir = table.get_dir(u32::from(src_fd))?;
757 let target_dir = table.get_dir(u32::from(target_fd))?;
758 let symlink_follow = src_flags.contains(types::Lookupflags::SYMLINK_FOLLOW);
759 if symlink_follow {
760 return Err(Error::invalid_argument()
761 .context("symlink following on path_link is not supported"));
762 }
763
764 src_dir
765 .dir
766 .hard_link(
767 memory.as_cow_str(src_path)?.deref(),
768 target_dir.dir.deref(),
769 memory.as_cow_str(target_path)?.deref(),
770 )
771 .await
772 }
773
774 async fn path_open(
775 &mut self,
776 memory: &mut GuestMemory<'_>,
777 dirfd: types::Fd,
778 dirflags: types::Lookupflags,
779 path: GuestPtr<str>,
780 oflags: types::Oflags,
781 fs_rights_base: types::Rights,
782 _fs_rights_inheriting: types::Rights,
783 fdflags: types::Fdflags,
784 ) -> Result<types::Fd, Error> {
785 let table = self.table();
786 let dirfd = u32::from(dirfd);
787 if table.is::<FileEntry>(dirfd) {
788 return Err(Error::not_dir());
789 }
790 let dir_entry = table.get_dir(dirfd)?;
791
792 let symlink_follow = dirflags.contains(types::Lookupflags::SYMLINK_FOLLOW);
793
794 let oflags = OFlags::from(&oflags);
795 let fdflags = FdFlags::from(fdflags);
796 let path = memory.as_cow_str(path)?;
797
798 let read = fs_rights_base.contains(types::Rights::FD_READ);
799 let write = fs_rights_base.contains(types::Rights::FD_WRITE);
800 let access_mode = if read {
801 FileAccessMode::READ
802 } else {
803 FileAccessMode::empty()
804 } | if write {
805 FileAccessMode::WRITE
806 } else {
807 FileAccessMode::empty()
808 };
809
810 let file = dir_entry
811 .dir
812 .open_file(symlink_follow, path.deref(), oflags, read, write, fdflags)
813 .await?;
814 drop(dir_entry);
815
816 let fd = match file {
817 OpenResult::File(file) => table.push(Arc::new(FileEntry::new(file, access_mode)))?,
818 OpenResult::Dir(child_dir) => table.push(Arc::new(DirEntry::new(None, child_dir)))?,
819 };
820 Ok(types::Fd::from(fd))
821 }
822
823 async fn path_readlink(
824 &mut self,
825 memory: &mut GuestMemory<'_>,
826 dirfd: types::Fd,
827 path: GuestPtr<str>,
828 buf: GuestPtr<u8>,
829 buf_len: types::Size,
830 ) -> Result<types::Size, Error> {
831 let link = self
832 .table()
833 .get_dir(u32::from(dirfd))?
834 .dir
835 .read_link(memory.as_cow_str(path)?.deref())
836 .await?
837 .into_os_string()
838 .into_string()
839 .map_err(|_| Error::illegal_byte_sequence().context("link contents"))?;
840 let link_bytes = link.as_bytes();
841 let link_len = std::cmp::min(link_bytes.len(), buf_len as usize);
844 let buf = buf.as_array(link_len as u32);
845 memory.copy_from_slice(&link_bytes[..link_len], buf)?;
846 Ok(link_len as types::Size)
847 }
848
849 async fn path_remove_directory(
850 &mut self,
851 memory: &mut GuestMemory<'_>,
852 dirfd: types::Fd,
853 path: GuestPtr<str>,
854 ) -> Result<(), Error> {
855 self.table()
856 .get_dir(u32::from(dirfd))?
857 .dir
858 .remove_dir(memory.as_cow_str(path)?.deref())
859 .await
860 }
861
862 async fn path_rename(
863 &mut self,
864 memory: &mut GuestMemory<'_>,
865 src_fd: types::Fd,
866 src_path: GuestPtr<str>,
867 dest_fd: types::Fd,
868 dest_path: GuestPtr<str>,
869 ) -> Result<(), Error> {
870 let table = self.table();
871 let src_dir = table.get_dir(u32::from(src_fd))?;
872 let dest_dir = table.get_dir(u32::from(dest_fd))?;
873 src_dir
874 .dir
875 .rename(
876 memory.as_cow_str(src_path)?.deref(),
877 dest_dir.dir.deref(),
878 memory.as_cow_str(dest_path)?.deref(),
879 )
880 .await
881 }
882
883 async fn path_symlink(
884 &mut self,
885 memory: &mut GuestMemory<'_>,
886 src_path: GuestPtr<str>,
887 dirfd: types::Fd,
888 dest_path: GuestPtr<str>,
889 ) -> Result<(), Error> {
890 self.table()
891 .get_dir(u32::from(dirfd))?
892 .dir
893 .symlink(
894 memory.as_cow_str(src_path)?.deref(),
895 memory.as_cow_str(dest_path)?.deref(),
896 )
897 .await
898 }
899
900 async fn path_unlink_file(
901 &mut self,
902 memory: &mut GuestMemory<'_>,
903 dirfd: types::Fd,
904 path: GuestPtr<str>,
905 ) -> Result<(), Error> {
906 self.table()
907 .get_dir(u32::from(dirfd))?
908 .dir
909 .unlink_file(memory.as_cow_str(path)?.deref())
910 .await
911 }
912
913 async fn poll_oneoff(
914 &mut self,
915 memory: &mut GuestMemory<'_>,
916 subs: GuestPtr<types::Subscription>,
917 events: GuestPtr<types::Event>,
918 nsubscriptions: types::Size,
919 ) -> Result<types::Size, Error> {
920 if nsubscriptions == 0 {
921 return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));
922 }
923
924 if nsubscriptions == 1 {
929 let sub = memory.read(subs)?;
930 if let types::SubscriptionU::Clock(clocksub) = sub.u {
931 if !clocksub
932 .flags
933 .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
934 {
935 self.sched
936 .sleep(Duration::from_nanos(clocksub.timeout))
937 .await?;
938 memory.write(
939 events,
940 types::Event {
941 userdata: sub.userdata,
942 error: types::Errno::Success,
943 type_: types::Eventtype::Clock,
944 fd_readwrite: fd_readwrite_empty(),
945 },
946 )?;
947 return Ok(1);
948 }
949 }
950 }
951
952 let table = &self.table;
953 let mut read_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();
955 let mut write_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();
956
957 let mut poll = Poll::new();
958
959 let subs = subs.as_array(nsubscriptions);
960 for sub_elem in subs.iter() {
961 let sub_ptr = sub_elem?;
962 let sub = memory.read(sub_ptr)?;
963 match sub.u {
964 types::SubscriptionU::Clock(clocksub) => match clocksub.id {
965 types::Clockid::Monotonic => {
966 let clock = self.clocks.monotonic()?;
967 let precision = Duration::from_nanos(clocksub.precision);
968 let duration = Duration::from_nanos(clocksub.timeout);
969 let start = if clocksub
970 .flags
971 .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
972 {
973 clock.creation_time
974 } else {
975 clock.abs_clock.now(precision)
976 };
977 let deadline = start
978 .checked_add(duration)
979 .ok_or_else(|| Error::overflow().context("deadline"))?;
980 poll.subscribe_monotonic_clock(
981 &*clock.abs_clock,
982 deadline,
983 precision,
984 sub.userdata.into(),
985 )
986 }
987 types::Clockid::Realtime => {
988 let clock = self.clocks.monotonic()?;
994 let precision = Duration::from_nanos(clocksub.precision);
995 let duration = Duration::from_nanos(clocksub.timeout);
996 let deadline = if clocksub
997 .flags
998 .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
999 {
1000 return Err(Error::not_supported());
1001 } else {
1002 clock
1003 .abs_clock
1004 .now(precision)
1005 .checked_add(duration)
1006 .ok_or_else(|| Error::overflow().context("deadline"))?
1007 };
1008 poll.subscribe_monotonic_clock(
1009 &*clock.abs_clock,
1010 deadline,
1011 precision,
1012 sub.userdata.into(),
1013 )
1014 }
1015 _ => Err(Error::invalid_argument()
1016 .context("timer subscriptions only support monotonic timer"))?,
1017 },
1018 types::SubscriptionU::FdRead(readsub) => {
1019 let fd = readsub.file_descriptor;
1020 let file_ref = table.get_file(u32::from(fd))?;
1021 read_refs.push((file_ref, Some(sub.userdata.into())));
1022 }
1023 types::SubscriptionU::FdWrite(writesub) => {
1024 let fd = writesub.file_descriptor;
1025 let file_ref = table.get_file(u32::from(fd))?;
1026 write_refs.push((file_ref, Some(sub.userdata.into())));
1027 }
1028 }
1029 }
1030
1031 let mut read_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
1032 for (file_lock, userdata) in read_refs.iter_mut() {
1033 read_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));
1034 }
1035
1036 for (f, ud) in read_mut_refs.iter_mut() {
1037 poll.subscribe_read(*f, *ud);
1038 }
1039
1040 let mut write_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
1041 for (file_lock, userdata) in write_refs.iter_mut() {
1042 write_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));
1043 }
1044
1045 for (f, ud) in write_mut_refs.iter_mut() {
1046 poll.subscribe_write(*f, *ud);
1047 }
1048
1049 self.sched.poll_oneoff(&mut poll).await?;
1050
1051 let results = poll.results();
1052 let num_results = results.len();
1053 assert!(
1054 num_results <= nsubscriptions as usize,
1055 "results exceeds subscriptions"
1056 );
1057 let events = events.as_array(
1058 num_results
1059 .try_into()
1060 .expect("not greater than nsubscriptions"),
1061 );
1062 for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) {
1063 let event_ptr = event_elem?;
1064 let userdata: types::Userdata = userdata.into();
1065 memory.write(
1066 event_ptr,
1067 match result {
1068 SubscriptionResult::Read(r) => {
1069 let type_ = types::Eventtype::FdRead;
1070 match r {
1071 Ok((nbytes, flags)) => types::Event {
1072 userdata,
1073 error: types::Errno::Success,
1074 type_,
1075 fd_readwrite: types::EventFdReadwrite {
1076 nbytes,
1077 flags: types::Eventrwflags::from(&flags),
1078 },
1079 },
1080 Err(e) => types::Event {
1081 userdata,
1082 error: e.downcast().map_err(Error::trap)?,
1083 type_,
1084 fd_readwrite: fd_readwrite_empty(),
1085 },
1086 }
1087 }
1088 SubscriptionResult::Write(r) => {
1089 let type_ = types::Eventtype::FdWrite;
1090 match r {
1091 Ok((nbytes, flags)) => types::Event {
1092 userdata,
1093 error: types::Errno::Success,
1094 type_,
1095 fd_readwrite: types::EventFdReadwrite {
1096 nbytes,
1097 flags: types::Eventrwflags::from(&flags),
1098 },
1099 },
1100 Err(e) => types::Event {
1101 userdata,
1102 error: e.downcast().map_err(Error::trap)?,
1103 type_,
1104 fd_readwrite: fd_readwrite_empty(),
1105 },
1106 }
1107 }
1108 SubscriptionResult::MonotonicClock(r) => {
1109 let type_ = types::Eventtype::Clock;
1110 types::Event {
1111 userdata,
1112 error: match r {
1113 Ok(()) => types::Errno::Success,
1114 Err(e) => e.downcast().map_err(Error::trap)?,
1115 },
1116 type_,
1117 fd_readwrite: fd_readwrite_empty(),
1118 }
1119 }
1120 },
1121 )?;
1122 }
1123
1124 Ok(num_results.try_into().expect("results fit into memory"))
1125 }
1126
1127 async fn proc_exit(
1128 &mut self,
1129 _memory: &mut GuestMemory<'_>,
1130 status: types::Exitcode,
1131 ) -> anyhow::Error {
1132 if status < 126 {
1134 I32Exit(status as i32).into()
1135 } else {
1136 anyhow::Error::msg("exit with invalid exit status outside of [0..126)")
1137 }
1138 }
1139
1140 async fn proc_raise(
1141 &mut self,
1142 _memory: &mut GuestMemory<'_>,
1143 _sig: types::Signal,
1144 ) -> Result<(), Error> {
1145 Err(Error::trap(anyhow::Error::msg("proc_raise unsupported")))
1146 }
1147
1148 async fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), Error> {
1149 self.sched.sched_yield().await
1150 }
1151
1152 async fn random_get(
1153 &mut self,
1154 memory: &mut GuestMemory<'_>,
1155 buf: GuestPtr<u8>,
1156 buf_len: types::Size,
1157 ) -> Result<(), Error> {
1158 let buf = buf.as_array(buf_len);
1159 if memory.is_shared_memory() {
1160 let mut copied: u32 = 0;
1165 while copied < buf.len() {
1166 let len = (buf.len() - copied).min(MAX_SHARED_BUFFER_SIZE as u32);
1167 let mut tmp = vec![0; len as usize];
1168 self.random.lock().unwrap().try_fill_bytes(&mut tmp)?;
1169 let dest = buf.get_range(copied..copied + len).unwrap();
1170 memory.copy_from_slice(&tmp, dest)?;
1171 copied += len;
1172 }
1173 } else {
1174 let mem = &mut memory.as_slice_mut(buf)?.unwrap();
1177 self.random.lock().unwrap().try_fill_bytes(mem)?;
1178 }
1179 Ok(())
1180 }
1181
1182 async fn sock_accept(
1183 &mut self,
1184 _memory: &mut GuestMemory<'_>,
1185 fd: types::Fd,
1186 flags: types::Fdflags,
1187 ) -> Result<types::Fd, Error> {
1188 let table = self.table();
1189 let f = table.get_file(u32::from(fd))?;
1190 let file = f.file.sock_accept(FdFlags::from(flags)).await?;
1191 let fd = table.push(Arc::new(FileEntry::new(file, FileAccessMode::all())))?;
1192 Ok(types::Fd::from(fd))
1193 }
1194
1195 async fn sock_recv(
1196 &mut self,
1197 memory: &mut GuestMemory<'_>,
1198 fd: types::Fd,
1199 ri_data: types::IovecArray,
1200 ri_flags: types::Riflags,
1201 ) -> Result<(types::Size, types::Roflags), Error> {
1202 let f = self.table().get_file(u32::from(fd))?;
1203
1204 let iovs: Vec<wiggle::GuestPtr<[u8]>> = ri_data
1205 .iter()
1206 .map(|iov_ptr| {
1207 let iov_ptr = iov_ptr?;
1208 let iov: types::Iovec = memory.read(iov_ptr)?;
1209 Ok(iov.buf.as_array(iov.buf_len))
1210 })
1211 .collect::<Result<_, Error>>()?;
1212
1213 let is_shared_memory = memory.is_shared_memory();
1226 let (bytes_read, ro_flags) = if is_shared_memory {
1227 let iov = iovs.into_iter().next();
1231 if let Some(iov) = iov {
1232 let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];
1233 let (bytes_read, ro_flags) = f
1234 .file
1235 .sock_recv(&mut [IoSliceMut::new(&mut buffer)], RiFlags::from(ri_flags))
1236 .await?;
1237 let iov = iov
1238 .get_range(0..bytes_read.try_into()?)
1239 .expect("it should always be possible to slice the iov smaller");
1240 memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;
1241 (bytes_read, ro_flags)
1242 } else {
1243 return Ok((0, RoFlags::empty().into()));
1244 }
1245 } else {
1246 let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {
1251 Some(iov) => memory.as_slice_mut(iov)?.unwrap(),
1252 None => &mut [],
1253 };
1254
1255 f.file
1257 .sock_recv(&mut [IoSliceMut::new(guest_slice)], RiFlags::from(ri_flags))
1258 .await?
1259 };
1260
1261 Ok((types::Size::try_from(bytes_read)?, ro_flags.into()))
1262 }
1263
1264 async fn sock_send(
1265 &mut self,
1266 memory: &mut GuestMemory<'_>,
1267 fd: types::Fd,
1268 si_data: types::CiovecArray,
1269 _si_flags: types::Siflags,
1270 ) -> Result<types::Size, Error> {
1271 let f = self.table().get_file(u32::from(fd))?;
1272
1273 let guest_slices: Vec<Cow<[u8]>> = si_data
1274 .iter()
1275 .map(|iov_ptr| {
1276 let iov_ptr = iov_ptr?;
1277 let iov: types::Ciovec = memory.read(iov_ptr)?;
1278 Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)
1279 })
1280 .collect::<Result<_, Error>>()?;
1281
1282 let ioslices: Vec<IoSlice> = guest_slices
1283 .iter()
1284 .map(|s| IoSlice::new(s.deref()))
1285 .collect();
1286 let bytes_written = f.file.sock_send(&ioslices, SiFlags::empty()).await?;
1287
1288 Ok(types::Size::try_from(bytes_written)?)
1289 }
1290
1291 async fn sock_shutdown(
1292 &mut self,
1293 _memory: &mut GuestMemory<'_>,
1294 fd: types::Fd,
1295 how: types::Sdflags,
1296 ) -> Result<(), Error> {
1297 let f = self.table().get_file(u32::from(fd))?;
1298
1299 f.file.sock_shutdown(SdFlags::from(how)).await
1300 }
1301}
1302
1303impl From<types::Advice> for Advice {
1304 fn from(advice: types::Advice) -> Advice {
1305 match advice {
1306 types::Advice::Normal => Advice::Normal,
1307 types::Advice::Sequential => Advice::Sequential,
1308 types::Advice::Random => Advice::Random,
1309 types::Advice::Willneed => Advice::WillNeed,
1310 types::Advice::Dontneed => Advice::DontNeed,
1311 types::Advice::Noreuse => Advice::NoReuse,
1312 }
1313 }
1314}
1315
1316impl From<&FdStat> for types::Fdstat {
1317 fn from(fdstat: &FdStat) -> types::Fdstat {
1318 let mut fs_rights_base = types::Rights::empty();
1319 if fdstat.access_mode.contains(FileAccessMode::READ) {
1320 fs_rights_base |= types::Rights::FD_READ;
1321 }
1322 if fdstat.access_mode.contains(FileAccessMode::WRITE) {
1323 fs_rights_base |= types::Rights::FD_WRITE;
1324 }
1325 types::Fdstat {
1326 fs_filetype: types::Filetype::from(&fdstat.filetype),
1327 fs_rights_base,
1328 fs_rights_inheriting: types::Rights::empty(),
1329 fs_flags: types::Fdflags::from(fdstat.flags),
1330 }
1331 }
1332}
1333
1334impl From<&FileType> for types::Filetype {
1335 fn from(ft: &FileType) -> types::Filetype {
1336 match ft {
1337 FileType::Directory => types::Filetype::Directory,
1338 FileType::BlockDevice => types::Filetype::BlockDevice,
1339 FileType::CharacterDevice => types::Filetype::CharacterDevice,
1340 FileType::RegularFile => types::Filetype::RegularFile,
1341 FileType::SocketDgram => types::Filetype::SocketDgram,
1342 FileType::SocketStream => types::Filetype::SocketStream,
1343 FileType::SymbolicLink => types::Filetype::SymbolicLink,
1344 FileType::Unknown => types::Filetype::Unknown,
1345 FileType::Pipe => types::Filetype::Unknown,
1346 }
1347 }
1348}
1349
1350macro_rules! convert_flags {
1351 ($from:ty, $to:ty, $($flag:ident),+) => {
1352 impl From<$from> for $to {
1353 fn from(f: $from) -> $to {
1354 let mut out = <$to>::empty();
1355 $(
1356 if f.contains(<$from>::$flag) {
1357 out |= <$to>::$flag;
1358 }
1359 )+
1360 out
1361 }
1362 }
1363 }
1364}
1365
1366macro_rules! convert_flags_bidirectional {
1367 ($from:ty, $to:ty, $($rest:tt)*) => {
1368 convert_flags!($from, $to, $($rest)*);
1369 convert_flags!($to, $from, $($rest)*);
1370 }
1371}
1372
1373convert_flags_bidirectional!(
1374 FdFlags,
1375 types::Fdflags,
1376 APPEND,
1377 DSYNC,
1378 NONBLOCK,
1379 RSYNC,
1380 SYNC
1381);
1382
1383convert_flags_bidirectional!(RiFlags, types::Riflags, RECV_PEEK, RECV_WAITALL);
1384
1385convert_flags_bidirectional!(RoFlags, types::Roflags, RECV_DATA_TRUNCATED);
1386
1387convert_flags_bidirectional!(SdFlags, types::Sdflags, RD, WR);
1388
1389impl From<&types::Oflags> for OFlags {
1390 fn from(oflags: &types::Oflags) -> OFlags {
1391 let mut out = OFlags::empty();
1392 if oflags.contains(types::Oflags::CREAT) {
1393 out = out | OFlags::CREATE;
1394 }
1395 if oflags.contains(types::Oflags::DIRECTORY) {
1396 out = out | OFlags::DIRECTORY;
1397 }
1398 if oflags.contains(types::Oflags::EXCL) {
1399 out = out | OFlags::EXCLUSIVE;
1400 }
1401 if oflags.contains(types::Oflags::TRUNC) {
1402 out = out | OFlags::TRUNCATE;
1403 }
1404 out
1405 }
1406}
1407
1408impl From<&OFlags> for types::Oflags {
1409 fn from(oflags: &OFlags) -> types::Oflags {
1410 let mut out = types::Oflags::empty();
1411 if oflags.contains(OFlags::CREATE) {
1412 out = out | types::Oflags::CREAT;
1413 }
1414 if oflags.contains(OFlags::DIRECTORY) {
1415 out = out | types::Oflags::DIRECTORY;
1416 }
1417 if oflags.contains(OFlags::EXCLUSIVE) {
1418 out = out | types::Oflags::EXCL;
1419 }
1420 if oflags.contains(OFlags::TRUNCATE) {
1421 out = out | types::Oflags::TRUNC;
1422 }
1423 out
1424 }
1425}
1426impl From<Filestat> for types::Filestat {
1427 fn from(stat: Filestat) -> types::Filestat {
1428 types::Filestat {
1429 dev: stat.device_id,
1430 ino: stat.inode,
1431 filetype: types::Filetype::from(&stat.filetype),
1432 nlink: stat.nlink,
1433 size: stat.size,
1434 atim: stat
1435 .atim
1436 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1437 .unwrap_or(0),
1438 mtim: stat
1439 .mtim
1440 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1441 .unwrap_or(0),
1442 ctim: stat
1443 .ctim
1444 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1445 .unwrap_or(0),
1446 }
1447 }
1448}
1449
1450impl TryFrom<&ReaddirEntity> for types::Dirent {
1451 type Error = Error;
1452 fn try_from(e: &ReaddirEntity) -> Result<types::Dirent, Error> {
1453 Ok(types::Dirent {
1454 d_ino: e.inode,
1455 d_namlen: e.name.as_bytes().len().try_into()?,
1456 d_type: types::Filetype::from(&e.filetype),
1457 d_next: e.next.into(),
1458 })
1459 }
1460}
1461
1462fn dirent_bytes(dirent: types::Dirent) -> Vec<u8> {
1463 use wiggle::GuestType;
1464 assert_eq!(
1465 types::Dirent::guest_size(),
1466 std::mem::size_of::<types::Dirent>() as u32,
1467 "Dirent guest repr and host repr should match"
1468 );
1469 assert_eq!(
1470 1,
1471 std::mem::size_of_val(&dirent.d_type),
1472 "Dirent member d_type should be endian-invariant"
1473 );
1474 let size = types::Dirent::guest_size()
1475 .try_into()
1476 .expect("Dirent is smaller than 2^32");
1477 let mut bytes = Vec::with_capacity(size);
1478 bytes.resize(size, 0);
1479 let ptr = bytes.as_mut_ptr().cast::<types::Dirent>();
1480 let guest_dirent = types::Dirent {
1481 d_ino: dirent.d_ino.to_le(),
1482 d_namlen: dirent.d_namlen.to_le(),
1483 d_type: dirent.d_type, d_next: dirent.d_next.to_le(),
1485 };
1486 unsafe { ptr.write_unaligned(guest_dirent) };
1487 bytes
1488}
1489
1490impl From<&RwEventFlags> for types::Eventrwflags {
1491 fn from(flags: &RwEventFlags) -> types::Eventrwflags {
1492 let mut out = types::Eventrwflags::empty();
1493 if flags.contains(RwEventFlags::HANGUP) {
1494 out = out | types::Eventrwflags::FD_READWRITE_HANGUP;
1495 }
1496 out
1497 }
1498}
1499
1500fn fd_readwrite_empty() -> types::EventFdReadwrite {
1501 types::EventFdReadwrite {
1502 nbytes: 0,
1503 flags: types::Eventrwflags::empty(),
1504 }
1505}
1506
1507fn systimespec(
1508 set: bool,
1509 ts: types::Timestamp,
1510 now: bool,
1511) -> Result<Option<SystemTimeSpec>, Error> {
1512 if set && now {
1513 Err(Error::invalid_argument())
1514 } else if set {
1515 Ok(Some(SystemTimeSpec::Absolute(
1516 SystemClock::UNIX_EPOCH + Duration::from_nanos(ts),
1517 )))
1518 } else if now {
1519 Ok(Some(SystemTimeSpec::SymbolicNow))
1520 } else {
1521 Ok(None)
1522 }
1523}
1524
1525pub(crate) fn directory_base_rights() -> types::Rights {
1529 types::Rights::PATH_CREATE_DIRECTORY
1530 | types::Rights::PATH_CREATE_FILE
1531 | types::Rights::PATH_LINK_SOURCE
1532 | types::Rights::PATH_LINK_TARGET
1533 | types::Rights::PATH_OPEN
1534 | types::Rights::FD_READDIR
1535 | types::Rights::PATH_READLINK
1536 | types::Rights::PATH_RENAME_SOURCE
1537 | types::Rights::PATH_RENAME_TARGET
1538 | types::Rights::PATH_SYMLINK
1539 | types::Rights::PATH_REMOVE_DIRECTORY
1540 | types::Rights::PATH_UNLINK_FILE
1541 | types::Rights::PATH_FILESTAT_GET
1542 | types::Rights::PATH_FILESTAT_SET_TIMES
1543 | types::Rights::FD_FILESTAT_GET
1544 | types::Rights::FD_FILESTAT_SET_TIMES
1545}
1546
1547pub(crate) fn directory_inheriting_rights() -> types::Rights {
1551 types::Rights::FD_DATASYNC
1552 | types::Rights::FD_READ
1553 | types::Rights::FD_SEEK
1554 | types::Rights::FD_FDSTAT_SET_FLAGS
1555 | types::Rights::FD_SYNC
1556 | types::Rights::FD_TELL
1557 | types::Rights::FD_WRITE
1558 | types::Rights::FD_ADVISE
1559 | types::Rights::FD_ALLOCATE
1560 | types::Rights::FD_FILESTAT_GET
1561 | types::Rights::FD_FILESTAT_SET_SIZE
1562 | types::Rights::FD_FILESTAT_SET_TIMES
1563 | types::Rights::POLL_FD_READWRITE
1564 | directory_base_rights()
1565}