1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3#[cfg(feature = "enable-serde")]
4const _: () = {
5 #[deprecated(
6 note = "The `enable-serde` feature is deprecated and will be removed in the next major release of Wasmer."
7 )]
8 fn __enable_serde_deprecated() {}
9 let _ = __enable_serde_deprecated;
10};
11
12#[cfg(test)]
13#[macro_use]
14extern crate pretty_assertions;
15
16use futures::future::BoxFuture;
17use shared_buffer::OwnedBuffer;
18use std::any::Any;
19use std::ffi::OsString;
20use std::fmt;
21use std::io;
22use std::ops::Deref;
23use std::path::{Path, PathBuf};
24use std::pin::Pin;
25use std::task::Context;
26use std::task::Poll;
27use thiserror::Error;
28
29pub mod arc_box_file;
30pub mod arc_file;
31pub mod arc_fs;
32pub mod buffer_file;
33pub mod builder;
34pub mod combine_file;
35pub mod cow_file;
36pub mod dual_write_file;
37pub mod empty_fs;
38#[cfg(feature = "host-fs")]
39pub mod host_fs;
40pub mod mem_fs;
41pub mod mount_fs;
42pub mod null_file;
43pub mod passthru_fs;
44pub mod random_file;
45pub mod special_file;
46pub mod tmp_fs;
47pub mod zero_file;
48mod filesystems;
50pub(crate) mod ops;
51mod overlay_fs;
52pub mod pipe;
53mod static_file;
54#[cfg(feature = "static-fs")]
55pub mod static_fs;
56mod trace_fs;
57#[cfg(feature = "webc-fs")]
58mod webc_volume_fs;
59
60pub mod limiter;
61
62pub use arc_box_file::*;
63pub use arc_file::*;
64pub use arc_fs::*;
65pub use buffer_file::*;
66pub use builder::*;
67pub use combine_file::*;
68pub use cow_file::*;
69pub use dual_write_file::*;
70pub use empty_fs::*;
71pub use filesystems::FileSystems;
72pub use mount_fs::*;
73pub use null_file::*;
74pub use overlay_fs::OverlayFileSystem;
75pub use passthru_fs::*;
76pub use pipe::*;
77pub use special_file::*;
78pub use static_file::StaticFile;
79pub use tmp_fs::*;
80pub use trace_fs::TraceFileSystem;
81#[cfg(feature = "webc-fs")]
82pub use webc_volume_fs::WebcVolumeFileSystem;
83pub use zero_file::*;
84
85pub type Result<T> = std::result::Result<T, FsError>;
86
87pub use tokio::io::ReadBuf;
89pub use tokio::io::{AsyncRead, AsyncReadExt};
90pub use tokio::io::{AsyncSeek, AsyncSeekExt};
91pub use tokio::io::{AsyncWrite, AsyncWriteExt};
92
93pub trait CloneableVirtualFile: VirtualFile + Clone {}
94
95pub use ops::{copy_reference, copy_reference_ext, create_dir_all, walk};
96
97pub trait FileSystem: fmt::Debug + Send + Sync + 'static + Upcastable {
98 fn readlink(&self, path: &Path) -> Result<PathBuf>;
99 fn read_dir(&self, path: &Path) -> Result<ReadDir>;
100 fn create_dir(&self, path: &Path) -> Result<()>;
101 fn create_symlink(&self, _source: &Path, _target: &Path) -> Result<()> {
102 Err(FsError::Unsupported)
103 }
104 fn hard_link(&self, _source: &Path, _target: &Path) -> Result<()> {
105 Err(FsError::Unsupported)
106 }
107 fn remove_dir(&self, path: &Path) -> Result<()>;
108 fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>>;
109 fn metadata(&self, path: &Path) -> Result<Metadata>;
110 fn symlink_metadata(&self, path: &Path) -> Result<Metadata>;
114 fn remove_file(&self, path: &Path) -> Result<()>;
115
116 fn new_open_options(&self) -> OpenOptions<'_>;
117}
118
119impl dyn FileSystem + 'static {
120 #[inline]
121 pub fn downcast_ref<T: 'static>(&'_ self) -> Option<&'_ T> {
122 self.upcast_any_ref().downcast_ref::<T>()
123 }
124 #[inline]
125 pub fn downcast_mut<T: 'static>(&'_ mut self) -> Option<&'_ mut T> {
126 self.upcast_any_mut().downcast_mut::<T>()
127 }
128}
129
130#[async_trait::async_trait]
131impl<D, F> FileSystem for D
132where
133 D: Deref<Target = F> + std::fmt::Debug + Send + Sync + 'static,
134 F: FileSystem + ?Sized,
135{
136 fn read_dir(&self, path: &Path) -> Result<ReadDir> {
137 (**self).read_dir(path)
138 }
139
140 fn readlink(&self, path: &Path) -> Result<PathBuf> {
141 (**self).readlink(path)
142 }
143
144 fn create_dir(&self, path: &Path) -> Result<()> {
145 (**self).create_dir(path)
146 }
147
148 fn create_symlink(&self, source: &Path, target: &Path) -> Result<()> {
149 (**self).create_symlink(source, target)
150 }
151
152 fn remove_dir(&self, path: &Path) -> Result<()> {
153 (**self).remove_dir(path)
154 }
155
156 fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>> {
157 Box::pin(async { (**self).rename(from, to).await })
158 }
159
160 fn metadata(&self, path: &Path) -> Result<Metadata> {
161 (**self).metadata(path)
162 }
163
164 fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
165 (**self).symlink_metadata(path)
166 }
167
168 fn remove_file(&self, path: &Path) -> Result<()> {
169 (**self).remove_file(path)
170 }
171
172 fn new_open_options(&self) -> OpenOptions<'_> {
173 (**self).new_open_options()
174 }
175}
176
177pub trait FileOpener {
178 fn open(
179 &self,
180 path: &Path,
181 conf: &OpenOptionsConfig,
182 ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>>;
183}
184
185#[derive(Debug, Clone)]
186pub struct OpenOptionsConfig {
187 pub read: bool,
188 pub write: bool,
189 pub create_new: bool,
190 pub create: bool,
191 pub append: bool,
192 pub truncate: bool,
193}
194
195impl OpenOptionsConfig {
196 pub fn minimum_rights(&self, parent_rights: &Self) -> Self {
198 Self {
199 read: parent_rights.read && self.read,
200 write: parent_rights.write && self.write,
201 create_new: parent_rights.create_new && self.create_new,
202 create: parent_rights.create && self.create,
203 append: parent_rights.append && self.append,
204 truncate: parent_rights.truncate && self.truncate,
205 }
206 }
207
208 pub const fn read(&self) -> bool {
209 self.read
210 }
211
212 pub const fn write(&self) -> bool {
213 self.write
214 }
215
216 pub const fn create_new(&self) -> bool {
217 self.create_new
218 }
219
220 pub const fn create(&self) -> bool {
221 self.create
222 }
223
224 pub const fn append(&self) -> bool {
225 self.append
226 }
227
228 pub const fn truncate(&self) -> bool {
229 self.truncate
230 }
231
232 pub const fn would_mutate(&self) -> bool {
235 let OpenOptionsConfig {
236 read: _,
237 write,
238 create_new,
239 create,
240 append,
241 truncate,
242 } = *self;
243 append || write || create || create_new || truncate
244 }
245}
246
247impl fmt::Debug for OpenOptions<'_> {
248 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249 self.conf.fmt(f)
250 }
251}
252
253pub struct OpenOptions<'a> {
254 opener: &'a dyn FileOpener,
255 conf: OpenOptionsConfig,
256}
257
258impl<'a> OpenOptions<'a> {
259 pub fn new(opener: &'a dyn FileOpener) -> Self {
260 Self {
261 opener,
262 conf: OpenOptionsConfig {
263 read: false,
264 write: false,
265 create_new: false,
266 create: false,
267 append: false,
268 truncate: false,
269 },
270 }
271 }
272
273 pub fn get_config(&self) -> OpenOptionsConfig {
274 self.conf.clone()
275 }
276
277 pub fn options(&mut self, options: OpenOptionsConfig) -> &mut Self {
279 self.conf = options;
280 self
281 }
282
283 pub fn read(&mut self, read: bool) -> &mut Self {
288 self.conf.read = read;
289 self
290 }
291
292 pub fn write(&mut self, write: bool) -> &mut Self {
300 self.conf.write = write;
301 self
302 }
303
304 pub fn append(&mut self, append: bool) -> &mut Self {
311 self.conf.append = append;
312 self
313 }
314
315 pub fn truncate(&mut self, truncate: bool) -> &mut Self {
322 self.conf.truncate = truncate;
323 self
324 }
325
326 pub fn create(&mut self, create: bool) -> &mut Self {
328 self.conf.create = create;
329 self
330 }
331
332 pub fn create_new(&mut self, create_new: bool) -> &mut Self {
334 self.conf.create_new = create_new;
335 self
336 }
337
338 pub fn open<P: AsRef<Path>>(
339 &mut self,
340 path: P,
341 ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
342 self.opener.open(path.as_ref(), &self.conf)
343 }
344}
345
346pub trait VirtualFile:
348 fmt::Debug + AsyncRead + AsyncWrite + AsyncSeek + Unpin + Upcastable + Send
349{
350 fn last_accessed(&self) -> u64;
352
353 fn last_modified(&self) -> u64;
355
356 fn created_time(&self) -> u64;
358
359 #[allow(unused_variables)]
360 fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
362 Ok(())
363 }
364
365 fn size(&self) -> u64;
367
368 fn set_len(&mut self, new_size: u64) -> Result<()>;
371
372 fn unlink(&mut self) -> Result<()>;
378
379 fn is_open(&self) -> bool {
382 true
383 }
384
385 fn get_special_fd(&self) -> Option<u32> {
389 None
390 }
391
392 fn write_from_mmap(&mut self, _offset: u64, _len: u64) -> std::io::Result<()> {
395 Err(std::io::ErrorKind::Unsupported.into())
396 }
397
398 fn copy_reference(
402 &mut self,
403 mut src: Box<dyn VirtualFile + Send + Sync + 'static>,
404 ) -> BoxFuture<'_, std::io::Result<()>> {
405 Box::pin(async move {
406 let bytes_written = tokio::io::copy(&mut src, self).await?;
407 tracing::trace!(bytes_written, "Copying file into host filesystem");
408 Ok(())
409 })
410 }
411
412 fn copy_from_owned_buffer(&mut self, src: &OwnedBuffer) -> BoxFuture<'_, std::io::Result<()>> {
416 let src = src.clone();
417 Box::pin(async move {
418 let mut bytes = src.as_slice();
419 let bytes_written = tokio::io::copy(&mut bytes, self).await?;
420 tracing::trace!(bytes_written, "Copying file into host filesystem");
421 Ok(())
422 })
423 }
424
425 fn as_owned_buffer(&self) -> Option<OwnedBuffer> {
432 None
433 }
434
435 fn poll_read_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>>;
437
438 fn poll_write_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>>;
440}
441
442pub trait Upcastable {
445 fn upcast_any_ref(&'_ self) -> &'_ dyn Any;
446 fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any;
447 fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
448}
449
450impl<T: Any + fmt::Debug + 'static> Upcastable for T {
451 #[inline]
452 fn upcast_any_ref(&'_ self) -> &'_ dyn Any {
453 self
454 }
455 #[inline]
456 fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any {
457 self
458 }
459 #[inline]
460 fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
461 self
462 }
463}
464
465#[derive(Debug, Copy, Clone, PartialEq, Eq)]
467pub enum StdioMode {
468 Piped,
470 Inherit,
472 Null,
474 Log,
476}
477
478#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)]
480pub enum FsError {
481 #[error("fd not a directory")]
483 BaseNotDirectory,
484 #[error("fd not a file")]
486 NotAFile,
487 #[error("invalid fd")]
489 InvalidFd,
490 #[error("file exists")]
492 AlreadyExists,
493 #[error("lock error")]
495 Lock,
496 #[error("io error")]
499 IOError,
500 #[error("address is in use")]
502 AddressInUse,
503 #[error("address could not be found")]
505 AddressNotAvailable,
506 #[error("broken pipe (was closed)")]
508 BrokenPipe,
509 #[error("connection aborted")]
511 ConnectionAborted,
512 #[error("connection refused")]
514 ConnectionRefused,
515 #[error("connection reset")]
517 ConnectionReset,
518 #[error("operation interrupted")]
520 Interrupted,
521 #[error("invalid internal data")]
523 InvalidData,
524 #[error("invalid input")]
526 InvalidInput,
527 #[error("connection is not open")]
529 NotConnected,
530 #[error("entry not found")]
532 EntryNotFound,
533 #[error("can't access device")]
535 NoDevice,
536 #[error("permission denied")]
538 PermissionDenied,
539 #[error("time out")]
541 TimedOut,
542 #[error("unexpected eof")]
544 UnexpectedEof,
545 #[error("blocking operation. try again")]
547 WouldBlock,
548 #[error("write returned 0")]
550 WriteZero,
551 #[error("directory not empty")]
553 DirectoryNotEmpty,
554 #[error("storage full")]
555 StorageFull,
556 #[error("unknown error found")]
558 UnknownError,
559 #[error("unsupported")]
561 Unsupported,
562}
563
564impl From<io::Error> for FsError {
565 fn from(io_error: io::Error) -> Self {
566 match io_error.kind() {
567 io::ErrorKind::AddrInUse => FsError::AddressInUse,
568 io::ErrorKind::AddrNotAvailable => FsError::AddressNotAvailable,
569 io::ErrorKind::AlreadyExists => FsError::AlreadyExists,
570 io::ErrorKind::BrokenPipe => FsError::BrokenPipe,
571 io::ErrorKind::ConnectionAborted => FsError::ConnectionAborted,
572 io::ErrorKind::ConnectionRefused => FsError::ConnectionRefused,
573 io::ErrorKind::ConnectionReset => FsError::ConnectionReset,
574 io::ErrorKind::Interrupted => FsError::Interrupted,
575 io::ErrorKind::InvalidData => FsError::InvalidData,
576 io::ErrorKind::InvalidInput => FsError::InvalidInput,
577 io::ErrorKind::NotConnected => FsError::NotConnected,
578 io::ErrorKind::NotFound => FsError::EntryNotFound,
579 io::ErrorKind::PermissionDenied => FsError::PermissionDenied,
580 io::ErrorKind::TimedOut => FsError::TimedOut,
581 io::ErrorKind::UnexpectedEof => FsError::UnexpectedEof,
582 io::ErrorKind::WouldBlock => FsError::WouldBlock,
583 io::ErrorKind::WriteZero => FsError::WriteZero,
584 io::ErrorKind::Other => FsError::IOError,
587 _ => FsError::UnknownError,
589 }
590 }
591}
592
593impl From<FsError> for io::Error {
594 fn from(val: FsError) -> Self {
595 let kind = match val {
596 FsError::AddressInUse => io::ErrorKind::AddrInUse,
597 FsError::AddressNotAvailable => io::ErrorKind::AddrNotAvailable,
598 FsError::AlreadyExists => io::ErrorKind::AlreadyExists,
599 FsError::BrokenPipe => io::ErrorKind::BrokenPipe,
600 FsError::ConnectionAborted => io::ErrorKind::ConnectionAborted,
601 FsError::ConnectionRefused => io::ErrorKind::ConnectionRefused,
602 FsError::ConnectionReset => io::ErrorKind::ConnectionReset,
603 FsError::Interrupted => io::ErrorKind::Interrupted,
604 FsError::InvalidData => io::ErrorKind::InvalidData,
605 FsError::InvalidInput => io::ErrorKind::InvalidInput,
606 FsError::NotConnected => io::ErrorKind::NotConnected,
607 FsError::EntryNotFound => io::ErrorKind::NotFound,
608 FsError::PermissionDenied => io::ErrorKind::PermissionDenied,
609 FsError::TimedOut => io::ErrorKind::TimedOut,
610 FsError::UnexpectedEof => io::ErrorKind::UnexpectedEof,
611 FsError::WouldBlock => io::ErrorKind::WouldBlock,
612 FsError::WriteZero => io::ErrorKind::WriteZero,
613 FsError::IOError => io::ErrorKind::Other,
614 FsError::BaseNotDirectory => io::ErrorKind::Other,
615 FsError::NotAFile => io::ErrorKind::Other,
616 FsError::InvalidFd => io::ErrorKind::Other,
617 FsError::Lock => io::ErrorKind::Other,
618 FsError::NoDevice => io::ErrorKind::Other,
619 FsError::DirectoryNotEmpty => io::ErrorKind::Other,
620 FsError::UnknownError => io::ErrorKind::Other,
621 FsError::StorageFull => io::ErrorKind::Other,
622 FsError::Unsupported => io::ErrorKind::Unsupported,
623 };
626 kind.into()
627 }
628}
629
630#[derive(Debug)]
631pub struct ReadDir {
632 pub(crate) data: Vec<DirEntry>,
634 index: usize,
635}
636
637impl ReadDir {
638 pub fn new(data: Vec<DirEntry>) -> Self {
639 Self { data, index: 0 }
640 }
641 pub fn is_empty(&self) -> bool {
642 self.data.is_empty()
643 }
644}
645
646#[derive(Debug, Clone, PartialEq, Eq)]
647pub struct DirEntry {
648 pub path: PathBuf,
649 pub metadata: Result<Metadata>,
651}
652
653impl DirEntry {
654 pub fn path(&self) -> PathBuf {
655 self.path.clone()
656 }
657
658 pub fn metadata(&self) -> Result<Metadata> {
659 self.metadata.clone()
660 }
661
662 pub fn file_type(&self) -> Result<FileType> {
663 let metadata = self.metadata.clone()?;
664 Ok(metadata.file_type())
665 }
666
667 pub fn file_name(&self) -> OsString {
668 self.path
669 .file_name()
670 .unwrap_or(self.path.as_os_str())
671 .to_owned()
672 }
673
674 pub fn is_white_out(&self) -> Option<PathBuf> {
675 ops::is_white_out(&self.path)
676 }
677}
678
679#[allow(clippy::len_without_is_empty)] #[derive(Clone, Debug, Default, PartialEq, Eq)]
681pub struct Metadata {
683 pub ft: FileType,
684 pub accessed: u64,
685 pub created: u64,
686 pub modified: u64,
687 pub len: u64,
688}
689
690impl Metadata {
691 pub fn is_file(&self) -> bool {
692 self.ft.is_file()
693 }
694
695 pub fn is_dir(&self) -> bool {
696 self.ft.is_dir()
697 }
698
699 pub fn accessed(&self) -> u64 {
700 self.accessed
701 }
702
703 pub fn created(&self) -> u64 {
704 self.created
705 }
706
707 pub fn modified(&self) -> u64 {
708 self.modified
709 }
710
711 pub fn file_type(&self) -> FileType {
712 self.ft.clone()
713 }
714
715 pub fn len(&self) -> u64 {
716 self.len
717 }
718}
719
720#[derive(Clone, Debug, Default, PartialEq, Eq)]
721pub struct FileType {
723 pub dir: bool,
724 pub file: bool,
725 pub symlink: bool,
726 pub char_device: bool,
729 pub block_device: bool,
730 pub socket: bool,
731 pub fifo: bool,
732}
733
734impl FileType {
735 pub fn new_dir() -> Self {
736 Self {
737 dir: true,
738 ..Default::default()
739 }
740 }
741
742 pub fn new_file() -> Self {
743 Self {
744 file: true,
745 ..Default::default()
746 }
747 }
748
749 pub fn is_dir(&self) -> bool {
750 self.dir
751 }
752 pub fn is_file(&self) -> bool {
753 self.file
754 }
755 pub fn is_symlink(&self) -> bool {
756 self.symlink
757 }
758 pub fn is_char_device(&self) -> bool {
759 self.char_device
760 }
761 pub fn is_block_device(&self) -> bool {
762 self.block_device
763 }
764 pub fn is_socket(&self) -> bool {
765 self.socket
766 }
767 pub fn is_fifo(&self) -> bool {
768 self.fifo
769 }
770}
771
772impl Iterator for ReadDir {
773 type Item = Result<DirEntry>;
774
775 fn next(&mut self) -> Option<Result<DirEntry>> {
776 if let Some(v) = self.data.get(self.index).cloned() {
777 self.index += 1;
778 return Some(Ok(v));
779 }
780 None
781 }
782}