Skip to main content

wasmtime_wasi/p3/filesystem/
host.rs

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    // Add or subtract the seconds based on sign.
65    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    // Always add the nanos
79    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    /// Update the internal `offset` field after reading `amt` bytes from the file.
116    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            // Once a blocking file, always a blocking file, so assert as such.
143            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        // Lazily spawn a read task if one hasn't already been spawned yet.
166        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        // Await the completion of the read task. Note that this is not a
181        // cancellable await point because we can't cancel the other task, so
182        // the `finish` parameter is ignored.
183        let result = match Pin::new(&mut *task).poll(cx) {
184            // If cancellation is requested, then flag that to Tokio. Note that
185            // this still waits for the actual completion of the spawned task,
186            // which won't actually happen if it's already executing.
187            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            // On windows, filter out files like `C:\DumpStack.log.tmp` which we
233            // can't get full metadata for.
234            #[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 this is a 0-length read then `mpsc::Receiver` does not expose an
298        // API to wait for an item to be available without taking it out of the
299        // channel. In lieu of that just say that we're complete and ready for a
300        // read.
301        if dst.remaining(&mut store)? == Some(0) {
302            return Poll::Ready(Ok(StreamResult::Completed));
303        }
304
305        match self.rx.poll_recv(cx) {
306            // If an item is on the channel then send that along and say that
307            // the read is now complete with one item being yielded.
308            Poll::Ready(Some(item)) => {
309                dst.set_buffer(Some(item));
310                Poll::Ready(Ok(StreamResult::Completed))
311            }
312
313            // If there's nothing left on the channel then that means that an
314            // error occurred or the iterator is done. In both cases an
315            // un-cancellable wait for the spawned task is entered and we await
316            // its completion. Upon completion there our own stream is closed
317            // with the result (sending an error code on our oneshot) and then
318            // the stream is reported as dropped.
319            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            // If an item isn't ready yet then cancel this outstanding request
327            // if `finish` is set, otherwise propagate the `Pending` status.
328            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    /// Update the internal `offset` field after writing `amt` bytes from the file.
376    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            // Once a blocking file, always a blocking file, so assert as such.
417            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            // If cancellation is requested, then flag that to Tokio. Note that
442            // this still waits for the actual completion of the spawned task,
443            // which won't actually happen if it's already executing.
444            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}