1use crate::filesystem::sys;
2use crate::filesystem::{Descriptor, WasiFilesystemCtxView};
3use crate::p2::bindings::clocks::wall_clock;
4use crate::p2::bindings::filesystem::preopens;
5use crate::p2::bindings::filesystem::types::{
6 self, ErrorCode, HostDescriptor, HostDirectoryEntryStream,
7};
8use crate::p2::filesystem::{FileInputStream, FileOutputStream, ReaddirIterator};
9use crate::p2::{FsError, FsResult};
10use std::time::SystemTime;
11use wasmtime::component::Resource;
12use wasmtime_wasi_io::streams::{DynInputStream, DynOutputStream};
13
14mod sync;
15
16impl preopens::Host for WasiFilesystemCtxView<'_> {
17 fn get_directories(&mut self) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
18 self.get_directories()
19 }
20}
21
22impl types::Host for WasiFilesystemCtxView<'_> {
23 fn convert_error_code(&mut self, err: FsError) -> wasmtime::Result<ErrorCode> {
24 err.downcast()
25 }
26
27 fn filesystem_error_code(
28 &mut self,
29 err: Resource<wasmtime::Error>,
30 ) -> wasmtime::Result<Option<ErrorCode>> {
31 let err = self.table.get(&err)?;
32
33 if let Some(err) = err.downcast_ref::<std::io::Error>() {
36 return Ok(Some(ErrorCode::from(err)));
37 }
38
39 Ok(None)
40 }
41}
42
43impl HostDescriptor for WasiFilesystemCtxView<'_> {
44 async fn advise(
45 &mut self,
46 fd: Resource<types::Descriptor>,
47 offset: types::Filesize,
48 len: types::Filesize,
49 advice: types::Advice,
50 ) -> FsResult<()> {
51 let f = self.table.get(&fd)?.file()?;
52 f.advise(offset, len, advice.into()).await?;
53 Ok(())
54 }
55
56 async fn sync_data(&mut self, fd: Resource<types::Descriptor>) -> FsResult<()> {
57 let descriptor = self.table.get(&fd)?;
58 descriptor.sync_data().await?;
59 Ok(())
60 }
61
62 async fn get_flags(
63 &mut self,
64 fd: Resource<types::Descriptor>,
65 ) -> FsResult<types::DescriptorFlags> {
66 let descriptor = self.table.get(&fd)?;
67 let flags = descriptor.get_flags().await?;
68 Ok(flags.into())
69 }
70
71 async fn get_type(
72 &mut self,
73 fd: Resource<types::Descriptor>,
74 ) -> FsResult<types::DescriptorType> {
75 let descriptor = self.table.get(&fd)?;
76 let ty = descriptor.get_type().await?;
77 Ok(ty.into())
78 }
79
80 async fn set_size(
81 &mut self,
82 fd: Resource<types::Descriptor>,
83 size: types::Filesize,
84 ) -> FsResult<()> {
85 let f = self.table.get(&fd)?.file()?;
86 f.set_size(size).await?;
87 Ok(())
88 }
89
90 async fn set_times(
91 &mut self,
92 fd: Resource<types::Descriptor>,
93 atim: types::NewTimestamp,
94 mtim: types::NewTimestamp,
95 ) -> FsResult<()> {
96 let descriptor = self.table.get(&fd)?;
97 let atim = systemtimespec_from(atim)?;
98 let mtim = systemtimespec_from(mtim)?;
99 descriptor.set_times(atim, mtim).await?;
100 Ok(())
101 }
102
103 async fn read(
104 &mut self,
105 fd: Resource<types::Descriptor>,
106 len: types::Filesize,
107 offset: types::Filesize,
108 ) -> FsResult<(Vec<u8>, bool)> {
109 let f = self.table.get(&fd)?.file()?;
110
111 let (mut buffer, r) = f
112 .run_blocking(move |f| {
113 let mut buffer = vec![
114 0;
115 len.try_into()
116 .unwrap_or(usize::MAX)
117 .min(crate::MAX_READ_SIZE_ALLOC)
118 ];
119 let r = sys::read_at_cursor_unspecified(f, &mut buffer, offset);
120 (buffer, r)
121 })
122 .await;
123
124 let (bytes_read, state) = match r? {
125 0 => (0, true),
126 n => (n, false),
127 };
128
129 buffer.truncate(bytes_read);
130
131 Ok((buffer, state))
132 }
133
134 async fn write(
135 &mut self,
136 fd: Resource<types::Descriptor>,
137 buf: Vec<u8>,
138 offset: types::Filesize,
139 ) -> FsResult<types::Filesize> {
140 let f = self.table.get(&fd)?.file()?;
141 if f.perms.write_not_permitted() {
142 return Err(ErrorCode::NotPermitted.into());
143 }
144
145 let bytes_written = f
146 .run_blocking(move |f| sys::write_at_cursor_unspecified(f, &buf, offset))
147 .await?;
148
149 Ok(types::Filesize::try_from(bytes_written).expect("usize fits in Filesize"))
150 }
151
152 async fn read_directory(
153 &mut self,
154 fd: Resource<types::Descriptor>,
155 ) -> FsResult<Resource<types::DirectoryEntryStream>> {
156 let d = self.table.get(&fd)?.dir()?;
157
158 enum ReaddirError {
159 Io(std::io::Error),
160 IllegalSequence,
161 }
162 impl From<std::io::Error> for ReaddirError {
163 fn from(e: std::io::Error) -> ReaddirError {
164 ReaddirError::Io(e)
165 }
166 }
167
168 let entries = d
169 .run_blocking(|d| {
170 Ok::<_, std::io::Error>(
174 crate::filesystem::primitives::read_dir(d)?
175 .map(|entry| {
176 let (filename, ty) = entry?;
177 let type_ = descriptortype_from(ty);
178 let name = filename
179 .into_string()
180 .map_err(|_| ReaddirError::IllegalSequence)?;
181 Ok(types::DirectoryEntry { type_, name })
182 })
183 .collect::<Vec<Result<types::DirectoryEntry, ReaddirError>>>(),
184 )
185 })
186 .await?
187 .into_iter();
188
189 #[cfg(windows)]
192 let entries = entries.filter(|entry| {
193 use windows_sys::Win32::Foundation::{ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION};
194 if let Err(ReaddirError::Io(err)) = entry {
195 if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32)
196 || err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32)
197 {
198 return false;
199 }
200 }
201 true
202 });
203 let entries = entries.map(|r| match r {
204 Ok(r) => Ok(r),
205 Err(ReaddirError::Io(e)) => Err(e.into()),
206 Err(ReaddirError::IllegalSequence) => Err(ErrorCode::IllegalByteSequence.into()),
207 });
208 Ok(self.table.push(ReaddirIterator::new(entries))?)
209 }
210
211 async fn sync(&mut self, fd: Resource<types::Descriptor>) -> FsResult<()> {
212 let descriptor = self.table.get(&fd)?;
213 descriptor.sync().await?;
214 Ok(())
215 }
216
217 async fn create_directory_at(
218 &mut self,
219 fd: Resource<types::Descriptor>,
220 path: String,
221 ) -> FsResult<()> {
222 let d = self.table.get(&fd)?.dir()?;
223 d.create_directory_at(path).await?;
224 Ok(())
225 }
226
227 async fn stat(&mut self, fd: Resource<types::Descriptor>) -> FsResult<types::DescriptorStat> {
228 let descriptor = self.table.get(&fd)?;
229 let stat = descriptor.stat().await?;
230 Ok(stat.try_into()?)
231 }
232
233 async fn stat_at(
234 &mut self,
235 fd: Resource<types::Descriptor>,
236 path_flags: types::PathFlags,
237 path: String,
238 ) -> FsResult<types::DescriptorStat> {
239 let d = self.table.get(&fd)?.dir()?;
240 let stat = d.stat_at(path_flags.into(), path).await?;
241 Ok(stat.try_into()?)
242 }
243
244 async fn set_times_at(
245 &mut self,
246 fd: Resource<types::Descriptor>,
247 path_flags: types::PathFlags,
248 path: String,
249 atim: types::NewTimestamp,
250 mtim: types::NewTimestamp,
251 ) -> FsResult<()> {
252 let d = self.table.get(&fd)?.dir()?;
253 let atim = systemtimespec_from(atim)?;
254 let mtim = systemtimespec_from(mtim)?;
255 d.set_times_at(path_flags.into(), path, atim, mtim).await?;
256 Ok(())
257 }
258
259 async fn link_at(
260 &mut self,
261 fd: Resource<types::Descriptor>,
262 old_path_flags: types::PathFlags,
263 old_path: String,
264 new_descriptor: Resource<types::Descriptor>,
265 new_path: String,
266 ) -> FsResult<()> {
267 let old_dir = self.table.get(&fd)?.dir()?;
268 let new_dir = self.table.get(&new_descriptor)?.dir()?;
269 old_dir
270 .link_at(old_path_flags.into(), old_path, new_dir, new_path)
271 .await?;
272 Ok(())
273 }
274
275 async fn open_at(
276 &mut self,
277 fd: Resource<types::Descriptor>,
278 path_flags: types::PathFlags,
279 path: String,
280 oflags: types::OpenFlags,
281 flags: types::DescriptorFlags,
282 ) -> FsResult<Resource<types::Descriptor>> {
283 let d = self.table.get(&fd)?.dir()?;
284 let fd = d
285 .open_at(
286 path_flags.into(),
287 path,
288 oflags.into(),
289 flags.into(),
290 self.ctx.allow_blocking_current_thread,
291 )
292 .await?;
293 let fd = self.table.push(fd)?;
294 Ok(fd)
295 }
296
297 fn drop(&mut self, fd: Resource<types::Descriptor>) -> wasmtime::Result<()> {
298 self.table.delete(fd)?;
304
305 Ok(())
306 }
307
308 async fn readlink_at(
309 &mut self,
310 fd: Resource<types::Descriptor>,
311 path: String,
312 ) -> FsResult<String> {
313 let d = self.table.get(&fd)?.dir()?;
314 let path = d.readlink_at(path).await?;
315 Ok(path)
316 }
317
318 async fn remove_directory_at(
319 &mut self,
320 fd: Resource<types::Descriptor>,
321 path: String,
322 ) -> FsResult<()> {
323 let d = self.table.get(&fd)?.dir()?;
324 d.remove_directory_at(path).await?;
325 Ok(())
326 }
327
328 async fn rename_at(
329 &mut self,
330 fd: Resource<types::Descriptor>,
331 old_path: String,
332 new_fd: Resource<types::Descriptor>,
333 new_path: String,
334 ) -> FsResult<()> {
335 let old_dir = self.table.get(&fd)?.dir()?;
336 let new_dir = self.table.get(&new_fd)?.dir()?;
337 old_dir.rename_at(old_path, new_dir, new_path).await?;
338 Ok(())
339 }
340
341 async fn symlink_at(
342 &mut self,
343 fd: Resource<types::Descriptor>,
344 src_path: String,
345 dest_path: String,
346 ) -> FsResult<()> {
347 let d = self.table.get(&fd)?.dir()?;
348 d.symlink_at(src_path, dest_path).await?;
349 Ok(())
350 }
351
352 async fn unlink_file_at(
353 &mut self,
354 fd: Resource<types::Descriptor>,
355 path: String,
356 ) -> FsResult<()> {
357 let d = self.table.get(&fd)?.dir()?;
358 d.unlink_file_at(path).await?;
359 Ok(())
360 }
361
362 fn read_via_stream(
363 &mut self,
364 fd: Resource<types::Descriptor>,
365 offset: types::Filesize,
366 ) -> FsResult<Resource<DynInputStream>> {
367 let f = match self.table.get(&fd)? {
370 Descriptor::File(f) => f,
371 Descriptor::Dir(_) => return Err(ErrorCode::IsDirectory.into()),
372 };
373
374 let reader: DynInputStream = Box::new(FileInputStream::new(f, offset));
376
377 let index = self.table.push(reader)?;
379
380 Ok(index)
381 }
382
383 fn write_via_stream(
384 &mut self,
385 fd: Resource<types::Descriptor>,
386 offset: types::Filesize,
387 ) -> FsResult<Resource<DynOutputStream>> {
388 let f = self.table.get(&fd)?.file()?;
390
391 if f.perms.write_not_permitted() {
392 Err(types::ErrorCode::NotPermitted)?;
393 }
394
395 let writer = FileOutputStream::write_at(f, offset);
397 let writer: DynOutputStream = Box::new(writer);
398
399 let index = self.table.push(writer)?;
401
402 Ok(index)
403 }
404
405 fn append_via_stream(
406 &mut self,
407 fd: Resource<types::Descriptor>,
408 ) -> FsResult<Resource<DynOutputStream>> {
409 let f = self.table.get(&fd)?.file()?;
411
412 if f.perms.write_not_permitted() {
413 Err(types::ErrorCode::NotPermitted)?;
414 }
415
416 let appender = FileOutputStream::append(f);
418 let appender: DynOutputStream = Box::new(appender);
419
420 let index = self.table.push(appender)?;
422
423 Ok(index)
424 }
425
426 async fn is_same_object(
427 &mut self,
428 a: Resource<types::Descriptor>,
429 b: Resource<types::Descriptor>,
430 ) -> wasmtime::Result<bool> {
431 let descriptor_a = self.table.get(&a)?;
432 let descriptor_b = self.table.get(&b)?;
433 descriptor_a.is_same_object(descriptor_b).await
434 }
435 async fn metadata_hash(
436 &mut self,
437 fd: Resource<types::Descriptor>,
438 ) -> FsResult<types::MetadataHashValue> {
439 let fd = self.table.get(&fd)?;
440 let meta = fd.metadata_hash().await?;
441 Ok(meta.into())
442 }
443 async fn metadata_hash_at(
444 &mut self,
445 fd: Resource<types::Descriptor>,
446 path_flags: types::PathFlags,
447 path: String,
448 ) -> FsResult<types::MetadataHashValue> {
449 let d = self.table.get(&fd)?.dir()?;
450 let meta = d.metadata_hash_at(path_flags.into(), path).await?;
451 Ok(meta.into())
452 }
453}
454
455impl HostDirectoryEntryStream for WasiFilesystemCtxView<'_> {
456 async fn read_directory_entry(
457 &mut self,
458 stream: Resource<types::DirectoryEntryStream>,
459 ) -> FsResult<Option<types::DirectoryEntry>> {
460 let readdir = self.table.get(&stream)?;
461 readdir.next()
462 }
463
464 fn drop(&mut self, stream: Resource<types::DirectoryEntryStream>) -> wasmtime::Result<()> {
465 self.table.delete(stream)?;
466 Ok(())
467 }
468}
469
470impl From<types::Advice> for crate::filesystem::Advice {
471 fn from(advice: types::Advice) -> Self {
472 match advice {
473 types::Advice::Normal => Self::Normal,
474 types::Advice::Sequential => Self::Sequential,
475 types::Advice::Random => Self::Random,
476 types::Advice::WillNeed => Self::WillNeed,
477 types::Advice::DontNeed => Self::DontNeed,
478 types::Advice::NoReuse => Self::NoReuse,
479 }
480 }
481}
482
483impl From<types::OpenFlags> for crate::filesystem::OpenFlags {
484 fn from(flags: types::OpenFlags) -> Self {
485 let mut out = Self::empty();
486 if flags.contains(types::OpenFlags::CREATE) {
487 out |= Self::CREATE;
488 }
489 if flags.contains(types::OpenFlags::DIRECTORY) {
490 out |= Self::DIRECTORY;
491 }
492 if flags.contains(types::OpenFlags::EXCLUSIVE) {
493 out |= Self::EXCLUSIVE;
494 }
495 if flags.contains(types::OpenFlags::TRUNCATE) {
496 out |= Self::TRUNCATE;
497 }
498 out
499 }
500}
501
502impl From<types::PathFlags> for crate::filesystem::PathFlags {
503 fn from(flags: types::PathFlags) -> Self {
504 let mut out = Self::empty();
505 if flags.contains(types::PathFlags::SYMLINK_FOLLOW) {
506 out |= Self::SYMLINK_FOLLOW;
507 }
508 out
509 }
510}
511
512impl From<crate::filesystem::DescriptorFlags> for types::DescriptorFlags {
513 fn from(flags: crate::filesystem::DescriptorFlags) -> Self {
514 let mut out = Self::empty();
515 if flags.contains(crate::filesystem::DescriptorFlags::READ) {
516 out |= Self::READ;
517 }
518 if flags.contains(crate::filesystem::DescriptorFlags::WRITE) {
519 out |= Self::WRITE;
520 }
521 if flags.contains(crate::filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
522 out |= Self::FILE_INTEGRITY_SYNC;
523 }
524 if flags.contains(crate::filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
525 out |= Self::DATA_INTEGRITY_SYNC;
526 }
527 if flags.contains(crate::filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
528 out |= Self::REQUESTED_WRITE_SYNC;
529 }
530 if flags.contains(crate::filesystem::DescriptorFlags::MUTATE_DIRECTORY) {
531 out |= Self::MUTATE_DIRECTORY;
532 }
533 out
534 }
535}
536
537impl From<types::DescriptorFlags> for crate::filesystem::DescriptorFlags {
538 fn from(flags: types::DescriptorFlags) -> Self {
539 let mut out = Self::empty();
540 if flags.contains(types::DescriptorFlags::READ) {
541 out |= Self::READ;
542 }
543 if flags.contains(types::DescriptorFlags::WRITE) {
544 out |= Self::WRITE;
545 }
546 if flags.contains(types::DescriptorFlags::FILE_INTEGRITY_SYNC) {
547 out |= Self::FILE_INTEGRITY_SYNC;
548 }
549 if flags.contains(types::DescriptorFlags::DATA_INTEGRITY_SYNC) {
550 out |= Self::DATA_INTEGRITY_SYNC;
551 }
552 if flags.contains(types::DescriptorFlags::REQUESTED_WRITE_SYNC) {
553 out |= Self::REQUESTED_WRITE_SYNC;
554 }
555 if flags.contains(types::DescriptorFlags::MUTATE_DIRECTORY) {
556 out |= Self::MUTATE_DIRECTORY;
557 }
558 out
559 }
560}
561
562impl From<crate::filesystem::MetadataHashValue> for types::MetadataHashValue {
563 fn from(
564 crate::filesystem::MetadataHashValue { lower, upper }: crate::filesystem::MetadataHashValue,
565 ) -> Self {
566 Self { lower, upper }
567 }
568}
569
570impl TryFrom<crate::filesystem::DescriptorStat> for types::DescriptorStat {
571 type Error = ErrorCode;
572
573 fn try_from(
574 crate::filesystem::DescriptorStat {
575 type_,
576 link_count,
577 size,
578 data_access_timestamp,
579 data_modification_timestamp,
580 status_change_timestamp,
581 }: crate::filesystem::DescriptorStat,
582 ) -> Result<Self, ErrorCode> {
583 Ok(Self {
587 type_: type_.into(),
588 link_count,
589 size,
590 data_access_timestamp: data_access_timestamp.and_then(|t| t.try_into().ok()),
591 data_modification_timestamp: data_modification_timestamp
592 .and_then(|t| t.try_into().ok()),
593 status_change_timestamp: status_change_timestamp.and_then(|t| t.try_into().ok()),
594 })
595 }
596}
597
598impl From<crate::filesystem::DescriptorType> for types::DescriptorType {
599 fn from(ty: crate::filesystem::DescriptorType) -> Self {
600 match ty {
601 crate::filesystem::DescriptorType::Unknown => Self::Unknown,
602 crate::filesystem::DescriptorType::BlockDevice => Self::BlockDevice,
603 crate::filesystem::DescriptorType::CharacterDevice => Self::CharacterDevice,
604 crate::filesystem::DescriptorType::Directory => Self::Directory,
605 crate::filesystem::DescriptorType::SymbolicLink => Self::SymbolicLink,
606 crate::filesystem::DescriptorType::RegularFile => Self::RegularFile,
607 }
608 }
609}
610
611#[cfg(unix)]
612fn from_raw_os_error(err: Option<i32>) -> Option<ErrorCode> {
613 use rustix::io::Errno as RustixErrno;
614 if err.is_none() {
615 return None;
616 }
617 Some(match RustixErrno::from_raw_os_error(err.unwrap()) {
618 RustixErrno::PIPE => ErrorCode::Pipe,
619 RustixErrno::PERM => ErrorCode::NotPermitted,
620 RustixErrno::NOENT => ErrorCode::NoEntry,
621 RustixErrno::NOMEM => ErrorCode::InsufficientMemory,
622 RustixErrno::IO => ErrorCode::Io,
623 RustixErrno::BADF => ErrorCode::BadDescriptor,
624 RustixErrno::BUSY => ErrorCode::Busy,
625 RustixErrno::ACCESS => ErrorCode::Access,
626 RustixErrno::NOTDIR => ErrorCode::NotDirectory,
627 RustixErrno::ISDIR => ErrorCode::IsDirectory,
628 RustixErrno::INVAL => ErrorCode::Invalid,
629 RustixErrno::EXIST => ErrorCode::Exist,
630 RustixErrno::FBIG => ErrorCode::FileTooLarge,
631 RustixErrno::NOSPC => ErrorCode::InsufficientSpace,
632 RustixErrno::SPIPE => ErrorCode::InvalidSeek,
633 RustixErrno::MLINK => ErrorCode::TooManyLinks,
634 RustixErrno::NAMETOOLONG => ErrorCode::NameTooLong,
635 RustixErrno::NOTEMPTY => ErrorCode::NotEmpty,
636 RustixErrno::LOOP => ErrorCode::Loop,
637 RustixErrno::OVERFLOW => ErrorCode::Overflow,
638 RustixErrno::ILSEQ => ErrorCode::IllegalByteSequence,
639 RustixErrno::NOTSUP => ErrorCode::Unsupported,
640 RustixErrno::ALREADY => ErrorCode::Already,
641 RustixErrno::INPROGRESS => ErrorCode::InProgress,
642 RustixErrno::INTR => ErrorCode::Interrupted,
643
644 #[allow(
645 unreachable_patterns,
646 reason = "on some platforms, these have the same value as other errno values"
647 )]
648 RustixErrno::OPNOTSUPP => ErrorCode::Unsupported,
649
650 _ => return None,
651 })
652}
653#[cfg(windows)]
654fn from_raw_os_error(raw_os_error: Option<i32>) -> Option<ErrorCode> {
655 use windows_sys::Win32::Foundation;
656 Some(match raw_os_error.map(|code| code as u32) {
657 Some(Foundation::ERROR_FILE_NOT_FOUND) => ErrorCode::NoEntry,
658 Some(Foundation::ERROR_PATH_NOT_FOUND) => ErrorCode::NoEntry,
659 Some(Foundation::ERROR_ACCESS_DENIED) => ErrorCode::Access,
660 Some(Foundation::ERROR_SHARING_VIOLATION) => ErrorCode::Access,
661 Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => ErrorCode::NotPermitted,
662 Some(Foundation::ERROR_INVALID_HANDLE) => ErrorCode::BadDescriptor,
663 Some(Foundation::ERROR_INVALID_NAME) => ErrorCode::NoEntry,
664 Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => ErrorCode::InsufficientMemory,
665 Some(Foundation::ERROR_OUTOFMEMORY) => ErrorCode::InsufficientMemory,
666 Some(Foundation::ERROR_DIR_NOT_EMPTY) => ErrorCode::NotEmpty,
667 Some(Foundation::ERROR_NOT_READY) => ErrorCode::Busy,
668 Some(Foundation::ERROR_BUSY) => ErrorCode::Busy,
669 Some(Foundation::ERROR_NOT_SUPPORTED) => ErrorCode::Unsupported,
670 Some(Foundation::ERROR_FILE_EXISTS) => ErrorCode::Exist,
671 Some(Foundation::ERROR_BROKEN_PIPE) => ErrorCode::Pipe,
672 Some(Foundation::ERROR_BUFFER_OVERFLOW) => ErrorCode::NameTooLong,
673 Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => ErrorCode::Invalid,
674 Some(Foundation::ERROR_NEGATIVE_SEEK) => ErrorCode::Invalid,
675 Some(Foundation::ERROR_DIRECTORY) => ErrorCode::NotDirectory,
676 Some(Foundation::ERROR_ALREADY_EXISTS) => ErrorCode::Exist,
677 Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => ErrorCode::Loop,
678 Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => ErrorCode::IsDirectory,
679 _ => return None,
680 })
681}
682
683impl From<std::io::Error> for ErrorCode {
684 fn from(err: std::io::Error) -> ErrorCode {
685 ErrorCode::from(&err)
686 }
687}
688
689impl<'a> From<&'a std::io::Error> for ErrorCode {
690 fn from(err: &'a std::io::Error) -> ErrorCode {
691 match from_raw_os_error(err.raw_os_error()) {
692 Some(errno) => errno,
693 None => {
694 tracing::debug!("unknown raw os error: {err}");
695 match err.kind() {
696 std::io::ErrorKind::NotFound => ErrorCode::NoEntry,
697 std::io::ErrorKind::PermissionDenied => ErrorCode::NotPermitted,
698 std::io::ErrorKind::AlreadyExists => ErrorCode::Exist,
699 std::io::ErrorKind::InvalidInput => ErrorCode::Invalid,
700 _ => ErrorCode::Io,
701 }
702 }
703 }
704 }
705}
706
707impl From<std::num::TryFromIntError> for ErrorCode {
708 fn from(_err: std::num::TryFromIntError) -> ErrorCode {
709 ErrorCode::Overflow
710 }
711}
712
713fn descriptortype_from(ft: crate::filesystem::primitives::FileType) -> types::DescriptorType {
714 crate::filesystem::DescriptorType::from(ft).into()
715}
716
717fn systemtime_from(t: wall_clock::Datetime) -> Result<std::time::SystemTime, ErrorCode> {
718 let duration = core::time::Duration::new(
721 t.seconds
722 .checked_add(u64::from(t.nanoseconds / 1_000_000_000))
723 .ok_or(ErrorCode::Overflow)?,
724 t.nanoseconds % 1_000_000_000,
725 );
726 std::time::SystemTime::UNIX_EPOCH
727 .checked_add(duration)
728 .ok_or(ErrorCode::Overflow)
729}
730
731fn systemtimespec_from(t: types::NewTimestamp) -> Result<Option<SystemTime>, ErrorCode> {
732 match t {
733 types::NewTimestamp::NoChange => Ok(None),
734 types::NewTimestamp::Now => Ok(Some(SystemTime::now())),
735 types::NewTimestamp::Timestamp(st) => {
736 let st = systemtime_from(st)?;
737 Ok(Some(st))
738 }
739 }
740}
741
742impl From<crate::clocks::DatetimeError> for ErrorCode {
743 fn from(_: crate::clocks::DatetimeError) -> ErrorCode {
744 ErrorCode::Overflow
745 }
746}
747
748#[cfg(test)]
749mod test {
750 use super::*;
751 use wasmtime::component::ResourceTable;
752
753 #[test]
754 fn table_readdir_works() {
755 let mut table = ResourceTable::new();
756 let ix = table
757 .push(ReaddirIterator::new(std::iter::empty()))
758 .unwrap();
759 let _ = table.get(&ix).unwrap();
760 table.delete(ix).unwrap();
761 }
762}
763
764mod named {
765 use crate::filesystem::WasiFilesystemNamedView;
766 use crate::p2::bindings::filesystem::types::{
767 Advice, Descriptor, DescriptorFlags, DescriptorStat, DescriptorType, DirectoryEntry,
768 DirectoryEntryStream, ErrorCode, Filesize, MetadataHashValue, NewTimestamp, OpenFlags,
769 PathFlags,
770 };
771 use crate::p2::bindings::named_imports::wasi::filesystem::{preopens, types};
772 use crate::p2::{FsError, FsResult};
773 use crate::{NamedId, WasiCtxNamedView};
774 use wasmtime::component::Resource;
775 use wasmtime_wasi_io::streams::{DynInputStream, DynOutputStream};
776
777 impl<T> preopens::Host for WasiCtxNamedView<'_, T>
778 where
779 T: WasiFilesystemNamedView,
780 {
781 fn get_directories(
782 &mut self,
783 id: NamedId,
784 ) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
785 super::preopens::Host::get_directories(&mut self.0.filesystem(id))
786 }
787 }
788
789 impl<T> types::Host for WasiCtxNamedView<'_, T>
790 where
791 T: WasiFilesystemNamedView,
792 {
793 fn convert_error_code(&mut self, err: FsError) -> wasmtime::Result<ErrorCode> {
794 err.downcast()
795 }
796
797 fn filesystem_error_code(
798 &mut self,
799 id: NamedId,
800 err: Resource<wasmtime::Error>,
801 ) -> wasmtime::Result<Option<ErrorCode>> {
802 super::types::Host::filesystem_error_code(&mut self.0.filesystem(id), err)
803 }
804 }
805
806 impl<T> types::HostDescriptor for WasiCtxNamedView<'_, T>
807 where
808 T: WasiFilesystemNamedView,
809 {
810 async fn advise(
811 &mut self,
812 id: NamedId,
813 fd: Resource<Descriptor>,
814 offset: Filesize,
815 len: Filesize,
816 advice: Advice,
817 ) -> FsResult<()> {
818 super::HostDescriptor::advise(&mut self.0.filesystem(id), fd, offset, len, advice).await
819 }
820
821 async fn sync_data(&mut self, id: NamedId, fd: Resource<Descriptor>) -> FsResult<()> {
822 super::HostDescriptor::sync_data(&mut self.0.filesystem(id), fd).await
823 }
824
825 async fn get_flags(
826 &mut self,
827 id: NamedId,
828 fd: Resource<Descriptor>,
829 ) -> FsResult<DescriptorFlags> {
830 super::HostDescriptor::get_flags(&mut self.0.filesystem(id), fd).await
831 }
832
833 async fn get_type(
834 &mut self,
835 id: NamedId,
836 fd: Resource<Descriptor>,
837 ) -> FsResult<DescriptorType> {
838 super::HostDescriptor::get_type(&mut self.0.filesystem(id), fd).await
839 }
840
841 async fn set_size(
842 &mut self,
843 id: NamedId,
844 fd: Resource<Descriptor>,
845 size: Filesize,
846 ) -> FsResult<()> {
847 super::HostDescriptor::set_size(&mut self.0.filesystem(id), fd, size).await
848 }
849
850 async fn set_times(
851 &mut self,
852 id: NamedId,
853 fd: Resource<Descriptor>,
854 atim: NewTimestamp,
855 mtim: NewTimestamp,
856 ) -> FsResult<()> {
857 super::HostDescriptor::set_times(&mut self.0.filesystem(id), fd, atim, mtim).await
858 }
859
860 async fn read(
861 &mut self,
862 id: NamedId,
863 fd: Resource<Descriptor>,
864 len: Filesize,
865 offset: Filesize,
866 ) -> FsResult<(Vec<u8>, bool)> {
867 super::HostDescriptor::read(&mut self.0.filesystem(id), fd, len, offset).await
868 }
869
870 async fn write(
871 &mut self,
872 id: NamedId,
873 fd: Resource<Descriptor>,
874 buf: Vec<u8>,
875 offset: Filesize,
876 ) -> FsResult<Filesize> {
877 super::HostDescriptor::write(&mut self.0.filesystem(id), fd, buf, offset).await
878 }
879
880 async fn read_directory(
881 &mut self,
882 id: NamedId,
883 fd: Resource<Descriptor>,
884 ) -> FsResult<Resource<DirectoryEntryStream>> {
885 super::HostDescriptor::read_directory(&mut self.0.filesystem(id), fd).await
886 }
887
888 async fn sync(&mut self, id: NamedId, fd: Resource<Descriptor>) -> FsResult<()> {
889 super::HostDescriptor::sync(&mut self.0.filesystem(id), fd).await
890 }
891
892 async fn create_directory_at(
893 &mut self,
894 id: NamedId,
895 fd: Resource<Descriptor>,
896 path: String,
897 ) -> FsResult<()> {
898 super::HostDescriptor::create_directory_at(&mut self.0.filesystem(id), fd, path).await
899 }
900
901 async fn stat(
902 &mut self,
903 id: NamedId,
904 fd: Resource<Descriptor>,
905 ) -> FsResult<DescriptorStat> {
906 super::HostDescriptor::stat(&mut self.0.filesystem(id), fd).await
907 }
908
909 async fn stat_at(
910 &mut self,
911 id: NamedId,
912 fd: Resource<Descriptor>,
913 path_flags: PathFlags,
914 path: String,
915 ) -> FsResult<DescriptorStat> {
916 super::HostDescriptor::stat_at(&mut self.0.filesystem(id), fd, path_flags, path).await
917 }
918
919 async fn set_times_at(
920 &mut self,
921 id: NamedId,
922 fd: Resource<Descriptor>,
923 path_flags: PathFlags,
924 path: String,
925 atim: NewTimestamp,
926 mtim: NewTimestamp,
927 ) -> FsResult<()> {
928 super::HostDescriptor::set_times_at(
929 &mut self.0.filesystem(id),
930 fd,
931 path_flags,
932 path,
933 atim,
934 mtim,
935 )
936 .await
937 }
938
939 async fn link_at(
940 &mut self,
941 id: NamedId,
942 fd: Resource<Descriptor>,
943 old_path_flags: PathFlags,
944 old_path: String,
945 new_descriptor: Resource<Descriptor>,
946 new_path: String,
947 ) -> FsResult<()> {
948 super::HostDescriptor::link_at(
949 &mut self.0.filesystem(id),
950 fd,
951 old_path_flags,
952 old_path,
953 new_descriptor,
954 new_path,
955 )
956 .await
957 }
958
959 async fn open_at(
960 &mut self,
961 id: NamedId,
962 fd: Resource<Descriptor>,
963 path_flags: PathFlags,
964 path: String,
965 oflags: OpenFlags,
966 flags: DescriptorFlags,
967 ) -> FsResult<Resource<Descriptor>> {
968 super::HostDescriptor::open_at(
969 &mut self.0.filesystem(id),
970 fd,
971 path_flags,
972 path,
973 oflags,
974 flags,
975 )
976 .await
977 }
978
979 fn drop(&mut self, id: NamedId, fd: Resource<Descriptor>) -> wasmtime::Result<()> {
980 super::HostDescriptor::drop(&mut self.0.filesystem(id), fd)
981 }
982
983 async fn readlink_at(
984 &mut self,
985 id: NamedId,
986 fd: Resource<Descriptor>,
987 path: String,
988 ) -> FsResult<String> {
989 super::HostDescriptor::readlink_at(&mut self.0.filesystem(id), fd, path).await
990 }
991
992 async fn remove_directory_at(
993 &mut self,
994 id: NamedId,
995 fd: Resource<Descriptor>,
996 path: String,
997 ) -> FsResult<()> {
998 super::HostDescriptor::remove_directory_at(&mut self.0.filesystem(id), fd, path).await
999 }
1000
1001 async fn rename_at(
1002 &mut self,
1003 id: NamedId,
1004 fd: Resource<Descriptor>,
1005 old_path: String,
1006 new_fd: Resource<Descriptor>,
1007 new_path: String,
1008 ) -> FsResult<()> {
1009 super::HostDescriptor::rename_at(
1010 &mut self.0.filesystem(id),
1011 fd,
1012 old_path,
1013 new_fd,
1014 new_path,
1015 )
1016 .await
1017 }
1018
1019 async fn symlink_at(
1020 &mut self,
1021 id: NamedId,
1022 fd: Resource<Descriptor>,
1023 src_path: String,
1024 dest_path: String,
1025 ) -> FsResult<()> {
1026 super::HostDescriptor::symlink_at(&mut self.0.filesystem(id), fd, src_path, dest_path)
1027 .await
1028 }
1029
1030 async fn unlink_file_at(
1031 &mut self,
1032 id: NamedId,
1033 fd: Resource<Descriptor>,
1034 path: String,
1035 ) -> FsResult<()> {
1036 super::HostDescriptor::unlink_file_at(&mut self.0.filesystem(id), fd, path).await
1037 }
1038
1039 fn read_via_stream(
1040 &mut self,
1041 id: NamedId,
1042 fd: Resource<Descriptor>,
1043 offset: Filesize,
1044 ) -> FsResult<Resource<DynInputStream>> {
1045 super::HostDescriptor::read_via_stream(&mut self.0.filesystem(id), fd, offset)
1046 }
1047
1048 fn write_via_stream(
1049 &mut self,
1050 id: NamedId,
1051 fd: Resource<Descriptor>,
1052 offset: Filesize,
1053 ) -> FsResult<Resource<DynOutputStream>> {
1054 super::HostDescriptor::write_via_stream(&mut self.0.filesystem(id), fd, offset)
1055 }
1056
1057 fn append_via_stream(
1058 &mut self,
1059 id: NamedId,
1060 fd: Resource<Descriptor>,
1061 ) -> FsResult<Resource<DynOutputStream>> {
1062 super::HostDescriptor::append_via_stream(&mut self.0.filesystem(id), fd)
1063 }
1064
1065 async fn is_same_object(
1066 &mut self,
1067 id: NamedId,
1068 a: Resource<Descriptor>,
1069 b: Resource<Descriptor>,
1070 ) -> wasmtime::Result<bool> {
1071 super::HostDescriptor::is_same_object(&mut self.0.filesystem(id), a, b).await
1072 }
1073
1074 async fn metadata_hash(
1075 &mut self,
1076 id: NamedId,
1077 fd: Resource<Descriptor>,
1078 ) -> FsResult<MetadataHashValue> {
1079 super::HostDescriptor::metadata_hash(&mut self.0.filesystem(id), fd).await
1080 }
1081
1082 async fn metadata_hash_at(
1083 &mut self,
1084 id: NamedId,
1085 fd: Resource<Descriptor>,
1086 path_flags: PathFlags,
1087 path: String,
1088 ) -> FsResult<MetadataHashValue> {
1089 super::HostDescriptor::metadata_hash_at(
1090 &mut self.0.filesystem(id),
1091 fd,
1092 path_flags,
1093 path,
1094 )
1095 .await
1096 }
1097 }
1098
1099 impl<T> types::HostDirectoryEntryStream for WasiCtxNamedView<'_, T>
1100 where
1101 T: WasiFilesystemNamedView,
1102 {
1103 async fn read_directory_entry(
1104 &mut self,
1105 id: NamedId,
1106 stream: Resource<DirectoryEntryStream>,
1107 ) -> FsResult<Option<DirectoryEntry>> {
1108 super::HostDirectoryEntryStream::read_directory_entry(
1109 &mut self.0.filesystem(id),
1110 stream,
1111 )
1112 .await
1113 }
1114
1115 fn drop(
1116 &mut self,
1117 id: NamedId,
1118 stream: Resource<DirectoryEntryStream>,
1119 ) -> wasmtime::Result<()> {
1120 super::HostDirectoryEntryStream::drop(&mut self.0.filesystem(id), stream)
1121 }
1122 }
1123}