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