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