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;
54mod trace_fs;
55#[cfg(feature = "webc-fs")]
56mod webc_volume_fs;
57
58pub mod limiter;
59
60pub use arc_box_file::*;
61pub use arc_file::*;
62pub use arc_fs::*;
63pub use buffer_file::*;
64pub use builder::*;
65pub use combine_file::*;
66pub use cow_file::*;
67pub use dual_write_file::*;
68pub use empty_fs::*;
69pub use filesystems::FileSystems;
70pub use mount_fs::*;
71pub use null_file::*;
72pub use overlay_fs::OverlayFileSystem;
73pub use passthru_fs::*;
74pub use pipe::*;
75pub use special_file::*;
76pub use static_file::StaticFile;
77pub use tmp_fs::*;
78pub use trace_fs::TraceFileSystem;
79#[cfg(feature = "webc-fs")]
80pub use webc_volume_fs::WebcVolumeFileSystem;
81pub use zero_file::*;
82
83pub type Result<T> = std::result::Result<T, FsError>;
84
85pub use tokio::io::ReadBuf;
87pub use tokio::io::{AsyncRead, AsyncReadExt};
88pub use tokio::io::{AsyncSeek, AsyncSeekExt};
89pub use tokio::io::{AsyncWrite, AsyncWriteExt};
90
91pub trait CloneableVirtualFile: VirtualFile + Clone {}
92
93pub use ops::{copy_reference, copy_reference_ext, create_dir_all, walk};
94
95pub trait FileSystem: fmt::Debug + Send + Sync + 'static + Upcastable {
96 fn readlink(&self, path: &Path) -> Result<PathBuf>;
97 fn read_dir(&self, path: &Path) -> Result<ReadDir>;
98 fn create_dir(&self, path: &Path) -> Result<()>;
99 fn create_symlink(&self, _source: &Path, _target: &Path) -> Result<()> {
100 Err(FsError::Unsupported)
101 }
102 fn hard_link(&self, _source: &Path, _target: &Path) -> Result<()> {
103 Err(FsError::Unsupported)
104 }
105 fn remove_dir(&self, path: &Path) -> Result<()>;
106 fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>>;
107 fn metadata(&self, path: &Path) -> Result<Metadata>;
108 fn symlink_metadata(&self, path: &Path) -> Result<Metadata>;
112 fn remove_file(&self, path: &Path) -> Result<()>;
113
114 fn new_open_options(&self) -> OpenOptions<'_>;
115}
116
117impl dyn FileSystem + 'static {
118 #[inline]
119 pub fn downcast_ref<T: 'static>(&'_ self) -> Option<&'_ T> {
120 self.upcast_any_ref().downcast_ref::<T>()
121 }
122 #[inline]
123 pub fn downcast_mut<T: 'static>(&'_ mut self) -> Option<&'_ mut T> {
124 self.upcast_any_mut().downcast_mut::<T>()
125 }
126}
127
128#[async_trait::async_trait]
129impl<D, F> FileSystem for D
130where
131 D: Deref<Target = F> + std::fmt::Debug + Send + Sync + 'static,
132 F: FileSystem + ?Sized,
133{
134 fn read_dir(&self, path: &Path) -> Result<ReadDir> {
135 (**self).read_dir(path)
136 }
137
138 fn readlink(&self, path: &Path) -> Result<PathBuf> {
139 (**self).readlink(path)
140 }
141
142 fn create_dir(&self, path: &Path) -> Result<()> {
143 (**self).create_dir(path)
144 }
145
146 fn create_symlink(&self, source: &Path, target: &Path) -> Result<()> {
147 (**self).create_symlink(source, target)
148 }
149
150 fn remove_dir(&self, path: &Path) -> Result<()> {
151 (**self).remove_dir(path)
152 }
153
154 fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>> {
155 Box::pin(async { (**self).rename(from, to).await })
156 }
157
158 fn metadata(&self, path: &Path) -> Result<Metadata> {
159 (**self).metadata(path)
160 }
161
162 fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
163 (**self).symlink_metadata(path)
164 }
165
166 fn remove_file(&self, path: &Path) -> Result<()> {
167 (**self).remove_file(path)
168 }
169
170 fn new_open_options(&self) -> OpenOptions<'_> {
171 (**self).new_open_options()
172 }
173}
174
175pub trait FileOpener {
176 fn open(
177 &self,
178 path: &Path,
179 conf: &OpenOptionsConfig,
180 ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>>;
181}
182
183#[derive(Debug, Clone)]
184pub struct OpenOptionsConfig {
185 pub read: bool,
186 pub write: bool,
187 pub create_new: bool,
188 pub create: bool,
189 pub append: bool,
190 pub truncate: bool,
191}
192
193impl OpenOptionsConfig {
194 pub fn minimum_rights(&self, parent_rights: &Self) -> Self {
196 Self {
197 read: parent_rights.read && self.read,
198 write: parent_rights.write && self.write,
199 create_new: parent_rights.create_new && self.create_new,
200 create: parent_rights.create && self.create,
201 append: parent_rights.append && self.append,
202 truncate: parent_rights.truncate && self.truncate,
203 }
204 }
205
206 pub const fn read(&self) -> bool {
207 self.read
208 }
209
210 pub const fn write(&self) -> bool {
211 self.write
212 }
213
214 pub const fn create_new(&self) -> bool {
215 self.create_new
216 }
217
218 pub const fn create(&self) -> bool {
219 self.create
220 }
221
222 pub const fn append(&self) -> bool {
223 self.append
224 }
225
226 pub const fn truncate(&self) -> bool {
227 self.truncate
228 }
229
230 pub const fn would_mutate(&self) -> bool {
233 let OpenOptionsConfig {
234 read: _,
235 write,
236 create_new,
237 create,
238 append,
239 truncate,
240 } = *self;
241 append || write || create || create_new || truncate
242 }
243}
244
245impl fmt::Debug for OpenOptions<'_> {
246 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247 self.conf.fmt(f)
248 }
249}
250
251pub struct OpenOptions<'a> {
252 opener: &'a dyn FileOpener,
253 conf: OpenOptionsConfig,
254}
255
256impl<'a> OpenOptions<'a> {
257 pub fn new(opener: &'a dyn FileOpener) -> Self {
258 Self {
259 opener,
260 conf: OpenOptionsConfig {
261 read: false,
262 write: false,
263 create_new: false,
264 create: false,
265 append: false,
266 truncate: false,
267 },
268 }
269 }
270
271 pub fn get_config(&self) -> OpenOptionsConfig {
272 self.conf.clone()
273 }
274
275 pub fn options(&mut self, options: OpenOptionsConfig) -> &mut Self {
277 self.conf = options;
278 self
279 }
280
281 pub fn read(&mut self, read: bool) -> &mut Self {
286 self.conf.read = read;
287 self
288 }
289
290 pub fn write(&mut self, write: bool) -> &mut Self {
298 self.conf.write = write;
299 self
300 }
301
302 pub fn append(&mut self, append: bool) -> &mut Self {
309 self.conf.append = append;
310 self
311 }
312
313 pub fn truncate(&mut self, truncate: bool) -> &mut Self {
320 self.conf.truncate = truncate;
321 self
322 }
323
324 pub fn create(&mut self, create: bool) -> &mut Self {
326 self.conf.create = create;
327 self
328 }
329
330 pub fn create_new(&mut self, create_new: bool) -> &mut Self {
332 self.conf.create_new = create_new;
333 self
334 }
335
336 pub fn open<P: AsRef<Path>>(
337 &mut self,
338 path: P,
339 ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
340 self.opener.open(path.as_ref(), &self.conf)
341 }
342}
343
344pub trait VirtualFile:
346 fmt::Debug + AsyncRead + AsyncWrite + AsyncSeek + Unpin + Upcastable + Send
347{
348 fn last_accessed(&self) -> u64;
350
351 fn last_modified(&self) -> u64;
353
354 fn created_time(&self) -> u64;
356
357 #[allow(unused_variables)]
358 fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
360 Ok(())
361 }
362
363 fn size(&self) -> u64;
365
366 fn set_len(&mut self, new_size: u64) -> Result<()>;
369
370 fn unlink(&mut self) -> Result<()>;
376
377 fn is_open(&self) -> bool {
380 true
381 }
382
383 fn get_special_fd(&self) -> Option<u32> {
387 None
388 }
389
390 fn write_from_mmap(&mut self, _offset: u64, _len: u64) -> std::io::Result<()> {
393 Err(std::io::ErrorKind::Unsupported.into())
394 }
395
396 fn copy_reference(
400 &mut self,
401 mut src: Box<dyn VirtualFile + Send + Sync + 'static>,
402 ) -> BoxFuture<'_, std::io::Result<()>> {
403 Box::pin(async move {
404 let bytes_written = tokio::io::copy(&mut src, self).await?;
405 tracing::trace!(bytes_written, "Copying file into host filesystem");
406 Ok(())
407 })
408 }
409
410 fn copy_from_owned_buffer(&mut self, src: &OwnedBuffer) -> BoxFuture<'_, std::io::Result<()>> {
414 let src = src.clone();
415 Box::pin(async move {
416 let mut bytes = src.as_slice();
417 let bytes_written = tokio::io::copy(&mut bytes, self).await?;
418 tracing::trace!(bytes_written, "Copying file into host filesystem");
419 Ok(())
420 })
421 }
422
423 fn as_owned_buffer(&self) -> Option<OwnedBuffer> {
430 None
431 }
432
433 fn poll_read_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>>;
435
436 fn poll_write_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>>;
438}
439
440pub trait Upcastable {
443 fn upcast_any_ref(&'_ self) -> &'_ dyn Any;
444 fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any;
445 fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
446}
447
448impl<T: Any + fmt::Debug + 'static> Upcastable for T {
449 #[inline]
450 fn upcast_any_ref(&'_ self) -> &'_ dyn Any {
451 self
452 }
453 #[inline]
454 fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any {
455 self
456 }
457 #[inline]
458 fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
459 self
460 }
461}
462
463#[derive(Debug, Copy, Clone, PartialEq, Eq)]
465pub enum StdioMode {
466 Piped,
468 Inherit,
470 Null,
472 Log,
474}
475
476#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)]
478pub enum FsError {
479 #[error("fd not a directory")]
481 BaseNotDirectory,
482 #[error("fd not a file")]
484 NotAFile,
485 #[error("invalid fd")]
487 InvalidFd,
488 #[error("file exists")]
490 AlreadyExists,
491 #[error("lock error")]
493 Lock,
494 #[error("io error")]
497 IOError,
498 #[error("address is in use")]
500 AddressInUse,
501 #[error("address could not be found")]
503 AddressNotAvailable,
504 #[error("broken pipe (was closed)")]
506 BrokenPipe,
507 #[error("connection aborted")]
509 ConnectionAborted,
510 #[error("connection refused")]
512 ConnectionRefused,
513 #[error("connection reset")]
515 ConnectionReset,
516 #[error("operation interrupted")]
518 Interrupted,
519 #[error("invalid internal data")]
521 InvalidData,
522 #[error("invalid input")]
524 InvalidInput,
525 #[error("connection is not open")]
527 NotConnected,
528 #[error("entry not found")]
530 EntryNotFound,
531 #[error("can't access device")]
533 NoDevice,
534 #[error("permission denied")]
536 PermissionDenied,
537 #[error("time out")]
539 TimedOut,
540 #[error("unexpected eof")]
542 UnexpectedEof,
543 #[error("blocking operation. try again")]
545 WouldBlock,
546 #[error("write returned 0")]
548 WriteZero,
549 #[error("directory not empty")]
551 DirectoryNotEmpty,
552 #[error("storage full")]
553 StorageFull,
554 #[error("unknown error found")]
556 UnknownError,
557 #[error("unsupported")]
559 Unsupported,
560}
561
562impl From<io::Error> for FsError {
563 fn from(io_error: io::Error) -> Self {
564 match io_error.kind() {
565 io::ErrorKind::AddrInUse => FsError::AddressInUse,
566 io::ErrorKind::AddrNotAvailable => FsError::AddressNotAvailable,
567 io::ErrorKind::AlreadyExists => FsError::AlreadyExists,
568 io::ErrorKind::BrokenPipe => FsError::BrokenPipe,
569 io::ErrorKind::ConnectionAborted => FsError::ConnectionAborted,
570 io::ErrorKind::ConnectionRefused => FsError::ConnectionRefused,
571 io::ErrorKind::ConnectionReset => FsError::ConnectionReset,
572 io::ErrorKind::Interrupted => FsError::Interrupted,
573 io::ErrorKind::InvalidData => FsError::InvalidData,
574 io::ErrorKind::InvalidInput => FsError::InvalidInput,
575 io::ErrorKind::NotConnected => FsError::NotConnected,
576 io::ErrorKind::NotFound => FsError::EntryNotFound,
577 io::ErrorKind::PermissionDenied => FsError::PermissionDenied,
578 io::ErrorKind::TimedOut => FsError::TimedOut,
579 io::ErrorKind::UnexpectedEof => FsError::UnexpectedEof,
580 io::ErrorKind::WouldBlock => FsError::WouldBlock,
581 io::ErrorKind::WriteZero => FsError::WriteZero,
582 io::ErrorKind::Other => FsError::IOError,
585 _ => FsError::UnknownError,
587 }
588 }
589}
590
591impl From<FsError> for io::Error {
592 fn from(val: FsError) -> Self {
593 let kind = match val {
594 FsError::AddressInUse => io::ErrorKind::AddrInUse,
595 FsError::AddressNotAvailable => io::ErrorKind::AddrNotAvailable,
596 FsError::AlreadyExists => io::ErrorKind::AlreadyExists,
597 FsError::BrokenPipe => io::ErrorKind::BrokenPipe,
598 FsError::ConnectionAborted => io::ErrorKind::ConnectionAborted,
599 FsError::ConnectionRefused => io::ErrorKind::ConnectionRefused,
600 FsError::ConnectionReset => io::ErrorKind::ConnectionReset,
601 FsError::Interrupted => io::ErrorKind::Interrupted,
602 FsError::InvalidData => io::ErrorKind::InvalidData,
603 FsError::InvalidInput => io::ErrorKind::InvalidInput,
604 FsError::NotConnected => io::ErrorKind::NotConnected,
605 FsError::EntryNotFound => io::ErrorKind::NotFound,
606 FsError::PermissionDenied => io::ErrorKind::PermissionDenied,
607 FsError::TimedOut => io::ErrorKind::TimedOut,
608 FsError::UnexpectedEof => io::ErrorKind::UnexpectedEof,
609 FsError::WouldBlock => io::ErrorKind::WouldBlock,
610 FsError::WriteZero => io::ErrorKind::WriteZero,
611 FsError::IOError => io::ErrorKind::Other,
612 FsError::BaseNotDirectory => io::ErrorKind::Other,
613 FsError::NotAFile => io::ErrorKind::Other,
614 FsError::InvalidFd => io::ErrorKind::Other,
615 FsError::Lock => io::ErrorKind::Other,
616 FsError::NoDevice => io::ErrorKind::Other,
617 FsError::DirectoryNotEmpty => io::ErrorKind::Other,
618 FsError::UnknownError => io::ErrorKind::Other,
619 FsError::StorageFull => io::ErrorKind::Other,
620 FsError::Unsupported => io::ErrorKind::Unsupported,
621 };
624 kind.into()
625 }
626}
627
628#[derive(Debug)]
629pub struct ReadDir {
630 pub(crate) data: Vec<DirEntry>,
632 index: usize,
633}
634
635impl ReadDir {
636 pub fn new(data: Vec<DirEntry>) -> Self {
637 Self { data, index: 0 }
638 }
639 pub fn is_empty(&self) -> bool {
640 self.data.is_empty()
641 }
642}
643
644#[derive(Debug, Clone, PartialEq, Eq)]
645pub struct DirEntry {
646 pub path: PathBuf,
647 pub metadata: Result<Metadata>,
649}
650
651impl DirEntry {
652 pub fn path(&self) -> PathBuf {
653 self.path.clone()
654 }
655
656 pub fn metadata(&self) -> Result<Metadata> {
657 self.metadata.clone()
658 }
659
660 pub fn file_type(&self) -> Result<FileType> {
661 let metadata = self.metadata.clone()?;
662 Ok(metadata.file_type())
663 }
664
665 pub fn file_name(&self) -> OsString {
666 self.path
667 .file_name()
668 .unwrap_or(self.path.as_os_str())
669 .to_owned()
670 }
671
672 pub fn is_white_out(&self) -> Option<PathBuf> {
673 ops::is_white_out(&self.path)
674 }
675}
676
677#[allow(clippy::len_without_is_empty)] #[derive(Clone, Debug, Default, PartialEq, Eq)]
679pub struct Metadata {
681 pub ft: FileType,
682 pub accessed: u64,
683 pub created: u64,
684 pub modified: u64,
685 pub len: u64,
686}
687
688impl Metadata {
689 pub fn is_file(&self) -> bool {
690 self.ft.is_file()
691 }
692
693 pub fn is_dir(&self) -> bool {
694 self.ft.is_dir()
695 }
696
697 pub fn accessed(&self) -> u64 {
698 self.accessed
699 }
700
701 pub fn created(&self) -> u64 {
702 self.created
703 }
704
705 pub fn modified(&self) -> u64 {
706 self.modified
707 }
708
709 pub fn file_type(&self) -> FileType {
710 self.ft.clone()
711 }
712
713 pub fn len(&self) -> u64 {
714 self.len
715 }
716}
717
718#[derive(Clone, Debug, Default, PartialEq, Eq)]
719pub struct FileType {
721 pub dir: bool,
722 pub file: bool,
723 pub symlink: bool,
724 pub char_device: bool,
727 pub block_device: bool,
728 pub socket: bool,
729 pub fifo: bool,
730}
731
732impl FileType {
733 pub fn new_dir() -> Self {
734 Self {
735 dir: true,
736 ..Default::default()
737 }
738 }
739
740 pub fn new_file() -> Self {
741 Self {
742 file: true,
743 ..Default::default()
744 }
745 }
746
747 pub fn is_dir(&self) -> bool {
748 self.dir
749 }
750 pub fn is_file(&self) -> bool {
751 self.file
752 }
753 pub fn is_symlink(&self) -> bool {
754 self.symlink
755 }
756 pub fn is_char_device(&self) -> bool {
757 self.char_device
758 }
759 pub fn is_block_device(&self) -> bool {
760 self.block_device
761 }
762 pub fn is_socket(&self) -> bool {
763 self.socket
764 }
765 pub fn is_fifo(&self) -> bool {
766 self.fifo
767 }
768}
769
770impl Iterator for ReadDir {
771 type Item = Result<DirEntry>;
772
773 fn next(&mut self) -> Option<Result<DirEntry>> {
774 if let Some(v) = self.data.get(self.index).cloned() {
775 self.index += 1;
776 return Some(Ok(v));
777 }
778 None
779 }
780}