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 if t.seconds >= 0 {
66 let duration =
67 core::time::Duration::new(t.seconds.try_into().map_err(|_| ErrorCode::Overflow)?, 0);
68 system_time = system_time
69 .checked_add(duration)
70 .ok_or(ErrorCode::Overflow)?;
71 } else {
72 let duration = core::time::Duration::new(t.seconds.unsigned_abs(), 0);
73 system_time = system_time
74 .checked_sub(duration)
75 .ok_or(ErrorCode::Overflow)?;
76 };
77
78 let duration = core::time::Duration::new(0, t.nanoseconds);
80 system_time = system_time
81 .checked_add(duration)
82 .ok_or(ErrorCode::Overflow)?;
83
84 Ok(system_time)
85}
86
87fn systemtimespec_from(t: NewTimestamp) -> Result<Option<SystemTime>, ErrorCode> {
88 match t {
89 NewTimestamp::NoChange => Ok(None),
90 NewTimestamp::Now => Ok(Some(SystemTime::now())),
91 NewTimestamp::Timestamp(st) => Ok(Some(systemtime_from(st)?)),
92 }
93}
94
95struct ReadStreamProducer {
96 file: File,
97 offset: u64,
98 result: Option<oneshot::Sender<Result<(), ErrorCode>>>,
99 task: Option<JoinHandle<std::io::Result<BytesMut>>>,
100 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 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 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 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 let result = match Pin::new(&mut *task).poll(cx) {
190 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 #[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 dst.remaining(&mut store)? == Some(0) {
308 return Poll::Ready(Ok(StreamResult::Completed));
309 }
310
311 match self.rx.poll_recv(cx) {
312 Poll::Ready(Some(item)) => {
315 dst.set_buffer(Some(item));
316 Poll::Ready(Ok(StreamResult::Completed))
317 }
318
319 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 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 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 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 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}