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 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 if !f.perms.contains(FilePerms::READ) {
111 return Err(ErrorCode::NotPermitted.into());
112 }
113
114 let (mut buffer, r) = f
115 .run_blocking(move |f| {
116 let mut buffer = vec![
117 0;
118 len.try_into()
119 .unwrap_or(usize::MAX)
120 .min(crate::MAX_READ_SIZE_ALLOC)
121 ];
122 let r = sys::read_at_cursor_unspecified(f, &mut buffer, offset);
123 (buffer, r)
124 })
125 .await;
126
127 let (bytes_read, state) = match r? {
128 0 => (0, true),
129 n => (n, false),
130 };
131
132 buffer.truncate(bytes_read);
133
134 Ok((buffer, state))
135 }
136
137 async fn write(
138 &mut self,
139 fd: Resource<types::Descriptor>,
140 buf: Vec<u8>,
141 offset: types::Filesize,
142 ) -> FsResult<types::Filesize> {
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| sys::write_at_cursor_unspecified(f, &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.try_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.try_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>) -> wasmtime::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 ) -> wasmtime::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>) -> wasmtime::Result<()> {
474 self.table.delete(stream)?;
475 Ok(())
476 }
477}
478
479impl From<types::Advice> for crate::filesystem::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 TryFrom<crate::filesystem::DescriptorStat> for types::DescriptorStat {
580 type Error = ErrorCode;
581
582 fn try_from(
583 crate::filesystem::DescriptorStat {
584 type_,
585 link_count,
586 size,
587 data_access_timestamp,
588 data_modification_timestamp,
589 status_change_timestamp,
590 }: crate::filesystem::DescriptorStat,
591 ) -> Result<Self, ErrorCode> {
592 Ok(Self {
593 type_: type_.into(),
594 link_count,
595 size,
596 data_access_timestamp: data_access_timestamp.map(|t| t.try_into()).transpose()?,
597 data_modification_timestamp: data_modification_timestamp
598 .map(|t| t.try_into())
599 .transpose()?,
600 status_change_timestamp: status_change_timestamp.map(|t| t.try_into()).transpose()?,
601 })
602 }
603}
604
605impl From<crate::filesystem::DescriptorType> for types::DescriptorType {
606 fn from(ty: crate::filesystem::DescriptorType) -> Self {
607 match ty {
608 crate::filesystem::DescriptorType::Unknown => Self::Unknown,
609 crate::filesystem::DescriptorType::BlockDevice => Self::BlockDevice,
610 crate::filesystem::DescriptorType::CharacterDevice => Self::CharacterDevice,
611 crate::filesystem::DescriptorType::Directory => Self::Directory,
612 crate::filesystem::DescriptorType::SymbolicLink => Self::SymbolicLink,
613 crate::filesystem::DescriptorType::RegularFile => Self::RegularFile,
614 }
615 }
616}
617
618#[cfg(unix)]
619fn from_raw_os_error(err: Option<i32>) -> Option<ErrorCode> {
620 use rustix::io::Errno as RustixErrno;
621 if err.is_none() {
622 return None;
623 }
624 Some(match RustixErrno::from_raw_os_error(err.unwrap()) {
625 RustixErrno::PIPE => ErrorCode::Pipe,
626 RustixErrno::PERM => ErrorCode::NotPermitted,
627 RustixErrno::NOENT => ErrorCode::NoEntry,
628 RustixErrno::NOMEM => ErrorCode::InsufficientMemory,
629 RustixErrno::IO => ErrorCode::Io,
630 RustixErrno::BADF => ErrorCode::BadDescriptor,
631 RustixErrno::BUSY => ErrorCode::Busy,
632 RustixErrno::ACCESS => ErrorCode::Access,
633 RustixErrno::NOTDIR => ErrorCode::NotDirectory,
634 RustixErrno::ISDIR => ErrorCode::IsDirectory,
635 RustixErrno::INVAL => ErrorCode::Invalid,
636 RustixErrno::EXIST => ErrorCode::Exist,
637 RustixErrno::FBIG => ErrorCode::FileTooLarge,
638 RustixErrno::NOSPC => ErrorCode::InsufficientSpace,
639 RustixErrno::SPIPE => ErrorCode::InvalidSeek,
640 RustixErrno::MLINK => ErrorCode::TooManyLinks,
641 RustixErrno::NAMETOOLONG => ErrorCode::NameTooLong,
642 RustixErrno::NOTEMPTY => ErrorCode::NotEmpty,
643 RustixErrno::LOOP => ErrorCode::Loop,
644 RustixErrno::OVERFLOW => ErrorCode::Overflow,
645 RustixErrno::ILSEQ => ErrorCode::IllegalByteSequence,
646 RustixErrno::NOTSUP => ErrorCode::Unsupported,
647 RustixErrno::ALREADY => ErrorCode::Already,
648 RustixErrno::INPROGRESS => ErrorCode::InProgress,
649 RustixErrno::INTR => ErrorCode::Interrupted,
650
651 #[allow(
652 unreachable_patterns,
653 reason = "on some platforms, these have the same value as other errno values"
654 )]
655 RustixErrno::OPNOTSUPP => ErrorCode::Unsupported,
656
657 _ => return None,
658 })
659}
660#[cfg(windows)]
661fn from_raw_os_error(raw_os_error: Option<i32>) -> Option<ErrorCode> {
662 use windows_sys::Win32::Foundation;
663 Some(match raw_os_error.map(|code| code as u32) {
664 Some(Foundation::ERROR_FILE_NOT_FOUND) => ErrorCode::NoEntry,
665 Some(Foundation::ERROR_PATH_NOT_FOUND) => ErrorCode::NoEntry,
666 Some(Foundation::ERROR_ACCESS_DENIED) => ErrorCode::Access,
667 Some(Foundation::ERROR_SHARING_VIOLATION) => ErrorCode::Access,
668 Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => ErrorCode::NotPermitted,
669 Some(Foundation::ERROR_INVALID_HANDLE) => ErrorCode::BadDescriptor,
670 Some(Foundation::ERROR_INVALID_NAME) => ErrorCode::NoEntry,
671 Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => ErrorCode::InsufficientMemory,
672 Some(Foundation::ERROR_OUTOFMEMORY) => ErrorCode::InsufficientMemory,
673 Some(Foundation::ERROR_DIR_NOT_EMPTY) => ErrorCode::NotEmpty,
674 Some(Foundation::ERROR_NOT_READY) => ErrorCode::Busy,
675 Some(Foundation::ERROR_BUSY) => ErrorCode::Busy,
676 Some(Foundation::ERROR_NOT_SUPPORTED) => ErrorCode::Unsupported,
677 Some(Foundation::ERROR_FILE_EXISTS) => ErrorCode::Exist,
678 Some(Foundation::ERROR_BROKEN_PIPE) => ErrorCode::Pipe,
679 Some(Foundation::ERROR_BUFFER_OVERFLOW) => ErrorCode::NameTooLong,
680 Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => ErrorCode::Invalid,
681 Some(Foundation::ERROR_NEGATIVE_SEEK) => ErrorCode::Invalid,
682 Some(Foundation::ERROR_DIRECTORY) => ErrorCode::NotDirectory,
683 Some(Foundation::ERROR_ALREADY_EXISTS) => ErrorCode::Exist,
684 Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => ErrorCode::Loop,
685 Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => ErrorCode::IsDirectory,
686 _ => return None,
687 })
688}
689
690impl From<std::io::Error> for ErrorCode {
691 fn from(err: std::io::Error) -> ErrorCode {
692 ErrorCode::from(&err)
693 }
694}
695
696impl<'a> From<&'a std::io::Error> for ErrorCode {
697 fn from(err: &'a std::io::Error) -> ErrorCode {
698 match from_raw_os_error(err.raw_os_error()) {
699 Some(errno) => errno,
700 None => {
701 tracing::debug!("unknown raw os error: {err}");
702 match err.kind() {
703 std::io::ErrorKind::NotFound => ErrorCode::NoEntry,
704 std::io::ErrorKind::PermissionDenied => ErrorCode::NotPermitted,
705 std::io::ErrorKind::AlreadyExists => ErrorCode::Exist,
706 std::io::ErrorKind::InvalidInput => ErrorCode::Invalid,
707 _ => ErrorCode::Io,
708 }
709 }
710 }
711 }
712}
713
714impl From<std::num::TryFromIntError> for ErrorCode {
715 fn from(_err: std::num::TryFromIntError) -> ErrorCode {
716 ErrorCode::Overflow
717 }
718}
719
720fn descriptortype_from(ft: cap_std::fs::FileType) -> types::DescriptorType {
721 use cap_fs_ext::FileTypeExt;
722 use types::DescriptorType;
723 if ft.is_dir() {
724 DescriptorType::Directory
725 } else if ft.is_symlink() {
726 DescriptorType::SymbolicLink
727 } else if ft.is_block_device() {
728 DescriptorType::BlockDevice
729 } else if ft.is_char_device() {
730 DescriptorType::CharacterDevice
731 } else if ft.is_file() {
732 DescriptorType::RegularFile
733 } else {
734 DescriptorType::Unknown
735 }
736}
737
738fn systemtime_from(t: wall_clock::Datetime) -> Result<std::time::SystemTime, ErrorCode> {
739 std::time::SystemTime::UNIX_EPOCH
740 .checked_add(core::time::Duration::new(t.seconds, t.nanoseconds))
741 .ok_or(ErrorCode::Overflow)
742}
743
744fn systemtimespec_from(
745 t: types::NewTimestamp,
746) -> Result<Option<fs_set_times::SystemTimeSpec>, ErrorCode> {
747 use fs_set_times::SystemTimeSpec;
748 match t {
749 types::NewTimestamp::NoChange => Ok(None),
750 types::NewTimestamp::Now => Ok(Some(SystemTimeSpec::SymbolicNow)),
751 types::NewTimestamp::Timestamp(st) => {
752 let st = systemtime_from(st)?;
753 Ok(Some(SystemTimeSpec::Absolute(st)))
754 }
755 }
756}
757
758impl From<crate::clocks::DatetimeError> for ErrorCode {
759 fn from(_: crate::clocks::DatetimeError) -> ErrorCode {
760 ErrorCode::Overflow
761 }
762}
763
764#[cfg(test)]
765mod test {
766 use super::*;
767 use wasmtime::component::ResourceTable;
768
769 #[test]
770 fn table_readdir_works() {
771 let mut table = ResourceTable::new();
772 let ix = table
773 .push(ReaddirIterator::new(std::iter::empty()))
774 .unwrap();
775 let _ = table.get(&ix).unwrap();
776 table.delete(ix).unwrap();
777 }
778}