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