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 cap_primitives::fs::read_base_dir(d)?
175 .map(|entry| {
176 let entry = entry?;
177 let meta = entry.metadata()?;
178 let type_ = descriptortype_from(meta.file_type());
179 let name = entry
180 .file_name()
181 .into_string()
182 .map_err(|_| ReaddirError::IllegalSequence)?;
183 Ok(types::DirectoryEntry { type_, name })
184 })
185 .collect::<Vec<Result<types::DirectoryEntry, ReaddirError>>>(),
186 )
187 })
188 .await?
189 .into_iter();
190
191 #[cfg(windows)]
194 let entries = entries.filter(|entry| {
195 use windows_sys::Win32::Foundation::{ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION};
196 if let Err(ReaddirError::Io(err)) = entry {
197 if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32)
198 || err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32)
199 {
200 return false;
201 }
202 }
203 true
204 });
205 let entries = entries.map(|r| match r {
206 Ok(r) => Ok(r),
207 Err(ReaddirError::Io(e)) => Err(e.into()),
208 Err(ReaddirError::IllegalSequence) => Err(ErrorCode::IllegalByteSequence.into()),
209 });
210 Ok(self.table.push(ReaddirIterator::new(entries))?)
211 }
212
213 async fn sync(&mut self, fd: Resource<types::Descriptor>) -> FsResult<()> {
214 let descriptor = self.table.get(&fd)?;
215 descriptor.sync().await?;
216 Ok(())
217 }
218
219 async fn create_directory_at(
220 &mut self,
221 fd: Resource<types::Descriptor>,
222 path: String,
223 ) -> FsResult<()> {
224 let d = self.table.get(&fd)?.dir()?;
225 d.create_directory_at(path).await?;
226 Ok(())
227 }
228
229 async fn stat(&mut self, fd: Resource<types::Descriptor>) -> FsResult<types::DescriptorStat> {
230 let descriptor = self.table.get(&fd)?;
231 let stat = descriptor.stat().await?;
232 Ok(stat.try_into()?)
233 }
234
235 async fn stat_at(
236 &mut self,
237 fd: Resource<types::Descriptor>,
238 path_flags: types::PathFlags,
239 path: String,
240 ) -> FsResult<types::DescriptorStat> {
241 let d = self.table.get(&fd)?.dir()?;
242 let stat = d.stat_at(path_flags.into(), path).await?;
243 Ok(stat.try_into()?)
244 }
245
246 async fn set_times_at(
247 &mut self,
248 fd: Resource<types::Descriptor>,
249 path_flags: types::PathFlags,
250 path: String,
251 atim: types::NewTimestamp,
252 mtim: types::NewTimestamp,
253 ) -> FsResult<()> {
254 let d = self.table.get(&fd)?.dir()?;
255 let atim = systemtimespec_from(atim)?;
256 let mtim = systemtimespec_from(mtim)?;
257 d.set_times_at(path_flags.into(), path, atim, mtim).await?;
258 Ok(())
259 }
260
261 async fn link_at(
262 &mut self,
263 fd: Resource<types::Descriptor>,
264 old_path_flags: types::PathFlags,
266 old_path: String,
267 new_descriptor: Resource<types::Descriptor>,
268 new_path: String,
269 ) -> FsResult<()> {
270 let old_dir = self.table.get(&fd)?.dir()?;
271 let new_dir = self.table.get(&new_descriptor)?.dir()?;
272 old_dir
273 .link_at(old_path_flags.into(), old_path, new_dir, new_path)
274 .await?;
275 Ok(())
276 }
277
278 async fn open_at(
279 &mut self,
280 fd: Resource<types::Descriptor>,
281 path_flags: types::PathFlags,
282 path: String,
283 oflags: types::OpenFlags,
284 flags: types::DescriptorFlags,
285 ) -> FsResult<Resource<types::Descriptor>> {
286 let d = self.table.get(&fd)?.dir()?;
287 let fd = d
288 .open_at(
289 path_flags.into(),
290 path,
291 oflags.into(),
292 flags.into(),
293 self.ctx.allow_blocking_current_thread,
294 )
295 .await?;
296 let fd = self.table.push(fd)?;
297 Ok(fd)
298 }
299
300 fn drop(&mut self, fd: Resource<types::Descriptor>) -> wasmtime::Result<()> {
301 self.table.delete(fd)?;
307
308 Ok(())
309 }
310
311 async fn readlink_at(
312 &mut self,
313 fd: Resource<types::Descriptor>,
314 path: String,
315 ) -> FsResult<String> {
316 let d = self.table.get(&fd)?.dir()?;
317 let path = d.readlink_at(path).await?;
318 Ok(path)
319 }
320
321 async fn remove_directory_at(
322 &mut self,
323 fd: Resource<types::Descriptor>,
324 path: String,
325 ) -> FsResult<()> {
326 let d = self.table.get(&fd)?.dir()?;
327 d.remove_directory_at(path).await?;
328 Ok(())
329 }
330
331 async fn rename_at(
332 &mut self,
333 fd: Resource<types::Descriptor>,
334 old_path: String,
335 new_fd: Resource<types::Descriptor>,
336 new_path: String,
337 ) -> FsResult<()> {
338 let old_dir = self.table.get(&fd)?.dir()?;
339 let new_dir = self.table.get(&new_fd)?.dir()?;
340 old_dir.rename_at(old_path, new_dir, new_path).await?;
341 Ok(())
342 }
343
344 async fn symlink_at(
345 &mut self,
346 fd: Resource<types::Descriptor>,
347 src_path: String,
348 dest_path: String,
349 ) -> FsResult<()> {
350 let d = self.table.get(&fd)?.dir()?;
351 d.symlink_at(src_path, dest_path).await?;
352 Ok(())
353 }
354
355 async fn unlink_file_at(
356 &mut self,
357 fd: Resource<types::Descriptor>,
358 path: String,
359 ) -> FsResult<()> {
360 let d = self.table.get(&fd)?.dir()?;
361 d.unlink_file_at(path).await?;
362 Ok(())
363 }
364
365 fn read_via_stream(
366 &mut self,
367 fd: Resource<types::Descriptor>,
368 offset: types::Filesize,
369 ) -> FsResult<Resource<DynInputStream>> {
370 let f = match self.table.get(&fd)? {
373 Descriptor::File(f) => f,
374 Descriptor::Dir(_) => return Err(ErrorCode::IsDirectory.into()),
375 };
376
377 let reader: DynInputStream = Box::new(FileInputStream::new(f, offset));
379
380 let index = self.table.push(reader)?;
382
383 Ok(index)
384 }
385
386 fn write_via_stream(
387 &mut self,
388 fd: Resource<types::Descriptor>,
389 offset: types::Filesize,
390 ) -> FsResult<Resource<DynOutputStream>> {
391 let f = self.table.get(&fd)?.file()?;
393
394 if f.perms.write_not_permitted() {
395 Err(types::ErrorCode::NotPermitted)?;
396 }
397
398 let writer = FileOutputStream::write_at(f, offset);
400 let writer: DynOutputStream = Box::new(writer);
401
402 let index = self.table.push(writer)?;
404
405 Ok(index)
406 }
407
408 fn append_via_stream(
409 &mut self,
410 fd: Resource<types::Descriptor>,
411 ) -> FsResult<Resource<DynOutputStream>> {
412 let f = self.table.get(&fd)?.file()?;
414
415 if f.perms.write_not_permitted() {
416 Err(types::ErrorCode::NotPermitted)?;
417 }
418
419 let appender = FileOutputStream::append(f);
421 let appender: DynOutputStream = Box::new(appender);
422
423 let index = self.table.push(appender)?;
425
426 Ok(index)
427 }
428
429 async fn is_same_object(
430 &mut self,
431 a: Resource<types::Descriptor>,
432 b: Resource<types::Descriptor>,
433 ) -> wasmtime::Result<bool> {
434 let descriptor_a = self.table.get(&a)?;
435 let descriptor_b = self.table.get(&b)?;
436 descriptor_a.is_same_object(descriptor_b).await
437 }
438 async fn metadata_hash(
439 &mut self,
440 fd: Resource<types::Descriptor>,
441 ) -> FsResult<types::MetadataHashValue> {
442 let fd = self.table.get(&fd)?;
443 let meta = fd.metadata_hash().await?;
444 Ok(meta.into())
445 }
446 async fn metadata_hash_at(
447 &mut self,
448 fd: Resource<types::Descriptor>,
449 path_flags: types::PathFlags,
450 path: String,
451 ) -> FsResult<types::MetadataHashValue> {
452 let d = self.table.get(&fd)?.dir()?;
453 let meta = d.metadata_hash_at(path_flags.into(), path).await?;
454 Ok(meta.into())
455 }
456}
457
458impl HostDirectoryEntryStream for WasiFilesystemCtxView<'_> {
459 async fn read_directory_entry(
460 &mut self,
461 stream: Resource<types::DirectoryEntryStream>,
462 ) -> FsResult<Option<types::DirectoryEntry>> {
463 let readdir = self.table.get(&stream)?;
464 readdir.next()
465 }
466
467 fn drop(&mut self, stream: Resource<types::DirectoryEntryStream>) -> wasmtime::Result<()> {
468 self.table.delete(stream)?;
469 Ok(())
470 }
471}
472
473impl From<types::Advice> for crate::filesystem::Advice {
474 fn from(advice: types::Advice) -> Self {
475 match advice {
476 types::Advice::Normal => Self::Normal,
477 types::Advice::Sequential => Self::Sequential,
478 types::Advice::Random => Self::Random,
479 types::Advice::WillNeed => Self::WillNeed,
480 types::Advice::DontNeed => Self::DontNeed,
481 types::Advice::NoReuse => Self::NoReuse,
482 }
483 }
484}
485
486impl From<types::OpenFlags> for crate::filesystem::OpenFlags {
487 fn from(flags: types::OpenFlags) -> Self {
488 let mut out = Self::empty();
489 if flags.contains(types::OpenFlags::CREATE) {
490 out |= Self::CREATE;
491 }
492 if flags.contains(types::OpenFlags::DIRECTORY) {
493 out |= Self::DIRECTORY;
494 }
495 if flags.contains(types::OpenFlags::EXCLUSIVE) {
496 out |= Self::EXCLUSIVE;
497 }
498 if flags.contains(types::OpenFlags::TRUNCATE) {
499 out |= Self::TRUNCATE;
500 }
501 out
502 }
503}
504
505impl From<types::PathFlags> for crate::filesystem::PathFlags {
506 fn from(flags: types::PathFlags) -> Self {
507 let mut out = Self::empty();
508 if flags.contains(types::PathFlags::SYMLINK_FOLLOW) {
509 out |= Self::SYMLINK_FOLLOW;
510 }
511 out
512 }
513}
514
515impl From<crate::filesystem::DescriptorFlags> for types::DescriptorFlags {
516 fn from(flags: crate::filesystem::DescriptorFlags) -> Self {
517 let mut out = Self::empty();
518 if flags.contains(crate::filesystem::DescriptorFlags::READ) {
519 out |= Self::READ;
520 }
521 if flags.contains(crate::filesystem::DescriptorFlags::WRITE) {
522 out |= Self::WRITE;
523 }
524 if flags.contains(crate::filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
525 out |= Self::FILE_INTEGRITY_SYNC;
526 }
527 if flags.contains(crate::filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
528 out |= Self::DATA_INTEGRITY_SYNC;
529 }
530 if flags.contains(crate::filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
531 out |= Self::REQUESTED_WRITE_SYNC;
532 }
533 if flags.contains(crate::filesystem::DescriptorFlags::MUTATE_DIRECTORY) {
534 out |= Self::MUTATE_DIRECTORY;
535 }
536 out
537 }
538}
539
540impl From<types::DescriptorFlags> for crate::filesystem::DescriptorFlags {
541 fn from(flags: types::DescriptorFlags) -> Self {
542 let mut out = Self::empty();
543 if flags.contains(types::DescriptorFlags::READ) {
544 out |= Self::READ;
545 }
546 if flags.contains(types::DescriptorFlags::WRITE) {
547 out |= Self::WRITE;
548 }
549 if flags.contains(types::DescriptorFlags::FILE_INTEGRITY_SYNC) {
550 out |= Self::FILE_INTEGRITY_SYNC;
551 }
552 if flags.contains(types::DescriptorFlags::DATA_INTEGRITY_SYNC) {
553 out |= Self::DATA_INTEGRITY_SYNC;
554 }
555 if flags.contains(types::DescriptorFlags::REQUESTED_WRITE_SYNC) {
556 out |= Self::REQUESTED_WRITE_SYNC;
557 }
558 if flags.contains(types::DescriptorFlags::MUTATE_DIRECTORY) {
559 out |= Self::MUTATE_DIRECTORY;
560 }
561 out
562 }
563}
564
565impl From<crate::filesystem::MetadataHashValue> for types::MetadataHashValue {
566 fn from(
567 crate::filesystem::MetadataHashValue { lower, upper }: crate::filesystem::MetadataHashValue,
568 ) -> Self {
569 Self { lower, upper }
570 }
571}
572
573impl TryFrom<crate::filesystem::DescriptorStat> for types::DescriptorStat {
574 type Error = ErrorCode;
575
576 fn try_from(
577 crate::filesystem::DescriptorStat {
578 type_,
579 link_count,
580 size,
581 data_access_timestamp,
582 data_modification_timestamp,
583 status_change_timestamp,
584 }: crate::filesystem::DescriptorStat,
585 ) -> Result<Self, ErrorCode> {
586 Ok(Self {
590 type_: type_.into(),
591 link_count,
592 size,
593 data_access_timestamp: data_access_timestamp.and_then(|t| t.try_into().ok()),
594 data_modification_timestamp: data_modification_timestamp
595 .and_then(|t| t.try_into().ok()),
596 status_change_timestamp: status_change_timestamp.and_then(|t| t.try_into().ok()),
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<std::num::TryFromIntError> for ErrorCode {
711 fn from(_err: std::num::TryFromIntError) -> ErrorCode {
712 ErrorCode::Overflow
713 }
714}
715
716fn descriptortype_from(ft: cap_primitives::fs::FileType) -> types::DescriptorType {
717 crate::filesystem::DescriptorType::from(ft).into()
718}
719
720fn systemtime_from(t: wall_clock::Datetime) -> Result<std::time::SystemTime, ErrorCode> {
721 std::time::SystemTime::UNIX_EPOCH
722 .checked_add(core::time::Duration::new(t.seconds, t.nanoseconds))
723 .ok_or(ErrorCode::Overflow)
724}
725
726fn systemtimespec_from(t: types::NewTimestamp) -> Result<Option<SystemTime>, ErrorCode> {
727 match t {
728 types::NewTimestamp::NoChange => Ok(None),
729 types::NewTimestamp::Now => Ok(Some(SystemTime::now())),
730 types::NewTimestamp::Timestamp(st) => {
731 let st = systemtime_from(st)?;
732 Ok(Some(st))
733 }
734 }
735}
736
737impl From<crate::clocks::DatetimeError> for ErrorCode {
738 fn from(_: crate::clocks::DatetimeError) -> ErrorCode {
739 ErrorCode::Overflow
740 }
741}
742
743#[cfg(test)]
744mod test {
745 use super::*;
746 use wasmtime::component::ResourceTable;
747
748 #[test]
749 fn table_readdir_works() {
750 let mut table = ResourceTable::new();
751 let ix = table
752 .push(ReaddirIterator::new(std::iter::empty()))
753 .unwrap();
754 let _ = table.get(&ix).unwrap();
755 table.delete(ix).unwrap();
756 }
757}