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 let max = self.ctx.max_io_size.get();
111
112 let (mut buffer, r) = f
113 .run_blocking(move |f| {
114 let mut buffer = vec![0; len.try_into().unwrap_or(usize::MAX).min(max)];
115 let r = sys::read_at_cursor_unspecified(f, &mut buffer, offset);
116 (buffer, r)
117 })
118 .await;
119
120 let (bytes_read, state) = match r? {
121 0 => (0, true),
122 n => (n, false),
123 };
124
125 buffer.truncate(bytes_read);
126
127 Ok((buffer, state))
128 }
129
130 async fn write(
131 &mut self,
132 fd: Resource<types::Descriptor>,
133 buf: Vec<u8>,
134 offset: types::Filesize,
135 ) -> FsResult<types::Filesize> {
136 let f = self.table.get(&fd)?.file()?;
137 if f.perms.write_not_permitted() {
138 return Err(ErrorCode::NotPermitted.into());
139 }
140
141 let bytes_written = f
142 .run_blocking(move |f| sys::write_at_cursor_unspecified(f, &buf, offset))
143 .await?;
144
145 Ok(types::Filesize::try_from(bytes_written).expect("usize fits in Filesize"))
146 }
147
148 async fn read_directory(
149 &mut self,
150 fd: Resource<types::Descriptor>,
151 ) -> FsResult<Resource<types::DirectoryEntryStream>> {
152 let d = self.table.get(&fd)?.dir()?;
153
154 enum ReaddirError {
155 Io(std::io::Error),
156 IllegalSequence,
157 }
158 impl From<std::io::Error> for ReaddirError {
159 fn from(e: std::io::Error) -> ReaddirError {
160 ReaddirError::Io(e)
161 }
162 }
163
164 let entries = d
165 .run_blocking(|d| {
166 Ok::<_, std::io::Error>(
170 crate::filesystem::primitives::read_dir(d)?
171 .map(|entry| {
172 let (filename, ty) = entry?;
173 let type_ = descriptortype_from(ty);
174 let name = filename
175 .into_string()
176 .map_err(|_| ReaddirError::IllegalSequence)?;
177 Ok(types::DirectoryEntry { type_, name })
178 })
179 .collect::<Vec<Result<types::DirectoryEntry, ReaddirError>>>(),
180 )
181 })
182 .await?
183 .into_iter();
184
185 #[cfg(windows)]
188 let entries = entries.filter(|entry| {
189 use windows_sys::Win32::Foundation::{ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION};
190 if let Err(ReaddirError::Io(err)) = entry {
191 if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32)
192 || err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32)
193 {
194 return false;
195 }
196 }
197 true
198 });
199 let entries = entries.map(|r| match r {
200 Ok(r) => Ok(r),
201 Err(ReaddirError::Io(e)) => Err(e.into()),
202 Err(ReaddirError::IllegalSequence) => Err(ErrorCode::IllegalByteSequence.into()),
203 });
204 Ok(self.table.push(ReaddirIterator::new(entries))?)
205 }
206
207 async fn sync(&mut self, fd: Resource<types::Descriptor>) -> FsResult<()> {
208 let descriptor = self.table.get(&fd)?;
209 descriptor.sync().await?;
210 Ok(())
211 }
212
213 async fn create_directory_at(
214 &mut self,
215 fd: Resource<types::Descriptor>,
216 path: String,
217 ) -> FsResult<()> {
218 let d = self.table.get(&fd)?.dir()?;
219 d.create_directory_at(path).await?;
220 Ok(())
221 }
222
223 async fn stat(&mut self, fd: Resource<types::Descriptor>) -> FsResult<types::DescriptorStat> {
224 let descriptor = self.table.get(&fd)?;
225 let stat = descriptor.stat().await?;
226 Ok(stat.try_into()?)
227 }
228
229 async fn stat_at(
230 &mut self,
231 fd: Resource<types::Descriptor>,
232 path_flags: types::PathFlags,
233 path: String,
234 ) -> FsResult<types::DescriptorStat> {
235 let d = self.table.get(&fd)?.dir()?;
236 let stat = d.stat_at(path_flags.into(), path).await?;
237 Ok(stat.try_into()?)
238 }
239
240 async fn set_times_at(
241 &mut self,
242 fd: Resource<types::Descriptor>,
243 path_flags: types::PathFlags,
244 path: String,
245 atim: types::NewTimestamp,
246 mtim: types::NewTimestamp,
247 ) -> FsResult<()> {
248 let d = self.table.get(&fd)?.dir()?;
249 let atim = systemtimespec_from(atim)?;
250 let mtim = systemtimespec_from(mtim)?;
251 d.set_times_at(path_flags.into(), path, atim, mtim).await?;
252 Ok(())
253 }
254
255 async fn link_at(
256 &mut self,
257 fd: Resource<types::Descriptor>,
258 old_path_flags: types::PathFlags,
259 old_path: String,
260 new_descriptor: Resource<types::Descriptor>,
261 new_path: String,
262 ) -> FsResult<()> {
263 let old_dir = self.table.get(&fd)?.dir()?;
264 let new_dir = self.table.get(&new_descriptor)?.dir()?;
265 old_dir
266 .link_at(old_path_flags.into(), old_path, new_dir, new_path)
267 .await?;
268 Ok(())
269 }
270
271 async fn open_at(
272 &mut self,
273 fd: Resource<types::Descriptor>,
274 path_flags: types::PathFlags,
275 path: String,
276 oflags: types::OpenFlags,
277 flags: types::DescriptorFlags,
278 ) -> FsResult<Resource<types::Descriptor>> {
279 let d = self.table.get(&fd)?.dir()?;
280 let fd = d
281 .open_at(
282 path_flags.into(),
283 path,
284 oflags.into(),
285 flags.into(),
286 self.ctx.allow_blocking_current_thread,
287 )
288 .await?;
289 let fd = self.table.push(fd)?;
290 Ok(fd)
291 }
292
293 fn drop(&mut self, fd: Resource<types::Descriptor>) -> wasmtime::Result<()> {
294 self.table.delete(fd)?;
300
301 Ok(())
302 }
303
304 async fn readlink_at(
305 &mut self,
306 fd: Resource<types::Descriptor>,
307 path: String,
308 ) -> FsResult<String> {
309 let d = self.table.get(&fd)?.dir()?;
310 let path = d.readlink_at(path).await?;
311 Ok(path)
312 }
313
314 async fn remove_directory_at(
315 &mut self,
316 fd: Resource<types::Descriptor>,
317 path: String,
318 ) -> FsResult<()> {
319 let d = self.table.get(&fd)?.dir()?;
320 d.remove_directory_at(path).await?;
321 Ok(())
322 }
323
324 async fn rename_at(
325 &mut self,
326 fd: Resource<types::Descriptor>,
327 old_path: String,
328 new_fd: Resource<types::Descriptor>,
329 new_path: String,
330 ) -> FsResult<()> {
331 let old_dir = self.table.get(&fd)?.dir()?;
332 let new_dir = self.table.get(&new_fd)?.dir()?;
333 old_dir.rename_at(old_path, new_dir, new_path).await?;
334 Ok(())
335 }
336
337 async fn symlink_at(
338 &mut self,
339 fd: Resource<types::Descriptor>,
340 src_path: String,
341 dest_path: String,
342 ) -> FsResult<()> {
343 let d = self.table.get(&fd)?.dir()?;
344 d.symlink_at(src_path, dest_path).await?;
345 Ok(())
346 }
347
348 async fn unlink_file_at(
349 &mut self,
350 fd: Resource<types::Descriptor>,
351 path: String,
352 ) -> FsResult<()> {
353 let d = self.table.get(&fd)?.dir()?;
354 d.unlink_file_at(path).await?;
355 Ok(())
356 }
357
358 fn read_via_stream(
359 &mut self,
360 fd: Resource<types::Descriptor>,
361 offset: types::Filesize,
362 ) -> FsResult<Resource<DynInputStream>> {
363 let f = match self.table.get(&fd)? {
366 Descriptor::File(f) => f,
367 Descriptor::Dir(_) => return Err(ErrorCode::IsDirectory.into()),
368 };
369
370 let reader: DynInputStream =
372 Box::new(FileInputStream::new(f, offset, self.ctx.max_io_size.get()));
373
374 let index = self.table.push(reader)?;
376
377 Ok(index)
378 }
379
380 fn write_via_stream(
381 &mut self,
382 fd: Resource<types::Descriptor>,
383 offset: types::Filesize,
384 ) -> FsResult<Resource<DynOutputStream>> {
385 let f = self.table.get(&fd)?.file()?;
387
388 if f.perms.write_not_permitted() {
389 Err(types::ErrorCode::NotPermitted)?;
390 }
391
392 let writer = FileOutputStream::write_at(f, offset, self.ctx.max_io_size.get());
394 let writer: DynOutputStream = Box::new(writer);
395
396 let index = self.table.push(writer)?;
398
399 Ok(index)
400 }
401
402 fn append_via_stream(
403 &mut self,
404 fd: Resource<types::Descriptor>,
405 ) -> FsResult<Resource<DynOutputStream>> {
406 let f = self.table.get(&fd)?.file()?;
408
409 if f.perms.write_not_permitted() {
410 Err(types::ErrorCode::NotPermitted)?;
411 }
412
413 let appender = FileOutputStream::append(f, self.ctx.max_io_size.get());
415 let appender: DynOutputStream = Box::new(appender);
416
417 let index = self.table.push(appender)?;
419
420 Ok(index)
421 }
422
423 async fn is_same_object(
424 &mut self,
425 a: Resource<types::Descriptor>,
426 b: Resource<types::Descriptor>,
427 ) -> wasmtime::Result<bool> {
428 let descriptor_a = self.table.get(&a)?;
429 let descriptor_b = self.table.get(&b)?;
430 descriptor_a.is_same_object(descriptor_b).await
431 }
432 async fn metadata_hash(
433 &mut self,
434 fd: Resource<types::Descriptor>,
435 ) -> FsResult<types::MetadataHashValue> {
436 let fd = self.table.get(&fd)?;
437 let meta = fd.metadata_hash().await?;
438 Ok(meta.into())
439 }
440 async fn metadata_hash_at(
441 &mut self,
442 fd: Resource<types::Descriptor>,
443 path_flags: types::PathFlags,
444 path: String,
445 ) -> FsResult<types::MetadataHashValue> {
446 let d = self.table.get(&fd)?.dir()?;
447 let meta = d.metadata_hash_at(path_flags.into(), path).await?;
448 Ok(meta.into())
449 }
450}
451
452impl HostDirectoryEntryStream for WasiFilesystemCtxView<'_> {
453 async fn read_directory_entry(
454 &mut self,
455 stream: Resource<types::DirectoryEntryStream>,
456 ) -> FsResult<Option<types::DirectoryEntry>> {
457 let readdir = self.table.get(&stream)?;
458 readdir.next()
459 }
460
461 fn drop(&mut self, stream: Resource<types::DirectoryEntryStream>) -> wasmtime::Result<()> {
462 self.table.delete(stream)?;
463 Ok(())
464 }
465}
466
467impl From<types::Advice> for crate::filesystem::Advice {
468 fn from(advice: types::Advice) -> Self {
469 match advice {
470 types::Advice::Normal => Self::Normal,
471 types::Advice::Sequential => Self::Sequential,
472 types::Advice::Random => Self::Random,
473 types::Advice::WillNeed => Self::WillNeed,
474 types::Advice::DontNeed => Self::DontNeed,
475 types::Advice::NoReuse => Self::NoReuse,
476 }
477 }
478}
479
480impl From<types::OpenFlags> for crate::filesystem::OpenFlags {
481 fn from(flags: types::OpenFlags) -> Self {
482 let mut out = Self::empty();
483 if flags.contains(types::OpenFlags::CREATE) {
484 out |= Self::CREATE;
485 }
486 if flags.contains(types::OpenFlags::DIRECTORY) {
487 out |= Self::DIRECTORY;
488 }
489 if flags.contains(types::OpenFlags::EXCLUSIVE) {
490 out |= Self::EXCLUSIVE;
491 }
492 if flags.contains(types::OpenFlags::TRUNCATE) {
493 out |= Self::TRUNCATE;
494 }
495 out
496 }
497}
498
499impl From<types::PathFlags> for crate::filesystem::PathFlags {
500 fn from(flags: types::PathFlags) -> Self {
501 let mut out = Self::empty();
502 if flags.contains(types::PathFlags::SYMLINK_FOLLOW) {
503 out |= Self::SYMLINK_FOLLOW;
504 }
505 out
506 }
507}
508
509impl From<crate::filesystem::DescriptorFlags> for types::DescriptorFlags {
510 fn from(flags: crate::filesystem::DescriptorFlags) -> Self {
511 let mut out = Self::empty();
512 if flags.contains(crate::filesystem::DescriptorFlags::READ) {
513 out |= Self::READ;
514 }
515 if flags.contains(crate::filesystem::DescriptorFlags::WRITE) {
516 out |= Self::WRITE;
517 }
518 if flags.contains(crate::filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
519 out |= Self::FILE_INTEGRITY_SYNC;
520 }
521 if flags.contains(crate::filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
522 out |= Self::DATA_INTEGRITY_SYNC;
523 }
524 if flags.contains(crate::filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
525 out |= Self::REQUESTED_WRITE_SYNC;
526 }
527 if flags.contains(crate::filesystem::DescriptorFlags::MUTATE_DIRECTORY) {
528 out |= Self::MUTATE_DIRECTORY;
529 }
530 out
531 }
532}
533
534impl From<types::DescriptorFlags> for crate::filesystem::DescriptorFlags {
535 fn from(flags: types::DescriptorFlags) -> Self {
536 let mut out = Self::empty();
537 if flags.contains(types::DescriptorFlags::READ) {
538 out |= Self::READ;
539 }
540 if flags.contains(types::DescriptorFlags::WRITE) {
541 out |= Self::WRITE;
542 }
543 if flags.contains(types::DescriptorFlags::FILE_INTEGRITY_SYNC) {
544 out |= Self::FILE_INTEGRITY_SYNC;
545 }
546 if flags.contains(types::DescriptorFlags::DATA_INTEGRITY_SYNC) {
547 out |= Self::DATA_INTEGRITY_SYNC;
548 }
549 if flags.contains(types::DescriptorFlags::REQUESTED_WRITE_SYNC) {
550 out |= Self::REQUESTED_WRITE_SYNC;
551 }
552 if flags.contains(types::DescriptorFlags::MUTATE_DIRECTORY) {
553 out |= Self::MUTATE_DIRECTORY;
554 }
555 out
556 }
557}
558
559impl From<crate::filesystem::MetadataHashValue> for types::MetadataHashValue {
560 fn from(
561 crate::filesystem::MetadataHashValue { lower, upper }: crate::filesystem::MetadataHashValue,
562 ) -> Self {
563 Self { lower, upper }
564 }
565}
566
567impl TryFrom<crate::filesystem::DescriptorStat> for types::DescriptorStat {
568 type Error = ErrorCode;
569
570 fn try_from(
571 crate::filesystem::DescriptorStat {
572 type_,
573 link_count,
574 size,
575 data_access_timestamp,
576 data_modification_timestamp,
577 status_change_timestamp,
578 }: crate::filesystem::DescriptorStat,
579 ) -> Result<Self, ErrorCode> {
580 Ok(Self {
584 type_: type_.into(),
585 link_count,
586 size,
587 data_access_timestamp: data_access_timestamp.and_then(|t| t.try_into().ok()),
588 data_modification_timestamp: data_modification_timestamp
589 .and_then(|t| t.try_into().ok()),
590 status_change_timestamp: status_change_timestamp.and_then(|t| t.try_into().ok()),
591 })
592 }
593}
594
595impl From<crate::filesystem::DescriptorType> for types::DescriptorType {
596 fn from(ty: crate::filesystem::DescriptorType) -> Self {
597 match ty {
598 crate::filesystem::DescriptorType::Unknown => Self::Unknown,
599 crate::filesystem::DescriptorType::BlockDevice => Self::BlockDevice,
600 crate::filesystem::DescriptorType::CharacterDevice => Self::CharacterDevice,
601 crate::filesystem::DescriptorType::Directory => Self::Directory,
602 crate::filesystem::DescriptorType::SymbolicLink => Self::SymbolicLink,
603 crate::filesystem::DescriptorType::RegularFile => Self::RegularFile,
604 }
605 }
606}
607
608#[cfg(unix)]
609fn from_raw_os_error(err: Option<i32>) -> Option<ErrorCode> {
610 use rustix::io::Errno as RustixErrno;
611 if err.is_none() {
612 return None;
613 }
614 Some(match RustixErrno::from_raw_os_error(err.unwrap()) {
615 RustixErrno::PIPE => ErrorCode::Pipe,
616 RustixErrno::PERM => ErrorCode::NotPermitted,
617 RustixErrno::NOENT => ErrorCode::NoEntry,
618 RustixErrno::NOMEM => ErrorCode::InsufficientMemory,
619 RustixErrno::IO => ErrorCode::Io,
620 RustixErrno::BADF => ErrorCode::BadDescriptor,
621 RustixErrno::BUSY => ErrorCode::Busy,
622 RustixErrno::ACCESS => ErrorCode::Access,
623 RustixErrno::NOTDIR => ErrorCode::NotDirectory,
624 RustixErrno::ISDIR => ErrorCode::IsDirectory,
625 RustixErrno::INVAL => ErrorCode::Invalid,
626 RustixErrno::EXIST => ErrorCode::Exist,
627 RustixErrno::FBIG => ErrorCode::FileTooLarge,
628 RustixErrno::NOSPC => ErrorCode::InsufficientSpace,
629 RustixErrno::SPIPE => ErrorCode::InvalidSeek,
630 RustixErrno::MLINK => ErrorCode::TooManyLinks,
631 RustixErrno::NAMETOOLONG => ErrorCode::NameTooLong,
632 RustixErrno::NOTEMPTY => ErrorCode::NotEmpty,
633 RustixErrno::LOOP => ErrorCode::Loop,
634 RustixErrno::OVERFLOW => ErrorCode::Overflow,
635 RustixErrno::ILSEQ => ErrorCode::IllegalByteSequence,
636 RustixErrno::NOTSUP => ErrorCode::Unsupported,
637 RustixErrno::ALREADY => ErrorCode::Already,
638 RustixErrno::INPROGRESS => ErrorCode::InProgress,
639 RustixErrno::INTR => ErrorCode::Interrupted,
640
641 #[allow(
642 unreachable_patterns,
643 reason = "on some platforms, these have the same value as other errno values"
644 )]
645 RustixErrno::OPNOTSUPP => ErrorCode::Unsupported,
646
647 _ => return None,
648 })
649}
650#[cfg(windows)]
651fn from_raw_os_error(raw_os_error: Option<i32>) -> Option<ErrorCode> {
652 use windows_sys::Win32::Foundation;
653 Some(match raw_os_error.map(|code| code as u32) {
654 Some(Foundation::ERROR_FILE_NOT_FOUND) => ErrorCode::NoEntry,
655 Some(Foundation::ERROR_PATH_NOT_FOUND) => ErrorCode::NoEntry,
656 Some(Foundation::ERROR_ACCESS_DENIED) => ErrorCode::Access,
657 Some(Foundation::ERROR_SHARING_VIOLATION) => ErrorCode::Access,
658 Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => ErrorCode::NotPermitted,
659 Some(Foundation::ERROR_INVALID_HANDLE) => ErrorCode::BadDescriptor,
660 Some(Foundation::ERROR_INVALID_NAME) => ErrorCode::NoEntry,
661 Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => ErrorCode::InsufficientMemory,
662 Some(Foundation::ERROR_OUTOFMEMORY) => ErrorCode::InsufficientMemory,
663 Some(Foundation::ERROR_DIR_NOT_EMPTY) => ErrorCode::NotEmpty,
664 Some(Foundation::ERROR_NOT_READY) => ErrorCode::Busy,
665 Some(Foundation::ERROR_BUSY) => ErrorCode::Busy,
666 Some(Foundation::ERROR_NOT_SUPPORTED) => ErrorCode::Unsupported,
667 Some(Foundation::ERROR_FILE_EXISTS) => ErrorCode::Exist,
668 Some(Foundation::ERROR_BROKEN_PIPE) => ErrorCode::Pipe,
669 Some(Foundation::ERROR_BUFFER_OVERFLOW) => ErrorCode::NameTooLong,
670 Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => ErrorCode::Invalid,
671 Some(Foundation::ERROR_NEGATIVE_SEEK) => ErrorCode::Invalid,
672 Some(Foundation::ERROR_DIRECTORY) => ErrorCode::NotDirectory,
673 Some(Foundation::ERROR_ALREADY_EXISTS) => ErrorCode::Exist,
674 Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => ErrorCode::Loop,
675 Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => ErrorCode::IsDirectory,
676 _ => return None,
677 })
678}
679
680impl From<std::io::Error> for ErrorCode {
681 fn from(err: std::io::Error) -> ErrorCode {
682 ErrorCode::from(&err)
683 }
684}
685
686impl<'a> From<&'a std::io::Error> for ErrorCode {
687 fn from(err: &'a std::io::Error) -> ErrorCode {
688 match from_raw_os_error(err.raw_os_error()) {
689 Some(errno) => errno,
690 None => {
691 tracing::debug!("unknown raw os error: {err}");
692 match err.kind() {
693 std::io::ErrorKind::NotFound => ErrorCode::NoEntry,
694 std::io::ErrorKind::PermissionDenied => ErrorCode::NotPermitted,
695 std::io::ErrorKind::AlreadyExists => ErrorCode::Exist,
696 std::io::ErrorKind::InvalidInput => ErrorCode::Invalid,
697 _ => ErrorCode::Io,
698 }
699 }
700 }
701 }
702}
703
704impl From<std::num::TryFromIntError> for ErrorCode {
705 fn from(_err: std::num::TryFromIntError) -> ErrorCode {
706 ErrorCode::Overflow
707 }
708}
709
710fn descriptortype_from(ft: crate::filesystem::primitives::FileType) -> types::DescriptorType {
711 crate::filesystem::DescriptorType::from(ft).into()
712}
713
714fn systemtime_from(t: wall_clock::Datetime) -> Result<std::time::SystemTime, ErrorCode> {
715 let duration = core::time::Duration::new(
718 t.seconds
719 .checked_add(u64::from(t.nanoseconds / 1_000_000_000))
720 .ok_or(ErrorCode::Overflow)?,
721 t.nanoseconds % 1_000_000_000,
722 );
723 std::time::SystemTime::UNIX_EPOCH
724 .checked_add(duration)
725 .ok_or(ErrorCode::Overflow)
726}
727
728fn systemtimespec_from(t: types::NewTimestamp) -> Result<Option<SystemTime>, ErrorCode> {
729 match t {
730 types::NewTimestamp::NoChange => Ok(None),
731 types::NewTimestamp::Now => Ok(Some(SystemTime::now())),
732 types::NewTimestamp::Timestamp(st) => {
733 let st = systemtime_from(st)?;
734 Ok(Some(st))
735 }
736 }
737}
738
739impl From<crate::clocks::DatetimeError> for ErrorCode {
740 fn from(_: crate::clocks::DatetimeError) -> ErrorCode {
741 ErrorCode::Overflow
742 }
743}
744
745#[cfg(test)]
746mod test {
747 use super::*;
748 use wasmtime::component::ResourceTable;
749
750 #[test]
751 fn table_readdir_works() {
752 let mut table = ResourceTable::new();
753 let ix = table
754 .push(ReaddirIterator::new(std::iter::empty()))
755 .unwrap();
756 let _ = table.get(&ix).unwrap();
757 table.delete(ix).unwrap();
758 }
759}
760
761mod named {
762 use crate::filesystem::WasiFilesystemNamedView;
763 use crate::p2::bindings::filesystem::types::{
764 Advice, Descriptor, DescriptorFlags, DescriptorStat, DescriptorType, DirectoryEntry,
765 DirectoryEntryStream, ErrorCode, Filesize, MetadataHashValue, NewTimestamp, OpenFlags,
766 PathFlags,
767 };
768 use crate::p2::bindings::named_imports::wasi::filesystem::{preopens, types};
769 use crate::p2::{FsError, FsResult};
770 use crate::{NamedId, WasiCtxNamedView};
771 use wasmtime::component::Resource;
772 use wasmtime_wasi_io::streams::{DynInputStream, DynOutputStream};
773
774 impl<T> preopens::Host for WasiCtxNamedView<'_, T>
775 where
776 T: WasiFilesystemNamedView,
777 {
778 fn get_directories(
779 &mut self,
780 id: NamedId,
781 ) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
782 super::preopens::Host::get_directories(&mut self.0.filesystem(id))
783 }
784 }
785
786 impl<T> types::Host for WasiCtxNamedView<'_, T>
787 where
788 T: WasiFilesystemNamedView,
789 {
790 fn convert_error_code(&mut self, err: FsError) -> wasmtime::Result<ErrorCode> {
791 err.downcast()
792 }
793
794 fn filesystem_error_code(
795 &mut self,
796 id: NamedId,
797 err: Resource<wasmtime::Error>,
798 ) -> wasmtime::Result<Option<ErrorCode>> {
799 super::types::Host::filesystem_error_code(&mut self.0.filesystem(id), err)
800 }
801 }
802
803 impl<T> types::HostDescriptor for WasiCtxNamedView<'_, T>
804 where
805 T: WasiFilesystemNamedView,
806 {
807 async fn advise(
808 &mut self,
809 id: NamedId,
810 fd: Resource<Descriptor>,
811 offset: Filesize,
812 len: Filesize,
813 advice: Advice,
814 ) -> FsResult<()> {
815 super::HostDescriptor::advise(&mut self.0.filesystem(id), fd, offset, len, advice).await
816 }
817
818 async fn sync_data(&mut self, id: NamedId, fd: Resource<Descriptor>) -> FsResult<()> {
819 super::HostDescriptor::sync_data(&mut self.0.filesystem(id), fd).await
820 }
821
822 async fn get_flags(
823 &mut self,
824 id: NamedId,
825 fd: Resource<Descriptor>,
826 ) -> FsResult<DescriptorFlags> {
827 super::HostDescriptor::get_flags(&mut self.0.filesystem(id), fd).await
828 }
829
830 async fn get_type(
831 &mut self,
832 id: NamedId,
833 fd: Resource<Descriptor>,
834 ) -> FsResult<DescriptorType> {
835 super::HostDescriptor::get_type(&mut self.0.filesystem(id), fd).await
836 }
837
838 async fn set_size(
839 &mut self,
840 id: NamedId,
841 fd: Resource<Descriptor>,
842 size: Filesize,
843 ) -> FsResult<()> {
844 super::HostDescriptor::set_size(&mut self.0.filesystem(id), fd, size).await
845 }
846
847 async fn set_times(
848 &mut self,
849 id: NamedId,
850 fd: Resource<Descriptor>,
851 atim: NewTimestamp,
852 mtim: NewTimestamp,
853 ) -> FsResult<()> {
854 super::HostDescriptor::set_times(&mut self.0.filesystem(id), fd, atim, mtim).await
855 }
856
857 async fn read(
858 &mut self,
859 id: NamedId,
860 fd: Resource<Descriptor>,
861 len: Filesize,
862 offset: Filesize,
863 ) -> FsResult<(Vec<u8>, bool)> {
864 super::HostDescriptor::read(&mut self.0.filesystem(id), fd, len, offset).await
865 }
866
867 async fn write(
868 &mut self,
869 id: NamedId,
870 fd: Resource<Descriptor>,
871 buf: Vec<u8>,
872 offset: Filesize,
873 ) -> FsResult<Filesize> {
874 super::HostDescriptor::write(&mut self.0.filesystem(id), fd, buf, offset).await
875 }
876
877 async fn read_directory(
878 &mut self,
879 id: NamedId,
880 fd: Resource<Descriptor>,
881 ) -> FsResult<Resource<DirectoryEntryStream>> {
882 super::HostDescriptor::read_directory(&mut self.0.filesystem(id), fd).await
883 }
884
885 async fn sync(&mut self, id: NamedId, fd: Resource<Descriptor>) -> FsResult<()> {
886 super::HostDescriptor::sync(&mut self.0.filesystem(id), fd).await
887 }
888
889 async fn create_directory_at(
890 &mut self,
891 id: NamedId,
892 fd: Resource<Descriptor>,
893 path: String,
894 ) -> FsResult<()> {
895 super::HostDescriptor::create_directory_at(&mut self.0.filesystem(id), fd, path).await
896 }
897
898 async fn stat(
899 &mut self,
900 id: NamedId,
901 fd: Resource<Descriptor>,
902 ) -> FsResult<DescriptorStat> {
903 super::HostDescriptor::stat(&mut self.0.filesystem(id), fd).await
904 }
905
906 async fn stat_at(
907 &mut self,
908 id: NamedId,
909 fd: Resource<Descriptor>,
910 path_flags: PathFlags,
911 path: String,
912 ) -> FsResult<DescriptorStat> {
913 super::HostDescriptor::stat_at(&mut self.0.filesystem(id), fd, path_flags, path).await
914 }
915
916 async fn set_times_at(
917 &mut self,
918 id: NamedId,
919 fd: Resource<Descriptor>,
920 path_flags: PathFlags,
921 path: String,
922 atim: NewTimestamp,
923 mtim: NewTimestamp,
924 ) -> FsResult<()> {
925 super::HostDescriptor::set_times_at(
926 &mut self.0.filesystem(id),
927 fd,
928 path_flags,
929 path,
930 atim,
931 mtim,
932 )
933 .await
934 }
935
936 async fn link_at(
937 &mut self,
938 id: NamedId,
939 fd: Resource<Descriptor>,
940 old_path_flags: PathFlags,
941 old_path: String,
942 new_descriptor: Resource<Descriptor>,
943 new_path: String,
944 ) -> FsResult<()> {
945 super::HostDescriptor::link_at(
946 &mut self.0.filesystem(id),
947 fd,
948 old_path_flags,
949 old_path,
950 new_descriptor,
951 new_path,
952 )
953 .await
954 }
955
956 async fn open_at(
957 &mut self,
958 id: NamedId,
959 fd: Resource<Descriptor>,
960 path_flags: PathFlags,
961 path: String,
962 oflags: OpenFlags,
963 flags: DescriptorFlags,
964 ) -> FsResult<Resource<Descriptor>> {
965 super::HostDescriptor::open_at(
966 &mut self.0.filesystem(id),
967 fd,
968 path_flags,
969 path,
970 oflags,
971 flags,
972 )
973 .await
974 }
975
976 fn drop(&mut self, id: NamedId, fd: Resource<Descriptor>) -> wasmtime::Result<()> {
977 super::HostDescriptor::drop(&mut self.0.filesystem(id), fd)
978 }
979
980 async fn readlink_at(
981 &mut self,
982 id: NamedId,
983 fd: Resource<Descriptor>,
984 path: String,
985 ) -> FsResult<String> {
986 super::HostDescriptor::readlink_at(&mut self.0.filesystem(id), fd, path).await
987 }
988
989 async fn remove_directory_at(
990 &mut self,
991 id: NamedId,
992 fd: Resource<Descriptor>,
993 path: String,
994 ) -> FsResult<()> {
995 super::HostDescriptor::remove_directory_at(&mut self.0.filesystem(id), fd, path).await
996 }
997
998 async fn rename_at(
999 &mut self,
1000 id: NamedId,
1001 fd: Resource<Descriptor>,
1002 old_path: String,
1003 new_fd: Resource<Descriptor>,
1004 new_path: String,
1005 ) -> FsResult<()> {
1006 super::HostDescriptor::rename_at(
1007 &mut self.0.filesystem(id),
1008 fd,
1009 old_path,
1010 new_fd,
1011 new_path,
1012 )
1013 .await
1014 }
1015
1016 async fn symlink_at(
1017 &mut self,
1018 id: NamedId,
1019 fd: Resource<Descriptor>,
1020 src_path: String,
1021 dest_path: String,
1022 ) -> FsResult<()> {
1023 super::HostDescriptor::symlink_at(&mut self.0.filesystem(id), fd, src_path, dest_path)
1024 .await
1025 }
1026
1027 async fn unlink_file_at(
1028 &mut self,
1029 id: NamedId,
1030 fd: Resource<Descriptor>,
1031 path: String,
1032 ) -> FsResult<()> {
1033 super::HostDescriptor::unlink_file_at(&mut self.0.filesystem(id), fd, path).await
1034 }
1035
1036 fn read_via_stream(
1037 &mut self,
1038 id: NamedId,
1039 fd: Resource<Descriptor>,
1040 offset: Filesize,
1041 ) -> FsResult<Resource<DynInputStream>> {
1042 super::HostDescriptor::read_via_stream(&mut self.0.filesystem(id), fd, offset)
1043 }
1044
1045 fn write_via_stream(
1046 &mut self,
1047 id: NamedId,
1048 fd: Resource<Descriptor>,
1049 offset: Filesize,
1050 ) -> FsResult<Resource<DynOutputStream>> {
1051 super::HostDescriptor::write_via_stream(&mut self.0.filesystem(id), fd, offset)
1052 }
1053
1054 fn append_via_stream(
1055 &mut self,
1056 id: NamedId,
1057 fd: Resource<Descriptor>,
1058 ) -> FsResult<Resource<DynOutputStream>> {
1059 super::HostDescriptor::append_via_stream(&mut self.0.filesystem(id), fd)
1060 }
1061
1062 async fn is_same_object(
1063 &mut self,
1064 id: NamedId,
1065 a: Resource<Descriptor>,
1066 b: Resource<Descriptor>,
1067 ) -> wasmtime::Result<bool> {
1068 super::HostDescriptor::is_same_object(&mut self.0.filesystem(id), a, b).await
1069 }
1070
1071 async fn metadata_hash(
1072 &mut self,
1073 id: NamedId,
1074 fd: Resource<Descriptor>,
1075 ) -> FsResult<MetadataHashValue> {
1076 super::HostDescriptor::metadata_hash(&mut self.0.filesystem(id), fd).await
1077 }
1078
1079 async fn metadata_hash_at(
1080 &mut self,
1081 id: NamedId,
1082 fd: Resource<Descriptor>,
1083 path_flags: PathFlags,
1084 path: String,
1085 ) -> FsResult<MetadataHashValue> {
1086 super::HostDescriptor::metadata_hash_at(
1087 &mut self.0.filesystem(id),
1088 fd,
1089 path_flags,
1090 path,
1091 )
1092 .await
1093 }
1094 }
1095
1096 impl<T> types::HostDirectoryEntryStream for WasiCtxNamedView<'_, T>
1097 where
1098 T: WasiFilesystemNamedView,
1099 {
1100 async fn read_directory_entry(
1101 &mut self,
1102 id: NamedId,
1103 stream: Resource<DirectoryEntryStream>,
1104 ) -> FsResult<Option<DirectoryEntry>> {
1105 super::HostDirectoryEntryStream::read_directory_entry(
1106 &mut self.0.filesystem(id),
1107 stream,
1108 )
1109 .await
1110 }
1111
1112 fn drop(
1113 &mut self,
1114 id: NamedId,
1115 stream: Resource<DirectoryEntryStream>,
1116 ) -> wasmtime::Result<()> {
1117 super::HostDirectoryEntryStream::drop(&mut self.0.filesystem(id), stream)
1118 }
1119 }
1120}