1#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_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 null_file;
33pub mod passthru_fs;
34pub mod random_file;
35pub mod special_file;
36pub mod tmp_fs;
37pub mod union_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 null_file::*;
64pub use overlay_fs::OverlayFileSystem;
65pub use passthru_fs::*;
66pub use pipe::*;
67pub use special_file::*;
68pub use static_file::StaticFile;
69pub use tmp_fs::*;
70pub use trace_fs::TraceFileSystem;
71pub use union_fs::*;
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};
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 remove_dir(&self, path: &Path) -> Result<()>;
93 fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>>;
94 fn metadata(&self, path: &Path) -> Result<Metadata>;
95 fn symlink_metadata(&self, path: &Path) -> Result<Metadata>;
99 fn remove_file(&self, path: &Path) -> Result<()>;
100
101 fn new_open_options(&self) -> OpenOptions;
102
103 fn mount(&self, name: String, path: &Path, fs: Box<dyn FileSystem + Send + Sync>)
104 -> Result<()>;
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 remove_dir(&self, path: &Path) -> Result<()> {
137 (**self).remove_dir(path)
138 }
139
140 fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<()>> {
141 Box::pin(async { (**self).rename(from, to).await })
142 }
143
144 fn metadata(&self, path: &Path) -> Result<Metadata> {
145 (**self).metadata(path)
146 }
147
148 fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
149 (**self).symlink_metadata(path)
150 }
151
152 fn remove_file(&self, path: &Path) -> Result<()> {
153 (**self).remove_file(path)
154 }
155
156 fn new_open_options(&self) -> OpenOptions {
157 (**self).new_open_options()
158 }
159
160 fn mount(
161 &self,
162 name: String,
163 path: &Path,
164 fs: Box<dyn FileSystem + Send + Sync>,
165 ) -> Result<()> {
166 (**self).mount(name, path, fs)
167 }
168}
169
170pub trait FileOpener {
171 fn open(
172 &self,
173 path: &Path,
174 conf: &OpenOptionsConfig,
175 ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>>;
176}
177
178#[derive(Debug, Clone)]
179pub struct OpenOptionsConfig {
180 pub read: bool,
181 pub write: bool,
182 pub create_new: bool,
183 pub create: bool,
184 pub append: bool,
185 pub truncate: bool,
186}
187
188impl OpenOptionsConfig {
189 pub fn minimum_rights(&self, parent_rights: &Self) -> Self {
191 Self {
192 read: parent_rights.read && self.read,
193 write: parent_rights.write && self.write,
194 create_new: parent_rights.create_new && self.create_new,
195 create: parent_rights.create && self.create,
196 append: parent_rights.append && self.append,
197 truncate: parent_rights.truncate && self.truncate,
198 }
199 }
200
201 pub const fn read(&self) -> bool {
202 self.read
203 }
204
205 pub const fn write(&self) -> bool {
206 self.write
207 }
208
209 pub const fn create_new(&self) -> bool {
210 self.create_new
211 }
212
213 pub const fn create(&self) -> bool {
214 self.create
215 }
216
217 pub const fn append(&self) -> bool {
218 self.append
219 }
220
221 pub const fn truncate(&self) -> bool {
222 self.truncate
223 }
224
225 pub const fn would_mutate(&self) -> bool {
228 let OpenOptionsConfig {
229 read: _,
230 write,
231 create_new,
232 create,
233 append,
234 truncate,
235 } = *self;
236 append || write || create || create_new || truncate
237 }
238}
239
240impl fmt::Debug for OpenOptions<'_> {
241 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
242 self.conf.fmt(f)
243 }
244}
245
246pub struct OpenOptions<'a> {
247 opener: &'a dyn FileOpener,
248 conf: OpenOptionsConfig,
249}
250
251impl<'a> OpenOptions<'a> {
252 pub fn new(opener: &'a dyn FileOpener) -> Self {
253 Self {
254 opener,
255 conf: OpenOptionsConfig {
256 read: false,
257 write: false,
258 create_new: false,
259 create: false,
260 append: false,
261 truncate: false,
262 },
263 }
264 }
265
266 pub fn get_config(&self) -> OpenOptionsConfig {
267 self.conf.clone()
268 }
269
270 pub fn options(&mut self, options: OpenOptionsConfig) -> &mut Self {
272 self.conf = options;
273 self
274 }
275
276 pub fn read(&mut self, read: bool) -> &mut Self {
281 self.conf.read = read;
282 self
283 }
284
285 pub fn write(&mut self, write: bool) -> &mut Self {
293 self.conf.write = write;
294 self
295 }
296
297 pub fn append(&mut self, append: bool) -> &mut Self {
304 self.conf.append = append;
305 self
306 }
307
308 pub fn truncate(&mut self, truncate: bool) -> &mut Self {
315 self.conf.truncate = truncate;
316 self
317 }
318
319 pub fn create(&mut self, create: bool) -> &mut Self {
321 self.conf.create = create;
322 self
323 }
324
325 pub fn create_new(&mut self, create_new: bool) -> &mut Self {
327 self.conf.create_new = create_new;
328 self
329 }
330
331 pub fn open<P: AsRef<Path>>(
332 &mut self,
333 path: P,
334 ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
335 self.opener.open(path.as_ref(), &self.conf)
336 }
337}
338
339pub trait VirtualFile:
342 fmt::Debug + AsyncRead + AsyncWrite + AsyncSeek + Unpin + Upcastable + Send
343{
344 fn last_accessed(&self) -> u64;
346
347 fn last_modified(&self) -> u64;
349
350 fn created_time(&self) -> u64;
352
353 #[allow(unused_variables)]
354 fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
356 Ok(())
357 }
358
359 fn size(&self) -> u64;
361
362 fn set_len(&mut self, new_size: u64) -> Result<()>;
365
366 fn unlink(&mut self) -> Result<()>;
368
369 fn is_open(&self) -> bool {
372 true
373 }
374
375 fn get_special_fd(&self) -> Option<u32> {
379 None
380 }
381
382 fn write_from_mmap(&mut self, _offset: u64, _len: u64) -> std::io::Result<()> {
385 Err(std::io::ErrorKind::Unsupported.into())
386 }
387
388 fn copy_reference(
392 &mut self,
393 mut src: Box<dyn VirtualFile + Send + Sync + 'static>,
394 ) -> BoxFuture<'_, std::io::Result<()>> {
395 Box::pin(async move {
396 let bytes_written = tokio::io::copy(&mut src, self).await?;
397 tracing::trace!(bytes_written, "Copying file into host filesystem");
398 Ok(())
399 })
400 }
401
402 fn copy_from_owned_buffer(&mut self, src: &OwnedBuffer) -> BoxFuture<'_, std::io::Result<()>> {
406 let src = src.clone();
407 Box::pin(async move {
408 let mut bytes = src.as_slice();
409 let bytes_written = tokio::io::copy(&mut bytes, self).await?;
410 tracing::trace!(bytes_written, "Copying file into host filesystem");
411 Ok(())
412 })
413 }
414
415 fn poll_read_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>>;
417
418 fn poll_write_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>>;
420}
421
422pub trait Upcastable {
425 fn upcast_any_ref(&'_ self) -> &'_ dyn Any;
426 fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any;
427 fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
428}
429
430impl<T: Any + fmt::Debug + 'static> Upcastable for T {
431 #[inline]
432 fn upcast_any_ref(&'_ self) -> &'_ dyn Any {
433 self
434 }
435 #[inline]
436 fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any {
437 self
438 }
439 #[inline]
440 fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
441 self
442 }
443}
444
445#[derive(Debug, Copy, Clone, PartialEq, Eq)]
447pub enum StdioMode {
448 Piped,
450 Inherit,
452 Null,
454 Log,
456}
457
458#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)]
460pub enum FsError {
461 #[error("fd not a directory")]
463 BaseNotDirectory,
464 #[error("fd not a file")]
466 NotAFile,
467 #[error("invalid fd")]
469 InvalidFd,
470 #[error("file exists")]
472 AlreadyExists,
473 #[error("lock error")]
475 Lock,
476 #[error("io error")]
479 IOError,
480 #[error("address is in use")]
482 AddressInUse,
483 #[error("address could not be found")]
485 AddressNotAvailable,
486 #[error("broken pipe (was closed)")]
488 BrokenPipe,
489 #[error("connection aborted")]
491 ConnectionAborted,
492 #[error("connection refused")]
494 ConnectionRefused,
495 #[error("connection reset")]
497 ConnectionReset,
498 #[error("operation interrupted")]
500 Interrupted,
501 #[error("invalid internal data")]
503 InvalidData,
504 #[error("invalid input")]
506 InvalidInput,
507 #[error("connection is not open")]
509 NotConnected,
510 #[error("entry not found")]
512 EntryNotFound,
513 #[error("can't access device")]
515 NoDevice,
516 #[error("permission denied")]
518 PermissionDenied,
519 #[error("time out")]
521 TimedOut,
522 #[error("unexpected eof")]
524 UnexpectedEof,
525 #[error("blocking operation. try again")]
527 WouldBlock,
528 #[error("write returned 0")]
530 WriteZero,
531 #[error("directory not empty")]
533 DirectoryNotEmpty,
534 #[error("storage full")]
535 StorageFull,
536 #[error("unknown error found")]
538 UnknownError,
539 #[error("unsupported")]
541 Unsupported,
542}
543
544impl From<io::Error> for FsError {
545 fn from(io_error: io::Error) -> Self {
546 match io_error.kind() {
547 io::ErrorKind::AddrInUse => FsError::AddressInUse,
548 io::ErrorKind::AddrNotAvailable => FsError::AddressNotAvailable,
549 io::ErrorKind::AlreadyExists => FsError::AlreadyExists,
550 io::ErrorKind::BrokenPipe => FsError::BrokenPipe,
551 io::ErrorKind::ConnectionAborted => FsError::ConnectionAborted,
552 io::ErrorKind::ConnectionRefused => FsError::ConnectionRefused,
553 io::ErrorKind::ConnectionReset => FsError::ConnectionReset,
554 io::ErrorKind::Interrupted => FsError::Interrupted,
555 io::ErrorKind::InvalidData => FsError::InvalidData,
556 io::ErrorKind::InvalidInput => FsError::InvalidInput,
557 io::ErrorKind::NotConnected => FsError::NotConnected,
558 io::ErrorKind::NotFound => FsError::EntryNotFound,
559 io::ErrorKind::PermissionDenied => FsError::PermissionDenied,
560 io::ErrorKind::TimedOut => FsError::TimedOut,
561 io::ErrorKind::UnexpectedEof => FsError::UnexpectedEof,
562 io::ErrorKind::WouldBlock => FsError::WouldBlock,
563 io::ErrorKind::WriteZero => FsError::WriteZero,
564 io::ErrorKind::Other => FsError::IOError,
567 _ => FsError::UnknownError,
569 }
570 }
571}
572
573impl From<FsError> for io::Error {
574 fn from(val: FsError) -> Self {
575 let kind = match val {
576 FsError::AddressInUse => io::ErrorKind::AddrInUse,
577 FsError::AddressNotAvailable => io::ErrorKind::AddrNotAvailable,
578 FsError::AlreadyExists => io::ErrorKind::AlreadyExists,
579 FsError::BrokenPipe => io::ErrorKind::BrokenPipe,
580 FsError::ConnectionAborted => io::ErrorKind::ConnectionAborted,
581 FsError::ConnectionRefused => io::ErrorKind::ConnectionRefused,
582 FsError::ConnectionReset => io::ErrorKind::ConnectionReset,
583 FsError::Interrupted => io::ErrorKind::Interrupted,
584 FsError::InvalidData => io::ErrorKind::InvalidData,
585 FsError::InvalidInput => io::ErrorKind::InvalidInput,
586 FsError::NotConnected => io::ErrorKind::NotConnected,
587 FsError::EntryNotFound => io::ErrorKind::NotFound,
588 FsError::PermissionDenied => io::ErrorKind::PermissionDenied,
589 FsError::TimedOut => io::ErrorKind::TimedOut,
590 FsError::UnexpectedEof => io::ErrorKind::UnexpectedEof,
591 FsError::WouldBlock => io::ErrorKind::WouldBlock,
592 FsError::WriteZero => io::ErrorKind::WriteZero,
593 FsError::IOError => io::ErrorKind::Other,
594 FsError::BaseNotDirectory => io::ErrorKind::Other,
595 FsError::NotAFile => io::ErrorKind::Other,
596 FsError::InvalidFd => io::ErrorKind::Other,
597 FsError::Lock => io::ErrorKind::Other,
598 FsError::NoDevice => io::ErrorKind::Other,
599 FsError::DirectoryNotEmpty => io::ErrorKind::Other,
600 FsError::UnknownError => io::ErrorKind::Other,
601 FsError::StorageFull => io::ErrorKind::Other,
602 FsError::Unsupported => io::ErrorKind::Unsupported,
603 };
606 kind.into()
607 }
608}
609
610#[derive(Debug)]
611pub struct ReadDir {
612 pub(crate) data: Vec<DirEntry>,
614 index: usize,
615}
616
617impl ReadDir {
618 pub fn new(data: Vec<DirEntry>) -> Self {
619 Self { data, index: 0 }
620 }
621 pub fn is_empty(&self) -> bool {
622 self.data.is_empty()
623 }
624}
625
626#[derive(Debug, Clone, PartialEq, Eq)]
627pub struct DirEntry {
628 pub path: PathBuf,
629 pub metadata: Result<Metadata>,
631}
632
633impl DirEntry {
634 pub fn path(&self) -> PathBuf {
635 self.path.clone()
636 }
637
638 pub fn metadata(&self) -> Result<Metadata> {
639 self.metadata.clone()
640 }
641
642 pub fn file_type(&self) -> Result<FileType> {
643 let metadata = self.metadata.clone()?;
644 Ok(metadata.file_type())
645 }
646
647 pub fn file_name(&self) -> OsString {
648 self.path
649 .file_name()
650 .unwrap_or(self.path.as_os_str())
651 .to_owned()
652 }
653
654 pub fn is_white_out(&self) -> Option<PathBuf> {
655 ops::is_white_out(&self.path)
656 }
657}
658
659#[allow(clippy::len_without_is_empty)] #[derive(Clone, Debug, Default, PartialEq, Eq)]
661pub struct Metadata {
663 pub ft: FileType,
664 pub accessed: u64,
665 pub created: u64,
666 pub modified: u64,
667 pub len: u64,
668}
669
670impl Metadata {
671 pub fn is_file(&self) -> bool {
672 self.ft.is_file()
673 }
674
675 pub fn is_dir(&self) -> bool {
676 self.ft.is_dir()
677 }
678
679 pub fn accessed(&self) -> u64 {
680 self.accessed
681 }
682
683 pub fn created(&self) -> u64 {
684 self.created
685 }
686
687 pub fn modified(&self) -> u64 {
688 self.modified
689 }
690
691 pub fn file_type(&self) -> FileType {
692 self.ft.clone()
693 }
694
695 pub fn len(&self) -> u64 {
696 self.len
697 }
698}
699
700#[derive(Clone, Debug, Default, PartialEq, Eq)]
701pub struct FileType {
703 pub dir: bool,
704 pub file: bool,
705 pub symlink: bool,
706 pub char_device: bool,
709 pub block_device: bool,
710 pub socket: bool,
711 pub fifo: bool,
712}
713
714impl FileType {
715 pub fn new_dir() -> Self {
716 Self {
717 dir: true,
718 ..Default::default()
719 }
720 }
721
722 pub fn new_file() -> Self {
723 Self {
724 file: true,
725 ..Default::default()
726 }
727 }
728
729 pub fn is_dir(&self) -> bool {
730 self.dir
731 }
732 pub fn is_file(&self) -> bool {
733 self.file
734 }
735 pub fn is_symlink(&self) -> bool {
736 self.symlink
737 }
738 pub fn is_char_device(&self) -> bool {
739 self.char_device
740 }
741 pub fn is_block_device(&self) -> bool {
742 self.block_device
743 }
744 pub fn is_socket(&self) -> bool {
745 self.socket
746 }
747 pub fn is_fifo(&self) -> bool {
748 self.fifo
749 }
750}
751
752impl Iterator for ReadDir {
753 type Item = Result<DirEntry>;
754
755 fn next(&mut self) -> Option<Result<DirEntry>> {
756 if let Some(v) = self.data.get(self.index).cloned() {
757 self.index += 1;
758 return Some(Ok(v));
759 }
760 None
761 }
762}