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