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