1use crate::filesystem::{Descriptor, WasiFilesystemCtxView};
2use crate::p2::bindings::clocks::wall_clock;
3use crate::p2::bindings::filesystem::preopens;
4use crate::p2::bindings::filesystem::types::{
5 self, ErrorCode, HostDescriptor, HostDirectoryEntryStream,
6};
7use crate::p2::filesystem::{FileInputStream, FileOutputStream, ReaddirIterator};
8use crate::p2::{FsError, FsResult};
9use crate::{DirPerms, FilePerms};
10use wasmtime::component::Resource;
11use wasmtime_wasi_io::streams::{DynInputStream, DynOutputStream};
12
13mod sync;
14
15impl preopens::Host for WasiFilesystemCtxView<'_> {
16 fn get_directories(&mut self) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
17 self.get_directories()
18 }
19}
20
21impl types::Host for WasiFilesystemCtxView<'_> {
22 fn convert_error_code(&mut self, err: FsError) -> anyhow::Result<ErrorCode> {
23 err.downcast()
24 }
25
26 fn filesystem_error_code(
27 &mut self,
28 err: Resource<anyhow::Error>,
29 ) -> anyhow::Result<Option<ErrorCode>> {
30 let err = self.table.get(&err)?;
31
32 if let Some(err) = err.downcast_ref::<std::io::Error>() {
35 return Ok(Some(ErrorCode::from(err)));
36 }
37
38 Ok(None)
39 }
40}
41
42impl HostDescriptor for WasiFilesystemCtxView<'_> {
43 async fn advise(
44 &mut self,
45 fd: Resource<types::Descriptor>,
46 offset: types::Filesize,
47 len: types::Filesize,
48 advice: types::Advice,
49 ) -> FsResult<()> {
50 let f = self.table.get(&fd)?.file()?;
51 f.advise(offset, len, advice.into()).await?;
52 Ok(())
53 }
54
55 async fn sync_data(&mut self, fd: Resource<types::Descriptor>) -> FsResult<()> {
56 let descriptor = self.table.get(&fd)?;
57 descriptor.sync_data().await?;
58 Ok(())
59 }
60
61 async fn get_flags(
62 &mut self,
63 fd: Resource<types::Descriptor>,
64 ) -> FsResult<types::DescriptorFlags> {
65 let descriptor = self.table.get(&fd)?;
66 let flags = descriptor.get_flags().await?;
67 Ok(flags.into())
68 }
69
70 async fn get_type(
71 &mut self,
72 fd: Resource<types::Descriptor>,
73 ) -> FsResult<types::DescriptorType> {
74 let descriptor = self.table.get(&fd)?;
75 let ty = descriptor.get_type().await?;
76 Ok(ty.into())
77 }
78
79 async fn set_size(
80 &mut self,
81 fd: Resource<types::Descriptor>,
82 size: types::Filesize,
83 ) -> FsResult<()> {
84 let f = self.table.get(&fd)?.file()?;
85 f.set_size(size).await?;
86 Ok(())
87 }
88
89 async fn set_times(
90 &mut self,
91 fd: Resource<types::Descriptor>,
92 atim: types::NewTimestamp,
93 mtim: types::NewTimestamp,
94 ) -> FsResult<()> {
95 let descriptor = self.table.get(&fd)?;
96 let atim = systemtimespec_from(atim)?;
97 let mtim = systemtimespec_from(mtim)?;
98 descriptor.set_times(atim, mtim).await?;
99 Ok(())
100 }
101
102 async fn read(
103 &mut self,
104 fd: Resource<types::Descriptor>,
105 len: types::Filesize,
106 offset: types::Filesize,
107 ) -> FsResult<(Vec<u8>, bool)> {
108 use std::io::IoSliceMut;
109 use system_interface::fs::FileIoExt;
110
111 let f = self.table.get(&fd)?.file()?;
112 if !f.perms.contains(FilePerms::READ) {
113 return Err(ErrorCode::NotPermitted.into());
114 }
115
116 let (mut buffer, r) = f
117 .run_blocking(move |f| {
118 let mut buffer = vec![0; len.try_into().unwrap_or(usize::MAX)];
119 let r = f.read_vectored_at(&mut [IoSliceMut::new(&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 use std::io::IoSlice;
141 use system_interface::fs::FileIoExt;
142
143 let f = self.table.get(&fd)?.file()?;
144 if !f.perms.contains(FilePerms::WRITE) {
145 return Err(ErrorCode::NotPermitted.into());
146 }
147
148 let bytes_written = f
149 .run_blocking(move |f| f.write_vectored_at(&[IoSlice::new(&buf)], offset))
150 .await?;
151
152 Ok(types::Filesize::try_from(bytes_written).expect("usize fits in Filesize"))
153 }
154
155 async fn read_directory(
156 &mut self,
157 fd: Resource<types::Descriptor>,
158 ) -> FsResult<Resource<types::DirectoryEntryStream>> {
159 let d = self.table.get(&fd)?.dir()?;
160 if !d.perms.contains(DirPerms::READ) {
161 return Err(ErrorCode::NotPermitted.into());
162 }
163
164 enum ReaddirError {
165 Io(std::io::Error),
166 IllegalSequence,
167 }
168 impl From<std::io::Error> for ReaddirError {
169 fn from(e: std::io::Error) -> ReaddirError {
170 ReaddirError::Io(e)
171 }
172 }
173
174 let entries = d
175 .run_blocking(|d| {
176 Ok::<_, std::io::Error>(
180 d.entries()?
181 .map(|entry| {
182 let entry = entry?;
183 let meta = entry.metadata()?;
184 let type_ = descriptortype_from(meta.file_type());
185 let name = entry
186 .file_name()
187 .into_string()
188 .map_err(|_| ReaddirError::IllegalSequence)?;
189 Ok(types::DirectoryEntry { type_, name })
190 })
191 .collect::<Vec<Result<types::DirectoryEntry, ReaddirError>>>(),
192 )
193 })
194 .await?
195 .into_iter();
196
197 #[cfg(windows)]
200 let entries = entries.filter(|entry| {
201 use windows_sys::Win32::Foundation::{ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION};
202 if let Err(ReaddirError::Io(err)) = entry {
203 if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32)
204 || err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32)
205 {
206 return false;
207 }
208 }
209 true
210 });
211 let entries = entries.map(|r| match r {
212 Ok(r) => Ok(r),
213 Err(ReaddirError::Io(e)) => Err(e.into()),
214 Err(ReaddirError::IllegalSequence) => Err(ErrorCode::IllegalByteSequence.into()),
215 });
216 Ok(self.table.push(ReaddirIterator::new(entries))?)
217 }
218
219 async fn sync(&mut self, fd: Resource<types::Descriptor>) -> FsResult<()> {
220 let descriptor = self.table.get(&fd)?;
221 descriptor.sync().await?;
222 Ok(())
223 }
224
225 async fn create_directory_at(
226 &mut self,
227 fd: Resource<types::Descriptor>,
228 path: String,
229 ) -> FsResult<()> {
230 let d = self.table.get(&fd)?.dir()?;
231 d.create_directory_at(path).await?;
232 Ok(())
233 }
234
235 async fn stat(&mut self, fd: Resource<types::Descriptor>) -> FsResult<types::DescriptorStat> {
236 let descriptor = self.table.get(&fd)?;
237 let stat = descriptor.stat().await?;
238 Ok(stat.into())
239 }
240
241 async fn stat_at(
242 &mut self,
243 fd: Resource<types::Descriptor>,
244 path_flags: types::PathFlags,
245 path: String,
246 ) -> FsResult<types::DescriptorStat> {
247 let d = self.table.get(&fd)?.dir()?;
248 let stat = d.stat_at(path_flags.into(), path).await?;
249 Ok(stat.into())
250 }
251
252 async fn set_times_at(
253 &mut self,
254 fd: Resource<types::Descriptor>,
255 path_flags: types::PathFlags,
256 path: String,
257 atim: types::NewTimestamp,
258 mtim: types::NewTimestamp,
259 ) -> FsResult<()> {
260 let d = self.table.get(&fd)?.dir()?;
261 let atim = systemtimespec_from(atim)?;
262 let mtim = systemtimespec_from(mtim)?;
263 d.set_times_at(path_flags.into(), path, atim, mtim).await?;
264 Ok(())
265 }
266
267 async fn link_at(
268 &mut self,
269 fd: Resource<types::Descriptor>,
270 old_path_flags: types::PathFlags,
272 old_path: String,
273 new_descriptor: Resource<types::Descriptor>,
274 new_path: String,
275 ) -> FsResult<()> {
276 let old_dir = self.table.get(&fd)?.dir()?;
277 let new_dir = self.table.get(&new_descriptor)?.dir()?;
278 old_dir
279 .link_at(old_path_flags.into(), old_path, new_dir, new_path)
280 .await?;
281 Ok(())
282 }
283
284 async fn open_at(
285 &mut self,
286 fd: Resource<types::Descriptor>,
287 path_flags: types::PathFlags,
288 path: String,
289 oflags: types::OpenFlags,
290 flags: types::DescriptorFlags,
291 ) -> FsResult<Resource<types::Descriptor>> {
292 let d = self.table.get(&fd)?.dir()?;
293 let fd = d
294 .open_at(
295 path_flags.into(),
296 path,
297 oflags.into(),
298 flags.into(),
299 self.ctx.allow_blocking_current_thread,
300 )
301 .await?;
302 let fd = self.table.push(fd)?;
303 Ok(fd)
304 }
305
306 fn drop(&mut self, fd: Resource<types::Descriptor>) -> anyhow::Result<()> {
307 self.table.delete(fd)?;
313
314 Ok(())
315 }
316
317 async fn readlink_at(
318 &mut self,
319 fd: Resource<types::Descriptor>,
320 path: String,
321 ) -> FsResult<String> {
322 let d = self.table.get(&fd)?.dir()?;
323 let path = d.readlink_at(path).await?;
324 Ok(path)
325 }
326
327 async fn remove_directory_at(
328 &mut self,
329 fd: Resource<types::Descriptor>,
330 path: String,
331 ) -> FsResult<()> {
332 let d = self.table.get(&fd)?.dir()?;
333 d.remove_directory_at(path).await?;
334 Ok(())
335 }
336
337 async fn rename_at(
338 &mut self,
339 fd: Resource<types::Descriptor>,
340 old_path: String,
341 new_fd: Resource<types::Descriptor>,
342 new_path: String,
343 ) -> FsResult<()> {
344 let old_dir = self.table.get(&fd)?.dir()?;
345 let new_dir = self.table.get(&new_fd)?.dir()?;
346 old_dir.rename_at(old_path, new_dir, new_path).await?;
347 Ok(())
348 }
349
350 async fn symlink_at(
351 &mut self,
352 fd: Resource<types::Descriptor>,
353 src_path: String,
354 dest_path: String,
355 ) -> FsResult<()> {
356 let d = self.table.get(&fd)?.dir()?;
357 d.symlink_at(src_path, dest_path).await?;
358 Ok(())
359 }
360
361 async fn unlink_file_at(
362 &mut self,
363 fd: Resource<types::Descriptor>,
364 path: String,
365 ) -> FsResult<()> {
366 let d = self.table.get(&fd)?.dir()?;
367 d.unlink_file_at(path).await?;
368 Ok(())
369 }
370
371 fn read_via_stream(
372 &mut self,
373 fd: Resource<types::Descriptor>,
374 offset: types::Filesize,
375 ) -> FsResult<Resource<DynInputStream>> {
376 let f = self.table.get(&fd)?.file()?;
378
379 if !f.perms.contains(FilePerms::READ) {
380 Err(types::ErrorCode::BadDescriptor)?;
381 }
382
383 let reader: DynInputStream = Box::new(FileInputStream::new(f, offset));
385
386 let index = self.table.push(reader)?;
388
389 Ok(index)
390 }
391
392 fn write_via_stream(
393 &mut self,
394 fd: Resource<types::Descriptor>,
395 offset: types::Filesize,
396 ) -> FsResult<Resource<DynOutputStream>> {
397 let f = self.table.get(&fd)?.file()?;
399
400 if !f.perms.contains(FilePerms::WRITE) {
401 Err(types::ErrorCode::BadDescriptor)?;
402 }
403
404 let writer = FileOutputStream::write_at(f, offset);
406 let writer: DynOutputStream = Box::new(writer);
407
408 let index = self.table.push(writer)?;
410
411 Ok(index)
412 }
413
414 fn append_via_stream(
415 &mut self,
416 fd: Resource<types::Descriptor>,
417 ) -> FsResult<Resource<DynOutputStream>> {
418 let f = self.table.get(&fd)?.file()?;
420
421 if !f.perms.contains(FilePerms::WRITE) {
422 Err(types::ErrorCode::BadDescriptor)?;
423 }
424
425 let appender = FileOutputStream::append(f);
427 let appender: DynOutputStream = Box::new(appender);
428
429 let index = self.table.push(appender)?;
431
432 Ok(index)
433 }
434
435 async fn is_same_object(
436 &mut self,
437 a: Resource<types::Descriptor>,
438 b: Resource<types::Descriptor>,
439 ) -> anyhow::Result<bool> {
440 let descriptor_a = self.table.get(&a)?;
441 let descriptor_b = self.table.get(&b)?;
442 descriptor_a.is_same_object(descriptor_b).await
443 }
444 async fn metadata_hash(
445 &mut self,
446 fd: Resource<types::Descriptor>,
447 ) -> FsResult<types::MetadataHashValue> {
448 let fd = self.table.get(&fd)?;
449 let meta = fd.metadata_hash().await?;
450 Ok(meta.into())
451 }
452 async fn metadata_hash_at(
453 &mut self,
454 fd: Resource<types::Descriptor>,
455 path_flags: types::PathFlags,
456 path: String,
457 ) -> FsResult<types::MetadataHashValue> {
458 let d = self.table.get(&fd)?.dir()?;
459 let meta = d.metadata_hash_at(path_flags.into(), path).await?;
460 Ok(meta.into())
461 }
462}
463
464impl HostDirectoryEntryStream for WasiFilesystemCtxView<'_> {
465 async fn read_directory_entry(
466 &mut self,
467 stream: Resource<types::DirectoryEntryStream>,
468 ) -> FsResult<Option<types::DirectoryEntry>> {
469 let readdir = self.table.get(&stream)?;
470 readdir.next()
471 }
472
473 fn drop(&mut self, stream: Resource<types::DirectoryEntryStream>) -> anyhow::Result<()> {
474 self.table.delete(stream)?;
475 Ok(())
476 }
477}
478
479impl From<types::Advice> for system_interface::fs::Advice {
480 fn from(advice: types::Advice) -> Self {
481 match advice {
482 types::Advice::Normal => Self::Normal,
483 types::Advice::Sequential => Self::Sequential,
484 types::Advice::Random => Self::Random,
485 types::Advice::WillNeed => Self::WillNeed,
486 types::Advice::DontNeed => Self::DontNeed,
487 types::Advice::NoReuse => Self::NoReuse,
488 }
489 }
490}
491
492impl From<types::OpenFlags> for crate::filesystem::OpenFlags {
493 fn from(flags: types::OpenFlags) -> Self {
494 let mut out = Self::empty();
495 if flags.contains(types::OpenFlags::CREATE) {
496 out |= Self::CREATE;
497 }
498 if flags.contains(types::OpenFlags::DIRECTORY) {
499 out |= Self::DIRECTORY;
500 }
501 if flags.contains(types::OpenFlags::EXCLUSIVE) {
502 out |= Self::EXCLUSIVE;
503 }
504 if flags.contains(types::OpenFlags::TRUNCATE) {
505 out |= Self::TRUNCATE;
506 }
507 out
508 }
509}
510
511impl From<types::PathFlags> for crate::filesystem::PathFlags {
512 fn from(flags: types::PathFlags) -> Self {
513 let mut out = Self::empty();
514 if flags.contains(types::PathFlags::SYMLINK_FOLLOW) {
515 out |= Self::SYMLINK_FOLLOW;
516 }
517 out
518 }
519}
520
521impl From<crate::filesystem::DescriptorFlags> for types::DescriptorFlags {
522 fn from(flags: crate::filesystem::DescriptorFlags) -> Self {
523 let mut out = Self::empty();
524 if flags.contains(crate::filesystem::DescriptorFlags::READ) {
525 out |= Self::READ;
526 }
527 if flags.contains(crate::filesystem::DescriptorFlags::WRITE) {
528 out |= Self::WRITE;
529 }
530 if flags.contains(crate::filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
531 out |= Self::FILE_INTEGRITY_SYNC;
532 }
533 if flags.contains(crate::filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
534 out |= Self::DATA_INTEGRITY_SYNC;
535 }
536 if flags.contains(crate::filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
537 out |= Self::REQUESTED_WRITE_SYNC;
538 }
539 if flags.contains(crate::filesystem::DescriptorFlags::MUTATE_DIRECTORY) {
540 out |= Self::MUTATE_DIRECTORY;
541 }
542 out
543 }
544}
545
546impl From<types::DescriptorFlags> for crate::filesystem::DescriptorFlags {
547 fn from(flags: types::DescriptorFlags) -> Self {
548 let mut out = Self::empty();
549 if flags.contains(types::DescriptorFlags::READ) {
550 out |= Self::READ;
551 }
552 if flags.contains(types::DescriptorFlags::WRITE) {
553 out |= Self::WRITE;
554 }
555 if flags.contains(types::DescriptorFlags::FILE_INTEGRITY_SYNC) {
556 out |= Self::FILE_INTEGRITY_SYNC;
557 }
558 if flags.contains(types::DescriptorFlags::DATA_INTEGRITY_SYNC) {
559 out |= Self::DATA_INTEGRITY_SYNC;
560 }
561 if flags.contains(types::DescriptorFlags::REQUESTED_WRITE_SYNC) {
562 out |= Self::REQUESTED_WRITE_SYNC;
563 }
564 if flags.contains(types::DescriptorFlags::MUTATE_DIRECTORY) {
565 out |= Self::MUTATE_DIRECTORY;
566 }
567 out
568 }
569}
570
571impl From<crate::filesystem::MetadataHashValue> for types::MetadataHashValue {
572 fn from(
573 crate::filesystem::MetadataHashValue { lower, upper }: crate::filesystem::MetadataHashValue,
574 ) -> Self {
575 Self { lower, upper }
576 }
577}
578
579impl From<crate::filesystem::DescriptorStat> for types::DescriptorStat {
580 fn from(
581 crate::filesystem::DescriptorStat {
582 type_,
583 link_count,
584 size,
585 data_access_timestamp,
586 data_modification_timestamp,
587 status_change_timestamp,
588 }: crate::filesystem::DescriptorStat,
589 ) -> Self {
590 Self {
591 type_: type_.into(),
592 link_count,
593 size,
594 data_access_timestamp: data_access_timestamp.map(Into::into),
595 data_modification_timestamp: data_modification_timestamp.map(Into::into),
596 status_change_timestamp: status_change_timestamp.map(Into::into),
597 }
598 }
599}
600
601impl From<crate::filesystem::DescriptorType> for types::DescriptorType {
602 fn from(ty: crate::filesystem::DescriptorType) -> Self {
603 match ty {
604 crate::filesystem::DescriptorType::Unknown => Self::Unknown,
605 crate::filesystem::DescriptorType::BlockDevice => Self::BlockDevice,
606 crate::filesystem::DescriptorType::CharacterDevice => Self::CharacterDevice,
607 crate::filesystem::DescriptorType::Directory => Self::Directory,
608 crate::filesystem::DescriptorType::SymbolicLink => Self::SymbolicLink,
609 crate::filesystem::DescriptorType::RegularFile => Self::RegularFile,
610 }
611 }
612}
613
614#[cfg(unix)]
615fn from_raw_os_error(err: Option<i32>) -> Option<ErrorCode> {
616 use rustix::io::Errno as RustixErrno;
617 if err.is_none() {
618 return None;
619 }
620 Some(match RustixErrno::from_raw_os_error(err.unwrap()) {
621 RustixErrno::PIPE => ErrorCode::Pipe,
622 RustixErrno::PERM => ErrorCode::NotPermitted,
623 RustixErrno::NOENT => ErrorCode::NoEntry,
624 RustixErrno::NOMEM => ErrorCode::InsufficientMemory,
625 RustixErrno::IO => ErrorCode::Io,
626 RustixErrno::BADF => ErrorCode::BadDescriptor,
627 RustixErrno::BUSY => ErrorCode::Busy,
628 RustixErrno::ACCESS => ErrorCode::Access,
629 RustixErrno::NOTDIR => ErrorCode::NotDirectory,
630 RustixErrno::ISDIR => ErrorCode::IsDirectory,
631 RustixErrno::INVAL => ErrorCode::Invalid,
632 RustixErrno::EXIST => ErrorCode::Exist,
633 RustixErrno::FBIG => ErrorCode::FileTooLarge,
634 RustixErrno::NOSPC => ErrorCode::InsufficientSpace,
635 RustixErrno::SPIPE => ErrorCode::InvalidSeek,
636 RustixErrno::MLINK => ErrorCode::TooManyLinks,
637 RustixErrno::NAMETOOLONG => ErrorCode::NameTooLong,
638 RustixErrno::NOTEMPTY => ErrorCode::NotEmpty,
639 RustixErrno::LOOP => ErrorCode::Loop,
640 RustixErrno::OVERFLOW => ErrorCode::Overflow,
641 RustixErrno::ILSEQ => ErrorCode::IllegalByteSequence,
642 RustixErrno::NOTSUP => ErrorCode::Unsupported,
643 RustixErrno::ALREADY => ErrorCode::Already,
644 RustixErrno::INPROGRESS => ErrorCode::InProgress,
645 RustixErrno::INTR => ErrorCode::Interrupted,
646
647 #[allow(
648 unreachable_patterns,
649 reason = "on some platforms, these have the same value as other errno values"
650 )]
651 RustixErrno::OPNOTSUPP => ErrorCode::Unsupported,
652
653 _ => return None,
654 })
655}
656#[cfg(windows)]
657fn from_raw_os_error(raw_os_error: Option<i32>) -> Option<ErrorCode> {
658 use windows_sys::Win32::Foundation;
659 Some(match raw_os_error.map(|code| code as u32) {
660 Some(Foundation::ERROR_FILE_NOT_FOUND) => ErrorCode::NoEntry,
661 Some(Foundation::ERROR_PATH_NOT_FOUND) => ErrorCode::NoEntry,
662 Some(Foundation::ERROR_ACCESS_DENIED) => ErrorCode::Access,
663 Some(Foundation::ERROR_SHARING_VIOLATION) => ErrorCode::Access,
664 Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => ErrorCode::NotPermitted,
665 Some(Foundation::ERROR_INVALID_HANDLE) => ErrorCode::BadDescriptor,
666 Some(Foundation::ERROR_INVALID_NAME) => ErrorCode::NoEntry,
667 Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => ErrorCode::InsufficientMemory,
668 Some(Foundation::ERROR_OUTOFMEMORY) => ErrorCode::InsufficientMemory,
669 Some(Foundation::ERROR_DIR_NOT_EMPTY) => ErrorCode::NotEmpty,
670 Some(Foundation::ERROR_NOT_READY) => ErrorCode::Busy,
671 Some(Foundation::ERROR_BUSY) => ErrorCode::Busy,
672 Some(Foundation::ERROR_NOT_SUPPORTED) => ErrorCode::Unsupported,
673 Some(Foundation::ERROR_FILE_EXISTS) => ErrorCode::Exist,
674 Some(Foundation::ERROR_BROKEN_PIPE) => ErrorCode::Pipe,
675 Some(Foundation::ERROR_BUFFER_OVERFLOW) => ErrorCode::NameTooLong,
676 Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => ErrorCode::Invalid,
677 Some(Foundation::ERROR_NEGATIVE_SEEK) => ErrorCode::Invalid,
678 Some(Foundation::ERROR_DIRECTORY) => ErrorCode::NotDirectory,
679 Some(Foundation::ERROR_ALREADY_EXISTS) => ErrorCode::Exist,
680 Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => ErrorCode::Loop,
681 Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => ErrorCode::IsDirectory,
682 _ => return None,
683 })
684}
685
686impl From<std::io::Error> for ErrorCode {
687 fn from(err: std::io::Error) -> ErrorCode {
688 ErrorCode::from(&err)
689 }
690}
691
692impl<'a> From<&'a std::io::Error> for ErrorCode {
693 fn from(err: &'a std::io::Error) -> ErrorCode {
694 match from_raw_os_error(err.raw_os_error()) {
695 Some(errno) => errno,
696 None => {
697 tracing::debug!("unknown raw os error: {err}");
698 match err.kind() {
699 std::io::ErrorKind::NotFound => ErrorCode::NoEntry,
700 std::io::ErrorKind::PermissionDenied => ErrorCode::NotPermitted,
701 std::io::ErrorKind::AlreadyExists => ErrorCode::Exist,
702 std::io::ErrorKind::InvalidInput => ErrorCode::Invalid,
703 _ => ErrorCode::Io,
704 }
705 }
706 }
707 }
708}
709
710impl From<cap_rand::Error> for ErrorCode {
711 fn from(err: cap_rand::Error) -> ErrorCode {
712 from_raw_os_error(err.raw_os_error()).unwrap_or(ErrorCode::Io)
714 }
715}
716
717impl From<std::num::TryFromIntError> for ErrorCode {
718 fn from(_err: std::num::TryFromIntError) -> ErrorCode {
719 ErrorCode::Overflow
720 }
721}
722
723fn descriptortype_from(ft: cap_std::fs::FileType) -> types::DescriptorType {
724 use cap_fs_ext::FileTypeExt;
725 use types::DescriptorType;
726 if ft.is_dir() {
727 DescriptorType::Directory
728 } else if ft.is_symlink() {
729 DescriptorType::SymbolicLink
730 } else if ft.is_block_device() {
731 DescriptorType::BlockDevice
732 } else if ft.is_char_device() {
733 DescriptorType::CharacterDevice
734 } else if ft.is_file() {
735 DescriptorType::RegularFile
736 } else {
737 DescriptorType::Unknown
738 }
739}
740
741fn systemtime_from(t: wall_clock::Datetime) -> Result<std::time::SystemTime, ErrorCode> {
742 std::time::SystemTime::UNIX_EPOCH
743 .checked_add(core::time::Duration::new(t.seconds, t.nanoseconds))
744 .ok_or(ErrorCode::Overflow)
745}
746
747fn systemtimespec_from(
748 t: types::NewTimestamp,
749) -> Result<Option<fs_set_times::SystemTimeSpec>, ErrorCode> {
750 use fs_set_times::SystemTimeSpec;
751 match t {
752 types::NewTimestamp::NoChange => Ok(None),
753 types::NewTimestamp::Now => Ok(Some(SystemTimeSpec::SymbolicNow)),
754 types::NewTimestamp::Timestamp(st) => {
755 let st = systemtime_from(st)?;
756 Ok(Some(SystemTimeSpec::Absolute(st)))
757 }
758 }
759}
760
761#[cfg(test)]
762mod test {
763 use super::*;
764 use wasmtime::component::ResourceTable;
765
766 #[test]
767 fn table_readdir_works() {
768 let mut table = ResourceTable::new();
769 let ix = table
770 .push(ReaddirIterator::new(std::iter::empty()))
771 .unwrap();
772 let _ = table.get(&ix).unwrap();
773 table.delete(ix).unwrap();
774 }
775}