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