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 #[cfg(windows)]
821 OpenResult::Dir(_) if write => {
822 return Err(types::Errno::Isdir.into());
823 }
824 OpenResult::File(file) => table.push(Arc::new(FileEntry::new(file, access_mode)))?,
825 OpenResult::Dir(child_dir) => table.push(Arc::new(DirEntry::new(None, child_dir)))?,
826 };
827 Ok(types::Fd::from(fd))
828 }
829
830 async fn path_readlink(
831 &mut self,
832 memory: &mut GuestMemory<'_>,
833 dirfd: types::Fd,
834 path: GuestPtr<str>,
835 buf: GuestPtr<u8>,
836 buf_len: types::Size,
837 ) -> Result<types::Size, Error> {
838 let link = self
839 .table()
840 .get_dir(u32::from(dirfd))?
841 .dir
842 .read_link(memory.as_cow_str(path)?.deref())
843 .await?
844 .into_os_string()
845 .into_string()
846 .map_err(|_| Error::illegal_byte_sequence().context("link contents"))?;
847 let link_bytes = link.as_bytes();
848 let link_len = std::cmp::min(link_bytes.len(), buf_len as usize);
851 let buf = buf.as_array(link_len as u32);
852 memory.copy_from_slice(&link_bytes[..link_len], buf)?;
853 Ok(link_len as types::Size)
854 }
855
856 async fn path_remove_directory(
857 &mut self,
858 memory: &mut GuestMemory<'_>,
859 dirfd: types::Fd,
860 path: GuestPtr<str>,
861 ) -> Result<(), Error> {
862 self.table()
863 .get_dir(u32::from(dirfd))?
864 .dir
865 .remove_dir(memory.as_cow_str(path)?.deref())
866 .await
867 }
868
869 async fn path_rename(
870 &mut self,
871 memory: &mut GuestMemory<'_>,
872 src_fd: types::Fd,
873 src_path: GuestPtr<str>,
874 dest_fd: types::Fd,
875 dest_path: GuestPtr<str>,
876 ) -> Result<(), Error> {
877 let table = self.table();
878 let src_dir = table.get_dir(u32::from(src_fd))?;
879 let dest_dir = table.get_dir(u32::from(dest_fd))?;
880 src_dir
881 .dir
882 .rename(
883 memory.as_cow_str(src_path)?.deref(),
884 dest_dir.dir.deref(),
885 memory.as_cow_str(dest_path)?.deref(),
886 )
887 .await
888 }
889
890 async fn path_symlink(
891 &mut self,
892 memory: &mut GuestMemory<'_>,
893 src_path: GuestPtr<str>,
894 dirfd: types::Fd,
895 dest_path: GuestPtr<str>,
896 ) -> Result<(), Error> {
897 self.table()
898 .get_dir(u32::from(dirfd))?
899 .dir
900 .symlink(
901 memory.as_cow_str(src_path)?.deref(),
902 memory.as_cow_str(dest_path)?.deref(),
903 )
904 .await
905 }
906
907 async fn path_unlink_file(
908 &mut self,
909 memory: &mut GuestMemory<'_>,
910 dirfd: types::Fd,
911 path: GuestPtr<str>,
912 ) -> Result<(), Error> {
913 self.table()
914 .get_dir(u32::from(dirfd))?
915 .dir
916 .unlink_file(memory.as_cow_str(path)?.deref())
917 .await
918 }
919
920 async fn poll_oneoff(
921 &mut self,
922 memory: &mut GuestMemory<'_>,
923 subs: GuestPtr<types::Subscription>,
924 events: GuestPtr<types::Event>,
925 nsubscriptions: types::Size,
926 ) -> Result<types::Size, Error> {
927 if nsubscriptions == 0 {
928 return Err(Error::invalid_argument().context("nsubscriptions must be nonzero"));
929 }
930
931 if nsubscriptions == 1 {
936 let sub = memory.read(subs)?;
937 if let types::SubscriptionU::Clock(clocksub) = sub.u {
938 if !clocksub
939 .flags
940 .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
941 {
942 self.sched
943 .sleep(Duration::from_nanos(clocksub.timeout))
944 .await?;
945 memory.write(
946 events,
947 types::Event {
948 userdata: sub.userdata,
949 error: types::Errno::Success,
950 type_: types::Eventtype::Clock,
951 fd_readwrite: fd_readwrite_empty(),
952 },
953 )?;
954 return Ok(1);
955 }
956 }
957 }
958
959 let table = &self.table;
960 let mut read_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();
962 let mut write_refs: Vec<(Arc<FileEntry>, Option<Userdata>)> = Vec::new();
963
964 let mut poll = Poll::new();
965
966 let subs = subs.as_array(nsubscriptions);
967 for sub_elem in subs.iter() {
968 let sub_ptr = sub_elem?;
969 let sub = memory.read(sub_ptr)?;
970 match sub.u {
971 types::SubscriptionU::Clock(clocksub) => match clocksub.id {
972 types::Clockid::Monotonic => {
973 let clock = self.clocks.monotonic()?;
974 let precision = Duration::from_nanos(clocksub.precision);
975 let duration = Duration::from_nanos(clocksub.timeout);
976 let start = if clocksub
977 .flags
978 .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
979 {
980 clock.creation_time
981 } else {
982 clock.abs_clock.now(precision)
983 };
984 let deadline = start
985 .checked_add(duration)
986 .ok_or_else(|| Error::overflow().context("deadline"))?;
987 poll.subscribe_monotonic_clock(
988 &*clock.abs_clock,
989 deadline,
990 precision,
991 sub.userdata.into(),
992 )
993 }
994 types::Clockid::Realtime => {
995 let clock = self.clocks.monotonic()?;
1001 let precision = Duration::from_nanos(clocksub.precision);
1002 let duration = Duration::from_nanos(clocksub.timeout);
1003 let deadline = if clocksub
1004 .flags
1005 .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
1006 {
1007 return Err(Error::not_supported());
1008 } else {
1009 clock
1010 .abs_clock
1011 .now(precision)
1012 .checked_add(duration)
1013 .ok_or_else(|| Error::overflow().context("deadline"))?
1014 };
1015 poll.subscribe_monotonic_clock(
1016 &*clock.abs_clock,
1017 deadline,
1018 precision,
1019 sub.userdata.into(),
1020 )
1021 }
1022 _ => Err(Error::invalid_argument()
1023 .context("timer subscriptions only support monotonic timer"))?,
1024 },
1025 types::SubscriptionU::FdRead(readsub) => {
1026 let fd = readsub.file_descriptor;
1027 let file_ref = table.get_file(u32::from(fd))?;
1028 read_refs.push((file_ref, Some(sub.userdata.into())));
1029 }
1030 types::SubscriptionU::FdWrite(writesub) => {
1031 let fd = writesub.file_descriptor;
1032 let file_ref = table.get_file(u32::from(fd))?;
1033 write_refs.push((file_ref, Some(sub.userdata.into())));
1034 }
1035 }
1036 }
1037
1038 let mut read_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
1039 for (file_lock, userdata) in read_refs.iter_mut() {
1040 read_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));
1041 }
1042
1043 for (f, ud) in read_mut_refs.iter_mut() {
1044 poll.subscribe_read(*f, *ud);
1045 }
1046
1047 let mut write_mut_refs: Vec<(&dyn WasiFile, Userdata)> = Vec::new();
1048 for (file_lock, userdata) in write_refs.iter_mut() {
1049 write_mut_refs.push((file_lock.file.deref(), userdata.take().unwrap()));
1050 }
1051
1052 for (f, ud) in write_mut_refs.iter_mut() {
1053 poll.subscribe_write(*f, *ud);
1054 }
1055
1056 self.sched.poll_oneoff(&mut poll).await?;
1057
1058 let results = poll.results();
1059 let num_results = results.len();
1060 assert!(
1061 num_results <= nsubscriptions as usize,
1062 "results exceeds subscriptions"
1063 );
1064 let events = events.as_array(
1065 num_results
1066 .try_into()
1067 .expect("not greater than nsubscriptions"),
1068 );
1069 for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) {
1070 let event_ptr = event_elem?;
1071 let userdata: types::Userdata = userdata.into();
1072 memory.write(
1073 event_ptr,
1074 match result {
1075 SubscriptionResult::Read(r) => {
1076 let type_ = types::Eventtype::FdRead;
1077 match r {
1078 Ok((nbytes, flags)) => types::Event {
1079 userdata,
1080 error: types::Errno::Success,
1081 type_,
1082 fd_readwrite: types::EventFdReadwrite {
1083 nbytes,
1084 flags: types::Eventrwflags::from(&flags),
1085 },
1086 },
1087 Err(e) => types::Event {
1088 userdata,
1089 error: e.downcast().map_err(Error::trap)?,
1090 type_,
1091 fd_readwrite: fd_readwrite_empty(),
1092 },
1093 }
1094 }
1095 SubscriptionResult::Write(r) => {
1096 let type_ = types::Eventtype::FdWrite;
1097 match r {
1098 Ok((nbytes, flags)) => types::Event {
1099 userdata,
1100 error: types::Errno::Success,
1101 type_,
1102 fd_readwrite: types::EventFdReadwrite {
1103 nbytes,
1104 flags: types::Eventrwflags::from(&flags),
1105 },
1106 },
1107 Err(e) => types::Event {
1108 userdata,
1109 error: e.downcast().map_err(Error::trap)?,
1110 type_,
1111 fd_readwrite: fd_readwrite_empty(),
1112 },
1113 }
1114 }
1115 SubscriptionResult::MonotonicClock(r) => {
1116 let type_ = types::Eventtype::Clock;
1117 types::Event {
1118 userdata,
1119 error: match r {
1120 Ok(()) => types::Errno::Success,
1121 Err(e) => e.downcast().map_err(Error::trap)?,
1122 },
1123 type_,
1124 fd_readwrite: fd_readwrite_empty(),
1125 }
1126 }
1127 },
1128 )?;
1129 }
1130
1131 Ok(num_results.try_into().expect("results fit into memory"))
1132 }
1133
1134 async fn proc_exit(
1135 &mut self,
1136 _memory: &mut GuestMemory<'_>,
1137 status: types::Exitcode,
1138 ) -> anyhow::Error {
1139 if status < 126 {
1141 I32Exit(status as i32).into()
1142 } else {
1143 anyhow::Error::msg("exit with invalid exit status outside of [0..126)")
1144 }
1145 }
1146
1147 async fn proc_raise(
1148 &mut self,
1149 _memory: &mut GuestMemory<'_>,
1150 _sig: types::Signal,
1151 ) -> Result<(), Error> {
1152 Err(Error::trap(anyhow::Error::msg("proc_raise unsupported")))
1153 }
1154
1155 async fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), Error> {
1156 self.sched.sched_yield().await
1157 }
1158
1159 async fn random_get(
1160 &mut self,
1161 memory: &mut GuestMemory<'_>,
1162 buf: GuestPtr<u8>,
1163 buf_len: types::Size,
1164 ) -> Result<(), Error> {
1165 let buf = buf.as_array(buf_len);
1166 if memory.is_shared_memory() {
1167 let mut copied: u32 = 0;
1172 while copied < buf.len() {
1173 let len = (buf.len() - copied).min(MAX_SHARED_BUFFER_SIZE as u32);
1174 let mut tmp = vec![0; len as usize];
1175 self.random.lock().unwrap().try_fill_bytes(&mut tmp)?;
1176 let dest = buf.get_range(copied..copied + len).unwrap();
1177 memory.copy_from_slice(&tmp, dest)?;
1178 copied += len;
1179 }
1180 } else {
1181 let mem = &mut memory.as_slice_mut(buf)?.unwrap();
1184 self.random.lock().unwrap().try_fill_bytes(mem)?;
1185 }
1186 Ok(())
1187 }
1188
1189 async fn sock_accept(
1190 &mut self,
1191 _memory: &mut GuestMemory<'_>,
1192 fd: types::Fd,
1193 flags: types::Fdflags,
1194 ) -> Result<types::Fd, Error> {
1195 let table = self.table();
1196 let f = table.get_file(u32::from(fd))?;
1197 let file = f.file.sock_accept(FdFlags::from(flags)).await?;
1198 let fd = table.push(Arc::new(FileEntry::new(file, FileAccessMode::all())))?;
1199 Ok(types::Fd::from(fd))
1200 }
1201
1202 async fn sock_recv(
1203 &mut self,
1204 memory: &mut GuestMemory<'_>,
1205 fd: types::Fd,
1206 ri_data: types::IovecArray,
1207 ri_flags: types::Riflags,
1208 ) -> Result<(types::Size, types::Roflags), Error> {
1209 let f = self.table().get_file(u32::from(fd))?;
1210
1211 let iovs: Vec<wiggle::GuestPtr<[u8]>> = ri_data
1212 .iter()
1213 .map(|iov_ptr| {
1214 let iov_ptr = iov_ptr?;
1215 let iov: types::Iovec = memory.read(iov_ptr)?;
1216 Ok(iov.buf.as_array(iov.buf_len))
1217 })
1218 .collect::<Result<_, Error>>()?;
1219
1220 let is_shared_memory = memory.is_shared_memory();
1233 let (bytes_read, ro_flags) = if is_shared_memory {
1234 let iov = iovs.into_iter().next();
1238 if let Some(iov) = iov {
1239 let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)];
1240 let (bytes_read, ro_flags) = f
1241 .file
1242 .sock_recv(&mut [IoSliceMut::new(&mut buffer)], RiFlags::from(ri_flags))
1243 .await?;
1244 let iov = iov
1245 .get_range(0..bytes_read.try_into()?)
1246 .expect("it should always be possible to slice the iov smaller");
1247 memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?;
1248 (bytes_read, ro_flags)
1249 } else {
1250 return Ok((0, RoFlags::empty().into()));
1251 }
1252 } else {
1253 let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() {
1258 Some(iov) => memory.as_slice_mut(iov)?.unwrap(),
1259 None => &mut [],
1260 };
1261
1262 f.file
1264 .sock_recv(&mut [IoSliceMut::new(guest_slice)], RiFlags::from(ri_flags))
1265 .await?
1266 };
1267
1268 Ok((types::Size::try_from(bytes_read)?, ro_flags.into()))
1269 }
1270
1271 async fn sock_send(
1272 &mut self,
1273 memory: &mut GuestMemory<'_>,
1274 fd: types::Fd,
1275 si_data: types::CiovecArray,
1276 _si_flags: types::Siflags,
1277 ) -> Result<types::Size, Error> {
1278 let f = self.table().get_file(u32::from(fd))?;
1279
1280 let guest_slices: Vec<Cow<[u8]>> = si_data
1281 .iter()
1282 .map(|iov_ptr| {
1283 let iov_ptr = iov_ptr?;
1284 let iov: types::Ciovec = memory.read(iov_ptr)?;
1285 Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?)
1286 })
1287 .collect::<Result<_, Error>>()?;
1288
1289 let ioslices: Vec<IoSlice> = guest_slices
1290 .iter()
1291 .map(|s| IoSlice::new(s.deref()))
1292 .collect();
1293 let bytes_written = f.file.sock_send(&ioslices, SiFlags::empty()).await?;
1294
1295 Ok(types::Size::try_from(bytes_written)?)
1296 }
1297
1298 async fn sock_shutdown(
1299 &mut self,
1300 _memory: &mut GuestMemory<'_>,
1301 fd: types::Fd,
1302 how: types::Sdflags,
1303 ) -> Result<(), Error> {
1304 let f = self.table().get_file(u32::from(fd))?;
1305
1306 f.file.sock_shutdown(SdFlags::from(how)).await
1307 }
1308}
1309
1310impl From<types::Advice> for Advice {
1311 fn from(advice: types::Advice) -> Advice {
1312 match advice {
1313 types::Advice::Normal => Advice::Normal,
1314 types::Advice::Sequential => Advice::Sequential,
1315 types::Advice::Random => Advice::Random,
1316 types::Advice::Willneed => Advice::WillNeed,
1317 types::Advice::Dontneed => Advice::DontNeed,
1318 types::Advice::Noreuse => Advice::NoReuse,
1319 }
1320 }
1321}
1322
1323impl From<&FdStat> for types::Fdstat {
1324 fn from(fdstat: &FdStat) -> types::Fdstat {
1325 let mut fs_rights_base = types::Rights::empty();
1326 if fdstat.access_mode.contains(FileAccessMode::READ) {
1327 fs_rights_base |= types::Rights::FD_READ;
1328 }
1329 if fdstat.access_mode.contains(FileAccessMode::WRITE) {
1330 fs_rights_base |= types::Rights::FD_WRITE;
1331 }
1332 types::Fdstat {
1333 fs_filetype: types::Filetype::from(&fdstat.filetype),
1334 fs_rights_base,
1335 fs_rights_inheriting: types::Rights::empty(),
1336 fs_flags: types::Fdflags::from(fdstat.flags),
1337 }
1338 }
1339}
1340
1341impl From<&FileType> for types::Filetype {
1342 fn from(ft: &FileType) -> types::Filetype {
1343 match ft {
1344 FileType::Directory => types::Filetype::Directory,
1345 FileType::BlockDevice => types::Filetype::BlockDevice,
1346 FileType::CharacterDevice => types::Filetype::CharacterDevice,
1347 FileType::RegularFile => types::Filetype::RegularFile,
1348 FileType::SocketDgram => types::Filetype::SocketDgram,
1349 FileType::SocketStream => types::Filetype::SocketStream,
1350 FileType::SymbolicLink => types::Filetype::SymbolicLink,
1351 FileType::Unknown => types::Filetype::Unknown,
1352 FileType::Pipe => types::Filetype::Unknown,
1353 }
1354 }
1355}
1356
1357macro_rules! convert_flags {
1358 ($from:ty, $to:ty, $($flag:ident),+) => {
1359 impl From<$from> for $to {
1360 fn from(f: $from) -> $to {
1361 let mut out = <$to>::empty();
1362 $(
1363 if f.contains(<$from>::$flag) {
1364 out |= <$to>::$flag;
1365 }
1366 )+
1367 out
1368 }
1369 }
1370 }
1371}
1372
1373macro_rules! convert_flags_bidirectional {
1374 ($from:ty, $to:ty, $($rest:tt)*) => {
1375 convert_flags!($from, $to, $($rest)*);
1376 convert_flags!($to, $from, $($rest)*);
1377 }
1378}
1379
1380convert_flags_bidirectional!(
1381 FdFlags,
1382 types::Fdflags,
1383 APPEND,
1384 DSYNC,
1385 NONBLOCK,
1386 RSYNC,
1387 SYNC
1388);
1389
1390convert_flags_bidirectional!(RiFlags, types::Riflags, RECV_PEEK, RECV_WAITALL);
1391
1392convert_flags_bidirectional!(RoFlags, types::Roflags, RECV_DATA_TRUNCATED);
1393
1394convert_flags_bidirectional!(SdFlags, types::Sdflags, RD, WR);
1395
1396impl From<&types::Oflags> for OFlags {
1397 fn from(oflags: &types::Oflags) -> OFlags {
1398 let mut out = OFlags::empty();
1399 if oflags.contains(types::Oflags::CREAT) {
1400 out = out | OFlags::CREATE;
1401 }
1402 if oflags.contains(types::Oflags::DIRECTORY) {
1403 out = out | OFlags::DIRECTORY;
1404 }
1405 if oflags.contains(types::Oflags::EXCL) {
1406 out = out | OFlags::EXCLUSIVE;
1407 }
1408 if oflags.contains(types::Oflags::TRUNC) {
1409 out = out | OFlags::TRUNCATE;
1410 }
1411 out
1412 }
1413}
1414
1415impl From<&OFlags> for types::Oflags {
1416 fn from(oflags: &OFlags) -> types::Oflags {
1417 let mut out = types::Oflags::empty();
1418 if oflags.contains(OFlags::CREATE) {
1419 out = out | types::Oflags::CREAT;
1420 }
1421 if oflags.contains(OFlags::DIRECTORY) {
1422 out = out | types::Oflags::DIRECTORY;
1423 }
1424 if oflags.contains(OFlags::EXCLUSIVE) {
1425 out = out | types::Oflags::EXCL;
1426 }
1427 if oflags.contains(OFlags::TRUNCATE) {
1428 out = out | types::Oflags::TRUNC;
1429 }
1430 out
1431 }
1432}
1433impl From<Filestat> for types::Filestat {
1434 fn from(stat: Filestat) -> types::Filestat {
1435 types::Filestat {
1436 dev: stat.device_id,
1437 ino: stat.inode,
1438 filetype: types::Filetype::from(&stat.filetype),
1439 nlink: stat.nlink,
1440 size: stat.size,
1441 atim: stat
1442 .atim
1443 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1444 .unwrap_or(0),
1445 mtim: stat
1446 .mtim
1447 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1448 .unwrap_or(0),
1449 ctim: stat
1450 .ctim
1451 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
1452 .unwrap_or(0),
1453 }
1454 }
1455}
1456
1457impl TryFrom<&ReaddirEntity> for types::Dirent {
1458 type Error = Error;
1459 fn try_from(e: &ReaddirEntity) -> Result<types::Dirent, Error> {
1460 Ok(types::Dirent {
1461 d_ino: e.inode,
1462 d_namlen: e.name.as_bytes().len().try_into()?,
1463 d_type: types::Filetype::from(&e.filetype),
1464 d_next: e.next.into(),
1465 })
1466 }
1467}
1468
1469fn dirent_bytes(dirent: types::Dirent) -> Vec<u8> {
1470 use wiggle::GuestType;
1471 assert_eq!(
1472 types::Dirent::guest_size(),
1473 std::mem::size_of::<types::Dirent>() as u32,
1474 "Dirent guest repr and host repr should match"
1475 );
1476 assert_eq!(
1477 1,
1478 std::mem::size_of_val(&dirent.d_type),
1479 "Dirent member d_type should be endian-invariant"
1480 );
1481 let size = types::Dirent::guest_size()
1482 .try_into()
1483 .expect("Dirent is smaller than 2^32");
1484 let mut bytes = Vec::with_capacity(size);
1485 bytes.resize(size, 0);
1486 let ptr = bytes.as_mut_ptr().cast::<types::Dirent>();
1487 let guest_dirent = types::Dirent {
1488 d_ino: dirent.d_ino.to_le(),
1489 d_namlen: dirent.d_namlen.to_le(),
1490 d_type: dirent.d_type, d_next: dirent.d_next.to_le(),
1492 };
1493 unsafe { ptr.write_unaligned(guest_dirent) };
1494 bytes
1495}
1496
1497impl From<&RwEventFlags> for types::Eventrwflags {
1498 fn from(flags: &RwEventFlags) -> types::Eventrwflags {
1499 let mut out = types::Eventrwflags::empty();
1500 if flags.contains(RwEventFlags::HANGUP) {
1501 out = out | types::Eventrwflags::FD_READWRITE_HANGUP;
1502 }
1503 out
1504 }
1505}
1506
1507fn fd_readwrite_empty() -> types::EventFdReadwrite {
1508 types::EventFdReadwrite {
1509 nbytes: 0,
1510 flags: types::Eventrwflags::empty(),
1511 }
1512}
1513
1514fn systimespec(
1515 set: bool,
1516 ts: types::Timestamp,
1517 now: bool,
1518) -> Result<Option<SystemTimeSpec>, Error> {
1519 if set && now {
1520 Err(Error::invalid_argument())
1521 } else if set {
1522 Ok(Some(SystemTimeSpec::Absolute(
1523 SystemClock::UNIX_EPOCH + Duration::from_nanos(ts),
1524 )))
1525 } else if now {
1526 Ok(Some(SystemTimeSpec::SymbolicNow))
1527 } else {
1528 Ok(None)
1529 }
1530}
1531
1532pub(crate) fn directory_base_rights() -> types::Rights {
1536 types::Rights::PATH_CREATE_DIRECTORY
1537 | types::Rights::PATH_CREATE_FILE
1538 | types::Rights::PATH_LINK_SOURCE
1539 | types::Rights::PATH_LINK_TARGET
1540 | types::Rights::PATH_OPEN
1541 | types::Rights::FD_READDIR
1542 | types::Rights::PATH_READLINK
1543 | types::Rights::PATH_RENAME_SOURCE
1544 | types::Rights::PATH_RENAME_TARGET
1545 | types::Rights::PATH_SYMLINK
1546 | types::Rights::PATH_REMOVE_DIRECTORY
1547 | types::Rights::PATH_UNLINK_FILE
1548 | types::Rights::PATH_FILESTAT_GET
1549 | types::Rights::PATH_FILESTAT_SET_TIMES
1550 | types::Rights::FD_FILESTAT_GET
1551 | types::Rights::FD_FILESTAT_SET_TIMES
1552}
1553
1554pub(crate) fn directory_inheriting_rights() -> types::Rights {
1558 types::Rights::FD_DATASYNC
1559 | types::Rights::FD_READ
1560 | types::Rights::FD_SEEK
1561 | types::Rights::FD_FDSTAT_SET_FLAGS
1562 | types::Rights::FD_SYNC
1563 | types::Rights::FD_TELL
1564 | types::Rights::FD_WRITE
1565 | types::Rights::FD_ADVISE
1566 | types::Rights::FD_ALLOCATE
1567 | types::Rights::FD_FILESTAT_GET
1568 | types::Rights::FD_FILESTAT_SET_SIZE
1569 | types::Rights::FD_FILESTAT_SET_TIMES
1570 | types::Rights::POLL_FD_READWRITE
1571 | directory_base_rights()
1572}