1use crate::filesystem::sys;
2use crate::filesystem::{Descriptor, Dir, File, WasiFilesystem, WasiFilesystemCtxView};
3use crate::p3::bindings::clocks::system_clock;
4use crate::p3::bindings::filesystem::types::{
5 self, Advice, DescriptorFlags, DescriptorStat, DescriptorType, DirectoryEntry, ErrorCode,
6 Filesize, MetadataHashValue, NewTimestamp, OpenFlags, PathFlags,
7};
8use crate::p3::filesystem::{FilesystemError, FilesystemResult, preopens};
9use crate::p3::{DEFAULT_BUFFER_CAPACITY, FallibleIteratorProducer};
10use bytes::BytesMut;
11use core::pin::Pin;
12use core::task::{Context, Poll, ready};
13use core::{iter, mem};
14use std::io;
15use std::sync::Arc;
16use std::time::SystemTime;
17use tokio::sync::{mpsc, oneshot};
18use tokio::task::{JoinHandle, spawn_blocking};
19use wasmtime::StoreContextMut;
20use wasmtime::component::{
21 Access, Accessor, Destination, FutureReader, Resource, ResourceTable, Source, StreamConsumer,
22 StreamProducer, StreamReader, StreamResult,
23};
24use wasmtime::error::Context as _;
25
26fn get_descriptor<'a>(
27 table: &'a ResourceTable,
28 fd: &'a Resource<Descriptor>,
29) -> FilesystemResult<&'a Descriptor> {
30 table
31 .get(fd)
32 .context("failed to get descriptor resource from table")
33 .map_err(FilesystemError::trap)
34}
35
36fn get_file<'a>(
37 table: &'a ResourceTable,
38 fd: &'a Resource<Descriptor>,
39) -> FilesystemResult<&'a File> {
40 let file = get_descriptor(table, fd).map(Descriptor::file)??;
41 Ok(file)
42}
43
44fn get_dir<'a>(
45 table: &'a ResourceTable,
46 fd: &'a Resource<Descriptor>,
47) -> FilesystemResult<&'a Dir> {
48 let dir = get_descriptor(table, fd).map(Descriptor::dir)??;
49 Ok(dir)
50}
51
52trait AccessorExt {
53 fn get_descriptor(&self, fd: &Resource<Descriptor>) -> FilesystemResult<Descriptor>;
54 fn get_file(&self, fd: &Resource<Descriptor>) -> FilesystemResult<File>;
55 fn get_dir(&self, fd: &Resource<Descriptor>) -> FilesystemResult<Dir>;
56 fn get_dir_pair(
57 &self,
58 a: &Resource<Descriptor>,
59 b: &Resource<Descriptor>,
60 ) -> FilesystemResult<(Dir, Dir)>;
61}
62
63impl<T> AccessorExt for Accessor<T, WasiFilesystem> {
64 fn get_descriptor(&self, fd: &Resource<Descriptor>) -> FilesystemResult<Descriptor> {
65 self.with(|mut store| {
66 let fd = get_descriptor(store.get().table, fd)?;
67 Ok(fd.clone())
68 })
69 }
70
71 fn get_file(&self, fd: &Resource<Descriptor>) -> FilesystemResult<File> {
72 self.with(|mut store| {
73 let file = get_file(store.get().table, fd)?;
74 Ok(file.clone())
75 })
76 }
77
78 fn get_dir(&self, fd: &Resource<Descriptor>) -> FilesystemResult<Dir> {
79 self.with(|mut store| {
80 let dir = get_dir(store.get().table, fd)?;
81 Ok(dir.clone())
82 })
83 }
84
85 fn get_dir_pair(
86 &self,
87 a: &Resource<Descriptor>,
88 b: &Resource<Descriptor>,
89 ) -> FilesystemResult<(Dir, Dir)> {
90 self.with(|mut store| {
91 let table = store.get().table;
92 let a = get_dir(table, a)?;
93 let b = get_dir(table, b)?;
94 Ok((a.clone(), b.clone()))
95 })
96 }
97}
98
99fn systemtime_from(t: system_clock::Instant) -> Result<std::time::SystemTime, ErrorCode> {
100 if let Ok(seconds) = t.seconds.try_into() {
101 std::time::SystemTime::UNIX_EPOCH
102 .checked_add(core::time::Duration::new(seconds, t.nanoseconds))
103 .ok_or(ErrorCode::Overflow)
104 } else {
105 std::time::SystemTime::UNIX_EPOCH
106 .checked_sub(core::time::Duration::new(
107 t.seconds.unsigned_abs(),
108 t.nanoseconds,
109 ))
110 .ok_or(ErrorCode::Overflow)
111 }
112}
113
114fn systemtimespec_from(t: NewTimestamp) -> Result<Option<SystemTime>, ErrorCode> {
115 match t {
116 NewTimestamp::NoChange => Ok(None),
117 NewTimestamp::Now => Ok(Some(SystemTime::now())),
118 NewTimestamp::Timestamp(st) => Ok(Some(systemtime_from(st)?)),
119 }
120}
121
122struct ReadStreamProducer {
123 file: File,
124 offset: u64,
125 result: Option<oneshot::Sender<Result<(), ErrorCode>>>,
126 task: Option<JoinHandle<std::io::Result<BytesMut>>>,
127}
128
129impl Drop for ReadStreamProducer {
130 fn drop(&mut self) {
131 self.close(Ok(()))
132 }
133}
134
135impl ReadStreamProducer {
136 fn close(&mut self, res: Result<(), ErrorCode>) {
137 if let Some(tx) = self.result.take() {
138 _ = tx.send(res);
139 }
140 }
141
142 fn complete_read(&mut self, amt: usize) -> StreamResult {
144 let Ok(amt) = amt.try_into() else {
145 self.close(Err(ErrorCode::Overflow));
146 return StreamResult::Dropped;
147 };
148 let Some(amt) = self.offset.checked_add(amt) else {
149 self.close(Err(ErrorCode::Overflow));
150 return StreamResult::Dropped;
151 };
152 self.offset = amt;
153 StreamResult::Completed
154 }
155}
156
157impl<D> StreamProducer<D> for ReadStreamProducer {
158 type Item = u8;
159 type Buffer = BytesMut;
160
161 fn poll_produce<'a>(
162 mut self: Pin<&mut Self>,
163 cx: &mut Context<'_>,
164 store: StoreContextMut<'a, D>,
165 mut dst: Destination<'a, Self::Item, Self::Buffer>,
166 finish: bool,
167 ) -> Poll<wasmtime::Result<StreamResult>> {
168 if let Some(file) = self.file.as_blocking_file() {
169 assert!(self.task.is_none());
171 let mut dst = dst.as_direct(store, DEFAULT_BUFFER_CAPACITY);
172 let buf = dst.remaining();
173 if buf.is_empty() {
174 return Poll::Ready(Ok(StreamResult::Completed));
175 }
176 return match sys::read_at_cursor_unspecified(file, buf, self.offset) {
177 Ok(0) => {
178 self.close(Ok(()));
179 Poll::Ready(Ok(StreamResult::Dropped))
180 }
181 Ok(n) => {
182 dst.mark_written(n);
183 Poll::Ready(Ok(self.complete_read(n)))
184 }
185 Err(err) => {
186 self.close(Err(err.into()));
187 Poll::Ready(Ok(StreamResult::Dropped))
188 }
189 };
190 }
191
192 let me = &mut *self;
194 let task = me.task.get_or_insert_with(|| {
195 let mut buf = dst.take_buffer();
196 buf.resize(DEFAULT_BUFFER_CAPACITY, 0);
197 let file = Arc::clone(me.file.as_file());
198 let offset = me.offset;
199 spawn_blocking(move || {
200 sys::read_at_cursor_unspecified(&file, &mut buf, offset).map(|n| {
201 buf.truncate(n);
202 buf
203 })
204 })
205 });
206
207 let result = match Pin::new(&mut *task).poll(cx) {
211 Poll::Pending if finish => {
215 task.abort();
216 ready!(Pin::new(task).poll(cx))
217 }
218 other => ready!(other),
219 };
220 self.task = None;
221 match result {
222 Ok(Ok(buf)) if buf.is_empty() => {
223 self.close(Ok(()));
224 Poll::Ready(Ok(StreamResult::Dropped))
225 }
226 Ok(Ok(buf)) => {
227 let n = buf.len();
228 dst.set_buffer(buf);
229 Poll::Ready(Ok(self.complete_read(n)))
230 }
231 Ok(Err(err)) => {
232 self.close(Err(err.into()));
233 Poll::Ready(Ok(StreamResult::Dropped))
234 }
235 Err(err) => {
236 if err.is_cancelled() {
237 return Poll::Ready(Ok(StreamResult::Cancelled));
238 }
239 panic!("I/O task should not panic: {err}")
240 }
241 }
242 }
243}
244
245fn map_dir_entry(
246 entry: std::io::Result<crate::filesystem::primitives::DirEntry>,
247) -> Result<Option<DirectoryEntry>, ErrorCode> {
248 match entry {
249 Ok(entry) => {
250 let meta = entry.metadata()?;
251 let Ok(name) = entry.file_name().into_string() else {
252 return Err(ErrorCode::IllegalByteSequence);
253 };
254 Ok(Some(DirectoryEntry {
255 type_: meta.file_type().into(),
256 name,
257 }))
258 }
259 Err(err) => {
260 #[cfg(windows)]
263 {
264 use windows_sys::Win32::Foundation::{
265 ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION,
266 };
267 if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32)
268 || err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32)
269 {
270 return Ok(None);
271 }
272 }
273 Err(err.into())
274 }
275 }
276}
277
278struct ReadDirStream {
279 rx: mpsc::Receiver<DirectoryEntry>,
280 task: JoinHandle<Result<(), ErrorCode>>,
281 result: Option<oneshot::Sender<Result<(), ErrorCode>>>,
282}
283
284impl ReadDirStream {
285 fn new(
286 dir: Arc<std::fs::File>,
287 result: oneshot::Sender<Result<(), ErrorCode>>,
288 ) -> ReadDirStream {
289 let (tx, rx) = mpsc::channel(1);
290 ReadDirStream {
291 task: spawn_blocking(move || {
292 let entries = crate::filesystem::primitives::read_base_dir(&dir)?;
293 for entry in entries {
294 if let Some(entry) = map_dir_entry(entry)? {
295 if let Err(_) = tx.blocking_send(entry) {
296 break;
297 }
298 }
299 }
300 Ok(())
301 }),
302 rx,
303 result: Some(result),
304 }
305 }
306
307 fn close(&mut self, res: Result<(), ErrorCode>) {
308 self.rx.close();
309 self.task.abort();
310 let _ = self.result.take().unwrap().send(res);
311 }
312}
313
314impl<D> StreamProducer<D> for ReadDirStream {
315 type Item = DirectoryEntry;
316 type Buffer = Option<DirectoryEntry>;
317
318 fn poll_produce<'a>(
319 mut self: Pin<&mut Self>,
320 cx: &mut Context<'_>,
321 mut store: StoreContextMut<'a, D>,
322 mut dst: Destination<'a, Self::Item, Self::Buffer>,
323 finish: bool,
324 ) -> Poll<wasmtime::Result<StreamResult>> {
325 if dst.remaining(&mut store) == Some(0) {
330 return Poll::Ready(Ok(StreamResult::Completed));
331 }
332
333 match self.rx.poll_recv(cx) {
334 Poll::Ready(Some(item)) => {
337 dst.set_buffer(Some(item));
338 Poll::Ready(Ok(StreamResult::Completed))
339 }
340
341 Poll::Ready(None) => {
348 let result = ready!(Pin::new(&mut self.task).poll(cx))
349 .expect("spawned task should not panic");
350 self.close(result);
351 Poll::Ready(Ok(StreamResult::Dropped))
352 }
353
354 Poll::Pending if finish => Poll::Ready(Ok(StreamResult::Cancelled)),
357 Poll::Pending => Poll::Pending,
358 }
359 }
360}
361
362impl Drop for ReadDirStream {
363 fn drop(&mut self) {
364 if self.result.is_some() {
365 self.close(Ok(()));
366 }
367 }
368}
369
370struct WriteStreamConsumer {
371 file: File,
372 location: WriteLocation,
373 result: Option<oneshot::Sender<Result<(), ErrorCode>>>,
374 buffer: BytesMut,
375 task: Option<JoinHandle<std::io::Result<(BytesMut, usize)>>>,
376}
377
378#[derive(Copy, Clone)]
379enum WriteLocation {
380 End,
381 Offset(u64),
382}
383
384impl WriteStreamConsumer {
385 fn new_at(file: File, offset: u64, result: oneshot::Sender<Result<(), ErrorCode>>) -> Self {
386 Self {
387 file,
388 location: WriteLocation::Offset(offset),
389 result: Some(result),
390 buffer: BytesMut::default(),
391 task: None,
392 }
393 }
394
395 fn new_append(file: File, result: oneshot::Sender<Result<(), ErrorCode>>) -> Self {
396 Self {
397 file,
398 location: WriteLocation::End,
399 result: Some(result),
400 buffer: BytesMut::default(),
401 task: None,
402 }
403 }
404
405 fn close(&mut self, res: Result<(), ErrorCode>) {
406 _ = self.result.take().unwrap().send(res);
407 }
408
409 fn complete_write(&mut self, amt: usize) -> StreamResult {
411 match &mut self.location {
412 WriteLocation::End => StreamResult::Completed,
413 WriteLocation::Offset(offset) => {
414 let Ok(amt) = amt.try_into() else {
415 self.close(Err(ErrorCode::Overflow));
416 return StreamResult::Dropped;
417 };
418 let Some(amt) = offset.checked_add(amt) else {
419 self.close(Err(ErrorCode::Overflow));
420 return StreamResult::Dropped;
421 };
422 *offset = amt;
423 StreamResult::Completed
424 }
425 }
426 }
427}
428
429impl WriteLocation {
430 fn write(&self, file: &std::fs::File, bytes: &[u8]) -> io::Result<usize> {
431 match *self {
432 WriteLocation::End => sys::append_cursor_unspecified(file, bytes),
433 WriteLocation::Offset(at) => sys::write_at_cursor_unspecified(file, bytes, at),
434 }
435 }
436}
437
438impl<D> StreamConsumer<D> for WriteStreamConsumer {
439 type Item = u8;
440
441 fn poll_consume(
442 mut self: Pin<&mut Self>,
443 cx: &mut Context<'_>,
444 store: StoreContextMut<D>,
445 src: Source<Self::Item>,
446 finish: bool,
447 ) -> Poll<wasmtime::Result<StreamResult>> {
448 let mut src = src.as_direct(store);
449 if let Some(file) = self.file.as_blocking_file() {
450 assert!(self.task.is_none());
452 return match self.location.write(file, src.remaining()) {
453 Ok(n) => {
454 src.mark_read(n);
455 Poll::Ready(Ok(self.complete_write(n)))
456 }
457 Err(err) => {
458 self.close(Err(err.into()));
459 Poll::Ready(Ok(StreamResult::Dropped))
460 }
461 };
462 }
463 let me = &mut *self;
464 let task = me.task.get_or_insert_with(|| {
465 debug_assert!(me.buffer.is_empty());
466 let remaining = src.remaining();
467 let n = remaining.len().min(DEFAULT_BUFFER_CAPACITY);
468 me.buffer.extend_from_slice(&remaining[..n]);
469 let buf = mem::take(&mut me.buffer);
470 let file = Arc::clone(me.file.as_file());
471 let location = me.location;
472 spawn_blocking(move || location.write(&file, &buf).map(|n| (buf, n)))
473 });
474 let result = match Pin::new(&mut *task).poll(cx) {
475 Poll::Pending if finish => {
479 task.abort();
480 ready!(Pin::new(task).poll(cx))
481 }
482 other => ready!(other),
483 };
484 self.task = None;
485 match result {
486 Ok(Ok((buf, n))) => {
487 src.mark_read(n);
488 self.buffer = buf;
489 self.buffer.clear();
490 Poll::Ready(Ok(self.complete_write(n)))
491 }
492 Ok(Err(err)) => {
493 self.close(Err(err.into()));
494 Poll::Ready(Ok(StreamResult::Dropped))
495 }
496 Err(err) => {
497 if err.is_cancelled() {
498 return Poll::Ready(Ok(StreamResult::Cancelled));
499 }
500 panic!("I/O task should not panic: {err}")
501 }
502 }
503 }
504}
505
506impl Drop for WriteStreamConsumer {
507 fn drop(&mut self) {
508 if self.result.is_some() {
509 self.close(Ok(()))
510 }
511 }
512}
513
514impl types::Host for WasiFilesystemCtxView<'_> {
515 fn convert_error_code(&mut self, error: FilesystemError) -> wasmtime::Result<ErrorCode> {
516 error.downcast()
517 }
518}
519
520impl<U> types::HostDescriptorWithStore<U> for WasiFilesystem {
521 fn read_via_stream(
522 mut store: Access<U, Self>,
523 fd: Resource<Descriptor>,
524 offset: Filesize,
525 ) -> wasmtime::Result<(StreamReader<u8>, FutureReader<Result<(), ErrorCode>>)> {
526 let file = match get_descriptor(store.get().table, &fd)? {
527 Descriptor::File(file) => file.clone(),
528 Descriptor::Dir(_) => {
529 return Ok((
530 StreamReader::new(&mut store, iter::empty())?,
531 FutureReader::new(&mut store, async move {
532 wasmtime::error::Ok(Err(ErrorCode::IsDirectory))
533 })?,
534 ));
535 }
536 };
537 let (result_tx, result_rx) = oneshot::channel();
538 Ok((
539 StreamReader::new(
540 &mut store,
541 ReadStreamProducer {
542 file,
543 offset,
544 result: Some(result_tx),
545 task: None,
546 },
547 )?,
548 FutureReader::new(&mut store, result_rx)?,
549 ))
550 }
551
552 fn write_via_stream(
553 mut store: Access<'_, U, Self>,
554 fd: Resource<Descriptor>,
555 mut data: StreamReader<u8>,
556 offset: Filesize,
557 ) -> wasmtime::Result<FutureReader<Result<(), ErrorCode>>> {
558 let (result_tx, result_rx) = oneshot::channel();
559 match get_file(store.get().table, &fd).and_then(|file| {
560 if file.perms.write_not_permitted() {
561 Err(ErrorCode::NotPermitted.into())
562 } else {
563 Ok(file.clone())
564 }
565 }) {
566 Ok(file) => {
567 data.pipe(
568 &mut store,
569 WriteStreamConsumer::new_at(file, offset, result_tx),
570 )?;
571 }
572 Err(err) => {
573 data.close(&mut store)?;
574 let _ = result_tx.send(Err(err.downcast().unwrap_or(ErrorCode::Io)));
575 }
576 }
577 FutureReader::new(&mut store, result_rx)
578 }
579
580 fn append_via_stream(
581 mut store: Access<'_, U, Self>,
582 fd: Resource<Descriptor>,
583 mut data: StreamReader<u8>,
584 ) -> wasmtime::Result<FutureReader<Result<(), ErrorCode>>> {
585 let (result_tx, result_rx) = oneshot::channel();
586 match get_file(store.get().table, &fd).and_then(|file| {
587 if file.perms.write_not_permitted() {
588 Err(ErrorCode::NotPermitted.into())
589 } else {
590 Ok(file.clone())
591 }
592 }) {
593 Ok(file) => {
594 data.pipe(&mut store, WriteStreamConsumer::new_append(file, result_tx))?;
595 }
596 Err(err) => {
597 data.close(&mut store)?;
598 let _ = result_tx.send(Err(err.downcast().unwrap_or(ErrorCode::Io)));
599 }
600 }
601 FutureReader::new(&mut store, result_rx)
602 }
603
604 async fn advise(
605 store: &Accessor<U, Self>,
606 fd: Resource<Descriptor>,
607 offset: Filesize,
608 length: Filesize,
609 advice: Advice,
610 ) -> FilesystemResult<()> {
611 let file = store.get_file(&fd)?;
612 file.advise(offset, length, advice.into()).await?;
613 Ok(())
614 }
615
616 async fn sync_data(
617 store: &Accessor<U, Self>,
618 fd: Resource<Descriptor>,
619 ) -> FilesystemResult<()> {
620 let fd = store.get_descriptor(&fd)?;
621 fd.sync_data().await?;
622 Ok(())
623 }
624
625 async fn get_flags(
626 store: &Accessor<U, Self>,
627 fd: Resource<Descriptor>,
628 ) -> FilesystemResult<DescriptorFlags> {
629 let fd = store.get_descriptor(&fd)?;
630 let flags = fd.get_flags().await?;
631 Ok(flags.into())
632 }
633
634 async fn get_type(
635 store: &Accessor<U, Self>,
636 fd: Resource<Descriptor>,
637 ) -> FilesystemResult<DescriptorType> {
638 let fd = store.get_descriptor(&fd)?;
639 let ty = fd.get_type().await?;
640 Ok(ty.into())
641 }
642
643 async fn set_size(
644 store: &Accessor<U, Self>,
645 fd: Resource<Descriptor>,
646 size: Filesize,
647 ) -> FilesystemResult<()> {
648 let file = store.get_file(&fd)?;
649 file.set_size(size).await?;
650 Ok(())
651 }
652
653 async fn set_times(
654 store: &Accessor<U, Self>,
655 fd: Resource<Descriptor>,
656 data_access_timestamp: NewTimestamp,
657 data_modification_timestamp: NewTimestamp,
658 ) -> FilesystemResult<()> {
659 let fd = store.get_descriptor(&fd)?;
660 let atim = systemtimespec_from(data_access_timestamp)?;
661 let mtim = systemtimespec_from(data_modification_timestamp)?;
662 fd.set_times(atim, mtim).await?;
663 Ok(())
664 }
665
666 fn read_directory(
667 mut store: Access<'_, U, Self>,
668 fd: Resource<Descriptor>,
669 ) -> wasmtime::Result<(
670 StreamReader<DirectoryEntry>,
671 FutureReader<Result<(), ErrorCode>>,
672 )> {
673 let (result_tx, result_rx) = oneshot::channel();
674 let stream = match get_dir(store.get().table, &fd) {
675 Ok(dir) => {
676 let allow_blocking_current_thread = dir.allow_blocking_current_thread;
677 let dir = Arc::clone(dir.as_dir());
678 if allow_blocking_current_thread {
679 match crate::filesystem::primitives::read_base_dir(&dir) {
680 Ok(readdir) => StreamReader::new(
681 &mut store,
682 FallibleIteratorProducer::new(
683 readdir.filter_map(|e| map_dir_entry(e).transpose()),
684 result_tx,
685 ),
686 )?,
687 Err(e) => {
688 let _ = result_tx.send(Err(e.into()));
689 StreamReader::new(&mut store, iter::empty())?
690 }
691 }
692 } else {
693 StreamReader::new(&mut store, ReadDirStream::new(dir, result_tx))?
694 }
695 }
696 Err(err) => {
697 let _ = result_tx.send(Err(err.downcast().unwrap_or(ErrorCode::Io)));
698 StreamReader::new(&mut store, iter::empty())?
699 }
700 };
701 Ok((stream, FutureReader::new(&mut store, result_rx)?))
702 }
703
704 async fn sync(store: &Accessor<U, Self>, fd: Resource<Descriptor>) -> FilesystemResult<()> {
705 let fd = store.get_descriptor(&fd)?;
706 fd.sync().await?;
707 Ok(())
708 }
709
710 async fn create_directory_at(
711 store: &Accessor<U, Self>,
712 fd: Resource<Descriptor>,
713 path: String,
714 ) -> FilesystemResult<()> {
715 let dir = store.get_dir(&fd)?;
716 dir.create_directory_at(path).await?;
717 Ok(())
718 }
719
720 async fn stat(
721 store: &Accessor<U, Self>,
722 fd: Resource<Descriptor>,
723 ) -> FilesystemResult<DescriptorStat> {
724 let fd = store.get_descriptor(&fd)?;
725 let stat = fd.stat().await?;
726 Ok(stat.into())
727 }
728
729 async fn stat_at(
730 store: &Accessor<U, Self>,
731 fd: Resource<Descriptor>,
732 path_flags: PathFlags,
733 path: String,
734 ) -> FilesystemResult<DescriptorStat> {
735 let dir = store.get_dir(&fd)?;
736 let stat = dir.stat_at(path_flags.into(), path).await?;
737 Ok(stat.into())
738 }
739
740 async fn set_times_at(
741 store: &Accessor<U, Self>,
742 fd: Resource<Descriptor>,
743 path_flags: PathFlags,
744 path: String,
745 data_access_timestamp: NewTimestamp,
746 data_modification_timestamp: NewTimestamp,
747 ) -> FilesystemResult<()> {
748 let dir = store.get_dir(&fd)?;
749 let atim = systemtimespec_from(data_access_timestamp)?;
750 let mtim = systemtimespec_from(data_modification_timestamp)?;
751 dir.set_times_at(path_flags.into(), path, atim, mtim)
752 .await?;
753 Ok(())
754 }
755
756 async fn link_at(
757 store: &Accessor<U, Self>,
758 fd: Resource<Descriptor>,
759 old_path_flags: PathFlags,
760 old_path: String,
761 new_fd: Resource<Descriptor>,
762 new_path: String,
763 ) -> FilesystemResult<()> {
764 let (old_dir, new_dir) = store.get_dir_pair(&fd, &new_fd)?;
765 old_dir
766 .link_at(old_path_flags.into(), old_path, &new_dir, new_path)
767 .await?;
768 Ok(())
769 }
770
771 async fn open_at(
772 store: &Accessor<U, Self>,
773 fd: Resource<Descriptor>,
774 path_flags: PathFlags,
775 path: String,
776 open_flags: OpenFlags,
777 flags: DescriptorFlags,
778 ) -> FilesystemResult<Resource<Descriptor>> {
779 let (allow_blocking_current_thread, dir) = store.with(|mut store| {
780 let store = store.get();
781 let dir = get_dir(&store.table, &fd)?;
782 FilesystemResult::Ok((store.ctx.allow_blocking_current_thread, dir.clone()))
783 })?;
784 let fd = dir
785 .open_at(
786 path_flags.into(),
787 path,
788 open_flags.into(),
789 flags.into(),
790 allow_blocking_current_thread,
791 )
792 .await?;
793 let fd = store.with(|mut store| store.get().table.push(fd))?;
794 Ok(fd)
795 }
796
797 async fn readlink_at(
798 store: &Accessor<U, Self>,
799 fd: Resource<Descriptor>,
800 path: String,
801 ) -> FilesystemResult<String> {
802 let dir = store.get_dir(&fd)?;
803 let path = dir.readlink_at(path).await?;
804 Ok(path)
805 }
806
807 async fn remove_directory_at(
808 store: &Accessor<U, Self>,
809 fd: Resource<Descriptor>,
810 path: String,
811 ) -> FilesystemResult<()> {
812 let dir = store.get_dir(&fd)?;
813 dir.remove_directory_at(path).await?;
814 Ok(())
815 }
816
817 async fn rename_at(
818 store: &Accessor<U, Self>,
819 fd: Resource<Descriptor>,
820 old_path: String,
821 new_fd: Resource<Descriptor>,
822 new_path: String,
823 ) -> FilesystemResult<()> {
824 let (old_dir, new_dir) = store.get_dir_pair(&fd, &new_fd)?;
825 old_dir.rename_at(old_path, &new_dir, new_path).await?;
826 Ok(())
827 }
828
829 async fn symlink_at(
830 store: &Accessor<U, Self>,
831 fd: Resource<Descriptor>,
832 old_path: String,
833 new_path: String,
834 ) -> FilesystemResult<()> {
835 let dir = store.get_dir(&fd)?;
836 dir.symlink_at(old_path, new_path).await?;
837 Ok(())
838 }
839
840 async fn unlink_file_at(
841 store: &Accessor<U, Self>,
842 fd: Resource<Descriptor>,
843 path: String,
844 ) -> FilesystemResult<()> {
845 let dir = store.get_dir(&fd)?;
846 dir.unlink_file_at(path).await?;
847 Ok(())
848 }
849
850 async fn is_same_object(
851 store: &Accessor<U, Self>,
852 fd: Resource<Descriptor>,
853 other: Resource<Descriptor>,
854 ) -> wasmtime::Result<bool> {
855 let (fd, other) = store.with(|mut store| {
856 let table = store.get().table;
857 let fd = get_descriptor(table, &fd)?.clone();
858 let other = get_descriptor(table, &other)?.clone();
859 wasmtime::error::Ok((fd, other))
860 })?;
861 fd.is_same_object(&other).await
862 }
863
864 async fn metadata_hash(
865 store: &Accessor<U, Self>,
866 fd: Resource<Descriptor>,
867 ) -> FilesystemResult<MetadataHashValue> {
868 let fd = store.get_descriptor(&fd)?;
869 let meta = fd.metadata_hash().await?;
870 Ok(meta.into())
871 }
872
873 async fn metadata_hash_at(
874 store: &Accessor<U, Self>,
875 fd: Resource<Descriptor>,
876 path_flags: PathFlags,
877 path: String,
878 ) -> FilesystemResult<MetadataHashValue> {
879 let dir = store.get_dir(&fd)?;
880 let meta = dir.metadata_hash_at(path_flags.into(), path).await?;
881 Ok(meta.into())
882 }
883}
884
885impl types::HostDescriptor for WasiFilesystemCtxView<'_> {
886 fn drop(&mut self, fd: Resource<Descriptor>) -> wasmtime::Result<()> {
887 self.table
888 .delete(fd)
889 .context("failed to delete descriptor resource from table")?;
890 Ok(())
891 }
892}
893
894impl preopens::Host for WasiFilesystemCtxView<'_> {
895 fn get_directories(&mut self) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
896 self.get_directories()
897 }
898}