1use crate::filesystem::sys;
2use crate::filesystem::{Descriptor, Dir, File, WasiFilesystem, WasiFilesystemCtxView};
3use crate::p3::bindings::clocks::system_clock;
4use crate::p3::bindings::filesystem::types::{
5 self, Advice, DescriptorFlags, DescriptorStat, DescriptorType, DirectoryEntry, ErrorCode,
6 Filesize, MetadataHashValue, NewTimestamp, OpenFlags, PathFlags,
7};
8use crate::p3::filesystem::{FilesystemError, FilesystemResult, preopens};
9use crate::p3::{DEFAULT_BUFFER_CAPACITY, FallibleIteratorProducer};
10use bytes::BytesMut;
11use core::pin::Pin;
12use core::task::{Context, Poll, ready};
13use core::{iter, mem};
14use std::ffi::OsString;
15use std::io;
16use std::sync::Arc;
17use std::time::SystemTime;
18use tokio::sync::{mpsc, oneshot};
19use tokio::task::{JoinHandle, spawn_blocking};
20use wasmtime::component::{
21 Access, Accessor, Destination, FutureReader, Resource, ResourceTable, Source, StreamConsumer,
22 StreamProducer, StreamReader, StreamResult,
23};
24use wasmtime::error::Context as _;
25use wasmtime::{AsContextMut, StoreContextMut};
26
27fn get_descriptor<'a>(
28 table: &'a ResourceTable,
29 fd: &'a Resource<Descriptor>,
30) -> FilesystemResult<&'a Descriptor> {
31 table
32 .get(fd)
33 .context("failed to get descriptor resource from table")
34 .map_err(FilesystemError::trap)
35}
36
37fn get_file<'a>(
38 table: &'a ResourceTable,
39 fd: &'a Resource<Descriptor>,
40) -> FilesystemResult<&'a File> {
41 let file = get_descriptor(table, fd).map(Descriptor::file)??;
42 Ok(file)
43}
44
45fn get_dir<'a>(
46 table: &'a ResourceTable,
47 fd: &'a Resource<Descriptor>,
48) -> FilesystemResult<&'a Dir> {
49 let dir = get_descriptor(table, fd).map(Descriptor::dir)??;
50 Ok(dir)
51}
52
53fn get_writable_file(table: &ResourceTable, fd: &Resource<Descriptor>) -> FilesystemResult<File> {
54 let file = get_file(table, fd)?;
55 if file.perms.write_not_permitted() {
56 return Err(ErrorCode::NotPermitted.into());
57 }
58 Ok(file.clone())
59}
60
61fn systemtime_from(t: system_clock::Instant) -> Result<std::time::SystemTime, ErrorCode> {
62 let mut system_time = std::time::SystemTime::UNIX_EPOCH;
63
64 if t.seconds >= 0 {
66 let duration =
67 core::time::Duration::new(t.seconds.try_into().map_err(|_| ErrorCode::Overflow)?, 0);
68 system_time = system_time
69 .checked_add(duration)
70 .ok_or(ErrorCode::Overflow)?;
71 } else {
72 let duration = core::time::Duration::new(t.seconds.unsigned_abs(), 0);
73 system_time = system_time
74 .checked_sub(duration)
75 .ok_or(ErrorCode::Overflow)?;
76 };
77
78 let duration = core::time::Duration::new(0, t.nanoseconds);
80 system_time = system_time
81 .checked_add(duration)
82 .ok_or(ErrorCode::Overflow)?;
83
84 Ok(system_time)
85}
86
87fn systemtimespec_from(t: NewTimestamp) -> Result<Option<SystemTime>, ErrorCode> {
88 match t {
89 NewTimestamp::NoChange => Ok(None),
90 NewTimestamp::Now => Ok(Some(SystemTime::now())),
91 NewTimestamp::Timestamp(st) => Ok(Some(systemtime_from(st)?)),
92 }
93}
94
95struct ReadStreamProducer {
96 file: File,
97 offset: u64,
98 result: Option<oneshot::Sender<Result<(), ErrorCode>>>,
99 task: Option<JoinHandle<std::io::Result<BytesMut>>>,
100}
101
102impl Drop for ReadStreamProducer {
103 fn drop(&mut self) {
104 self.close(Ok(()))
105 }
106}
107
108impl ReadStreamProducer {
109 fn close(&mut self, res: Result<(), ErrorCode>) {
110 if let Some(tx) = self.result.take() {
111 _ = tx.send(res);
112 }
113 }
114
115 fn complete_read(&mut self, amt: usize) -> StreamResult {
117 let Ok(amt) = amt.try_into() else {
118 self.close(Err(ErrorCode::Overflow));
119 return StreamResult::Dropped;
120 };
121 let Some(amt) = self.offset.checked_add(amt) else {
122 self.close(Err(ErrorCode::Overflow));
123 return StreamResult::Dropped;
124 };
125 self.offset = amt;
126 StreamResult::Completed
127 }
128}
129
130impl<D> StreamProducer<D> for ReadStreamProducer {
131 type Item = u8;
132 type Buffer = BytesMut;
133
134 fn poll_produce<'a>(
135 mut self: Pin<&mut Self>,
136 cx: &mut Context<'_>,
137 store: StoreContextMut<'a, D>,
138 mut dst: Destination<'a, Self::Item, Self::Buffer>,
139 finish: bool,
140 ) -> Poll<wasmtime::Result<StreamResult>> {
141 if let Some(file) = self.file.as_blocking_file() {
142 assert!(self.task.is_none());
144 let mut dst = dst.as_direct(store, DEFAULT_BUFFER_CAPACITY);
145 let buf = dst.remaining()?;
146 if buf.is_empty() {
147 return Poll::Ready(Ok(StreamResult::Completed));
148 }
149 return match sys::read_at_cursor_unspecified(file, buf, self.offset) {
150 Ok(0) => {
151 self.close(Ok(()));
152 Poll::Ready(Ok(StreamResult::Dropped))
153 }
154 Ok(n) => {
155 dst.mark_written(n);
156 Poll::Ready(Ok(self.complete_read(n)))
157 }
158 Err(err) => {
159 self.close(Err(err.into()));
160 Poll::Ready(Ok(StreamResult::Dropped))
161 }
162 };
163 }
164
165 let me = &mut *self;
167 let task = me.task.get_or_insert_with(|| {
168 let mut buf = dst.take_buffer();
169 buf.resize(DEFAULT_BUFFER_CAPACITY, 0);
170 let file = Arc::clone(me.file.as_file());
171 let offset = me.offset;
172 spawn_blocking(move || {
173 sys::read_at_cursor_unspecified(&file, &mut buf, offset).map(|n| {
174 buf.truncate(n);
175 buf
176 })
177 })
178 });
179
180 let result = match Pin::new(&mut *task).poll(cx) {
184 Poll::Pending if finish => {
188 task.abort();
189 ready!(Pin::new(task).poll(cx))
190 }
191 other => ready!(other),
192 };
193 self.task = None;
194 match result {
195 Ok(Ok(buf)) if buf.is_empty() => {
196 self.close(Ok(()));
197 Poll::Ready(Ok(StreamResult::Dropped))
198 }
199 Ok(Ok(buf)) => {
200 let n = buf.len();
201 dst.set_buffer(buf);
202 Poll::Ready(Ok(self.complete_read(n)))
203 }
204 Ok(Err(err)) => {
205 self.close(Err(err.into()));
206 Poll::Ready(Ok(StreamResult::Dropped))
207 }
208 Err(err) => {
209 if err.is_cancelled() {
210 return Poll::Ready(Ok(StreamResult::Cancelled));
211 }
212 panic!("I/O task should not panic: {err}")
213 }
214 }
215 }
216}
217
218fn map_dir_entry(
219 entry: std::io::Result<(OsString, crate::filesystem::primitives::FileType)>,
220) -> Result<Option<DirectoryEntry>, ErrorCode> {
221 match entry {
222 Ok((filename, ty)) => {
223 let Ok(name) = filename.into_string() else {
224 return Err(ErrorCode::IllegalByteSequence);
225 };
226 Ok(Some(DirectoryEntry {
227 type_: ty.into(),
228 name,
229 }))
230 }
231 Err(err) => {
232 #[cfg(windows)]
235 {
236 use windows_sys::Win32::Foundation::{
237 ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION,
238 };
239 if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32)
240 || err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32)
241 {
242 return Ok(None);
243 }
244 }
245 Err(err.into())
246 }
247 }
248}
249
250struct ReadDirStream {
251 rx: mpsc::Receiver<DirectoryEntry>,
252 task: JoinHandle<Result<(), ErrorCode>>,
253 result: Option<oneshot::Sender<Result<(), ErrorCode>>>,
254}
255
256impl ReadDirStream {
257 fn new(
258 dir: Arc<std::fs::File>,
259 result: oneshot::Sender<Result<(), ErrorCode>>,
260 ) -> ReadDirStream {
261 let (tx, rx) = mpsc::channel(1);
262 ReadDirStream {
263 task: spawn_blocking(move || {
264 let entries = crate::filesystem::primitives::read_dir(&dir)?;
265 for entry in entries {
266 if let Some(entry) = map_dir_entry(entry)? {
267 if let Err(_) = tx.blocking_send(entry) {
268 break;
269 }
270 }
271 }
272 Ok(())
273 }),
274 rx,
275 result: Some(result),
276 }
277 }
278
279 fn close(&mut self, res: Result<(), ErrorCode>) {
280 self.rx.close();
281 self.task.abort();
282 let _ = self.result.take().unwrap().send(res);
283 }
284}
285
286impl<D> StreamProducer<D> for ReadDirStream {
287 type Item = DirectoryEntry;
288 type Buffer = Option<DirectoryEntry>;
289
290 fn poll_produce<'a>(
291 mut self: Pin<&mut Self>,
292 cx: &mut Context<'_>,
293 mut store: StoreContextMut<'a, D>,
294 mut dst: Destination<'a, Self::Item, Self::Buffer>,
295 finish: bool,
296 ) -> Poll<wasmtime::Result<StreamResult>> {
297 if dst.remaining(&mut store)? == Some(0) {
302 return Poll::Ready(Ok(StreamResult::Completed));
303 }
304
305 match self.rx.poll_recv(cx) {
306 Poll::Ready(Some(item)) => {
309 dst.set_buffer(Some(item));
310 Poll::Ready(Ok(StreamResult::Completed))
311 }
312
313 Poll::Ready(None) => {
320 let result = ready!(Pin::new(&mut self.task).poll(cx))
321 .expect("spawned task should not panic");
322 self.close(result);
323 Poll::Ready(Ok(StreamResult::Dropped))
324 }
325
326 Poll::Pending if finish => Poll::Ready(Ok(StreamResult::Cancelled)),
329 Poll::Pending => Poll::Pending,
330 }
331 }
332}
333
334impl Drop for ReadDirStream {
335 fn drop(&mut self) {
336 if self.result.is_some() {
337 self.close(Ok(()));
338 }
339 }
340}
341
342struct WriteStreamConsumer {
343 file: File,
344 location: WriteLocation,
345 result: Option<oneshot::Sender<Result<(), ErrorCode>>>,
346 buffer: BytesMut,
347 task: Option<JoinHandle<std::io::Result<(BytesMut, usize)>>>,
348}
349
350#[derive(Copy, Clone)]
351enum WriteLocation {
352 End,
353 Offset(u64),
354}
355
356impl WriteStreamConsumer {
357 fn new(
358 file: File,
359 location: WriteLocation,
360 result: oneshot::Sender<Result<(), ErrorCode>>,
361 ) -> Self {
362 Self {
363 file,
364 location,
365 result: Some(result),
366 buffer: BytesMut::default(),
367 task: None,
368 }
369 }
370
371 fn close(&mut self, res: Result<(), ErrorCode>) {
372 _ = self.result.take().unwrap().send(res);
373 }
374
375 fn complete_write(&mut self, amt: usize) -> StreamResult {
377 match &mut self.location {
378 WriteLocation::End => StreamResult::Completed,
379 WriteLocation::Offset(offset) => {
380 let Ok(amt) = amt.try_into() else {
381 self.close(Err(ErrorCode::Overflow));
382 return StreamResult::Dropped;
383 };
384 let Some(amt) = offset.checked_add(amt) else {
385 self.close(Err(ErrorCode::Overflow));
386 return StreamResult::Dropped;
387 };
388 *offset = amt;
389 StreamResult::Completed
390 }
391 }
392 }
393}
394
395impl WriteLocation {
396 fn write(&self, file: &std::fs::File, bytes: &[u8]) -> io::Result<usize> {
397 match *self {
398 WriteLocation::End => sys::append_cursor_unspecified(file, bytes),
399 WriteLocation::Offset(at) => sys::write_at_cursor_unspecified(file, bytes, at),
400 }
401 }
402}
403
404impl<D> StreamConsumer<D> for WriteStreamConsumer {
405 type Item = u8;
406
407 fn poll_consume(
408 mut self: Pin<&mut Self>,
409 cx: &mut Context<'_>,
410 store: StoreContextMut<D>,
411 src: Source<Self::Item>,
412 finish: bool,
413 ) -> Poll<wasmtime::Result<StreamResult>> {
414 let mut src = src.as_direct(store);
415 if let Some(file) = self.file.as_blocking_file() {
416 assert!(self.task.is_none());
418 return match self.location.write(file, src.remaining()?) {
419 Ok(n) => {
420 src.mark_read(n);
421 Poll::Ready(Ok(self.complete_write(n)))
422 }
423 Err(err) => {
424 self.close(Err(err.into()));
425 Poll::Ready(Ok(StreamResult::Dropped))
426 }
427 };
428 }
429 let me = &mut *self;
430 let remaining = src.remaining()?;
431 let task = me.task.get_or_insert_with(|| {
432 debug_assert!(me.buffer.is_empty());
433 let n = remaining.len().min(DEFAULT_BUFFER_CAPACITY);
434 me.buffer.extend_from_slice(&remaining[..n]);
435 let buf = mem::take(&mut me.buffer);
436 let file = Arc::clone(me.file.as_file());
437 let location = me.location;
438 spawn_blocking(move || location.write(&file, &buf).map(|n| (buf, n)))
439 });
440 let result = match Pin::new(&mut *task).poll(cx) {
441 Poll::Pending if finish => {
445 task.abort();
446 ready!(Pin::new(task).poll(cx))
447 }
448 other => ready!(other),
449 };
450 self.task = None;
451 match result {
452 Ok(Ok((buf, n))) => {
453 src.mark_read(n);
454 self.buffer = buf;
455 self.buffer.clear();
456 Poll::Ready(Ok(self.complete_write(n)))
457 }
458 Ok(Err(err)) => {
459 self.close(Err(err.into()));
460 Poll::Ready(Ok(StreamResult::Dropped))
461 }
462 Err(err) => {
463 if err.is_cancelled() {
464 return Poll::Ready(Ok(StreamResult::Cancelled));
465 }
466 panic!("I/O task should not panic: {err}")
467 }
468 }
469 }
470}
471
472impl Drop for WriteStreamConsumer {
473 fn drop(&mut self) {
474 if self.result.is_some() {
475 self.close(Ok(()))
476 }
477 }
478}
479
480impl types::Host for WasiFilesystemCtxView<'_> {
481 fn convert_error_code(&mut self, error: FilesystemError) -> wasmtime::Result<ErrorCode> {
482 error.downcast()
483 }
484}
485
486fn read_via_stream(
487 mut store: impl AsContextMut,
488 fd: Descriptor,
489 offset: Filesize,
490) -> wasmtime::Result<(StreamReader<u8>, FutureReader<Result<(), ErrorCode>>)> {
491 let mut store = store.as_context_mut();
492 let file = match fd {
493 Descriptor::File(file) => file,
494 Descriptor::Dir(_) => {
495 return Ok((
496 StreamReader::new(&mut store, iter::empty())?,
497 FutureReader::new(&mut store, async move {
498 wasmtime::error::Ok(Err(ErrorCode::IsDirectory))
499 })?,
500 ));
501 }
502 };
503 let (result_tx, result_rx) = oneshot::channel();
504 Ok((
505 StreamReader::new(
506 &mut store,
507 ReadStreamProducer {
508 file,
509 offset,
510 result: Some(result_tx),
511 task: None,
512 },
513 )?,
514 FutureReader::new(&mut store, result_rx)?,
515 ))
516}
517
518fn write_via_stream(
519 mut store: impl AsContextMut,
520 file: FilesystemResult<File>,
521 mut data: StreamReader<u8>,
522 location: WriteLocation,
523) -> wasmtime::Result<FutureReader<Result<(), ErrorCode>>> {
524 let mut store = store.as_context_mut();
525 let (result_tx, result_rx) = oneshot::channel();
526 match file {
527 Ok(file) => {
528 data.pipe(
529 &mut store,
530 WriteStreamConsumer::new(file, location, result_tx),
531 )?;
532 }
533 Err(err) => {
534 data.close(&mut store)?;
535 let _ = result_tx.send(Err(err.downcast().unwrap_or(ErrorCode::Io)));
536 }
537 }
538 FutureReader::new(&mut store, result_rx)
539}
540
541fn read_directory(
542 mut store: impl AsContextMut,
543 dir: FilesystemResult<Dir>,
544) -> wasmtime::Result<(
545 StreamReader<DirectoryEntry>,
546 FutureReader<Result<(), ErrorCode>>,
547)> {
548 let mut store = store.as_context_mut();
549 let (result_tx, result_rx) = oneshot::channel();
550 let stream = match dir {
551 Ok(dir) => {
552 let allow_blocking_current_thread = dir.allow_blocking_current_thread;
553 let dir = Arc::clone(dir.as_dir());
554 if allow_blocking_current_thread {
555 match crate::filesystem::primitives::read_dir(&dir) {
556 Ok(readdir) => StreamReader::new(
557 &mut store,
558 FallibleIteratorProducer::new(
559 readdir.filter_map(|e| map_dir_entry(e).transpose()),
560 result_tx,
561 ),
562 )?,
563 Err(e) => {
564 let _ = result_tx.send(Err(e.into()));
565 StreamReader::new(&mut store, iter::empty())?
566 }
567 }
568 } else {
569 StreamReader::new(&mut store, ReadDirStream::new(dir, result_tx))?
570 }
571 }
572 Err(err) => {
573 let _ = result_tx.send(Err(err.downcast().unwrap_or(ErrorCode::Io)));
574 StreamReader::new(&mut store, iter::empty())?
575 }
576 };
577 Ok((stream, FutureReader::new(&mut store, result_rx)?))
578}
579
580impl WasiFilesystemCtxView<'_> {
581 fn advise(
582 &self,
583 fd: &Resource<Descriptor>,
584 offset: Filesize,
585 length: Filesize,
586 advice: Advice,
587 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
588 let file = get_file(self.table, fd).cloned();
589 async move {
590 file?.advise(offset, length, advice.into()).await?;
591 Ok(())
592 }
593 }
594
595 fn get_flags(
596 &self,
597 fd: &Resource<Descriptor>,
598 ) -> impl Future<Output = FilesystemResult<DescriptorFlags>> + use<> {
599 let fd = get_descriptor(self.table, fd).cloned();
600 async move {
601 let flags = fd?.get_flags().await?;
602 Ok(flags.into())
603 }
604 }
605
606 fn get_type(
607 &self,
608 fd: &Resource<Descriptor>,
609 ) -> impl Future<Output = FilesystemResult<DescriptorType>> + use<> {
610 let fd = get_descriptor(self.table, fd).cloned();
611 async move {
612 let ty = fd?.get_type().await?;
613 Ok(ty.into())
614 }
615 }
616
617 fn set_size(
618 &self,
619 fd: &Resource<Descriptor>,
620 size: Filesize,
621 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
622 let file = get_file(self.table, fd).cloned();
623 async move {
624 file?.set_size(size).await?;
625 Ok(())
626 }
627 }
628
629 fn set_times(
630 &self,
631 fd: &Resource<Descriptor>,
632 data_access_timestamp: NewTimestamp,
633 data_modification_timestamp: NewTimestamp,
634 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
635 let fd = get_descriptor(self.table, &fd).cloned();
636 async move {
637 let atim = systemtimespec_from(data_access_timestamp)?;
638 let mtim = systemtimespec_from(data_modification_timestamp)?;
639 fd?.set_times(atim, mtim).await?;
640 Ok(())
641 }
642 }
643
644 fn sync(
645 &self,
646 fd: &Resource<Descriptor>,
647 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
648 let fd = get_descriptor(self.table, &fd).cloned();
649 async move {
650 fd?.sync().await?;
651 Ok(())
652 }
653 }
654
655 fn sync_data(
656 &self,
657 fd: &Resource<Descriptor>,
658 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
659 let fd = get_descriptor(self.table, &fd).cloned();
660 async move {
661 fd?.sync_data().await?;
662 Ok(())
663 }
664 }
665
666 fn create_directory_at(
667 &self,
668 fd: &Resource<Descriptor>,
669 path: String,
670 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
671 let dir = get_dir(self.table, &fd).cloned();
672 async move {
673 dir?.create_directory_at(path).await?;
674 Ok(())
675 }
676 }
677
678 fn stat(
679 &self,
680 fd: &Resource<Descriptor>,
681 ) -> impl Future<Output = FilesystemResult<DescriptorStat>> + use<> {
682 let fd = get_descriptor(self.table, &fd).cloned();
683 async move {
684 let stat = fd?.stat().await?;
685 Ok(stat.into())
686 }
687 }
688
689 fn stat_at(
690 &self,
691 fd: &Resource<Descriptor>,
692 path_flags: PathFlags,
693 path: String,
694 ) -> impl Future<Output = FilesystemResult<DescriptorStat>> + use<> {
695 let dir = get_dir(self.table, &fd).cloned();
696 async move {
697 let stat = dir?.stat_at(path_flags.into(), path).await?;
698 Ok(stat.into())
699 }
700 }
701
702 fn set_times_at(
703 &self,
704 fd: &Resource<Descriptor>,
705 path_flags: PathFlags,
706 path: String,
707 data_access_timestamp: NewTimestamp,
708 data_modification_timestamp: NewTimestamp,
709 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
710 let dir = get_dir(self.table, &fd).cloned();
711 async move {
712 let atim = systemtimespec_from(data_access_timestamp)?;
713 let mtim = systemtimespec_from(data_modification_timestamp)?;
714 dir?.set_times_at(path_flags.into(), path, atim, mtim)
715 .await?;
716 Ok(())
717 }
718 }
719
720 fn link_at(
721 &self,
722 old_fd: &Resource<Descriptor>,
723 old_path_flags: PathFlags,
724 old_path: String,
725 new_fd: &Resource<Descriptor>,
726 new_path: String,
727 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
728 let old_dir = get_dir(self.table, old_fd).cloned();
729 let new_dir = get_dir(self.table, new_fd).cloned();
730
731 async move {
732 old_dir?
733 .link_at(old_path_flags.into(), old_path, &new_dir?, new_path)
734 .await?;
735 Ok(())
736 }
737 }
738
739 fn open_at(
740 &self,
741 fd: &Resource<Descriptor>,
742 path_flags: PathFlags,
743 path: String,
744 open_flags: OpenFlags,
745 flags: DescriptorFlags,
746 ) -> impl Future<Output = FilesystemResult<Descriptor>> + use<> {
747 let dir = get_dir(self.table, fd).cloned();
748 let allow_blocking_current_thread = self.ctx.allow_blocking_current_thread;
749 async move {
750 let fd = dir?
751 .open_at(
752 path_flags.into(),
753 path,
754 open_flags.into(),
755 flags.into(),
756 allow_blocking_current_thread,
757 )
758 .await?;
759 Ok(fd)
760 }
761 }
762
763 fn readlink_at(
764 &self,
765 fd: &Resource<Descriptor>,
766 path: String,
767 ) -> impl Future<Output = FilesystemResult<String>> + use<> {
768 let dir = get_dir(self.table, fd).cloned();
769 async move { Ok(dir?.readlink_at(path).await?) }
770 }
771
772 fn remove_directory_at(
773 &self,
774 fd: &Resource<Descriptor>,
775 path: String,
776 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
777 let dir = get_dir(self.table, fd).cloned();
778 async move {
779 dir?.remove_directory_at(path).await?;
780 Ok(())
781 }
782 }
783
784 fn rename_at(
785 &self,
786 fd: &Resource<Descriptor>,
787 old_path: String,
788 new_fd: &Resource<Descriptor>,
789 new_path: String,
790 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
791 let old_dir = get_dir(self.table, fd).cloned();
792 let new_dir = get_dir(self.table, new_fd).cloned();
793 async move {
794 old_dir?.rename_at(old_path, &new_dir?, new_path).await?;
795 Ok(())
796 }
797 }
798
799 fn symlink_at(
800 &self,
801 fd: &Resource<Descriptor>,
802 old_path: String,
803 new_path: String,
804 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
805 let dir = get_dir(self.table, fd).cloned();
806 async move {
807 dir?.symlink_at(old_path, new_path).await?;
808 Ok(())
809 }
810 }
811
812 fn unlink_file_at(
813 &self,
814 fd: &Resource<Descriptor>,
815 path: String,
816 ) -> impl Future<Output = FilesystemResult<()>> + use<> {
817 let dir = get_dir(self.table, fd).cloned();
818 async move {
819 dir?.unlink_file_at(path).await?;
820 Ok(())
821 }
822 }
823
824 fn is_same_object(
825 &self,
826 fd: &Resource<Descriptor>,
827 other: &Resource<Descriptor>,
828 ) -> impl Future<Output = wasmtime::Result<bool>> + use<> {
829 let fd = get_descriptor(self.table, fd).cloned();
830 let other = get_descriptor(self.table, other).cloned();
831 async move { fd?.is_same_object(&other?).await }
832 }
833
834 fn metadata_hash(
835 &self,
836 fd: &Resource<Descriptor>,
837 ) -> impl Future<Output = FilesystemResult<MetadataHashValue>> + use<> {
838 let fd = get_descriptor(self.table, fd).cloned();
839 async move {
840 let meta = fd?.metadata_hash().await?;
841 Ok(meta.into())
842 }
843 }
844
845 fn metadata_hash_at(
846 &self,
847 fd: &Resource<Descriptor>,
848 path_flags: PathFlags,
849 path: String,
850 ) -> impl Future<Output = FilesystemResult<MetadataHashValue>> + use<> {
851 let dir = get_dir(self.table, fd).cloned();
852 async move {
853 let meta = dir?.metadata_hash_at(path_flags.into(), path).await?;
854 Ok(meta.into())
855 }
856 }
857}
858
859impl<U> types::HostDescriptorWithStore<U> for WasiFilesystem {
860 fn read_via_stream(
861 mut store: Access<U, Self>,
862 fd: Resource<Descriptor>,
863 offset: Filesize,
864 ) -> wasmtime::Result<(StreamReader<u8>, FutureReader<Result<(), ErrorCode>>)> {
865 let fd = get_descriptor(store.get().table, &fd)?.clone();
866 read_via_stream(&mut store, fd, offset)
867 }
868
869 fn write_via_stream(
870 mut store: Access<'_, U, Self>,
871 fd: Resource<Descriptor>,
872 data: StreamReader<u8>,
873 offset: Filesize,
874 ) -> wasmtime::Result<FutureReader<Result<(), ErrorCode>>> {
875 let file = get_writable_file(store.get().table, &fd);
876 write_via_stream(&mut store, file, data, WriteLocation::Offset(offset))
877 }
878
879 fn append_via_stream(
880 mut store: Access<'_, U, Self>,
881 fd: Resource<Descriptor>,
882 data: StreamReader<u8>,
883 ) -> wasmtime::Result<FutureReader<Result<(), ErrorCode>>> {
884 let file = get_writable_file(store.get().table, &fd);
885 write_via_stream(&mut store, file, data, WriteLocation::End)
886 }
887
888 async fn advise(
889 store: &Accessor<U, Self>,
890 fd: Resource<Descriptor>,
891 offset: Filesize,
892 length: Filesize,
893 advice: Advice,
894 ) -> FilesystemResult<()> {
895 store
896 .with(|mut s| s.get().advise(&fd, offset, length, advice))
897 .await
898 }
899
900 async fn sync_data(
901 store: &Accessor<U, Self>,
902 fd: Resource<Descriptor>,
903 ) -> FilesystemResult<()> {
904 store.with(|mut s| s.get().sync_data(&fd)).await
905 }
906
907 async fn get_flags(
908 store: &Accessor<U, Self>,
909 fd: Resource<Descriptor>,
910 ) -> FilesystemResult<DescriptorFlags> {
911 store.with(|mut s| s.get().get_flags(&fd)).await
912 }
913
914 async fn get_type(
915 store: &Accessor<U, Self>,
916 fd: Resource<Descriptor>,
917 ) -> FilesystemResult<DescriptorType> {
918 store.with(|mut s| s.get().get_type(&fd)).await
919 }
920
921 async fn set_size(
922 store: &Accessor<U, Self>,
923 fd: Resource<Descriptor>,
924 size: Filesize,
925 ) -> FilesystemResult<()> {
926 store.with(|mut s| s.get().set_size(&fd, size)).await
927 }
928
929 async fn set_times(
930 store: &Accessor<U, Self>,
931 fd: Resource<Descriptor>,
932 data_access_timestamp: NewTimestamp,
933 data_modification_timestamp: NewTimestamp,
934 ) -> FilesystemResult<()> {
935 store
936 .with(|mut s| {
937 s.get()
938 .set_times(&fd, data_access_timestamp, data_modification_timestamp)
939 })
940 .await
941 }
942
943 fn read_directory(
944 mut store: Access<'_, U, Self>,
945 fd: Resource<Descriptor>,
946 ) -> wasmtime::Result<(
947 StreamReader<DirectoryEntry>,
948 FutureReader<Result<(), ErrorCode>>,
949 )> {
950 let dir = get_dir(store.get().table, &fd).cloned();
951 read_directory(&mut store, dir)
952 }
953
954 async fn sync(store: &Accessor<U, Self>, fd: Resource<Descriptor>) -> FilesystemResult<()> {
955 store.with(|mut s| s.get().sync(&fd)).await
956 }
957
958 async fn create_directory_at(
959 store: &Accessor<U, Self>,
960 fd: Resource<Descriptor>,
961 path: String,
962 ) -> FilesystemResult<()> {
963 store
964 .with(|mut s| s.get().create_directory_at(&fd, path))
965 .await
966 }
967
968 async fn stat(
969 store: &Accessor<U, Self>,
970 fd: Resource<Descriptor>,
971 ) -> FilesystemResult<DescriptorStat> {
972 store.with(|mut s| s.get().stat(&fd)).await
973 }
974
975 async fn stat_at(
976 store: &Accessor<U, Self>,
977 fd: Resource<Descriptor>,
978 path_flags: PathFlags,
979 path: String,
980 ) -> FilesystemResult<DescriptorStat> {
981 store
982 .with(|mut s| s.get().stat_at(&fd, path_flags, path))
983 .await
984 }
985
986 async fn set_times_at(
987 store: &Accessor<U, Self>,
988 fd: Resource<Descriptor>,
989 path_flags: PathFlags,
990 path: String,
991 data_access_timestamp: NewTimestamp,
992 data_modification_timestamp: NewTimestamp,
993 ) -> FilesystemResult<()> {
994 store
995 .with(|mut s| {
996 s.get().set_times_at(
997 &fd,
998 path_flags,
999 path,
1000 data_access_timestamp,
1001 data_modification_timestamp,
1002 )
1003 })
1004 .await
1005 }
1006
1007 async fn link_at(
1008 store: &Accessor<U, Self>,
1009 fd: Resource<Descriptor>,
1010 old_path_flags: PathFlags,
1011 old_path: String,
1012 new_fd: Resource<Descriptor>,
1013 new_path: String,
1014 ) -> FilesystemResult<()> {
1015 store
1016 .with(|mut s| {
1017 s.get()
1018 .link_at(&fd, old_path_flags, old_path, &new_fd, new_path)
1019 })
1020 .await
1021 }
1022
1023 async fn open_at(
1024 store: &Accessor<U, Self>,
1025 fd: Resource<Descriptor>,
1026 path_flags: PathFlags,
1027 path: String,
1028 open_flags: OpenFlags,
1029 flags: DescriptorFlags,
1030 ) -> FilesystemResult<Resource<Descriptor>> {
1031 let fd = store
1032 .with(|mut s| s.get().open_at(&fd, path_flags, path, open_flags, flags))
1033 .await?;
1034 let fd = store.with(|mut store| store.get().table.push(fd))?;
1035 Ok(fd)
1036 }
1037
1038 async fn readlink_at(
1039 store: &Accessor<U, Self>,
1040 fd: Resource<Descriptor>,
1041 path: String,
1042 ) -> FilesystemResult<String> {
1043 store.with(|mut s| s.get().readlink_at(&fd, path)).await
1044 }
1045
1046 async fn remove_directory_at(
1047 store: &Accessor<U, Self>,
1048 fd: Resource<Descriptor>,
1049 path: String,
1050 ) -> FilesystemResult<()> {
1051 store
1052 .with(|mut s| s.get().remove_directory_at(&fd, path))
1053 .await
1054 }
1055
1056 async fn rename_at(
1057 store: &Accessor<U, Self>,
1058 fd: Resource<Descriptor>,
1059 old_path: String,
1060 new_fd: Resource<Descriptor>,
1061 new_path: String,
1062 ) -> FilesystemResult<()> {
1063 store
1064 .with(|mut s| s.get().rename_at(&fd, old_path, &new_fd, new_path))
1065 .await
1066 }
1067
1068 async fn symlink_at(
1069 store: &Accessor<U, Self>,
1070 fd: Resource<Descriptor>,
1071 old_path: String,
1072 new_path: String,
1073 ) -> FilesystemResult<()> {
1074 store
1075 .with(|mut s| s.get().symlink_at(&fd, old_path, new_path))
1076 .await
1077 }
1078
1079 async fn unlink_file_at(
1080 store: &Accessor<U, Self>,
1081 fd: Resource<Descriptor>,
1082 path: String,
1083 ) -> FilesystemResult<()> {
1084 store.with(|mut s| s.get().unlink_file_at(&fd, path)).await
1085 }
1086
1087 async fn is_same_object(
1088 store: &Accessor<U, Self>,
1089 fd: Resource<Descriptor>,
1090 other: Resource<Descriptor>,
1091 ) -> wasmtime::Result<bool> {
1092 store
1093 .with(|mut s| s.get().is_same_object(&fd, &other))
1094 .await
1095 }
1096
1097 async fn metadata_hash(
1098 store: &Accessor<U, Self>,
1099 fd: Resource<Descriptor>,
1100 ) -> FilesystemResult<MetadataHashValue> {
1101 store.with(|mut s| s.get().metadata_hash(&fd)).await
1102 }
1103
1104 async fn metadata_hash_at(
1105 store: &Accessor<U, Self>,
1106 fd: Resource<Descriptor>,
1107 path_flags: PathFlags,
1108 path: String,
1109 ) -> FilesystemResult<MetadataHashValue> {
1110 store
1111 .with(|mut s| s.get().metadata_hash_at(&fd, path_flags, path))
1112 .await
1113 }
1114}
1115
1116impl types::HostDescriptor for WasiFilesystemCtxView<'_> {
1117 fn drop(&mut self, fd: Resource<Descriptor>) -> wasmtime::Result<()> {
1118 self.table
1119 .delete(fd)
1120 .context("failed to delete descriptor resource from table")?;
1121 Ok(())
1122 }
1123}
1124
1125impl preopens::Host for WasiFilesystemCtxView<'_> {
1126 fn get_directories(&mut self) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
1127 self.get_directories()
1128 }
1129}
1130
1131mod named {
1132 use crate::filesystem::{Descriptor, WasiFilesystemNamed, WasiFilesystemNamedView};
1133 use crate::p3::bindings::filesystem::types::{
1134 Advice, DescriptorFlags, DescriptorStat, DescriptorType, DirectoryEntry, ErrorCode,
1135 Filesize, MetadataHashValue, NewTimestamp, OpenFlags, PathFlags,
1136 };
1137 use crate::p3::bindings::named_imports::wasi::filesystem::{preopens, types};
1138 use crate::p3::filesystem::{FilesystemError, FilesystemResult};
1139 use crate::{NamedId, WasiCtxNamedView};
1140 use wasmtime::component::{Access, Accessor, FutureReader, Resource, StreamReader};
1141
1142 impl<T> types::Host for WasiCtxNamedView<'_, T>
1143 where
1144 T: WasiFilesystemNamedView,
1145 {
1146 fn convert_error_code(&mut self, error: FilesystemError) -> wasmtime::Result<ErrorCode> {
1147 error.downcast()
1148 }
1149 }
1150
1151 impl<T, U> types::HostDescriptorWithStore<U> for WasiFilesystemNamed<T>
1152 where
1153 T: WasiFilesystemNamedView,
1154 {
1155 fn read_via_stream(
1156 mut store: Access<U, Self>,
1157 id: NamedId,
1158 fd: Resource<Descriptor>,
1159 offset: Filesize,
1160 ) -> wasmtime::Result<(StreamReader<u8>, FutureReader<Result<(), ErrorCode>>)> {
1161 let fd = super::get_descriptor(store.get().0.filesystem(id).table, &fd)?.clone();
1162 super::read_via_stream(&mut store, fd, offset)
1163 }
1164
1165 fn write_via_stream(
1166 mut store: Access<'_, U, Self>,
1167 id: NamedId,
1168 fd: Resource<Descriptor>,
1169 data: StreamReader<u8>,
1170 offset: Filesize,
1171 ) -> wasmtime::Result<FutureReader<Result<(), ErrorCode>>> {
1172 let file = super::get_writable_file(store.get().0.filesystem(id).table, &fd);
1173 super::write_via_stream(&mut store, file, data, super::WriteLocation::Offset(offset))
1174 }
1175
1176 fn append_via_stream(
1177 mut store: Access<'_, U, Self>,
1178 id: NamedId,
1179 fd: Resource<Descriptor>,
1180 data: StreamReader<u8>,
1181 ) -> wasmtime::Result<FutureReader<Result<(), ErrorCode>>> {
1182 let file = super::get_writable_file(store.get().0.filesystem(id).table, &fd);
1183 super::write_via_stream(&mut store, file, data, super::WriteLocation::End)
1184 }
1185
1186 async fn advise(
1187 store: &Accessor<U, Self>,
1188 id: NamedId,
1189 fd: Resource<Descriptor>,
1190 offset: Filesize,
1191 length: Filesize,
1192 advice: Advice,
1193 ) -> FilesystemResult<()> {
1194 let result = store.with(|mut s| {
1195 let ctx = s.get().0.filesystem(id);
1196 ctx.advise(&fd, offset, length, advice)
1197 });
1198 result.await
1199 }
1200
1201 async fn sync_data(
1202 store: &Accessor<U, Self>,
1203 id: NamedId,
1204 fd: Resource<Descriptor>,
1205 ) -> FilesystemResult<()> {
1206 let result = store.with(|mut s| {
1207 let ctx = s.get().0.filesystem(id);
1208 ctx.sync_data(&fd)
1209 });
1210 result.await
1211 }
1212
1213 async fn get_flags(
1214 store: &Accessor<U, Self>,
1215 id: NamedId,
1216 fd: Resource<Descriptor>,
1217 ) -> FilesystemResult<DescriptorFlags> {
1218 let result = store.with(|mut s| {
1219 let ctx = s.get().0.filesystem(id);
1220 ctx.get_flags(&fd)
1221 });
1222 result.await
1223 }
1224
1225 async fn get_type(
1226 store: &Accessor<U, Self>,
1227 id: NamedId,
1228 fd: Resource<Descriptor>,
1229 ) -> FilesystemResult<DescriptorType> {
1230 let result = store.with(|mut s| {
1231 let ctx = s.get().0.filesystem(id);
1232 ctx.get_type(&fd)
1233 });
1234 result.await
1235 }
1236
1237 async fn set_size(
1238 store: &Accessor<U, Self>,
1239 id: NamedId,
1240 fd: Resource<Descriptor>,
1241 size: Filesize,
1242 ) -> FilesystemResult<()> {
1243 let result = store.with(|mut s| {
1244 let ctx = s.get().0.filesystem(id);
1245 ctx.set_size(&fd, size)
1246 });
1247 result.await
1248 }
1249
1250 async fn set_times(
1251 store: &Accessor<U, Self>,
1252 id: NamedId,
1253 fd: Resource<Descriptor>,
1254 data_access_timestamp: NewTimestamp,
1255 data_modification_timestamp: NewTimestamp,
1256 ) -> FilesystemResult<()> {
1257 let result = store.with(|mut s| {
1258 let ctx = s.get().0.filesystem(id);
1259 ctx.set_times(&fd, data_access_timestamp, data_modification_timestamp)
1260 });
1261 result.await
1262 }
1263
1264 fn read_directory(
1265 mut store: Access<'_, U, Self>,
1266 id: NamedId,
1267 fd: Resource<Descriptor>,
1268 ) -> wasmtime::Result<(
1269 StreamReader<DirectoryEntry>,
1270 FutureReader<Result<(), ErrorCode>>,
1271 )> {
1272 let dir = super::get_dir(store.get().0.filesystem(id).table, &fd).cloned();
1273 super::read_directory(&mut store, dir)
1274 }
1275
1276 async fn sync(
1277 store: &Accessor<U, Self>,
1278 id: NamedId,
1279 fd: Resource<Descriptor>,
1280 ) -> FilesystemResult<()> {
1281 let result = store.with(|mut s| {
1282 let ctx = s.get().0.filesystem(id);
1283 ctx.sync(&fd)
1284 });
1285 result.await
1286 }
1287
1288 async fn create_directory_at(
1289 store: &Accessor<U, Self>,
1290 id: NamedId,
1291 fd: Resource<Descriptor>,
1292 path: String,
1293 ) -> FilesystemResult<()> {
1294 let result = store.with(|mut s| {
1295 let ctx = s.get().0.filesystem(id);
1296 ctx.create_directory_at(&fd, path)
1297 });
1298 result.await
1299 }
1300
1301 async fn stat(
1302 store: &Accessor<U, Self>,
1303 id: NamedId,
1304 fd: Resource<Descriptor>,
1305 ) -> FilesystemResult<DescriptorStat> {
1306 let result = store.with(|mut s| {
1307 let ctx = s.get().0.filesystem(id);
1308 ctx.stat(&fd)
1309 });
1310 result.await
1311 }
1312
1313 async fn stat_at(
1314 store: &Accessor<U, Self>,
1315 id: NamedId,
1316 fd: Resource<Descriptor>,
1317 path_flags: PathFlags,
1318 path: String,
1319 ) -> FilesystemResult<DescriptorStat> {
1320 let result = store.with(|mut s| {
1321 let ctx = s.get().0.filesystem(id);
1322 ctx.stat_at(&fd, path_flags, path)
1323 });
1324 result.await
1325 }
1326
1327 async fn set_times_at(
1328 store: &Accessor<U, Self>,
1329 id: NamedId,
1330 fd: Resource<Descriptor>,
1331 path_flags: PathFlags,
1332 path: String,
1333 data_access_timestamp: NewTimestamp,
1334 data_modification_timestamp: NewTimestamp,
1335 ) -> FilesystemResult<()> {
1336 let result = store.with(|mut s| {
1337 let ctx = s.get().0.filesystem(id);
1338 ctx.set_times_at(
1339 &fd,
1340 path_flags,
1341 path,
1342 data_access_timestamp,
1343 data_modification_timestamp,
1344 )
1345 });
1346 result.await
1347 }
1348
1349 async fn link_at(
1350 store: &Accessor<U, Self>,
1351 id: NamedId,
1352 fd: Resource<Descriptor>,
1353 old_path_flags: PathFlags,
1354 old_path: String,
1355 new_fd: Resource<Descriptor>,
1356 new_path: String,
1357 ) -> FilesystemResult<()> {
1358 let result = store.with(|mut s| {
1359 let ctx = s.get().0.filesystem(id);
1360 ctx.link_at(&fd, old_path_flags, old_path, &new_fd, new_path)
1361 });
1362 result.await
1363 }
1364
1365 async fn open_at(
1366 store: &Accessor<U, Self>,
1367 id: NamedId,
1368 fd: Resource<Descriptor>,
1369 path_flags: PathFlags,
1370 path: String,
1371 open_flags: OpenFlags,
1372 flags: DescriptorFlags,
1373 ) -> FilesystemResult<Resource<Descriptor>> {
1374 let result = store.with(|mut s| {
1375 let ctx = s.get().0.filesystem(id);
1376 ctx.open_at(&fd, path_flags, path, open_flags, flags)
1377 });
1378 let fd = result.await?;
1379 let fd = store.with(|mut store| store.get().0.filesystem(id).table.push(fd))?;
1380 Ok(fd)
1381 }
1382
1383 async fn readlink_at(
1384 store: &Accessor<U, Self>,
1385 id: NamedId,
1386 fd: Resource<Descriptor>,
1387 path: String,
1388 ) -> FilesystemResult<String> {
1389 let result = store.with(|mut s| {
1390 let ctx = s.get().0.filesystem(id);
1391 ctx.readlink_at(&fd, path)
1392 });
1393 result.await
1394 }
1395
1396 async fn remove_directory_at(
1397 store: &Accessor<U, Self>,
1398 id: NamedId,
1399 fd: Resource<Descriptor>,
1400 path: String,
1401 ) -> FilesystemResult<()> {
1402 let result = store.with(|mut s| {
1403 let ctx = s.get().0.filesystem(id);
1404 ctx.remove_directory_at(&fd, path)
1405 });
1406 result.await
1407 }
1408
1409 async fn rename_at(
1410 store: &Accessor<U, Self>,
1411 id: NamedId,
1412 fd: Resource<Descriptor>,
1413 old_path: String,
1414 new_fd: Resource<Descriptor>,
1415 new_path: String,
1416 ) -> FilesystemResult<()> {
1417 let result = store.with(|mut s| {
1418 let ctx = s.get().0.filesystem(id);
1419 ctx.rename_at(&fd, old_path, &new_fd, new_path)
1420 });
1421 result.await
1422 }
1423
1424 async fn symlink_at(
1425 store: &Accessor<U, Self>,
1426 id: NamedId,
1427 fd: Resource<Descriptor>,
1428 old_path: String,
1429 new_path: String,
1430 ) -> FilesystemResult<()> {
1431 let result = store.with(|mut s| {
1432 let ctx = s.get().0.filesystem(id);
1433 ctx.symlink_at(&fd, old_path, new_path)
1434 });
1435 result.await
1436 }
1437
1438 async fn unlink_file_at(
1439 store: &Accessor<U, Self>,
1440 id: NamedId,
1441 fd: Resource<Descriptor>,
1442 path: String,
1443 ) -> FilesystemResult<()> {
1444 let result = store.with(|mut s| {
1445 let ctx = s.get().0.filesystem(id);
1446 ctx.unlink_file_at(&fd, path)
1447 });
1448 result.await
1449 }
1450
1451 async fn is_same_object(
1452 store: &Accessor<U, Self>,
1453 id: NamedId,
1454 fd: Resource<Descriptor>,
1455 other: Resource<Descriptor>,
1456 ) -> wasmtime::Result<bool> {
1457 let result = store.with(|mut s| {
1458 let ctx = s.get().0.filesystem(id);
1459 ctx.is_same_object(&fd, &other)
1460 });
1461 result.await
1462 }
1463
1464 async fn metadata_hash(
1465 store: &Accessor<U, Self>,
1466 id: NamedId,
1467 fd: Resource<Descriptor>,
1468 ) -> FilesystemResult<MetadataHashValue> {
1469 let result = store.with(|mut s| {
1470 let ctx = s.get().0.filesystem(id);
1471 ctx.metadata_hash(&fd)
1472 });
1473 result.await
1474 }
1475
1476 async fn metadata_hash_at(
1477 store: &Accessor<U, Self>,
1478 id: NamedId,
1479 fd: Resource<Descriptor>,
1480 path_flags: PathFlags,
1481 path: String,
1482 ) -> FilesystemResult<MetadataHashValue> {
1483 let result = store.with(|mut s| {
1484 let ctx = s.get().0.filesystem(id);
1485 ctx.metadata_hash_at(&fd, path_flags, path)
1486 });
1487 result.await
1488 }
1489 }
1490
1491 impl<T> types::HostDescriptor for WasiCtxNamedView<'_, T>
1492 where
1493 T: WasiFilesystemNamedView,
1494 {
1495 fn drop(&mut self, id: NamedId, fd: Resource<Descriptor>) -> wasmtime::Result<()> {
1496 super::types::HostDescriptor::drop(&mut self.0.filesystem(id), fd)
1497 }
1498 }
1499
1500 impl<T> preopens::Host for WasiCtxNamedView<'_, T>
1501 where
1502 T: WasiFilesystemNamedView,
1503 {
1504 fn get_directories(
1505 &mut self,
1506 id: NamedId,
1507 ) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
1508 super::preopens::Host::get_directories(&mut self.0.filesystem(id))
1509 }
1510 }
1511}