mirror of
https://github.com/mii443/wasmer.git
synced 2025-08-22 16:35:33 +00:00
Update wasmer-vfs implemenetation
* Add some new file systems * Add tests * General cleanup
This commit is contained in:
committed by
Christoph Herzog
parent
0cf7e0404d
commit
dfd7ff41b5
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -1155,6 +1155,12 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
@ -4288,7 +4294,7 @@ dependencies = [
|
||||
"wasmer-types",
|
||||
"wasmer-vfs",
|
||||
"wasmer-wasi",
|
||||
"webc",
|
||||
"webc 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4373,7 +4379,7 @@ dependencies = [
|
||||
"wasmer-wasi",
|
||||
"wasmer-wasi-experimental-io-devices",
|
||||
"wasmer-wast",
|
||||
"webc",
|
||||
"webc 3.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4628,6 +4634,9 @@ version = "3.0.0-rc.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"derivative",
|
||||
"filetime",
|
||||
"fs_extra",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"serde",
|
||||
@ -4635,7 +4644,8 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"typetag",
|
||||
"webc",
|
||||
"wasmer-wasi-types",
|
||||
"webc 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4725,7 +4735,7 @@ dependencies = [
|
||||
"wasmer-vnet",
|
||||
"wasmer-wasi-local-networking",
|
||||
"wasmer-wasi-types",
|
||||
"webc",
|
||||
"webc 0.1.0",
|
||||
"webc-vfs",
|
||||
"weezl",
|
||||
"winapi",
|
||||
@ -5033,13 +5043,35 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webc"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef87e7b955d5d1feaa8697ae129f1a9ce8859e151574ad3baceae9413b48d2f0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"indexmap",
|
||||
"leb128",
|
||||
"lexical-sort",
|
||||
"memchr",
|
||||
"path-clean",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"url",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webc-vfs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"wasmer-vfs",
|
||||
"webc",
|
||||
"webc 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
1
Makefile
1
Makefile
@ -483,6 +483,7 @@ test-packages:
|
||||
$(CARGO_BINARY) test $(CARGO_TARGET) --manifest-path lib/compiler-cranelift/Cargo.toml --release --no-default-features --features=std
|
||||
$(CARGO_BINARY) test $(CARGO_TARGET) --manifest-path lib/compiler-singlepass/Cargo.toml --release --no-default-features --features=std
|
||||
$(CARGO_BINARY) test $(CARGO_TARGET) --manifest-path lib/cli/Cargo.toml $(compiler_features) --release
|
||||
$(CARGO_BINARY) test $(CARGO_TARGET) --manifest-path lib/vfs/Cargo.toml $(compiler_features) --release
|
||||
|
||||
test-js: test-js-api test-js-wasi
|
||||
|
||||
|
4
build.rs
4
build.rs
@ -73,6 +73,10 @@ fn main() -> anyhow::Result<()> {
|
||||
for (wasi_filesystem_test_name, wasi_filesystem_kind) in &[
|
||||
("host_fs", "WasiFileSystemKind::Host"),
|
||||
("mem_fs", "WasiFileSystemKind::InMemory"),
|
||||
("tmp_fs", "WasiFileSystemKind::Tmp"),
|
||||
("passthru_fs", "WasiFileSystemKind::PassthruMemory"),
|
||||
("union_fs", "WasiFileSystemKind::UnionHostMemory"),
|
||||
("root_fs", "WasiFileSystemKind::RootFileSystemBuilder"),
|
||||
] {
|
||||
with_test_module(wasitests, wasi_filesystem_test_name, |wasitests| {
|
||||
test_directory(
|
||||
|
@ -18,6 +18,5 @@ wasmer-vfs = { path = "../vfs", version = "=3.0.0-rc.2", default-features = fals
|
||||
wasmer-wasi-types = { path = "../wasi-types/", version = "3.0.0-rc.2" }
|
||||
|
||||
[features]
|
||||
default = ["mem_fs"]
|
||||
mem_fs = ["wasmer-vfs/mem-fs"]
|
||||
default = []
|
||||
host_fs = ["wasmer-vfs/host-fs"]
|
||||
|
@ -12,18 +12,22 @@ thiserror = "1"
|
||||
tracing = { version = "0.1" }
|
||||
typetag = { version = "0.1", optional = true }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
|
||||
slab = { version = "0.4", optional = true }
|
||||
# FIXME: use proper dependency
|
||||
webc = { version = "*", optional = true, path="../../../pirita/crates/webc" }
|
||||
#webc = { version = "*", optional = true, git = "https://github.com/wasmerio/pirita", branch = "deploy" }
|
||||
slab = { version = "0.4" }
|
||||
derivative = "2.2.0"
|
||||
wasmer-wasi-types = { path = "../wasi-types", version = "3.0.0-rc.2" }
|
||||
anyhow = { version = "1.0.66", optional = true }
|
||||
async-trait = { version = "^0.1" }
|
||||
lazy_static = "1.4"
|
||||
fs_extra = { version = "1.2.0", optional = true }
|
||||
filetime = { version = "0.2.18", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["host-fs", "mem-fs", "webc-fs", "static-fs"]
|
||||
host-fs = ["libc"]
|
||||
mem-fs = ["slab"]
|
||||
host-fs = ["libc", "fs_extra", "filetime"]
|
||||
mem-fs = []
|
||||
webc-fs = ["webc", "anyhow"]
|
||||
static-fs = ["webc", "anyhow", "mem-fs"]
|
||||
enable-serde = [
|
||||
|
@ -1,10 +1,13 @@
|
||||
//! Used for sharing references to the same file across multiple file systems,
|
||||
//! effectively this is a symbolic link without all the complex path redirection
|
||||
|
||||
use crate::FileDescriptor;
|
||||
use crate::{ClonableVirtualFile, VirtualFile};
|
||||
use derivative::Derivative;
|
||||
use std::{
|
||||
io::{self, *},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use wasmer_vbus::FileDescriptor;
|
||||
use wasmer_vfs::{ClonableVirtualFile, VirtualFile};
|
||||
|
||||
#[derive(Derivative, Clone)]
|
||||
#[derivative(Debug)]
|
||||
@ -27,6 +30,7 @@ impl Seek for ArcFile {
|
||||
inner.seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for ArcFile {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
@ -62,23 +66,23 @@ impl VirtualFile for ArcFile {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
inner.size()
|
||||
}
|
||||
fn set_len(&mut self, new_size: u64) -> wasmer_vfs::Result<()> {
|
||||
fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.set_len(new_size)
|
||||
}
|
||||
fn unlink(&mut self) -> wasmer_vfs::Result<()> {
|
||||
fn unlink(&mut self) -> crate::Result<()> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.unlink()
|
||||
}
|
||||
fn bytes_available(&self) -> wasmer_vfs::Result<usize> {
|
||||
fn bytes_available(&self) -> crate::Result<usize> {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
inner.bytes_available()
|
||||
}
|
||||
fn bytes_available_read(&self) -> wasmer_vfs::Result<usize> {
|
||||
fn bytes_available_read(&self) -> crate::Result<usize> {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
inner.bytes_available_read()
|
||||
}
|
||||
fn bytes_available_write(&self) -> wasmer_vfs::Result<usize> {
|
||||
fn bytes_available_write(&self) -> crate::Result<usize> {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
inner.bytes_available_write()
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
//! Wraps a clonable Arc of a file system - in practice this is useful so you
|
||||
//! can pass clonable file systems with a Box<dyn FileSystem> to other interfaces
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
#[allow(unused_imports, dead_code)]
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use wasmer_vfs::*;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArcFileSystem {
|
@ -1,10 +1,11 @@
|
||||
use crate::{FileSystem, VirtualFile};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::*;
|
||||
use wasmer_vfs::{FileSystem, VirtualFile};
|
||||
use wasmer_wasi_types::types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO};
|
||||
|
||||
use super::{NullFile, SpecialFile};
|
||||
use super::{TmpFileSystem, ZeroFile};
|
||||
use super::{ZeroFile};
|
||||
use crate::tmp_fs::TmpFileSystem;
|
||||
|
||||
pub struct RootFileSystemBuilder {
|
||||
default_root_dirs: bool,
|
||||
@ -16,8 +17,8 @@ pub struct RootFileSystemBuilder {
|
||||
tty: Option<Box<dyn VirtualFile + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl RootFileSystemBuilder {
|
||||
pub fn new() -> Self {
|
||||
impl Default for RootFileSystemBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
default_root_dirs: true,
|
||||
default_dev_files: true,
|
||||
@ -28,6 +29,12 @@ impl RootFileSystemBuilder {
|
||||
tty: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RootFileSystemBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_stdin(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
|
||||
self.stdin.replace(file);
|
||||
@ -57,8 +64,8 @@ impl RootFileSystemBuilder {
|
||||
pub fn build(self) -> TmpFileSystem {
|
||||
let tmp = TmpFileSystem::new();
|
||||
if self.default_root_dirs {
|
||||
for root_dir in vec!["/.app", "/.private", "/bin", "/dev", "/etc", "/tmp"] {
|
||||
if let Err(err) = tmp.create_dir(&Path::new(root_dir)) {
|
||||
for root_dir in &["/.app", "/.private", "/bin", "/dev", "/etc", "/tmp"] {
|
||||
if let Err(err) = tmp.create_dir(Path::new(root_dir)) {
|
||||
debug!("failed to create dir [{}] - {}", root_dir, err);
|
||||
}
|
||||
}
|
||||
@ -98,3 +105,71 @@ impl RootFileSystemBuilder {
|
||||
tmp
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_file_system() {
|
||||
let root_fs = RootFileSystemBuilder::new().build();
|
||||
let mut dev_null = root_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/null")
|
||||
.unwrap();
|
||||
assert_eq!(dev_null.write(b"hello").unwrap(), 5);
|
||||
let mut buf = Vec::new();
|
||||
dev_null.read_to_end(&mut buf);
|
||||
assert!(buf.is_empty());
|
||||
assert!(dev_null.get_special_fd().is_none());
|
||||
|
||||
let mut dev_zero = root_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/zero")
|
||||
.unwrap();
|
||||
assert_eq!(dev_zero.write(b"hello").unwrap(), 5);
|
||||
let mut buf = vec![1; 10];
|
||||
dev_zero.read(&mut buf[..]).unwrap();
|
||||
assert_eq!(buf, vec![0; 10]);
|
||||
assert!(dev_zero.get_special_fd().is_none());
|
||||
|
||||
let mut dev_tty = root_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/tty")
|
||||
.unwrap();
|
||||
assert_eq!(dev_tty.write(b"hello").unwrap(), 5);
|
||||
let mut buf = Vec::new();
|
||||
dev_tty.read_to_end(&mut buf);
|
||||
assert!(buf.is_empty());
|
||||
assert!(dev_tty.get_special_fd().is_none());
|
||||
|
||||
root_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.open("/bin/wasmer")
|
||||
.unwrap();
|
||||
|
||||
let dev_stdin = root_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/stdin")
|
||||
.unwrap();
|
||||
assert_eq!(dev_stdin.get_special_fd().unwrap(), 0);
|
||||
let dev_stdout = root_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/stdout")
|
||||
.unwrap();
|
||||
assert_eq!(dev_stdout.get_special_fd().unwrap(), 1);
|
||||
let dev_stderr = root_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/stderr")
|
||||
.unwrap();
|
||||
assert_eq!(dev_stderr.get_special_fd().unwrap(), 2);
|
||||
}
|
224
lib/vfs/src/delegate_file.rs
Normal file
224
lib/vfs/src/delegate_file.rs
Normal file
@ -0,0 +1,224 @@
|
||||
//! Do not end up using this but left it anyway - allows one to implement
|
||||
//! the interface to a file using optionally supplied lambdas - good for
|
||||
//! capturing variables rather than having to implement ones own
|
||||
//! VirtualFile implementation.
|
||||
|
||||
use crate::FileDescriptor;
|
||||
use crate::FsError;
|
||||
use crate::VirtualFile;
|
||||
use derivative::Derivative;
|
||||
use std::{
|
||||
io::{self, *},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
type DelegateSeekFn = Box<dyn Fn(SeekFrom) -> io::Result<u64> + Send + Sync>;
|
||||
type DelegateWriteFn = Box<dyn Fn(&[u8]) -> io::Result<usize> + Send + Sync>;
|
||||
type DelegateFlushFn = Box<dyn Fn() -> io::Result<()> + Send + Sync>;
|
||||
type DelegateReadFn = Box<dyn Fn(&mut [u8]) -> io::Result<usize> + Send + Sync>;
|
||||
type DelegateSizeFn = Box<dyn Fn() -> u64 + Send + Sync>;
|
||||
type DelegateSetLenFn = Box<dyn Fn(u64) -> crate::Result<()> + Send + Sync>;
|
||||
type DelegateUnlinkFn = Box<dyn Fn() -> crate::Result<()> + Send + Sync>;
|
||||
type DelegateBytesAvailableFn = Box<dyn Fn() -> crate::Result<usize> + Send + Sync>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DelegateFileInner {
|
||||
seek: Option<DelegateSeekFn>,
|
||||
write: Option<DelegateWriteFn>,
|
||||
flush: Option<DelegateFlushFn>,
|
||||
read: Option<DelegateReadFn>,
|
||||
size: Option<DelegateSizeFn>,
|
||||
set_len: Option<DelegateSetLenFn>,
|
||||
unlink: Option<DelegateUnlinkFn>,
|
||||
bytes_available: Option<DelegateBytesAvailableFn>,
|
||||
}
|
||||
|
||||
/// Wrapper that forwards calls to `read`, `write`, etc.
|
||||
/// to custom, user-defined functions - similar to `VirtualFile`
|
||||
/// itself, except you don't have to create a new struct in order
|
||||
/// to implement functions
|
||||
#[derive(Derivative, Clone)]
|
||||
#[derivative(Debug)]
|
||||
pub struct DelegateFile {
|
||||
#[derivative(Debug = "ignore")]
|
||||
inner: Arc<RwLock<DelegateFileInner>>,
|
||||
}
|
||||
|
||||
impl DelegateFile {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_seek(
|
||||
&self,
|
||||
func: impl Fn(SeekFrom) -> io::Result<u64> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.seek.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_write(
|
||||
&self,
|
||||
func: impl Fn(&[u8]) -> io::Result<usize> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.write.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_flush(&self, func: impl Fn() -> io::Result<()> + Send + Sync + 'static) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.flush.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_read(
|
||||
&self,
|
||||
func: impl Fn(&mut [u8]) -> io::Result<usize> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.read.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_size(&self, func: impl Fn() -> u64 + Send + Sync + 'static) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.size.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_set_len(
|
||||
&self,
|
||||
func: impl Fn(u64) -> crate::Result<()> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.set_len.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_unlink(
|
||||
&self,
|
||||
func: impl Fn() -> crate::Result<()> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.unlink.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_bytes_available(
|
||||
&self,
|
||||
func: impl Fn() -> crate::Result<usize> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.bytes_available.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DelegateFile {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(DelegateFileInner::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for DelegateFile {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let seek = inner.seek.as_ref().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"seek function not loaded on DelegateFile",
|
||||
)
|
||||
})?;
|
||||
(seek)(pos)
|
||||
}
|
||||
}
|
||||
impl Write for DelegateFile {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let write = inner.write.as_ref().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"write function not loaded on DelegateFile",
|
||||
)
|
||||
})?;
|
||||
(write)(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let flush = inner.flush.as_ref().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"flush function not loaded on DelegateFile",
|
||||
)
|
||||
})?;
|
||||
(flush)()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for DelegateFile {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let read = inner.read.as_ref().ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"read function not loaded on DelegateFile",
|
||||
)
|
||||
})?;
|
||||
(read)(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualFile for DelegateFile {
|
||||
fn last_accessed(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn last_modified(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn created_time(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner.size.as_ref().map(|size| size()).unwrap_or(0)
|
||||
}
|
||||
fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let set_len = inner.set_len.as_ref().ok_or(FsError::UnknownError)?;
|
||||
(set_len)(new_size)
|
||||
}
|
||||
fn unlink(&mut self) -> crate::Result<()> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let unlink = inner.unlink.as_ref().ok_or(FsError::UnknownError)?;
|
||||
(unlink)()
|
||||
}
|
||||
fn bytes_available(&self) -> crate::Result<usize> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let bytes_available = inner
|
||||
.bytes_available
|
||||
.as_ref()
|
||||
.ok_or(FsError::UnknownError)?;
|
||||
(bytes_available)()
|
||||
}
|
||||
fn get_fd(&self) -> Option<FileDescriptor> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delegate_file() {
|
||||
let mut custom_write_buf = vec![0; 17];
|
||||
let mut file = DelegateFile::new();
|
||||
|
||||
file.with_write(|_| Ok(384));
|
||||
file.with_read(|_| Ok(986));
|
||||
file.with_seek(|_| Ok(996));
|
||||
|
||||
assert_eq!(file.read(custom_write_buf.as_mut_slice()).unwrap(), 986);
|
||||
assert_eq!(file.seek(SeekFrom::Start(0)).unwrap(), 996);
|
||||
assert_eq!(file.write(b"hello").unwrap(), 384);
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
//! When no file system is used by a WebC then this is used as a placeholder -
|
||||
//! as the name suggests it always returns file not found.
|
||||
|
||||
use std::path::Path;
|
||||
#[allow(unused_imports, dead_code)]
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use wasmer_vfs::*;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EmptyFileSystem {}
|
@ -4,6 +4,7 @@ use crate::{
|
||||
};
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{de, Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::convert::TryInto;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
@ -71,10 +72,19 @@ impl TryInto<RawHandle> for FileDescriptor {
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct FileSystem;
|
||||
|
||||
impl FileSystem {
|
||||
pub fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
|
||||
if !path.exists() {
|
||||
return Err(FsError::InvalidInput);
|
||||
}
|
||||
fs::canonicalize(path).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::FileSystem for FileSystem {
|
||||
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
|
||||
let read_dir = fs::read_dir(path)?;
|
||||
let data = read_dir
|
||||
let mut data = read_dir
|
||||
.map(|entry| {
|
||||
let entry = entry?;
|
||||
let metadata = entry.metadata()?;
|
||||
@ -85,22 +95,82 @@ impl crate::FileSystem for FileSystem {
|
||||
})
|
||||
.collect::<std::result::Result<Vec<DirEntry>, io::Error>>()
|
||||
.map_err::<FsError, _>(Into::into)?;
|
||||
data.sort_by(|a, b| match (a.metadata.as_ref(), b.metadata.as_ref()) {
|
||||
(Ok(a), Ok(b)) => a.modified.cmp(&b.modified),
|
||||
_ => Ordering::Equal,
|
||||
});
|
||||
Ok(ReadDir::new(data))
|
||||
}
|
||||
|
||||
fn create_dir(&self, path: &Path) -> Result<()> {
|
||||
if path.parent().is_none() {
|
||||
return Err(FsError::BaseNotDirectory);
|
||||
}
|
||||
fs::create_dir(path).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn remove_dir(&self, path: &Path) -> Result<()> {
|
||||
if path.parent().is_none() {
|
||||
return Err(FsError::BaseNotDirectory);
|
||||
}
|
||||
// https://github.com/rust-lang/rust/issues/86442
|
||||
// DirectoryNotEmpty is not implemented consistently
|
||||
if path.is_dir() && self.read_dir(path).map(|s| !s.is_empty()).unwrap_or(false) {
|
||||
return Err(FsError::DirectoryNotEmpty);
|
||||
}
|
||||
fs::remove_dir(path).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn rename(&self, from: &Path, to: &Path) -> Result<()> {
|
||||
fs::rename(from, to).map_err(Into::into)
|
||||
use filetime::{set_file_mtime, FileTime};
|
||||
if from.parent().is_none() {
|
||||
return Err(FsError::BaseNotDirectory);
|
||||
}
|
||||
if to.parent().is_none() {
|
||||
return Err(FsError::BaseNotDirectory);
|
||||
}
|
||||
if !from.exists() {
|
||||
return Err(FsError::EntryNotFound);
|
||||
}
|
||||
let from_parent = from.parent().unwrap();
|
||||
let to_parent = to.parent().unwrap();
|
||||
if !from_parent.exists() {
|
||||
return Err(FsError::EntryNotFound);
|
||||
}
|
||||
if !to_parent.exists() {
|
||||
return Err(FsError::EntryNotFound);
|
||||
}
|
||||
let result = if from_parent != to_parent {
|
||||
let _ = std::fs::create_dir_all(to_parent);
|
||||
if from.is_dir() {
|
||||
fs_extra::move_items(
|
||||
&[from],
|
||||
to,
|
||||
&fs_extra::dir::CopyOptions {
|
||||
copy_inside: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|_| FsError::UnknownError)?;
|
||||
let _ = fs_extra::remove_items(&[from]);
|
||||
Ok(())
|
||||
} else {
|
||||
let e: Result<()> = fs::copy(from, to).map(|_| ()).map_err(Into::into);
|
||||
let _ = e?;
|
||||
fs::remove_file(from).map(|_| ()).map_err(Into::into)
|
||||
}
|
||||
} else {
|
||||
fs::rename(from, to).map_err(Into::into)
|
||||
};
|
||||
let _ = set_file_mtime(to, FileTime::now()).map(|_| ());
|
||||
result
|
||||
}
|
||||
|
||||
fn remove_file(&self, path: &Path) -> Result<()> {
|
||||
if path.parent().is_none() {
|
||||
return Err(FsError::BaseNotDirectory);
|
||||
}
|
||||
fs::remove_file(path).map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -792,3 +862,608 @@ impl VirtualFile for Stdin {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::host_fs::FileSystem;
|
||||
use crate::FileSystem as FileSystemTrait;
|
||||
use crate::FsError;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn test_new_filesystem() {
|
||||
let fs = FileSystem::default();
|
||||
assert!(fs.read_dir(Path::new("/")).is_ok(), "hostfs can read root");
|
||||
std::fs::write("./foo2.txt", b"").unwrap();
|
||||
assert!(
|
||||
fs.new_open_options()
|
||||
.read(true)
|
||||
.open(Path::new("./foo2.txt"))
|
||||
.is_ok(),
|
||||
"created foo2.txt"
|
||||
);
|
||||
std::fs::remove_file("./foo2.txt").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_dir() {
|
||||
let fs = FileSystem::default();
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("/")),
|
||||
Err(FsError::BaseNotDirectory),
|
||||
"creating a directory that has no parent",
|
||||
);
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_create_dir"]);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_create_dir")),
|
||||
Ok(()),
|
||||
"creating a directory",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_create_dir/foo")),
|
||||
Ok(()),
|
||||
"creating a directory",
|
||||
);
|
||||
|
||||
assert!(
|
||||
Path::new("./test_create_dir/foo").exists(),
|
||||
"foo dir exists in host_fs"
|
||||
);
|
||||
|
||||
let cur_dir = read_dir_names(&fs, "./test_create_dir");
|
||||
|
||||
if !cur_dir.contains(&"foo".to_string()) {
|
||||
panic!("cur_dir does not contain foo: {cur_dir:#?}");
|
||||
}
|
||||
|
||||
assert!(
|
||||
cur_dir.contains(&"foo".to_string()),
|
||||
"the root is updated and well-defined"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_create_dir/foo/bar")),
|
||||
Ok(()),
|
||||
"creating a sub-directory",
|
||||
);
|
||||
|
||||
assert!(
|
||||
Path::new("./test_create_dir/foo/bar").exists(),
|
||||
"foo dir exists in host_fs"
|
||||
);
|
||||
|
||||
let foo_dir = read_dir_names(&fs, "./test_create_dir/foo");
|
||||
|
||||
assert!(
|
||||
foo_dir.contains(&"bar".to_string()),
|
||||
"the foo directory is updated and well-defined"
|
||||
);
|
||||
|
||||
let bar_dir = read_dir_names(&fs, "./test_create_dir/foo/bar");
|
||||
|
||||
assert!(
|
||||
bar_dir.is_empty(),
|
||||
"the foo directory is updated and well-defined"
|
||||
);
|
||||
let _ = fs_extra::remove_items(&["./test_create_dir"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_dir() {
|
||||
let fs = FileSystem::default();
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_remove_dir"]);
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_dir(Path::new("/")),
|
||||
Err(FsError::BaseNotDirectory),
|
||||
"removing a directory that has no parent",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_dir(Path::new("/foo")),
|
||||
Err(FsError::EntryNotFound),
|
||||
"cannot remove a directory that doesn't exist",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_remove_dir")),
|
||||
Ok(()),
|
||||
"creating a directory",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_remove_dir/foo")),
|
||||
Ok(()),
|
||||
"creating a directory",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_remove_dir/foo/bar")),
|
||||
Ok(()),
|
||||
"creating a sub-directory",
|
||||
);
|
||||
|
||||
assert!(
|
||||
Path::new("./test_remove_dir/foo/bar").exists(),
|
||||
"./foo/bar exists"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_dir(Path::new("./test_remove_dir/foo")),
|
||||
Err(FsError::DirectoryNotEmpty),
|
||||
"removing a directory that has children",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_dir(Path::new("./test_remove_dir/foo/bar")),
|
||||
Ok(()),
|
||||
"removing a sub-directory",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_dir(Path::new("./test_remove_dir/foo")),
|
||||
Ok(()),
|
||||
"removing a directory",
|
||||
);
|
||||
|
||||
let cur_dir = read_dir_names(&fs, "./test_remove_dir");
|
||||
|
||||
assert!(
|
||||
!cur_dir.contains(&"foo".to_string()),
|
||||
"the foo directory still exists"
|
||||
);
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_remove_dir"]);
|
||||
}
|
||||
|
||||
fn read_dir_names(fs: &dyn crate::FileSystem, path: &str) -> Vec<String> {
|
||||
fs.read_dir(Path::new(path))
|
||||
.unwrap()
|
||||
.filter_map(|entry| Some(entry.ok()?.file_name().to_str()?.to_string()))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename() {
|
||||
let fs = FileSystem::default();
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_rename"]);
|
||||
|
||||
assert_eq!(
|
||||
fs.rename(Path::new("/"), Path::new("/bar")),
|
||||
Err(FsError::BaseNotDirectory),
|
||||
"renaming a directory that has no parent",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.rename(Path::new("/foo"), Path::new("/")),
|
||||
Err(FsError::BaseNotDirectory),
|
||||
"renaming to a directory that has no parent",
|
||||
);
|
||||
|
||||
assert_eq!(fs.create_dir(Path::new("./test_rename")), Ok(()));
|
||||
assert_eq!(fs.create_dir(Path::new("./test_rename/foo")), Ok(()));
|
||||
assert_eq!(fs.create_dir(Path::new("./test_rename/foo/qux")), Ok(()));
|
||||
|
||||
assert_eq!(
|
||||
fs.rename(
|
||||
Path::new("./test_rename/foo"),
|
||||
Path::new("./test_rename/bar/baz")
|
||||
),
|
||||
Err(FsError::EntryNotFound),
|
||||
"renaming to a directory that has parent that doesn't exist",
|
||||
);
|
||||
|
||||
assert_eq!(fs.create_dir(Path::new("./test_rename/bar")), Ok(()));
|
||||
|
||||
assert_eq!(
|
||||
fs.rename(
|
||||
Path::new("./test_rename/foo"),
|
||||
Path::new("./test_rename/bar")
|
||||
),
|
||||
Ok(()),
|
||||
"renaming to a directory that has parent that exists",
|
||||
);
|
||||
|
||||
assert!(
|
||||
matches!(
|
||||
fs.new_open_options()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(Path::new("./test_rename/bar/hello1.txt")),
|
||||
Ok(_),
|
||||
),
|
||||
"creating a new file (`hello1.txt`)",
|
||||
);
|
||||
assert!(
|
||||
matches!(
|
||||
fs.new_open_options()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(Path::new("./test_rename/bar/hello2.txt")),
|
||||
Ok(_),
|
||||
),
|
||||
"creating a new file (`hello2.txt`)",
|
||||
);
|
||||
|
||||
let cur_dir = read_dir_names(&fs, "./test_rename");
|
||||
|
||||
assert!(
|
||||
!cur_dir.contains(&"foo".to_string()),
|
||||
"the foo directory still exists"
|
||||
);
|
||||
|
||||
assert!(
|
||||
cur_dir.contains(&"bar".to_string()),
|
||||
"the bar directory still exists"
|
||||
);
|
||||
|
||||
let bar_dir = read_dir_names(&fs, "./test_rename/bar");
|
||||
|
||||
if !bar_dir.contains(&"qux".to_string()) {
|
||||
println!("qux does not exist: {:?}", bar_dir)
|
||||
}
|
||||
|
||||
let qux_dir = read_dir_names(&fs, "./test_rename/bar/qux");
|
||||
|
||||
assert!(qux_dir.is_empty(), "the qux directory is empty");
|
||||
|
||||
assert!(
|
||||
Path::new("./test_rename/bar/hello1.txt").exists(),
|
||||
"the /bar/hello1.txt file exists"
|
||||
);
|
||||
|
||||
assert!(
|
||||
Path::new("./test_rename/bar/hello2.txt").exists(),
|
||||
"the /bar/hello2.txt file exists"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_rename/foo")),
|
||||
Ok(()),
|
||||
"create ./foo again",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.rename(
|
||||
Path::new("./test_rename/bar/hello2.txt"),
|
||||
Path::new("./test_rename/foo/world2.txt")
|
||||
),
|
||||
Ok(()),
|
||||
"renaming (and moving) a file",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.rename(
|
||||
Path::new("./test_rename/foo"),
|
||||
Path::new("./test_rename/bar/baz")
|
||||
),
|
||||
Ok(()),
|
||||
"renaming a directory",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.rename(
|
||||
Path::new("./test_rename/bar/hello1.txt"),
|
||||
Path::new("./test_rename/bar/world1.txt")
|
||||
),
|
||||
Ok(()),
|
||||
"renaming a file (in the same directory)",
|
||||
);
|
||||
|
||||
assert!(Path::new("./test_rename/bar").exists(), "./bar exists");
|
||||
assert!(
|
||||
Path::new("./test_rename/bar/baz").exists(),
|
||||
"./bar/baz exists"
|
||||
);
|
||||
assert!(
|
||||
!Path::new("./test_rename/foo").exists(),
|
||||
"foo does not exist anymore"
|
||||
);
|
||||
assert!(
|
||||
Path::new("./test_rename/bar/baz/world2.txt").exists(),
|
||||
"/bar/baz/world2.txt exists"
|
||||
);
|
||||
assert!(
|
||||
Path::new("./test_rename/bar/world1.txt").exists(),
|
||||
"/bar/world1.txt (ex hello1.txt) exists"
|
||||
);
|
||||
assert!(
|
||||
!Path::new("./test_rename/bar/hello1.txt").exists(),
|
||||
"hello1.txt was moved"
|
||||
);
|
||||
assert!(
|
||||
!Path::new("./test_rename/bar/hello2.txt").exists(),
|
||||
"hello2.txt was moved"
|
||||
);
|
||||
assert!(
|
||||
Path::new("./test_rename/bar/baz/world2.txt").exists(),
|
||||
"world2.txt was moved to the correct place"
|
||||
);
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_rename"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metadata() {
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
let root_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let _ = std::env::set_current_dir(root_dir);
|
||||
|
||||
let fs = FileSystem::default();
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_metadata"]);
|
||||
|
||||
assert_eq!(fs.create_dir(Path::new("./test_metadata")), Ok(()));
|
||||
|
||||
let root_metadata = fs.metadata(Path::new("./test_metadata")).unwrap();
|
||||
|
||||
assert!(root_metadata.ft.dir);
|
||||
assert!(root_metadata.accessed == root_metadata.created);
|
||||
assert!(root_metadata.modified == root_metadata.created);
|
||||
assert!(root_metadata.modified > 0);
|
||||
|
||||
assert_eq!(fs.create_dir(Path::new("./test_metadata/foo")), Ok(()));
|
||||
|
||||
let foo_metadata = fs.metadata(Path::new("./test_metadata/foo"));
|
||||
assert!(foo_metadata.is_ok());
|
||||
let foo_metadata = foo_metadata.unwrap();
|
||||
|
||||
assert!(foo_metadata.ft.dir);
|
||||
assert!(foo_metadata.accessed == foo_metadata.created);
|
||||
assert!(foo_metadata.modified == foo_metadata.created);
|
||||
assert!(foo_metadata.modified > 0);
|
||||
|
||||
sleep(Duration::from_secs(3));
|
||||
|
||||
assert_eq!(
|
||||
fs.rename(
|
||||
Path::new("./test_metadata/foo"),
|
||||
Path::new("./test_metadata/bar")
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
let bar_metadata = fs.metadata(Path::new("./test_metadata/bar")).unwrap();
|
||||
assert!(bar_metadata.ft.dir);
|
||||
assert!(bar_metadata.accessed == foo_metadata.accessed);
|
||||
assert!(bar_metadata.created == foo_metadata.created);
|
||||
assert!(bar_metadata.modified > foo_metadata.modified);
|
||||
|
||||
let root_metadata = fs.metadata(Path::new("./test_metadata/bar")).unwrap();
|
||||
assert!(
|
||||
root_metadata.modified > foo_metadata.modified,
|
||||
"the parent modified time was updated"
|
||||
);
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_metadata"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_file() {
|
||||
let fs = FileSystem::default();
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_remove_file"]);
|
||||
|
||||
assert!(fs.create_dir(Path::new("./test_remove_file")).is_ok());
|
||||
|
||||
assert!(
|
||||
matches!(
|
||||
fs.new_open_options()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(Path::new("./test_remove_file/foo.txt")),
|
||||
Ok(_)
|
||||
),
|
||||
"creating a new file",
|
||||
);
|
||||
|
||||
assert!(read_dir_names(&fs, "./test_remove_file").contains(&"foo.txt".to_string()));
|
||||
|
||||
assert!(Path::new("./test_remove_file/foo.txt").is_file());
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_file(Path::new("./test_remove_file/foo.txt")),
|
||||
Ok(()),
|
||||
"removing a file that exists",
|
||||
);
|
||||
|
||||
assert!(!Path::new("./test_remove_file/foo.txt").exists());
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_file(Path::new("./test_remove_file/foo.txt")),
|
||||
Err(FsError::EntryNotFound),
|
||||
"removing a file that doesn't exists",
|
||||
);
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_remove_file"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_readdir() {
|
||||
let fs = FileSystem::default();
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_readdir"]);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_readdir/")),
|
||||
Ok(()),
|
||||
"creating `test_readdir`"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_readdir/foo")),
|
||||
Ok(()),
|
||||
"creating `foo`"
|
||||
);
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_readdir/foo/sub")),
|
||||
Ok(()),
|
||||
"creating `sub`"
|
||||
);
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_readdir/bar")),
|
||||
Ok(()),
|
||||
"creating `bar`"
|
||||
);
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_readdir/baz")),
|
||||
Ok(()),
|
||||
"creating `bar`"
|
||||
);
|
||||
assert!(
|
||||
matches!(
|
||||
fs.new_open_options()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(Path::new("./test_readdir/a.txt")),
|
||||
Ok(_)
|
||||
),
|
||||
"creating `a.txt`",
|
||||
);
|
||||
assert!(
|
||||
matches!(
|
||||
fs.new_open_options()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(Path::new("./test_readdir/b.txt")),
|
||||
Ok(_)
|
||||
),
|
||||
"creating `b.txt`",
|
||||
);
|
||||
|
||||
let readdir = fs.read_dir(Path::new("./test_readdir"));
|
||||
|
||||
assert!(readdir.is_ok(), "reading the directory `./test_readdir/`");
|
||||
|
||||
let mut readdir = readdir.unwrap();
|
||||
|
||||
let next = readdir.next().unwrap().unwrap();
|
||||
assert!(next.path.ends_with("foo"), "checking entry #1");
|
||||
assert!(next.path.is_dir(), "checking entry #1");
|
||||
|
||||
let next = readdir.next().unwrap().unwrap();
|
||||
assert!(next.path.ends_with("bar"), "checking entry #2");
|
||||
assert!(next.path.is_dir(), "checking entry #2");
|
||||
|
||||
let next = readdir.next().unwrap().unwrap();
|
||||
assert!(next.path.ends_with("baz"), "checking entry #3");
|
||||
assert!(next.path.is_dir(), "checking entry #3");
|
||||
|
||||
let next = readdir.next().unwrap().unwrap();
|
||||
assert!(next.path.ends_with("a.txt"), "checking entry #2");
|
||||
assert!(next.path.is_file(), "checking entry #4");
|
||||
|
||||
let next = readdir.next().unwrap().unwrap();
|
||||
assert!(next.path.ends_with("b.txt"), "checking entry #2");
|
||||
assert!(next.path.is_file(), "checking entry #5");
|
||||
|
||||
if let Some(s) = readdir.next() {
|
||||
panic!("next: {s:?}");
|
||||
}
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_readdir"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_canonicalize() {
|
||||
let fs = FileSystem::default();
|
||||
|
||||
let root_dir = env!("CARGO_MANIFEST_DIR");
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_canonicalize"]);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_canonicalize")),
|
||||
Ok(()),
|
||||
"creating `test_canonicalize`"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_canonicalize/foo")),
|
||||
Ok(()),
|
||||
"creating `foo`"
|
||||
);
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_canonicalize/foo/bar")),
|
||||
Ok(()),
|
||||
"creating `bar`"
|
||||
);
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_canonicalize/foo/bar/baz")),
|
||||
Ok(()),
|
||||
"creating `baz`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.create_dir(Path::new("./test_canonicalize/foo/bar/baz/qux")),
|
||||
Ok(()),
|
||||
"creating `qux`",
|
||||
);
|
||||
assert!(
|
||||
matches!(
|
||||
fs.new_open_options()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(Path::new("./test_canonicalize/foo/bar/baz/qux/hello.txt")),
|
||||
Ok(_)
|
||||
),
|
||||
"creating `hello.txt`",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new("./test_canonicalize")),
|
||||
Ok(Path::new(&format!("{root_dir}/test_canonicalize")).to_path_buf()),
|
||||
"canonicalizing `/`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new("foo")),
|
||||
Err(FsError::InvalidInput),
|
||||
"canonicalizing `foo`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new("./test_canonicalize/././././foo/")),
|
||||
Ok(Path::new(&format!("{root_dir}/test_canonicalize/foo")).to_path_buf()),
|
||||
"canonicalizing `/././././foo/`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new("./test_canonicalize/foo/bar//")),
|
||||
Ok(Path::new(&format!("{root_dir}/test_canonicalize/foo/bar")).to_path_buf()),
|
||||
"canonicalizing `/foo/bar//`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new("./test_canonicalize/foo/bar/../bar")),
|
||||
Ok(Path::new(&format!("{root_dir}/test_canonicalize/foo/bar")).to_path_buf()),
|
||||
"canonicalizing `/foo/bar/../bar`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new("./test_canonicalize/foo/bar/../..")),
|
||||
Ok(Path::new(&format!("{root_dir}/test_canonicalize")).to_path_buf()),
|
||||
"canonicalizing `/foo/bar/../..`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new("/foo/bar/../../..")),
|
||||
Err(FsError::InvalidInput),
|
||||
"canonicalizing `/foo/bar/../../..`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new("C:/foo/")),
|
||||
Err(FsError::InvalidInput),
|
||||
"canonicalizing `C:/foo/`",
|
||||
);
|
||||
assert_eq!(
|
||||
fs.canonicalize(Path::new(
|
||||
"./test_canonicalize/foo/./../foo/bar/../../foo/bar/./baz/./../baz/qux/../../baz/./qux/hello.txt"
|
||||
)),
|
||||
Ok(Path::new(&format!("{root_dir}/test_canonicalize/foo/bar/baz/qux/hello.txt")).to_path_buf()),
|
||||
"canonicalizing a crazily stupid path name",
|
||||
);
|
||||
|
||||
let _ = fs_extra::remove_items(&["./test_canonicalize"]);
|
||||
}
|
||||
}
|
||||
|
@ -9,23 +9,42 @@ use std::sync::Arc;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(all(not(feature = "host-fs"), not(feature = "mem-fs")))]
|
||||
compile_error!("At least the `host-fs` or the `mem-fs` feature must be enabled. Please, pick one.");
|
||||
|
||||
//#[cfg(all(feature = "mem-fs", feature = "enable-serde"))]
|
||||
//compile_warn!("`mem-fs` does not support `enable-serde` for the moment.");
|
||||
|
||||
pub mod arc_file;
|
||||
pub mod arc_fs;
|
||||
pub mod builder;
|
||||
pub mod delegate_file;
|
||||
pub mod empty_fs;
|
||||
#[cfg(feature = "host-fs")]
|
||||
pub mod host_fs;
|
||||
#[cfg(feature = "mem-fs")]
|
||||
pub mod mem_fs;
|
||||
pub mod null_file;
|
||||
pub mod passthru_fs;
|
||||
pub mod special_file;
|
||||
pub mod tmp_fs;
|
||||
pub mod union_fs;
|
||||
pub mod zero_file;
|
||||
// tty_file -> see wasmer_wasi::tty_file
|
||||
#[cfg(feature = "static-fs")]
|
||||
pub mod static_fs;
|
||||
#[cfg(feature = "webc-fs")]
|
||||
pub mod webc_fs;
|
||||
|
||||
pub use arc_file::*;
|
||||
pub use arc_fs::*;
|
||||
pub use builder::*;
|
||||
pub use delegate_file::*;
|
||||
pub use empty_fs::*;
|
||||
pub use null_file::*;
|
||||
pub use passthru_fs::*;
|
||||
pub use special_file::*;
|
||||
pub use tmp_fs::*;
|
||||
pub use union_fs::*;
|
||||
pub use zero_file::*;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, FsError>;
|
||||
|
||||
pub trait ClonableVirtualFile: VirtualFile + Clone {}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct FileDescriptor(usize);
|
||||
@ -339,17 +358,18 @@ pub trait VirtualFile: fmt::Debug + Write + Read + Seek + Upcastable {
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns a special file descriptor when opening this file rather than
|
||||
/// generating a new one
|
||||
fn get_special_fd(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Used for polling. Default returns `None` because this method cannot be implemented for most types
|
||||
/// Returns the underlying host fd
|
||||
fn get_fd(&self) -> Option<FileDescriptor> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Used for "special" files such as `stdin`, `stdout` and `stderr`.
|
||||
/// Always returns the same file descriptor (0, 1 or 2). Returns `None`
|
||||
/// on normal files
|
||||
fn get_special_fd(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct VirtualFileAsyncRead<'a, T: ?Sized> {
|
||||
@ -419,7 +439,6 @@ pub trait Upcastable {
|
||||
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
pub trait ClonableVirtualFile: VirtualFile + Clone {}
|
||||
|
||||
impl<T: Any + fmt::Debug + 'static> Upcastable for T {
|
||||
#[inline]
|
||||
@ -601,6 +620,9 @@ impl ReadDir {
|
||||
pub fn new(data: Vec<DirEntry>) -> Self {
|
||||
Self { data, index: 0 }
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -71,7 +71,7 @@ impl FileHandle {
|
||||
|
||||
let inode = fs.storage.get(self.inode);
|
||||
match inode {
|
||||
Some(Node::ArcFile { fs, path, .. }) => {
|
||||
Some(Node::ArcFile(ArcFileNode { fs, path, .. })) => {
|
||||
self.arc_file.replace(
|
||||
fs.new_open_options()
|
||||
.read(self.readable)
|
||||
@ -88,7 +88,7 @@ impl FileHandle {
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.map_err(|err| err.clone())?
|
||||
.map_err(|err| *err)?
|
||||
.as_mut())
|
||||
}
|
||||
}
|
||||
@ -143,13 +143,15 @@ impl VirtualFile for FileHandle {
|
||||
|
||||
let inode = fs.storage.get(self.inode);
|
||||
match inode {
|
||||
Some(Node::File { file, .. }) => file.len().try_into().unwrap_or(0),
|
||||
Some(Node::ReadOnlyFile { file, .. }) => file.len().try_into().unwrap_or(0),
|
||||
Some(Node::CustomFile { file, .. }) => {
|
||||
let file = file.lock().unwrap();
|
||||
file.size().try_into().unwrap_or(0)
|
||||
Some(Node::File(FileNode { file, .. })) => file.len().try_into().unwrap_or(0),
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { file, .. })) => {
|
||||
file.len().try_into().unwrap_or(0)
|
||||
}
|
||||
Some(Node::ArcFile { fs, path, .. }) => match self.arc_file.as_ref() {
|
||||
Some(Node::CustomFile(CustomFileNode { file, .. })) => {
|
||||
let file = file.lock().unwrap();
|
||||
file.size()
|
||||
}
|
||||
Some(Node::ArcFile(ArcFileNode { fs, path, .. })) => match self.arc_file.as_ref() {
|
||||
Some(file) => file.as_ref().map(|file| file.size()).unwrap_or(0),
|
||||
None => fs
|
||||
.new_open_options()
|
||||
@ -169,22 +171,25 @@ impl VirtualFile for FileHandle {
|
||||
|
||||
let inode = fs.storage.get_mut(self.inode);
|
||||
match inode {
|
||||
Some(Node::File { file, metadata, .. }) => {
|
||||
Some(Node::File(FileNode { file, metadata, .. })) => {
|
||||
file.buffer
|
||||
.resize(new_size.try_into().map_err(|_| FsError::UnknownError)?, 0);
|
||||
metadata.len = new_size;
|
||||
}
|
||||
Some(Node::CustomFile { file, metadata, .. }) => {
|
||||
Some(Node::CustomFile(CustomFileNode { file, metadata, .. })) => {
|
||||
let mut file = file.lock().unwrap();
|
||||
file.set_len(new_size)?;
|
||||
metadata.len = new_size;
|
||||
}
|
||||
Some(Node::ReadOnlyFile { .. }) => return Err(FsError::PermissionDenied),
|
||||
Some(Node::ArcFile { .. }) => {
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { .. })) => {
|
||||
return Err(FsError::PermissionDenied)
|
||||
}
|
||||
Some(Node::ArcFile(ArcFileNode { .. })) => {
|
||||
drop(fs);
|
||||
self.lazy_load_arc_file_mut()
|
||||
.map(|file| file.set_len(new_size))??;
|
||||
}
|
||||
None => return Err(FsError::EntryNotFound),
|
||||
_ => return Err(FsError::NotAFile),
|
||||
}
|
||||
|
||||
@ -205,7 +210,7 @@ impl VirtualFile for FileHandle {
|
||||
.storage
|
||||
.iter()
|
||||
.find_map(|(inode_of_parent, node)| match node {
|
||||
Node::Directory { children, .. } => {
|
||||
Node::Directory(DirectoryNode { children, .. }) => {
|
||||
children.iter().enumerate().find_map(|(nth, inode)| {
|
||||
if inode == &inode_of_file {
|
||||
Some((nth, inode_of_parent))
|
||||
@ -241,17 +246,21 @@ impl VirtualFile for FileHandle {
|
||||
|
||||
let inode = fs.storage.get(self.inode);
|
||||
match inode {
|
||||
Some(Node::File { file, .. }) => Ok(file.buffer.len() - (self.cursor as usize)),
|
||||
Some(Node::ReadOnlyFile { file, .. }) => Ok(file.buffer.len() - (self.cursor as usize)),
|
||||
Some(Node::CustomFile { file, .. }) => {
|
||||
Some(Node::File(FileNode { file, .. })) => {
|
||||
Ok(file.buffer.len() - (self.cursor as usize))
|
||||
}
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { file, .. })) => {
|
||||
Ok(file.buffer.len() - (self.cursor as usize))
|
||||
}
|
||||
Some(Node::CustomFile(CustomFileNode { file, .. })) => {
|
||||
let file = file.lock().unwrap();
|
||||
file.bytes_available()
|
||||
}
|
||||
Some(Node::ArcFile { fs, path, .. }) => match self.arc_file.as_ref() {
|
||||
Some(Node::ArcFile(ArcFileNode { fs, path, .. })) => match self.arc_file.as_ref() {
|
||||
Some(file) => file
|
||||
.as_ref()
|
||||
.map(|file| file.bytes_available())
|
||||
.map_err(|err| err.clone())?,
|
||||
.map_err(|err| *err)?,
|
||||
None => fs
|
||||
.new_open_options()
|
||||
.read(self.readable)
|
||||
@ -260,6 +269,7 @@ impl VirtualFile for FileHandle {
|
||||
.open(path.as_path())
|
||||
.map(|file| file.bytes_available())?,
|
||||
},
|
||||
None => Err(FsError::EntryNotFound),
|
||||
_ => Err(FsError::NotAFile),
|
||||
}
|
||||
}
|
||||
@ -278,11 +288,11 @@ impl VirtualFile for FileHandle {
|
||||
|
||||
let inode = fs.storage.get(self.inode);
|
||||
match inode {
|
||||
Some(Node::CustomFile { file, .. }) => {
|
||||
Some(Node::CustomFile(CustomFileNode { file, .. })) => {
|
||||
let file = file.lock().unwrap();
|
||||
file.get_special_fd()
|
||||
}
|
||||
Some(Node::ArcFile { fs, path, .. }) => match self.arc_file.as_ref() {
|
||||
Some(Node::ArcFile(ArcFileNode { fs, path, .. })) => match self.arc_file.as_ref() {
|
||||
Some(file) => file
|
||||
.as_ref()
|
||||
.map(|file| file.get_special_fd())
|
||||
@ -430,23 +440,23 @@ mod test_virtual_file {
|
||||
assert!(
|
||||
matches!(
|
||||
fs_inner.storage.get(ROOT_INODE),
|
||||
Some(Node::Directory {
|
||||
Some(Node::Directory(DirectoryNode {
|
||||
inode: ROOT_INODE,
|
||||
name,
|
||||
children,
|
||||
..
|
||||
}) if name == "/" && children == &[1]
|
||||
})) if name == "/" && children == &[1]
|
||||
),
|
||||
"`/` contains `foo.txt`",
|
||||
);
|
||||
assert!(
|
||||
matches!(
|
||||
fs_inner.storage.get(1),
|
||||
Some(Node::File {
|
||||
Some(Node::File(FileNode {
|
||||
inode: 1,
|
||||
name,
|
||||
..
|
||||
}) if name == "foo.txt"
|
||||
})) if name == "foo.txt"
|
||||
),
|
||||
"`foo.txt` exists and is a file",
|
||||
);
|
||||
@ -465,12 +475,12 @@ mod test_virtual_file {
|
||||
assert!(
|
||||
matches!(
|
||||
fs_inner.storage.get(ROOT_INODE),
|
||||
Some(Node::Directory {
|
||||
Some(Node::Directory(DirectoryNode {
|
||||
inode: ROOT_INODE,
|
||||
name,
|
||||
children,
|
||||
..
|
||||
}) if name == "/" && children.is_empty()
|
||||
})) if name == "/" && children.is_empty()
|
||||
),
|
||||
"`/` is empty",
|
||||
);
|
||||
@ -530,16 +540,18 @@ impl Read for FileHandle {
|
||||
|
||||
let inode = fs.storage.get(self.inode);
|
||||
match inode {
|
||||
Some(Node::File { file, .. }) => file.read(buf, &mut self.cursor),
|
||||
Some(Node::ReadOnlyFile { file, .. }) => file.read(buf, &mut self.cursor),
|
||||
Some(Node::CustomFile { file, .. }) => {
|
||||
Some(Node::File(FileNode { file, .. })) => file.read(buf, &mut self.cursor),
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { file, .. })) => {
|
||||
file.read(buf, &mut self.cursor)
|
||||
}
|
||||
Some(Node::CustomFile(CustomFileNode { file, .. })) => {
|
||||
let mut file = file.lock().unwrap();
|
||||
let _ = file.seek(io::SeekFrom::Start(self.cursor as u64));
|
||||
let read = file.read(buf)?;
|
||||
self.cursor += read as u64;
|
||||
Ok(read)
|
||||
}
|
||||
Some(Node::ArcFile { .. }) => {
|
||||
Some(Node::ArcFile(ArcFileNode { .. })) => {
|
||||
drop(fs);
|
||||
self.lazy_load_arc_file_mut()
|
||||
.map(|file| file.read(buf))
|
||||
@ -577,16 +589,18 @@ impl Read for FileHandle {
|
||||
|
||||
let inode = fs.storage.get_mut(self.inode);
|
||||
match inode {
|
||||
Some(Node::File { file, .. }) => file.read_to_end(buf, &mut self.cursor),
|
||||
Some(Node::ReadOnlyFile { file, .. }) => file.read_to_end(buf, &mut self.cursor),
|
||||
Some(Node::CustomFile { file, .. }) => {
|
||||
Some(Node::File(FileNode { file, .. })) => file.read_to_end(buf, &mut self.cursor),
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { file, .. })) => {
|
||||
file.read_to_end(buf, &mut self.cursor)
|
||||
}
|
||||
Some(Node::CustomFile(CustomFileNode { file, .. })) => {
|
||||
let mut file = file.lock().unwrap();
|
||||
let _ = file.seek(io::SeekFrom::Start(self.cursor as u64));
|
||||
let read = file.read_to_end(buf)?;
|
||||
self.cursor += read as u64;
|
||||
Ok(read)
|
||||
}
|
||||
Some(Node::ArcFile { .. }) => {
|
||||
Some(Node::ArcFile(ArcFileNode { .. })) => {
|
||||
drop(fs);
|
||||
self.lazy_load_arc_file_mut()
|
||||
.map(|file| file.read_to_end(buf))
|
||||
@ -643,16 +657,18 @@ impl Read for FileHandle {
|
||||
|
||||
let inode = fs.storage.get(self.inode);
|
||||
match inode {
|
||||
Some(Node::File { file, .. }) => file.read_exact(buf, &mut self.cursor),
|
||||
Some(Node::ReadOnlyFile { file, .. }) => file.read_exact(buf, &mut self.cursor),
|
||||
Some(Node::CustomFile { file, .. }) => {
|
||||
Some(Node::File(FileNode { file, .. })) => file.read_exact(buf, &mut self.cursor),
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { file, .. })) => {
|
||||
file.read_exact(buf, &mut self.cursor)
|
||||
}
|
||||
Some(Node::CustomFile(CustomFileNode { file, .. })) => {
|
||||
let mut file = file.lock().unwrap();
|
||||
let _ = file.seek(io::SeekFrom::Start(self.cursor as u64));
|
||||
file.read_exact(buf)?;
|
||||
self.cursor += buf.len() as u64;
|
||||
Ok(())
|
||||
}
|
||||
Some(Node::ArcFile { .. }) => {
|
||||
Some(Node::ArcFile(ArcFileNode { .. })) => {
|
||||
drop(fs);
|
||||
self.lazy_load_arc_file_mut()
|
||||
.map(|file| file.read_exact(buf))
|
||||
@ -700,16 +716,18 @@ impl Seek for FileHandle {
|
||||
|
||||
let inode = fs.storage.get_mut(self.inode);
|
||||
match inode {
|
||||
Some(Node::File { file, .. }) => file.seek(position, &mut self.cursor),
|
||||
Some(Node::ReadOnlyFile { file, .. }) => file.seek(position, &mut self.cursor),
|
||||
Some(Node::CustomFile { file, .. }) => {
|
||||
Some(Node::File(FileNode { file, .. })) => file.seek(position, &mut self.cursor),
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { file, .. })) => {
|
||||
file.seek(position, &mut self.cursor)
|
||||
}
|
||||
Some(Node::CustomFile(CustomFileNode { file, .. })) => {
|
||||
let mut file = file.lock().unwrap();
|
||||
let _ = file.seek(io::SeekFrom::Start(self.cursor as u64));
|
||||
let pos = file.seek(position)?;
|
||||
self.cursor = pos;
|
||||
Ok(pos)
|
||||
}
|
||||
Some(Node::ArcFile { .. }) => {
|
||||
Some(Node::ArcFile(_)) => {
|
||||
drop(fs);
|
||||
self.lazy_load_arc_file_mut()
|
||||
.map(|file| file.seek(position))
|
||||
@ -749,25 +767,25 @@ impl Write for FileHandle {
|
||||
|
||||
let inode = fs.storage.get_mut(self.inode);
|
||||
let bytes_written = match inode {
|
||||
Some(Node::File { file, metadata, .. }) => {
|
||||
Some(Node::File(FileNode { file, metadata, .. })) => {
|
||||
let bytes_written = file.write(buf, &mut self.cursor)?;
|
||||
metadata.len = file.len().try_into().unwrap();
|
||||
bytes_written
|
||||
}
|
||||
Some(Node::ReadOnlyFile { file, metadata, .. }) => {
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { file, metadata, .. })) => {
|
||||
let bytes_written = file.write(buf, &mut self.cursor)?;
|
||||
metadata.len = file.len().try_into().unwrap();
|
||||
bytes_written
|
||||
}
|
||||
Some(Node::CustomFile { file, metadata, .. }) => {
|
||||
Some(Node::CustomFile(CustomFileNode { file, metadata, .. })) => {
|
||||
let mut file = file.lock().unwrap();
|
||||
let _ = file.seek(io::SeekFrom::Start(self.cursor as u64));
|
||||
let bytes_written = file.write(buf)?;
|
||||
self.cursor += bytes_written as u64;
|
||||
metadata.len = file.size().try_into().unwrap();
|
||||
metadata.len = file.size();
|
||||
bytes_written
|
||||
}
|
||||
Some(Node::ArcFile { .. }) => {
|
||||
Some(Node::ArcFile(_)) => {
|
||||
drop(fs);
|
||||
self.lazy_load_arc_file_mut()
|
||||
.map(|file| file.write(buf))
|
||||
@ -796,13 +814,13 @@ impl Write for FileHandle {
|
||||
|
||||
let inode = fs.storage.get_mut(self.inode);
|
||||
match inode {
|
||||
Some(Node::File { file, .. }) => file.flush(),
|
||||
Some(Node::ReadOnlyFile { file, .. }) => file.flush(),
|
||||
Some(Node::CustomFile { file, .. }) => {
|
||||
Some(Node::File(FileNode { file, .. })) => file.flush(),
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { file, .. })) => file.flush(),
|
||||
Some(Node::CustomFile(CustomFileNode { file, .. })) => {
|
||||
let mut file = file.lock().unwrap();
|
||||
file.flush()
|
||||
}
|
||||
Some(Node::ArcFile { .. }) => {
|
||||
Some(Node::ArcFile(ArcFileNode { .. })) => {
|
||||
drop(fs);
|
||||
self.lazy_load_arc_file_mut()
|
||||
.map(|file| file.flush())
|
||||
@ -1101,6 +1119,13 @@ impl File {
|
||||
impl File {
|
||||
pub fn read(&self, buf: &mut [u8], cursor: &mut u64) -> io::Result<usize> {
|
||||
let cur_pos = *cursor as usize;
|
||||
let buffer_len = buf.len();
|
||||
if *cursor > buffer_len as u64 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
format!("file cursor {cursor} > buffer length {buffer_len}"),
|
||||
));
|
||||
}
|
||||
let max_to_read = cmp::min(self.buffer.len() - cur_pos, buf.len());
|
||||
let data_to_copy = &self.buffer[cur_pos..][..max_to_read];
|
||||
|
||||
@ -1115,25 +1140,17 @@ impl File {
|
||||
|
||||
pub fn read_to_end(&self, buf: &mut Vec<u8>, cursor: &mut u64) -> io::Result<usize> {
|
||||
let cur_pos = *cursor as usize;
|
||||
let data_to_copy = &self.buffer[cur_pos..];
|
||||
let max_to_read = data_to_copy.len();
|
||||
|
||||
// `buf` is too small to contain the data. Let's resize it.
|
||||
if max_to_read > buf.len() {
|
||||
// Let's resize the capacity if needed.
|
||||
if max_to_read > buf.capacity() {
|
||||
buf.reserve_exact(max_to_read - buf.capacity());
|
||||
}
|
||||
|
||||
// SAFETY: The space is reserved, and it's going to be
|
||||
// filled with `copy_from_slice` below.
|
||||
unsafe { buf.set_len(max_to_read) }
|
||||
let buffer_len = buf.len();
|
||||
if *cursor > buffer_len as u64 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
format!("file cursor {cursor} > buffer length {buffer_len}"),
|
||||
));
|
||||
}
|
||||
|
||||
// SAFETY: `buf` and `data_to_copy` have the same size, see
|
||||
// above.
|
||||
buf.copy_from_slice(data_to_copy);
|
||||
|
||||
let data_to_copy = &self.buffer[cur_pos..];
|
||||
let max_to_read = data_to_copy.len();
|
||||
buf.extend_from_slice(data_to_copy);
|
||||
*cursor += max_to_read as u64;
|
||||
|
||||
Ok(max_to_read)
|
||||
@ -1220,14 +1237,29 @@ impl File {
|
||||
// The cursor is somewhere in the buffer: not the happy path.
|
||||
position => {
|
||||
self.buffer.reserve_exact(buf.len());
|
||||
|
||||
let mut remainder = self.buffer.split_off(position as usize);
|
||||
self.buffer.extend_from_slice(buf);
|
||||
self.buffer.append(&mut remainder);
|
||||
let position = position as usize;
|
||||
if position >= self.buffer.len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
format!(
|
||||
"wrong cursor position {position} > buffer length {}",
|
||||
self.buffer.len()
|
||||
),
|
||||
));
|
||||
}
|
||||
if position + buf.len() > self.buffer.len() {
|
||||
let (a, b) = buf.split_at(self.buffer.len() - position);
|
||||
self.buffer[position..].clone_from_slice(a);
|
||||
self.buffer.extend_from_slice(b);
|
||||
} else {
|
||||
// pos + buffer fits in
|
||||
self.buffer.truncate(position as usize + buf.len());
|
||||
self.buffer[position..].clone_from_slice(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*cursor += buf.len() as u64;
|
||||
*cursor = cursor.saturating_add(buf.len() as u64);
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
@ -1237,7 +1269,8 @@ impl File {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read only file that uses copy-on-write
|
||||
/// Read only file that uses copy-on-write, used for mapping
|
||||
/// files from the `pirita` filesystem
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ReadOnlyFile {
|
||||
buffer: Cow<'static, [u8]>,
|
||||
@ -1269,6 +1302,13 @@ impl ReadOnlyFile {
|
||||
}
|
||||
|
||||
pub fn read_to_end(&self, buf: &mut Vec<u8>, cursor: &mut u64) -> io::Result<usize> {
|
||||
let buffer_len = self.buffer.len();
|
||||
if *cursor > buffer_len as u64 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
format!("file cursor {cursor} > buffer length {buffer_len}"),
|
||||
));
|
||||
}
|
||||
let cur_pos = *cursor as usize;
|
||||
let data_to_copy = &self.buffer[cur_pos..];
|
||||
let max_to_read = data_to_copy.len();
|
||||
|
@ -39,7 +39,7 @@ impl FileOpener {
|
||||
|
||||
// Creating the file in the storage.
|
||||
let inode_of_file = fs.storage.vacant_entry().key();
|
||||
let real_inode_of_file = fs.storage.insert(Node::ReadOnlyFile {
|
||||
let real_inode_of_file = fs.storage.insert(Node::ReadOnlyFile(ReadOnlyFileNode {
|
||||
inode: inode_of_file,
|
||||
name: name_of_file,
|
||||
file,
|
||||
@ -57,7 +57,7 @@ impl FileOpener {
|
||||
len: 0,
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
inode_of_file, real_inode_of_file,
|
||||
@ -102,7 +102,7 @@ impl FileOpener {
|
||||
|
||||
// Creating the file in the storage.
|
||||
let inode_of_file = fs_lock.storage.vacant_entry().key();
|
||||
let real_inode_of_file = fs_lock.storage.insert(Node::ArcFile {
|
||||
let real_inode_of_file = fs_lock.storage.insert(Node::ArcFile(ArcFileNode {
|
||||
inode: inode_of_file,
|
||||
name: name_of_file,
|
||||
fs,
|
||||
@ -120,7 +120,7 @@ impl FileOpener {
|
||||
len: 0,
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
inode_of_file, real_inode_of_file,
|
||||
@ -165,25 +165,26 @@ impl FileOpener {
|
||||
|
||||
// Creating the file in the storage.
|
||||
let inode_of_file = fs_lock.storage.vacant_entry().key();
|
||||
let real_inode_of_file = fs_lock.storage.insert(Node::ArcDirectory {
|
||||
inode: inode_of_file,
|
||||
name: name_of_file,
|
||||
fs,
|
||||
path,
|
||||
metadata: {
|
||||
let time = time();
|
||||
Metadata {
|
||||
ft: FileType {
|
||||
file: true,
|
||||
..Default::default()
|
||||
},
|
||||
accessed: time,
|
||||
created: time,
|
||||
modified: time,
|
||||
len: 0,
|
||||
}
|
||||
},
|
||||
});
|
||||
let real_inode_of_file =
|
||||
fs_lock.storage.insert(Node::ArcDirectory(ArcDirectoryNode {
|
||||
inode: inode_of_file,
|
||||
name: name_of_file,
|
||||
fs,
|
||||
path,
|
||||
metadata: {
|
||||
let time = time();
|
||||
Metadata {
|
||||
ft: FileType {
|
||||
file: true,
|
||||
..Default::default()
|
||||
},
|
||||
accessed: time,
|
||||
created: time,
|
||||
modified: time,
|
||||
len: 0,
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
inode_of_file, real_inode_of_file,
|
||||
@ -228,7 +229,7 @@ impl FileOpener {
|
||||
|
||||
// Creating the file in the storage.
|
||||
let inode_of_file = fs_lock.storage.vacant_entry().key();
|
||||
let real_inode_of_file = fs_lock.storage.insert(Node::CustomFile {
|
||||
let real_inode_of_file = fs_lock.storage.insert(Node::CustomFile(CustomFileNode {
|
||||
inode: inode_of_file,
|
||||
name: name_of_file,
|
||||
file: Mutex::new(file),
|
||||
@ -245,7 +246,7 @@ impl FileOpener {
|
||||
len: 0,
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
inode_of_file, real_inode_of_file,
|
||||
@ -367,7 +368,7 @@ impl crate::FileOpener for FileOpener {
|
||||
|
||||
let inode = fs.storage.get_mut(inode_of_file);
|
||||
match inode {
|
||||
Some(Node::File { metadata, file, .. }) => {
|
||||
Some(Node::File(FileNode { metadata, file, .. })) => {
|
||||
// Update the accessed time.
|
||||
metadata.accessed = time();
|
||||
|
||||
@ -383,7 +384,7 @@ impl crate::FileOpener for FileOpener {
|
||||
}
|
||||
}
|
||||
|
||||
Some(Node::ReadOnlyFile { metadata, .. }) => {
|
||||
Some(Node::ReadOnlyFile(ReadOnlyFileNode { metadata, .. })) => {
|
||||
// Update the accessed time.
|
||||
metadata.accessed = time();
|
||||
|
||||
@ -393,7 +394,7 @@ impl crate::FileOpener for FileOpener {
|
||||
}
|
||||
}
|
||||
|
||||
Some(Node::CustomFile { metadata, file, .. }) => {
|
||||
Some(Node::CustomFile(CustomFileNode { metadata, file, .. })) => {
|
||||
// Update the accessed time.
|
||||
metadata.accessed = time();
|
||||
|
||||
@ -410,9 +411,9 @@ impl crate::FileOpener for FileOpener {
|
||||
}
|
||||
}
|
||||
|
||||
Some(Node::ArcFile {
|
||||
Some(Node::ArcFile(ArcFileNode {
|
||||
metadata, fs, path, ..
|
||||
}) => {
|
||||
})) => {
|
||||
// Update the accessed time.
|
||||
metadata.accessed = time();
|
||||
|
||||
@ -442,6 +443,7 @@ impl crate::FileOpener for FileOpener {
|
||||
}
|
||||
}
|
||||
|
||||
None => return Err(FsError::EntryNotFound),
|
||||
_ => return Err(FsError::NotAFile),
|
||||
}
|
||||
|
||||
@ -459,7 +461,7 @@ impl crate::FileOpener for FileOpener {
|
||||
|
||||
// Creating the file in the storage.
|
||||
let inode_of_file = fs.storage.vacant_entry().key();
|
||||
let real_inode_of_file = fs.storage.insert(Node::File {
|
||||
let real_inode_of_file = fs.storage.insert(Node::File(FileNode {
|
||||
inode: inode_of_file,
|
||||
name: name_of_file,
|
||||
file,
|
||||
@ -477,7 +479,7 @@ impl crate::FileOpener for FileOpener {
|
||||
len: 0,
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
inode_of_file, real_inode_of_file,
|
||||
@ -539,12 +541,12 @@ mod test_file_opener {
|
||||
assert!(
|
||||
matches!(
|
||||
fs_inner.storage.get(ROOT_INODE),
|
||||
Some(Node::Directory {
|
||||
Some(Node::Directory(DirectoryNode {
|
||||
inode: ROOT_INODE,
|
||||
name,
|
||||
children,
|
||||
..
|
||||
}) if name == "/" && children == &[1]
|
||||
})) if name == "/" && children == &[1]
|
||||
),
|
||||
"`/` contains `foo.txt`",
|
||||
);
|
||||
@ -578,7 +580,7 @@ mod test_file_opener {
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(path!("/foo/bar.txt")),
|
||||
Err(FsError::NotAFile),
|
||||
Err(FsError::EntryNotFound),
|
||||
),
|
||||
"creating a file in a directory that doesn't exist",
|
||||
);
|
||||
|
@ -22,10 +22,9 @@ pub struct FileSystem {
|
||||
|
||||
impl FileSystem {
|
||||
pub fn new_open_options_ext(&self) -> FileOpener {
|
||||
let opener = FileOpener {
|
||||
FileOpener {
|
||||
filesystem: self.clone(),
|
||||
};
|
||||
opener
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(&self, other: &Arc<dyn crate::FileSystem + Send + Sync>) {
|
||||
@ -48,27 +47,25 @@ impl FileSystem {
|
||||
}
|
||||
let _ = crate::FileSystem::create_dir(self, next.as_path());
|
||||
if let Ok(dir) = other.read_dir(next.as_path()) {
|
||||
for sub_dir in dir.into_iter() {
|
||||
if let Ok(sub_dir) = sub_dir {
|
||||
match sub_dir.file_type() {
|
||||
Ok(t) if t.is_dir() => {
|
||||
remaining.push_back(sub_dir.path());
|
||||
}
|
||||
Ok(t) if t.is_file() => {
|
||||
if sub_dir.file_name().to_string_lossy().starts_with(".wh.") {
|
||||
let rm = next.to_string_lossy();
|
||||
let rm = &rm[".wh.".len()..];
|
||||
let rm = PathBuf::from(rm);
|
||||
let _ = crate::FileSystem::remove_dir(self, rm.as_path());
|
||||
let _ = crate::FileSystem::remove_file(self, rm.as_path());
|
||||
continue;
|
||||
}
|
||||
let _ = self
|
||||
.new_open_options_ext()
|
||||
.insert_arc_file(sub_dir.path(), other.clone());
|
||||
}
|
||||
_ => {}
|
||||
for sub_dir in dir.flatten() {
|
||||
match sub_dir.file_type() {
|
||||
Ok(t) if t.is_dir() => {
|
||||
remaining.push_back(sub_dir.path());
|
||||
}
|
||||
Ok(t) if t.is_file() => {
|
||||
if sub_dir.file_name().to_string_lossy().starts_with(".wh.") {
|
||||
let rm = next.to_string_lossy();
|
||||
let rm = &rm[".wh.".len()..];
|
||||
let rm = PathBuf::from(rm);
|
||||
let _ = crate::FileSystem::remove_dir(self, rm.as_path());
|
||||
let _ = crate::FileSystem::remove_file(self, rm.as_path());
|
||||
continue;
|
||||
}
|
||||
let _ = self
|
||||
.new_open_options_ext()
|
||||
.insert_arc_file(sub_dir.path(), other.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,7 +116,7 @@ impl FileSystem {
|
||||
|
||||
// Creating the directory in the storage.
|
||||
let inode_of_directory = fs.storage.vacant_entry().key();
|
||||
let real_inode_of_directory = fs.storage.insert(Node::ArcDirectory {
|
||||
let real_inode_of_directory = fs.storage.insert(Node::ArcDirectory(ArcDirectoryNode {
|
||||
inode: inode_of_directory,
|
||||
name: name_of_directory,
|
||||
fs: other.clone(),
|
||||
@ -138,7 +135,7 @@ impl FileSystem {
|
||||
len: 0,
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
inode_of_directory, real_inode_of_directory,
|
||||
@ -170,7 +167,7 @@ impl crate::FileSystem for FileSystem {
|
||||
// Check it's a directory and fetch the immediate children as `DirEntry`.
|
||||
let inode = guard.storage.get(inode_of_directory);
|
||||
let children = match inode {
|
||||
Some(Node::Directory { children, .. }) => children
|
||||
Some(Node::Directory(DirectoryNode { children, .. })) => children
|
||||
.iter()
|
||||
.filter_map(|inode| guard.storage.get(*inode))
|
||||
.map(|node| DirEntry {
|
||||
@ -184,7 +181,7 @@ impl crate::FileSystem for FileSystem {
|
||||
})
|
||||
.collect(),
|
||||
|
||||
Some(Node::ArcDirectory { fs, path, .. }) => {
|
||||
Some(Node::ArcDirectory(ArcDirectoryNode { fs, path, .. })) => {
|
||||
return fs.read_dir(path.as_path());
|
||||
}
|
||||
|
||||
@ -229,13 +226,17 @@ impl crate::FileSystem for FileSystem {
|
||||
(inode_of_parent, name_of_directory)
|
||||
};
|
||||
|
||||
if self.read_dir(path).is_ok() {
|
||||
return Err(FsError::AlreadyExists);
|
||||
}
|
||||
|
||||
{
|
||||
// Write lock.
|
||||
let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
|
||||
|
||||
// Creating the directory in the storage.
|
||||
let inode_of_directory = fs.storage.vacant_entry().key();
|
||||
let real_inode_of_directory = fs.storage.insert(Node::Directory {
|
||||
let real_inode_of_directory = fs.storage.insert(Node::Directory(DirectoryNode {
|
||||
inode: inode_of_directory,
|
||||
name: name_of_directory,
|
||||
children: Vec::new(),
|
||||
@ -253,7 +254,7 @@ impl crate::FileSystem for FileSystem {
|
||||
len: 0,
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
inode_of_directory, real_inode_of_directory,
|
||||
@ -328,11 +329,9 @@ impl crate::FileSystem for FileSystem {
|
||||
}
|
||||
|
||||
fn rename(&self, from: &Path, to: &Path) -> Result<()> {
|
||||
let (
|
||||
(position_of_from, inode, inode_of_from_parent),
|
||||
(inode_of_to_parent, name_of_to),
|
||||
inode_dest,
|
||||
) = {
|
||||
let name_of_to;
|
||||
|
||||
let ((position_of_from, inode, inode_of_from_parent), inode_of_to_parent) = {
|
||||
// Read lock.
|
||||
let fs = self.inner.read().map_err(|_| FsError::Lock)?;
|
||||
|
||||
@ -348,7 +347,7 @@ impl crate::FileSystem for FileSystem {
|
||||
.file_name()
|
||||
.ok_or(FsError::InvalidInput)?
|
||||
.to_os_string();
|
||||
let name_of_to = to.file_name().ok_or(FsError::InvalidInput)?.to_os_string();
|
||||
name_of_to = to.file_name().ok_or(FsError::InvalidInput)?.to_os_string();
|
||||
|
||||
// Find the parent inodes.
|
||||
let inode_of_from_parent = match fs.inode_of_parent(parent_of_from)? {
|
||||
@ -364,20 +363,15 @@ impl crate::FileSystem for FileSystem {
|
||||
}
|
||||
};
|
||||
|
||||
// Find the inode of the dest file if it exists
|
||||
let maybe_position_and_inode_of_file =
|
||||
fs.as_parent_get_position_and_inode_of_file(inode_of_to_parent, &name_of_to)?;
|
||||
|
||||
// Get the child indexes to update in the parent nodes, in
|
||||
// addition to the inode of the directory to update.
|
||||
let (position_of_from, inode) = fs
|
||||
.as_parent_get_position_and_inode(inode_of_from_parent, &name_of_from)?
|
||||
.ok_or(FsError::NotAFile)?;
|
||||
.ok_or(FsError::EntryNotFound)?;
|
||||
|
||||
(
|
||||
(position_of_from, inode, inode_of_from_parent),
|
||||
(inode_of_to_parent, name_of_to),
|
||||
maybe_position_and_inode_of_file,
|
||||
inode_of_to_parent,
|
||||
)
|
||||
};
|
||||
|
||||
@ -392,19 +386,26 @@ impl crate::FileSystem for FileSystem {
|
||||
// Write lock.
|
||||
let mut fs = self.inner.write().map_err(|_| FsError::Lock)?;
|
||||
|
||||
if let Some((position, inode_of_file)) = inode_dest {
|
||||
// Remove the file from the storage.
|
||||
match inode_of_file {
|
||||
InodeResolution::Found(inode_of_file) => {
|
||||
fs.storage.remove(inode_of_file);
|
||||
}
|
||||
InodeResolution::Redirect(..) => {
|
||||
return Err(FsError::InvalidInput);
|
||||
}
|
||||
}
|
||||
// If we rename to a path that already exists, we've updated the name of the
|
||||
// current inode, but we still have the old inode in there
|
||||
if inode_of_from_parent == inode_of_to_parent {
|
||||
let target_already_exists = fs
|
||||
.as_parent_get_position_and_inode(inode_of_to_parent, &name_of_to)
|
||||
.ok()
|
||||
.and_then(|o| o);
|
||||
if let Some((position_of_to, inode_of_to)) = target_already_exists {
|
||||
let inode_of_to = match inode_of_to {
|
||||
InodeResolution::Found(a) => a,
|
||||
InodeResolution::Redirect(..) => {
|
||||
return Err(FsError::InvalidInput);
|
||||
}
|
||||
};
|
||||
|
||||
// Remove the child from the parent directory.
|
||||
fs.remove_child_from_node(inode_of_to_parent, position)?;
|
||||
// Remove the file from the storage.
|
||||
fs.storage.remove(inode_of_to);
|
||||
|
||||
fs.remove_child_from_node(inode_of_to_parent, position_of_to)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the file name, and update the modified time.
|
||||
@ -424,14 +425,14 @@ impl crate::FileSystem for FileSystem {
|
||||
else {
|
||||
let inode = fs.storage.get_mut(inode_of_from_parent);
|
||||
match inode {
|
||||
Some(Node::Directory {
|
||||
Some(Node::Directory(DirectoryNode {
|
||||
metadata: Metadata { modified, .. },
|
||||
..
|
||||
}) => *modified = time(),
|
||||
Some(Node::ArcDirectory {
|
||||
})) => *modified = time(),
|
||||
Some(Node::ArcDirectory(ArcDirectoryNode {
|
||||
metadata: Metadata { modified, .. },
|
||||
..
|
||||
}) => *modified = time(),
|
||||
})) => *modified = time(),
|
||||
_ => return Err(FsError::UnknownError),
|
||||
}
|
||||
}
|
||||
@ -489,7 +490,7 @@ impl crate::FileSystem for FileSystem {
|
||||
|
||||
match maybe_position_and_inode_of_file {
|
||||
Some((position, inode_of_file)) => (inode_of_parent, position, inode_of_file),
|
||||
None => return Err(FsError::NotAFile),
|
||||
None => return Err(FsError::EntryNotFound),
|
||||
}
|
||||
};
|
||||
|
||||
@ -561,23 +562,23 @@ impl FileSystemInner {
|
||||
let mut components = path.components();
|
||||
|
||||
match components.next() {
|
||||
Some(Component::RootDir) | None => {}
|
||||
Some(Component::RootDir) => {}
|
||||
_ => return Err(FsError::BaseNotDirectory),
|
||||
}
|
||||
|
||||
while let Some(component) = components.next() {
|
||||
node = match node {
|
||||
Node::Directory { children, .. } => children
|
||||
Node::Directory(DirectoryNode { children, .. }) => children
|
||||
.iter()
|
||||
.filter_map(|inode| self.storage.get(*inode))
|
||||
.find(|node| node.name() == component.as_os_str())
|
||||
.ok_or(FsError::EntryNotFound)?,
|
||||
Node::ArcDirectory {
|
||||
Node::ArcDirectory(ArcDirectoryNode {
|
||||
fs, path: fs_path, ..
|
||||
} => {
|
||||
}) => {
|
||||
let mut path = fs_path.clone();
|
||||
path.push(PathBuf::from(component.as_os_str()));
|
||||
while let Some(component) = components.next() {
|
||||
for component in components.by_ref() {
|
||||
path.push(PathBuf::from(component.as_os_str()));
|
||||
}
|
||||
return Ok(InodeResolution::Redirect(fs.clone(), path));
|
||||
@ -596,14 +597,16 @@ impl FileSystemInner {
|
||||
InodeResolution::Found(inode_of_parent) => {
|
||||
// Ensure it is a directory.
|
||||
match self.storage.get(inode_of_parent) {
|
||||
Some(Node::Directory { .. }) => Ok(InodeResolution::Found(inode_of_parent)),
|
||||
Some(Node::ArcDirectory { .. }) => Ok(InodeResolution::Found(inode_of_parent)),
|
||||
Some(Node::Directory(DirectoryNode { .. })) => {
|
||||
Ok(InodeResolution::Found(inode_of_parent))
|
||||
}
|
||||
Some(Node::ArcDirectory(ArcDirectoryNode { .. })) => {
|
||||
Ok(InodeResolution::Found(inode_of_parent))
|
||||
}
|
||||
_ => Err(FsError::BaseNotDirectory),
|
||||
}
|
||||
}
|
||||
InodeResolution::Redirect(fs, path) => {
|
||||
return Ok(InodeResolution::Redirect(fs, path));
|
||||
}
|
||||
InodeResolution::Redirect(fs, path) => Ok(InodeResolution::Redirect(fs, path)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -616,17 +619,17 @@ impl FileSystemInner {
|
||||
directory_must_be_empty: DirectoryMustBeEmpty,
|
||||
) -> Result<(usize, InodeResolution)> {
|
||||
match self.storage.get(inode_of_parent) {
|
||||
Some(Node::Directory { children, .. }) => children
|
||||
Some(Node::Directory(DirectoryNode { children, .. })) => children
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
|
||||
.find_map(|(nth, node)| match node {
|
||||
Node::Directory {
|
||||
Node::Directory(DirectoryNode {
|
||||
inode,
|
||||
name,
|
||||
children,
|
||||
..
|
||||
} if name.as_os_str() == name_of_directory => {
|
||||
}) if name.as_os_str() == name_of_directory => {
|
||||
if directory_must_be_empty.no() || children.is_empty() {
|
||||
Some(Ok((nth, InodeResolution::Found(*inode))))
|
||||
} else {
|
||||
@ -639,9 +642,9 @@ impl FileSystemInner {
|
||||
.ok_or(FsError::InvalidInput)
|
||||
.and_then(identity), // flatten
|
||||
|
||||
Some(Node::ArcDirectory {
|
||||
Some(Node::ArcDirectory(ArcDirectoryNode {
|
||||
fs, path: fs_path, ..
|
||||
}) => {
|
||||
})) => {
|
||||
let mut path = fs_path.clone();
|
||||
path.push(name_of_directory);
|
||||
Ok((0, InodeResolution::Redirect(fs.clone(), path)))
|
||||
@ -659,15 +662,15 @@ impl FileSystemInner {
|
||||
name_of_file: &OsString,
|
||||
) -> Result<Option<(usize, InodeResolution)>> {
|
||||
match self.storage.get(inode_of_parent) {
|
||||
Some(Node::Directory { children, .. }) => children
|
||||
Some(Node::Directory(DirectoryNode { children, .. })) => children
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
|
||||
.find_map(|(nth, node)| match node {
|
||||
Node::File { inode, name, .. }
|
||||
| Node::ReadOnlyFile { inode, name, .. }
|
||||
| Node::CustomFile { inode, name, .. }
|
||||
| Node::ArcFile { inode, name, .. }
|
||||
Node::File(FileNode { inode, name, .. })
|
||||
| Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. })
|
||||
| Node::CustomFile(CustomFileNode { inode, name, .. })
|
||||
| Node::ArcFile(ArcFileNode { inode, name, .. })
|
||||
if name.as_os_str() == name_of_file =>
|
||||
{
|
||||
Some(Some((nth, InodeResolution::Found(*inode))))
|
||||
@ -677,9 +680,9 @@ impl FileSystemInner {
|
||||
.or(Some(None))
|
||||
.ok_or(FsError::InvalidInput),
|
||||
|
||||
Some(Node::ArcDirectory {
|
||||
Some(Node::ArcDirectory(ArcDirectoryNode {
|
||||
fs, path: fs_path, ..
|
||||
}) => {
|
||||
})) => {
|
||||
let mut path = fs_path.clone();
|
||||
path.push(name_of_file);
|
||||
Ok(Some((0, InodeResolution::Redirect(fs.clone(), path))))
|
||||
@ -698,16 +701,16 @@ impl FileSystemInner {
|
||||
name_of: &OsString,
|
||||
) -> Result<Option<(usize, InodeResolution)>> {
|
||||
match self.storage.get(inode_of_parent) {
|
||||
Some(Node::Directory { children, .. }) => children
|
||||
Some(Node::Directory(DirectoryNode { children, .. })) => children
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node)))
|
||||
.find_map(|(nth, node)| match node {
|
||||
Node::File { inode, name, .. }
|
||||
| Node::Directory { inode, name, .. }
|
||||
| Node::ReadOnlyFile { inode, name, .. }
|
||||
| Node::CustomFile { inode, name, .. }
|
||||
| Node::ArcFile { inode, name, .. }
|
||||
Node::File(FileNode { inode, name, .. })
|
||||
| Node::Directory(DirectoryNode { inode, name, .. })
|
||||
| Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. })
|
||||
| Node::CustomFile(CustomFileNode { inode, name, .. })
|
||||
| Node::ArcFile(ArcFileNode { inode, name, .. })
|
||||
if name.as_os_str() == name_of =>
|
||||
{
|
||||
Some(Some((nth, InodeResolution::Found(*inode))))
|
||||
@ -717,9 +720,9 @@ impl FileSystemInner {
|
||||
.or(Some(None))
|
||||
.ok_or(FsError::InvalidInput),
|
||||
|
||||
Some(Node::ArcDirectory {
|
||||
Some(Node::ArcDirectory(ArcDirectoryNode {
|
||||
fs, path: fs_path, ..
|
||||
}) => {
|
||||
})) => {
|
||||
let mut path = fs_path.clone();
|
||||
path.push(name_of);
|
||||
Ok(Some((0, InodeResolution::Redirect(fs.clone(), path))))
|
||||
@ -748,11 +751,11 @@ impl FileSystemInner {
|
||||
/// `inode` must represents an existing directory.
|
||||
pub(super) fn add_child_to_node(&mut self, inode: Inode, new_child: Inode) -> Result<()> {
|
||||
match self.storage.get_mut(inode) {
|
||||
Some(Node::Directory {
|
||||
Some(Node::Directory(DirectoryNode {
|
||||
children,
|
||||
metadata: Metadata { modified, .. },
|
||||
..
|
||||
}) => {
|
||||
})) => {
|
||||
children.push(new_child);
|
||||
*modified = time();
|
||||
|
||||
@ -772,11 +775,11 @@ impl FileSystemInner {
|
||||
/// `inode` must represents an existing directory.
|
||||
pub(super) fn remove_child_from_node(&mut self, inode: Inode, position: usize) -> Result<()> {
|
||||
match self.storage.get_mut(inode) {
|
||||
Some(Node::Directory {
|
||||
Some(Node::Directory(DirectoryNode {
|
||||
children,
|
||||
metadata: Metadata { modified, .. },
|
||||
..
|
||||
}) => {
|
||||
})) => {
|
||||
children.remove(position);
|
||||
*modified = time();
|
||||
|
||||
@ -866,19 +869,19 @@ impl fmt::Debug for FileSystemInner {
|
||||
"{inode:<8} {ty:<4} {indentation_symbol:indentation_width$}{name}",
|
||||
inode = node.inode(),
|
||||
ty = match node {
|
||||
Node::File { .. } => "file",
|
||||
Node::ReadOnlyFile { .. } => "ro-file",
|
||||
Node::ArcFile { .. } => "arc-file",
|
||||
Node::CustomFile { .. } => "custom-file",
|
||||
Node::Directory { .. } => "dir",
|
||||
Node::ArcDirectory { .. } => "arc-dir",
|
||||
Node::File(FileNode { .. }) => "file",
|
||||
Node::ReadOnlyFile(ReadOnlyFileNode { .. }) => "ro-file",
|
||||
Node::ArcFile(ArcFileNode { .. }) => "arc-file",
|
||||
Node::CustomFile(CustomFileNode { .. }) => "custom-file",
|
||||
Node::Directory(DirectoryNode { .. }) => "dir",
|
||||
Node::ArcDirectory(ArcDirectoryNode { .. }) => "arc-dir",
|
||||
},
|
||||
name = node.name().to_string_lossy(),
|
||||
indentation_symbol = " ",
|
||||
indentation_width = indentation * 2 + 1,
|
||||
)?;
|
||||
|
||||
if let Node::Directory { children, .. } = node {
|
||||
if let Node::Directory(DirectoryNode { children, .. }) = node {
|
||||
debug(
|
||||
children
|
||||
.iter()
|
||||
@ -908,7 +911,7 @@ impl Default for FileSystemInner {
|
||||
let time = time();
|
||||
|
||||
let mut slab = Slab::new();
|
||||
slab.insert(Node::Directory {
|
||||
slab.insert(Node::Directory(DirectoryNode {
|
||||
inode: ROOT_INODE,
|
||||
name: OsString::from("/"),
|
||||
children: Vec::new(),
|
||||
@ -922,7 +925,7 @@ impl Default for FileSystemInner {
|
||||
modified: time,
|
||||
len: 0,
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
Self { storage: slab }
|
||||
}
|
||||
@ -1071,7 +1074,7 @@ mod test_filesystem {
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_dir(path!("/foo")),
|
||||
Err(FsError::NotAFile),
|
||||
Err(FsError::EntryNotFound),
|
||||
"cannot remove a directory that doesn't exist",
|
||||
);
|
||||
|
||||
@ -1136,7 +1139,7 @@ mod test_filesystem {
|
||||
|
||||
assert_eq!(
|
||||
fs.rename(path!("/foo"), path!("/bar/baz")),
|
||||
Err(FsError::NotAFile),
|
||||
Err(FsError::EntryNotFound),
|
||||
"renaming to a directory that has parent that doesn't exist",
|
||||
);
|
||||
|
||||
@ -1484,7 +1487,7 @@ mod test_filesystem {
|
||||
|
||||
assert_eq!(
|
||||
fs.remove_file(path!("/foo.txt")),
|
||||
Err(FsError::NotAFile),
|
||||
Err(FsError::EntryNotFound),
|
||||
"removing a file that exists",
|
||||
);
|
||||
}
|
||||
|
@ -18,101 +18,119 @@ use std::{
|
||||
type Inode = usize;
|
||||
const ROOT_INODE: Inode = 0;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FileNode {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
file: File,
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReadOnlyFileNode {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
file: ReadOnlyFile,
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ArcFileNode {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
fs: Arc<dyn crate::FileSystem + Send + Sync>,
|
||||
path: PathBuf,
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CustomFileNode {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
file: Mutex<Box<dyn crate::VirtualFile + Send + Sync>>,
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DirectoryNode {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
children: Vec<Inode>,
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ArcDirectoryNode {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
fs: Arc<dyn crate::FileSystem + Send + Sync>,
|
||||
path: PathBuf,
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Node {
|
||||
File {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
file: File,
|
||||
metadata: Metadata,
|
||||
},
|
||||
ReadOnlyFile {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
file: ReadOnlyFile,
|
||||
metadata: Metadata,
|
||||
},
|
||||
ArcFile {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
fs: Arc<dyn crate::FileSystem + Send + Sync>,
|
||||
path: PathBuf,
|
||||
metadata: Metadata,
|
||||
},
|
||||
CustomFile {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
file: Mutex<Box<dyn crate::VirtualFile + Send + Sync>>,
|
||||
metadata: Metadata,
|
||||
},
|
||||
Directory {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
children: Vec<Inode>,
|
||||
metadata: Metadata,
|
||||
},
|
||||
ArcDirectory {
|
||||
inode: Inode,
|
||||
name: OsString,
|
||||
fs: Arc<dyn crate::FileSystem + Send + Sync>,
|
||||
path: PathBuf,
|
||||
metadata: Metadata,
|
||||
},
|
||||
File(FileNode),
|
||||
ReadOnlyFile(ReadOnlyFileNode),
|
||||
ArcFile(ArcFileNode),
|
||||
CustomFile(CustomFileNode),
|
||||
Directory(DirectoryNode),
|
||||
ArcDirectory(ArcDirectoryNode),
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn inode(&self) -> Inode {
|
||||
*match self {
|
||||
Self::File { inode, .. } => inode,
|
||||
Self::ReadOnlyFile { inode, .. } => inode,
|
||||
Self::ArcFile { inode, .. } => inode,
|
||||
Self::CustomFile { inode, .. } => inode,
|
||||
Self::Directory { inode, .. } => inode,
|
||||
Self::ArcDirectory { inode, .. } => inode,
|
||||
Self::File(FileNode { inode, .. }) => inode,
|
||||
Self::ReadOnlyFile(ReadOnlyFileNode { inode, .. }) => inode,
|
||||
Self::ArcFile(ArcFileNode { inode, .. }) => inode,
|
||||
Self::CustomFile(CustomFileNode { inode, .. }) => inode,
|
||||
Self::Directory(DirectoryNode { inode, .. }) => inode,
|
||||
Self::ArcDirectory(ArcDirectoryNode { inode, .. }) => inode,
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &OsStr {
|
||||
match self {
|
||||
Self::File { name, .. } => name.as_os_str(),
|
||||
Self::ReadOnlyFile { name, .. } => name.as_os_str(),
|
||||
Self::ArcFile { name, .. } => name.as_os_str(),
|
||||
Self::CustomFile { name, .. } => name.as_os_str(),
|
||||
Self::Directory { name, .. } => name.as_os_str(),
|
||||
Self::ArcDirectory { name, .. } => name.as_os_str(),
|
||||
Self::File(FileNode { name, .. }) => name.as_os_str(),
|
||||
Self::ReadOnlyFile(ReadOnlyFileNode { name, .. }) => name.as_os_str(),
|
||||
Self::ArcFile(ArcFileNode { name, .. }) => name.as_os_str(),
|
||||
Self::CustomFile(CustomFileNode { name, .. }) => name.as_os_str(),
|
||||
Self::Directory(DirectoryNode { name, .. }) => name.as_os_str(),
|
||||
Self::ArcDirectory(ArcDirectoryNode { name, .. }) => name.as_os_str(),
|
||||
}
|
||||
}
|
||||
|
||||
fn metadata(&self) -> &Metadata {
|
||||
match self {
|
||||
Self::File { metadata, .. } => metadata,
|
||||
Self::ReadOnlyFile { metadata, .. } => metadata,
|
||||
Self::ArcFile { metadata, .. } => metadata,
|
||||
Self::CustomFile { metadata, .. } => metadata,
|
||||
Self::Directory { metadata, .. } => metadata,
|
||||
Self::ArcDirectory { metadata, .. } => metadata,
|
||||
Self::File(FileNode { metadata, .. }) => metadata,
|
||||
Self::ReadOnlyFile(ReadOnlyFileNode { metadata, .. }) => metadata,
|
||||
Self::ArcFile(ArcFileNode { metadata, .. }) => metadata,
|
||||
Self::CustomFile(CustomFileNode { metadata, .. }) => metadata,
|
||||
Self::Directory(DirectoryNode { metadata, .. }) => metadata,
|
||||
Self::ArcDirectory(ArcDirectoryNode { metadata, .. }) => metadata,
|
||||
}
|
||||
}
|
||||
|
||||
fn metadata_mut(&mut self) -> &mut Metadata {
|
||||
match self {
|
||||
Self::File { metadata, .. } => metadata,
|
||||
Self::ReadOnlyFile { metadata, .. } => metadata,
|
||||
Self::ArcFile { metadata, .. } => metadata,
|
||||
Self::CustomFile { metadata, .. } => metadata,
|
||||
Self::Directory { metadata, .. } => metadata,
|
||||
Self::ArcDirectory { metadata, .. } => metadata,
|
||||
Self::File(FileNode { metadata, .. }) => metadata,
|
||||
Self::ReadOnlyFile(ReadOnlyFileNode { metadata, .. }) => metadata,
|
||||
Self::ArcFile(ArcFileNode { metadata, .. }) => metadata,
|
||||
Self::CustomFile(CustomFileNode { metadata, .. }) => metadata,
|
||||
Self::Directory(DirectoryNode { metadata, .. }) => metadata,
|
||||
Self::ArcDirectory(ArcDirectoryNode { metadata, .. }) => metadata,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_name(&mut self, new_name: OsString) {
|
||||
match self {
|
||||
Self::File { name, .. } => *name = new_name,
|
||||
Self::ReadOnlyFile { name, .. } => *name = new_name,
|
||||
Self::ArcFile { name, .. } => *name = new_name,
|
||||
Self::CustomFile { name, .. } => *name = new_name,
|
||||
Self::Directory { name, .. } => *name = new_name,
|
||||
Self::ArcDirectory { name, .. } => *name = new_name,
|
||||
Self::File(FileNode { name, .. }) => *name = new_name,
|
||||
Self::ReadOnlyFile(ReadOnlyFileNode { name, .. }) => *name = new_name,
|
||||
Self::ArcFile(ArcFileNode { name, .. }) => *name = new_name,
|
||||
Self::CustomFile(CustomFileNode { name, .. }) => *name = new_name,
|
||||
Self::Directory(DirectoryNode { name, .. }) => *name = new_name,
|
||||
Self::ArcDirectory(ArcDirectoryNode { name, .. }) => *name = new_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
//! NullFile is a special file for `/dev/null`, which returns 0 for all
|
||||
//! operations except writing.
|
||||
|
||||
use std::io::{self, *};
|
||||
|
||||
use wasmer_vbus::FileDescriptor;
|
||||
use wasmer_vfs::{ClonableVirtualFile, VirtualFile};
|
||||
use crate::FileDescriptor;
|
||||
use crate::{ClonableVirtualFile, VirtualFile};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct NullFile {}
|
||||
@ -11,6 +14,7 @@ impl Seek for NullFile {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for NullFile {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
@ -39,13 +43,13 @@ impl VirtualFile for NullFile {
|
||||
fn size(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn set_len(&mut self, _new_size: u64) -> wasmer_vfs::Result<()> {
|
||||
fn set_len(&mut self, _new_size: u64) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn unlink(&mut self) -> wasmer_vfs::Result<()> {
|
||||
fn unlink(&mut self) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn bytes_available(&self) -> wasmer_vfs::Result<usize> {
|
||||
fn bytes_available(&self) -> crate::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
fn get_fd(&self) -> Option<FileDescriptor> {
|
@ -1,8 +1,12 @@
|
||||
//! Wraps a boxed file system with an implemented trait VirtualSystem -
|
||||
//! this is needed so that a Box<dyn VirtualFileSystem> can be wrapped in
|
||||
//! an Arc and shared - some of the interfaces pass around a Box<dyn VirtualFileSystem>
|
||||
|
||||
use std::path::Path;
|
||||
#[allow(unused_imports, dead_code)]
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use wasmer_vfs::*;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PassthruFileSystem {
|
||||
@ -48,3 +52,39 @@ impl FileSystem for PassthruFileSystem {
|
||||
self.fs.new_open_options()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_passthru_fs_2() {
|
||||
let mem_fs = crate::mem_fs::FileSystem::default();
|
||||
|
||||
mem_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open("/foo.txt")
|
||||
.unwrap()
|
||||
.write(b"hello")
|
||||
.unwrap();
|
||||
|
||||
let mut buf = Vec::new();
|
||||
mem_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.open("/foo.txt")
|
||||
.unwrap()
|
||||
.read_to_end(&mut buf)
|
||||
.unwrap();
|
||||
assert_eq!(buf, b"hello");
|
||||
|
||||
let passthru_fs = PassthruFileSystem::new(Box::new(mem_fs.clone()));
|
||||
let mut buf = Vec::new();
|
||||
passthru_fs
|
||||
.new_open_options()
|
||||
.read(true)
|
||||
.open("/foo.txt")
|
||||
.unwrap()
|
||||
.read_to_end(&mut buf)
|
||||
.unwrap();
|
||||
assert_eq!(buf, b"hello");
|
||||
}
|
@ -1,9 +1,14 @@
|
||||
//! Used for /dev/stdin, /dev/stdout, dev/stderr - returns a
|
||||
//! static file descriptor (0, 1, 2)
|
||||
|
||||
use std::io::{self, *};
|
||||
|
||||
use wasmer_vbus::FileDescriptor;
|
||||
use wasmer_vfs::VirtualFile;
|
||||
use crate::FileDescriptor;
|
||||
use crate::VirtualFile;
|
||||
use wasmer_wasi_types::wasi::Fd;
|
||||
|
||||
/// A "special" file is a file that is locked
|
||||
/// to one file descriptor (i.e. stdout => 0, stdin => 1), etc.
|
||||
#[derive(Debug)]
|
||||
pub struct SpecialFile {
|
||||
fd: Fd,
|
||||
@ -48,13 +53,13 @@ impl VirtualFile for SpecialFile {
|
||||
fn size(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn set_len(&mut self, _new_size: u64) -> wasmer_vfs::Result<()> {
|
||||
fn set_len(&mut self, _new_size: u64) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn unlink(&mut self) -> wasmer_vfs::Result<()> {
|
||||
fn unlink(&mut self) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn bytes_available(&self) -> wasmer_vfs::Result<usize> {
|
||||
fn bytes_available(&self) -> crate::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
fn get_special_fd(&self) -> Option<u32> {
|
@ -1,3 +1,7 @@
|
||||
//! Wraps the memory file system implementation - this has been
|
||||
//! enhanced to support mounting file systems, shared static files,
|
||||
//! readonly files, etc...
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused)]
|
||||
use std::collections::HashMap;
|
||||
@ -12,21 +16,18 @@ use std::sync::Mutex;
|
||||
#[allow(unused_imports, dead_code)]
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use crate::{types as wasi_types, WasiFile, WasiFsError};
|
||||
use wasmer_vfs::mem_fs;
|
||||
use wasmer_vfs::Result as FsResult;
|
||||
use wasmer_vfs::*;
|
||||
use crate::mem_fs;
|
||||
use crate::Result as FsResult;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TmpFileSystem {
|
||||
fs: mem_fs::FileSystem,
|
||||
}
|
||||
|
||||
impl TmpFileSystem {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
fs: mem_fs::FileSystem::default(),
|
||||
}
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn new_open_options_ext(&self) -> mem_fs::FileOpener {
|
1076
lib/vfs/src/union_fs.rs
Normal file
1076
lib/vfs/src/union_fs.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,10 @@
|
||||
//! Used for /dev/zero - infinitely returns zero
|
||||
//! which is useful for commands like `dd if=/dev/zero of=bigfile.img size=1G`
|
||||
|
||||
use std::io::{self, *};
|
||||
|
||||
use wasmer_vbus::FileDescriptor;
|
||||
use wasmer_vfs::VirtualFile;
|
||||
use crate::FileDescriptor;
|
||||
use crate::VirtualFile;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ZeroFile {}
|
||||
@ -22,9 +25,7 @@ impl Write for ZeroFile {
|
||||
|
||||
impl Read for ZeroFile {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
for b in buf.iter_mut() {
|
||||
*b = 0;
|
||||
}
|
||||
buf.fill(0);
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
@ -42,13 +43,13 @@ impl VirtualFile for ZeroFile {
|
||||
fn size(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn set_len(&mut self, _new_size: u64) -> wasmer_vfs::Result<()> {
|
||||
fn set_len(&mut self, _new_size: u64) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn unlink(&mut self) -> wasmer_vfs::Result<()> {
|
||||
fn unlink(&mut self) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn bytes_available(&self) -> wasmer_vfs::Result<usize> {
|
||||
fn bytes_available(&self) -> crate::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
fn get_fd(&self) -> Option<FileDescriptor> {
|
@ -13,6 +13,5 @@ bytes = "1"
|
||||
async-trait = { version = "^0.1" }
|
||||
|
||||
[features]
|
||||
default = ["mem_fs"]
|
||||
mem_fs = ["wasmer-vfs/mem-fs"]
|
||||
default = []
|
||||
host_fs = ["wasmer-vfs/host-fs"]
|
||||
|
@ -24,5 +24,4 @@ async-trait = { version = "^0.1" }
|
||||
[features]
|
||||
default = ["host_fs"]
|
||||
wasix = [ ]
|
||||
host_fs = ["wasmer-vnet/host_fs", "wasmer-vfs/host-fs"]
|
||||
mem_fs = ["wasmer-vnet/mem_fs", "wasmer-vfs/mem-fs"]
|
||||
host_fs = ["wasmer-vnet/host_fs", "wasmer-vfs/host-fs"]
|
@ -98,7 +98,7 @@ compiler-cranelift = [ "wasmer-compiler-cranelift" ]
|
||||
compiler-llvm = [ "wasmer-compiler-llvm" ]
|
||||
compiler-singlepass = [ "wasmer-compiler-singlepass" ]
|
||||
|
||||
js = ["wasmer/js", "mem-fs", "wasmer-vfs/no-time", "getrandom/js", "chrono", "wasmer-wasi-types/js"]
|
||||
js = ["wasmer/js", "wasmer-vfs/no-time", "getrandom/js", "chrono", "wasmer-wasi-types/js"]
|
||||
js-default = ["js", "wasmer/js-default"]
|
||||
test-js = ["js", "wasmer/js-default", "wasmer/wat"]
|
||||
|
||||
|
@ -5,9 +5,9 @@ use std::{
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
};
|
||||
|
||||
use crate::{fs::TmpFileSystem, syscalls::platform_clock_time_get};
|
||||
use crate::{syscalls::platform_clock_time_get};
|
||||
use derivative::*;
|
||||
use wasmer_vfs::FileSystem;
|
||||
use wasmer_vfs::{FileSystem, TmpFileSystem};
|
||||
use wasmer_wasi_types::wasi::Snapshot0Clockid;
|
||||
|
||||
use super::hash_of_binary;
|
||||
|
@ -1,173 +0,0 @@
|
||||
use derivative::Derivative;
|
||||
use std::{
|
||||
io::{self, *},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use wasmer_vbus::FileDescriptor;
|
||||
use wasmer_vfs::VirtualFile;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DelegateFileInner {
|
||||
seek: Option<Box<dyn Fn(SeekFrom) -> io::Result<u64> + Send + Sync>>,
|
||||
write: Option<Box<dyn Fn(&[u8]) -> io::Result<usize> + Send + Sync>>,
|
||||
flush: Option<Box<dyn Fn() -> io::Result<()> + Send + Sync>>,
|
||||
read: Option<Box<dyn Fn(&mut [u8]) -> io::Result<usize> + Send + Sync>>,
|
||||
size: Option<Box<dyn Fn() -> u64 + Send + Sync>>,
|
||||
set_len: Option<Box<dyn Fn(u64) -> wasmer_vfs::Result<()> + Send + Sync>>,
|
||||
unlink: Option<Box<dyn Fn() -> wasmer_vfs::Result<()> + Send + Sync>>,
|
||||
bytes_available: Option<Box<dyn Fn() -> wasmer_vfs::Result<usize> + Send + Sync>>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Clone)]
|
||||
#[derivative(Debug)]
|
||||
pub struct DelegateFile {
|
||||
#[derivative(Debug = "ignore")]
|
||||
inner: Arc<RwLock<DelegateFileInner>>,
|
||||
}
|
||||
|
||||
impl DelegateFile {
|
||||
pub fn with_seek(
|
||||
&self,
|
||||
func: impl Fn(SeekFrom) -> io::Result<u64> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.seek.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_write(
|
||||
&self,
|
||||
func: impl Fn(&[u8]) -> io::Result<usize> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.write.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_flush(&self, func: impl Fn() -> io::Result<()> + Send + Sync + 'static) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.flush.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_read(
|
||||
&self,
|
||||
func: impl Fn(&mut [u8]) -> io::Result<usize> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.read.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_size(&self, func: impl Fn() -> u64 + Send + Sync + 'static) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.size.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_set_len(
|
||||
&self,
|
||||
func: impl Fn(u64) -> wasmer_vfs::Result<()> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.set_len.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_unlink(
|
||||
&self,
|
||||
func: impl Fn() -> wasmer_vfs::Result<()> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.unlink.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_bytes_available(
|
||||
&self,
|
||||
func: impl Fn() -> wasmer_vfs::Result<usize> + Send + Sync + 'static,
|
||||
) -> &Self {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.bytes_available.replace(Box::new(func));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DelegateFile {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(DelegateFileInner::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for DelegateFile {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner.seek.as_ref().map(|seek| seek(pos)).unwrap_or(Ok(0))
|
||||
}
|
||||
}
|
||||
impl Write for DelegateFile {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner
|
||||
.write
|
||||
.as_ref()
|
||||
.map(|write| write(buf))
|
||||
.unwrap_or(Ok(buf.len()))
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner.flush.as_ref().map(|flush| flush()).unwrap_or(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for DelegateFile {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner.read.as_ref().map(|read| read(buf)).unwrap_or(Ok(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualFile for DelegateFile {
|
||||
fn last_accessed(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn last_modified(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn created_time(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner.size.as_ref().map(|size| size()).unwrap_or(0)
|
||||
}
|
||||
fn set_len(&mut self, new_size: u64) -> wasmer_vfs::Result<()> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner
|
||||
.set_len
|
||||
.as_ref()
|
||||
.map(|set_len| set_len(new_size))
|
||||
.unwrap_or(Ok(()))
|
||||
}
|
||||
fn unlink(&mut self) -> wasmer_vfs::Result<()> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner
|
||||
.unlink
|
||||
.as_ref()
|
||||
.map(|unlink| unlink())
|
||||
.unwrap_or(Ok(()))
|
||||
}
|
||||
fn bytes_available(&self) -> wasmer_vfs::Result<usize> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
inner
|
||||
.bytes_available
|
||||
.as_ref()
|
||||
.map(|bytes_available| bytes_available())
|
||||
.unwrap_or(Ok(0))
|
||||
}
|
||||
fn get_fd(&self) -> Option<FileDescriptor> {
|
||||
None
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
mod arc_file;
|
||||
mod arc_fs;
|
||||
mod builder;
|
||||
mod delegate_file;
|
||||
mod empty_fs;
|
||||
mod null_file;
|
||||
mod passthru_fs;
|
||||
mod special_file;
|
||||
mod tmp_fs;
|
||||
mod tty_file;
|
||||
mod union_fs;
|
||||
mod zero_file;
|
||||
|
||||
pub use arc_file::*;
|
||||
pub use arc_fs::*;
|
||||
pub use builder::*;
|
||||
pub use delegate_file::*;
|
||||
pub use empty_fs::*;
|
||||
pub use null_file::*;
|
||||
pub use passthru_fs::*;
|
||||
pub use special_file::*;
|
||||
pub use tmp_fs::*;
|
||||
pub use tty_file::*;
|
||||
pub use union_fs::*;
|
||||
pub use zero_file::*;
|
@ -1,81 +0,0 @@
|
||||
use std::{
|
||||
io::{self, *},
|
||||
sync::Arc,
|
||||
};
|
||||
use wasmer_vbus::FileDescriptor;
|
||||
use wasmer_vfs::VirtualFile;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TtyFile {
|
||||
runtime: Arc<dyn crate::WasiRuntimeImplementation + Send + Sync + 'static>,
|
||||
stdin: Box<dyn VirtualFile + Send + Sync + 'static>,
|
||||
}
|
||||
|
||||
impl TtyFile {
|
||||
pub fn new(
|
||||
runtime: Arc<dyn crate::WasiRuntimeImplementation + Send + Sync + 'static>,
|
||||
stdin: Box<dyn VirtualFile + Send + Sync + 'static>,
|
||||
) -> Self {
|
||||
Self { runtime, stdin }
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for TtyFile {
|
||||
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
impl Write for TtyFile {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.runtime.stdout(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for TtyFile {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.stdin.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualFile for TtyFile {
|
||||
fn last_accessed(&self) -> u64 {
|
||||
self.stdin.last_accessed()
|
||||
}
|
||||
fn last_modified(&self) -> u64 {
|
||||
self.stdin.last_modified()
|
||||
}
|
||||
fn created_time(&self) -> u64 {
|
||||
self.stdin.created_time()
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn set_len(&mut self, _new_size: u64) -> wasmer_vfs::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn unlink(&mut self) -> wasmer_vfs::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn bytes_available(&self) -> wasmer_vfs::Result<usize> {
|
||||
self.stdin.bytes_available()
|
||||
}
|
||||
fn bytes_available_read(&self) -> wasmer_vfs::Result<usize> {
|
||||
self.stdin.bytes_available_read()
|
||||
}
|
||||
fn bytes_available_write(&self) -> wasmer_vfs::Result<usize> {
|
||||
self.stdin.bytes_available_write()
|
||||
}
|
||||
fn get_fd(&self) -> Option<FileDescriptor> {
|
||||
None
|
||||
}
|
||||
fn is_open(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn get_special_fd(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
}
|
@ -1,431 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused)]
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Add;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Weak;
|
||||
#[allow(unused_imports, dead_code)]
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
use wasmer_vfs::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MountPoint {
|
||||
pub path: String,
|
||||
pub name: String,
|
||||
pub fs: Option<Arc<Box<dyn FileSystem>>>,
|
||||
pub weak_fs: Weak<Box<dyn FileSystem>>,
|
||||
pub temp_holding: Arc<Mutex<Option<Arc<Box<dyn FileSystem>>>>>,
|
||||
pub should_sanitize: bool,
|
||||
pub new_path: Option<String>,
|
||||
}
|
||||
|
||||
impl Clone for MountPoint {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
path: self.path.clone(),
|
||||
name: self.name.clone(),
|
||||
fs: None,
|
||||
weak_fs: self.weak_fs.clone(),
|
||||
temp_holding: self.temp_holding.clone(),
|
||||
should_sanitize: self.should_sanitize,
|
||||
new_path: self.new_path.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MountPoint {
|
||||
pub fn fs(&self) -> Option<Arc<Box<dyn FileSystem>>> {
|
||||
match &self.fs {
|
||||
Some(a) => Some(a.clone()),
|
||||
None => self.weak_fs.upgrade(),
|
||||
}
|
||||
}
|
||||
|
||||
fn solidify(&mut self) {
|
||||
if self.fs.is_none() {
|
||||
self.fs = self.weak_fs.upgrade();
|
||||
}
|
||||
{
|
||||
let mut guard = self.temp_holding.lock().unwrap();
|
||||
let fs = guard.take();
|
||||
if self.fs.is_none() {
|
||||
self.fs = fs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn strong(&self) -> Option<StrongMountPoint> {
|
||||
match self.fs() {
|
||||
Some(fs) => Some(StrongMountPoint {
|
||||
path: self.path.clone(),
|
||||
name: self.name.clone(),
|
||||
fs,
|
||||
should_sanitize: self.should_sanitize,
|
||||
new_path: self.new_path.clone(),
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StrongMountPoint {
|
||||
pub path: String,
|
||||
pub name: String,
|
||||
pub fs: Arc<Box<dyn FileSystem>>,
|
||||
pub should_sanitize: bool,
|
||||
pub new_path: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnionFileSystem {
|
||||
pub mounts: Vec<MountPoint>,
|
||||
}
|
||||
|
||||
impl UnionFileSystem {
|
||||
pub fn new() -> UnionFileSystem {
|
||||
UnionFileSystem { mounts: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.mounts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl UnionFileSystem {
|
||||
pub fn mount(
|
||||
&mut self,
|
||||
name: &str,
|
||||
path: &str,
|
||||
should_sanitize: bool,
|
||||
fs: Box<dyn FileSystem>,
|
||||
new_path: Option<&str>,
|
||||
) {
|
||||
self.unmount(path);
|
||||
let mut path = path.to_string();
|
||||
if path.starts_with("/") == false {
|
||||
path.insert(0, '/');
|
||||
}
|
||||
if path.ends_with("/") == false {
|
||||
path += "/";
|
||||
}
|
||||
let new_path = new_path.map(|new_path| {
|
||||
let mut new_path = new_path.to_string();
|
||||
if new_path.ends_with("/") == false {
|
||||
new_path += "/";
|
||||
}
|
||||
new_path
|
||||
});
|
||||
let fs = Arc::new(fs);
|
||||
|
||||
let mount = MountPoint {
|
||||
path,
|
||||
name: name.to_string(),
|
||||
fs: None,
|
||||
weak_fs: Arc::downgrade(&fs),
|
||||
temp_holding: Arc::new(Mutex::new(Some(fs.clone()))),
|
||||
should_sanitize,
|
||||
new_path,
|
||||
};
|
||||
|
||||
self.mounts.push(mount);
|
||||
}
|
||||
|
||||
pub fn unmount(&mut self, path: &str) {
|
||||
let path1 = path.to_string();
|
||||
let mut path2 = path1.clone();
|
||||
if path2.starts_with("/") == false {
|
||||
path2.insert(0, '/');
|
||||
}
|
||||
let mut path3 = path2.clone();
|
||||
if path3.ends_with("/") == false {
|
||||
path3.push_str("/")
|
||||
}
|
||||
if path2.ends_with("/") {
|
||||
path2 = (&path2[..(path2.len() - 1)]).to_string();
|
||||
}
|
||||
|
||||
self.mounts
|
||||
.retain(|mount| mount.path != path2 && mount.path != path3);
|
||||
}
|
||||
|
||||
fn read_dir_internal(&self, path: &Path) -> Result<ReadDir> {
|
||||
let path = path.to_string_lossy();
|
||||
|
||||
let mut ret = None;
|
||||
for (path, mount) in filter_mounts(&self.mounts, path.as_ref()) {
|
||||
match mount.fs.read_dir(Path::new(path.as_str())) {
|
||||
Ok(dir) => {
|
||||
if ret.is_none() {
|
||||
ret = Some(Vec::new());
|
||||
}
|
||||
let ret = ret.as_mut().unwrap();
|
||||
for sub in dir {
|
||||
if let Ok(sub) = sub {
|
||||
ret.push(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
debug!("failed to read dir - {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match ret {
|
||||
Some(ret) => Ok(ReadDir::new(ret)),
|
||||
None => Err(FsError::EntryNotFound),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sanitize(mut self) -> Self {
|
||||
self.solidify();
|
||||
self.mounts.retain(|mount| mount.should_sanitize == false);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn solidify(&mut self) {
|
||||
for mount in self.mounts.iter_mut() {
|
||||
mount.solidify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystem for UnionFileSystem {
|
||||
fn read_dir(&self, path: &Path) -> Result<ReadDir> {
|
||||
debug!("read_dir: path={}", path.display());
|
||||
self.read_dir_internal(path)
|
||||
}
|
||||
fn create_dir(&self, path: &Path) -> Result<()> {
|
||||
debug!("create_dir: path={}", path.display());
|
||||
|
||||
if self.read_dir_internal(path).is_ok() {
|
||||
//return Err(FsError::AlreadyExists);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let path = path.to_string_lossy();
|
||||
let mut ret_error = FsError::EntryNotFound;
|
||||
for (path, mount) in filter_mounts(&self.mounts, path.as_ref()) {
|
||||
match mount.fs.create_dir(Path::new(path.as_str())) {
|
||||
Ok(ret) => {
|
||||
return Ok(ret);
|
||||
}
|
||||
Err(err) => {
|
||||
ret_error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ret_error)
|
||||
}
|
||||
fn remove_dir(&self, path: &Path) -> Result<()> {
|
||||
debug!("remove_dir: path={}", path.display());
|
||||
let mut ret_error = FsError::EntryNotFound;
|
||||
let path = path.to_string_lossy();
|
||||
for (path, mount) in filter_mounts(&self.mounts, path.as_ref()) {
|
||||
match mount.fs.remove_dir(Path::new(path.as_str())) {
|
||||
Ok(ret) => {
|
||||
return Ok(ret);
|
||||
}
|
||||
Err(err) => {
|
||||
ret_error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ret_error)
|
||||
}
|
||||
fn rename(&self, from: &Path, to: &Path) -> Result<()> {
|
||||
debug!("rename: from={} to={}", from.display(), to.display());
|
||||
let mut ret_error = FsError::EntryNotFound;
|
||||
let from = from.to_string_lossy();
|
||||
let to = to.to_string_lossy();
|
||||
for (path, mount) in filter_mounts(&self.mounts, from.as_ref()) {
|
||||
let mut to = if to.starts_with(mount.path.as_str()) {
|
||||
(&to[mount.path.len()..]).to_string()
|
||||
} else {
|
||||
ret_error = FsError::UnknownError;
|
||||
continue;
|
||||
};
|
||||
if to.starts_with("/") == false {
|
||||
to = format!("/{}", to);
|
||||
}
|
||||
match mount
|
||||
.fs
|
||||
.rename(Path::new(from.as_ref()), Path::new(to.as_str()))
|
||||
{
|
||||
Ok(ret) => {
|
||||
trace!("rename ok");
|
||||
return Ok(ret);
|
||||
}
|
||||
Err(err) => {
|
||||
trace!("rename error (from={}, to={}) - {}", from, to, err);
|
||||
ret_error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("rename failed - {}", ret_error);
|
||||
Err(ret_error)
|
||||
}
|
||||
fn metadata(&self, path: &Path) -> Result<Metadata> {
|
||||
debug!("metadata: path={}", path.display());
|
||||
let mut ret_error = FsError::EntryNotFound;
|
||||
let path = path.to_string_lossy();
|
||||
for (path, mount) in filter_mounts(&self.mounts, path.as_ref()) {
|
||||
match mount.fs.metadata(Path::new(path.as_str())) {
|
||||
Ok(ret) => {
|
||||
return Ok(ret);
|
||||
}
|
||||
Err(err) => {
|
||||
// This fixes a bug when attempting to create the directory /usr when it does not exist
|
||||
// on the x86 version of memfs
|
||||
// TODO: patch wasmer_vfs and remove
|
||||
if let FsError::NotAFile = &err {
|
||||
ret_error = FsError::EntryNotFound;
|
||||
} else {
|
||||
debug!("metadata failed: (path={}) - {}", path, err);
|
||||
ret_error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ret_error)
|
||||
}
|
||||
fn symlink_metadata(&self, path: &Path) -> Result<Metadata> {
|
||||
debug!("symlink_metadata: path={}", path.display());
|
||||
let mut ret_error = FsError::EntryNotFound;
|
||||
let path = path.to_string_lossy();
|
||||
for (path_inner, mount) in filter_mounts(&self.mounts, path.as_ref()) {
|
||||
match mount.fs.symlink_metadata(Path::new(path_inner.as_str())) {
|
||||
Ok(ret) => {
|
||||
return Ok(ret);
|
||||
}
|
||||
Err(err) => {
|
||||
// This fixes a bug when attempting to create the directory /usr when it does not exist
|
||||
// on the x86 version of memfs
|
||||
// TODO: patch wasmer_vfs and remove
|
||||
if let FsError::NotAFile = &err {
|
||||
ret_error = FsError::EntryNotFound;
|
||||
} else {
|
||||
debug!("metadata failed: (path={}) - {}", path, err);
|
||||
ret_error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("symlink_metadata: failed={}", ret_error);
|
||||
Err(ret_error)
|
||||
}
|
||||
fn remove_file(&self, path: &Path) -> Result<()> {
|
||||
debug!("remove_file: path={}", path.display());
|
||||
let mut ret_error = FsError::EntryNotFound;
|
||||
let path = path.to_string_lossy();
|
||||
for (path, mount) in filter_mounts(&self.mounts, path.as_ref()) {
|
||||
match mount.fs.remove_file(Path::new(path.as_str())) {
|
||||
Ok(ret) => {
|
||||
return Ok(ret);
|
||||
}
|
||||
Err(err) => {
|
||||
ret_error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ret_error)
|
||||
}
|
||||
fn new_open_options(&self) -> OpenOptions {
|
||||
let opener = Box::new(UnionFileOpener {
|
||||
mounts: self.mounts.clone(),
|
||||
});
|
||||
OpenOptions::new(opener)
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_mounts(
|
||||
mounts: &Vec<MountPoint>,
|
||||
mut target: &str,
|
||||
) -> impl Iterator<Item = (String, StrongMountPoint)> {
|
||||
let mut biggest_path = 0usize;
|
||||
let mut ret = Vec::new();
|
||||
for mount in mounts.iter().rev() {
|
||||
let mut test_mount_path1 = mount.path.clone();
|
||||
if test_mount_path1.ends_with("/") == false {
|
||||
test_mount_path1.push_str("/");
|
||||
}
|
||||
|
||||
let mut test_mount_path2 = mount.path.clone();
|
||||
if test_mount_path2.ends_with("/") == true {
|
||||
test_mount_path2 = test_mount_path2[..(test_mount_path2.len() - 1)].to_string();
|
||||
}
|
||||
|
||||
if target == test_mount_path1 || target == test_mount_path2 {
|
||||
if let Some(mount) = mount.strong() {
|
||||
biggest_path = biggest_path.max(mount.path.len());
|
||||
let mut path = "/".to_string();
|
||||
if let Some(ref np) = mount.new_path {
|
||||
path = np.to_string();
|
||||
}
|
||||
ret.push((path, mount));
|
||||
}
|
||||
} else if target.starts_with(test_mount_path1.as_str()) {
|
||||
if let Some(mount) = mount.strong() {
|
||||
biggest_path = biggest_path.max(mount.path.len());
|
||||
let path = &target[test_mount_path2.len()..];
|
||||
let mut path = path.to_string();
|
||||
if let Some(ref np) = mount.new_path {
|
||||
path = format!("{}{}", np, &path[1..]);
|
||||
}
|
||||
ret.push((path, mount));
|
||||
}
|
||||
}
|
||||
}
|
||||
ret.retain(|(a, b)| b.path.len() >= biggest_path);
|
||||
ret.into_iter()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnionFileOpener {
|
||||
mounts: Vec<MountPoint>,
|
||||
}
|
||||
|
||||
impl FileOpener for UnionFileOpener {
|
||||
fn open(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
conf: &OpenOptionsConfig,
|
||||
) -> Result<Box<dyn VirtualFile + Send + Sync>> {
|
||||
debug!("open: path={}", path.display());
|
||||
let mut ret_err = FsError::EntryNotFound;
|
||||
let path = path.to_string_lossy();
|
||||
if conf.create() || conf.create_new() {
|
||||
for (path, mount) in filter_mounts(&self.mounts, path.as_ref()) {
|
||||
if let Ok(mut ret) = mount
|
||||
.fs
|
||||
.new_open_options()
|
||||
.truncate(conf.truncate())
|
||||
.append(conf.append())
|
||||
.read(conf.read())
|
||||
.write(conf.write())
|
||||
.open(path)
|
||||
{
|
||||
if conf.create_new() {
|
||||
ret.unlink();
|
||||
continue;
|
||||
}
|
||||
return Ok(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (path, mount) in filter_mounts(&self.mounts, path.as_ref()) {
|
||||
match mount.fs.new_open_options().options(conf.clone()).open(path) {
|
||||
Ok(ret) => return Ok(ret),
|
||||
Err(err) if ret_err == FsError::EntryNotFound => {
|
||||
ret_err = err;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Err(ret_err)
|
||||
}
|
||||
}
|
@ -44,12 +44,12 @@ mod macros;
|
||||
pub mod bin_factory;
|
||||
#[cfg(feature = "os")]
|
||||
pub mod builtins;
|
||||
pub mod fs;
|
||||
#[cfg(feature = "os")]
|
||||
pub mod os;
|
||||
pub mod runtime;
|
||||
mod state;
|
||||
mod syscalls;
|
||||
mod tty_file;
|
||||
mod utils;
|
||||
#[cfg(feature = "os")]
|
||||
pub mod wapm;
|
||||
@ -71,6 +71,7 @@ pub use crate::state::{
|
||||
ALL_RIGHTS, VIRTUAL_ROOT_FD,
|
||||
};
|
||||
pub use crate::syscalls::types;
|
||||
pub use crate::tty_file::TtyFile;
|
||||
#[cfg(feature = "wasix")]
|
||||
pub use crate::utils::is_wasix_module;
|
||||
pub use crate::utils::{get_wasi_version, get_wasi_versions, is_wasi_module, WasiVersion};
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#[cfg(feature = "os")]
|
||||
use crate::bin_factory::CachedCompiledModules;
|
||||
use crate::fs::{ArcFile, TmpFileSystem};
|
||||
use crate::state::{WasiFs, WasiFsRoot, WasiState};
|
||||
use crate::syscalls::types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO};
|
||||
use crate::{
|
||||
@ -17,7 +16,7 @@ use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
use thiserror::Error;
|
||||
use wasmer::AsStoreMut;
|
||||
use wasmer_vfs::{FsError, VirtualFile};
|
||||
use wasmer_vfs::{ArcFile, FsError, TmpFileSystem, VirtualFile};
|
||||
|
||||
/// Creates an empty [`WasiStateBuilder`].
|
||||
///
|
||||
|
@ -371,7 +371,7 @@ impl WasiInodes {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WasiFsRoot {
|
||||
Sandbox(Arc<crate::fs::TmpFileSystem>),
|
||||
Sandbox(Arc<wasmer_vfs::tmp_fs::TmpFileSystem>),
|
||||
Backing(Arc<Box<dyn FileSystem>>),
|
||||
}
|
||||
|
||||
@ -505,7 +505,7 @@ pub fn default_fs_backing() -> Box<dyn wasmer_vfs::FileSystem + Send + Sync> {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "host-fs")] {
|
||||
Box::new(wasmer_vfs::host_fs::FileSystem::default())
|
||||
} else if #[cfg(feature = "mem-fs")] {
|
||||
} else if #[cfg(not(feature = "host-fs"))] {
|
||||
Box::new(wasmer_vfs::mem_fs::FileSystem::default())
|
||||
} else {
|
||||
Box::new(FallbackFileSystem::default())
|
||||
|
@ -11,7 +11,7 @@ use wasmer_wasi_types::wasi::{BusErrno, Errno, Rights};
|
||||
pub use crate::{fs::NullFile as Stderr, fs::NullFile as Stdin, fs::NullFile as Stdout};
|
||||
#[cfg(feature = "host-fs")]
|
||||
pub use wasmer_vfs::host_fs::{Stderr, Stdin, Stdout};
|
||||
#[cfg(all(feature = "mem-fs", not(feature = "host-fs")))]
|
||||
#[cfg(not(feature = "host-fs"))]
|
||||
pub use wasmer_vfs::mem_fs::{Stderr, Stdin, Stdout};
|
||||
|
||||
use wasmer_vfs::{FsError, VirtualFile};
|
||||
|
166
lib/wasi/src/tty_file.rs
Normal file
166
lib/wasi/src/tty_file.rs
Normal file
@ -0,0 +1,166 @@
|
||||
use std::{
|
||||
io::{self, *},
|
||||
sync::Arc,
|
||||
};
|
||||
use wasmer_vfs::FileDescriptor;
|
||||
use wasmer_vfs::VirtualFile;
|
||||
|
||||
/// Special file for `/dev/tty` that can print to stdout
|
||||
/// (hence the requirement for a `WasiRuntimeImplementation`)
|
||||
#[derive(Debug)]
|
||||
pub struct TtyFile {
|
||||
runtime: Arc<dyn crate::WasiRuntimeImplementation + Send + Sync + 'static>,
|
||||
stdin: Box<dyn VirtualFile + Send + Sync + 'static>,
|
||||
}
|
||||
|
||||
impl TtyFile {
|
||||
pub fn new(
|
||||
runtime: Arc<dyn crate::WasiRuntimeImplementation + Send + Sync + 'static>,
|
||||
stdin: Box<dyn VirtualFile + Send + Sync + 'static>,
|
||||
) -> Self {
|
||||
Self { runtime, stdin }
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for TtyFile {
|
||||
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
impl Write for TtyFile {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.runtime.stdout(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for TtyFile {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.stdin.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualFile for TtyFile {
|
||||
fn last_accessed(&self) -> u64 {
|
||||
self.stdin.last_accessed()
|
||||
}
|
||||
fn last_modified(&self) -> u64 {
|
||||
self.stdin.last_modified()
|
||||
}
|
||||
fn created_time(&self) -> u64 {
|
||||
self.stdin.created_time()
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn set_len(&mut self, _new_size: u64) -> wasmer_vfs::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn unlink(&mut self) -> wasmer_vfs::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn bytes_available(&self) -> wasmer_vfs::Result<usize> {
|
||||
self.stdin.bytes_available()
|
||||
}
|
||||
fn bytes_available_read(&self) -> wasmer_vfs::Result<usize> {
|
||||
self.stdin.bytes_available_read()
|
||||
}
|
||||
fn bytes_available_write(&self) -> wasmer_vfs::Result<usize> {
|
||||
self.stdin.bytes_available_write()
|
||||
}
|
||||
fn get_fd(&self) -> Option<FileDescriptor> {
|
||||
None
|
||||
}
|
||||
fn is_open(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn get_special_fd(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::{VirtualNetworking, WasiRuntimeImplementation, WasiThreadId};
|
||||
use std::ops::Deref;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc, Mutex,
|
||||
};
|
||||
use wasmer_vbus::{UnsupportedVirtualBus, VirtualBus};
|
||||
|
||||
struct FakeRuntimeImplementation {
|
||||
pub data: Arc<Mutex<Vec<u8>>>,
|
||||
pub bus: Box<dyn VirtualBus + Sync>,
|
||||
pub networking: Box<dyn VirtualNetworking + Sync>,
|
||||
pub thread_id_seed: AtomicU32,
|
||||
}
|
||||
|
||||
impl Default for FakeRuntimeImplementation {
|
||||
fn default() -> Self {
|
||||
FakeRuntimeImplementation {
|
||||
data: Arc::new(Mutex::new(Vec::new())),
|
||||
#[cfg(not(feature = "host-vnet"))]
|
||||
networking: Box::new(wasmer_vnet::UnsupportedVirtualNetworking::default()),
|
||||
#[cfg(feature = "host-vnet")]
|
||||
networking: Box::new(wasmer_wasi_local_networking::LocalNetworking::default()),
|
||||
bus: Box::new(UnsupportedVirtualBus::default()),
|
||||
thread_id_seed: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FakeRuntimeImplementation {
|
||||
fn get_stdout_written(&self) -> Option<Vec<u8>> {
|
||||
let s = self.data.try_lock().ok()?;
|
||||
Some(s.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for FakeRuntimeImplementation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "FakeRuntimeImplementation")
|
||||
}
|
||||
}
|
||||
|
||||
impl WasiRuntimeImplementation for FakeRuntimeImplementation {
|
||||
fn bus(&self) -> &(dyn VirtualBus) {
|
||||
self.bus.deref()
|
||||
}
|
||||
|
||||
fn networking(&self) -> &(dyn VirtualNetworking) {
|
||||
self.networking.deref()
|
||||
}
|
||||
|
||||
fn thread_generate_id(&self) -> WasiThreadId {
|
||||
self.thread_id_seed.fetch_add(1, Ordering::Relaxed).into()
|
||||
}
|
||||
|
||||
fn stdout(&self, data: &[u8]) -> std::io::Result<()> {
|
||||
if let Ok(mut s) = self.data.try_lock() {
|
||||
s.extend_from_slice(data);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tty_file() {
|
||||
use crate::state::WasiBidirectionalPipePair;
|
||||
use crate::tty_file::TtyFile;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
|
||||
let mut pair = WasiBidirectionalPipePair::new();
|
||||
pair.set_blocking(false);
|
||||
|
||||
let rt = Arc::new(FakeRuntimeImplementation::default());
|
||||
let mut tty_file = TtyFile::new(rt.clone(), Box::new(pair));
|
||||
tty_file.write(b"hello");
|
||||
assert_eq!(rt.get_stdout_written().unwrap(), b"hello".to_vec());
|
||||
}
|
||||
}
|
@ -111,6 +111,7 @@ wasitests::nightly_2022_10_18::host_fs::poll_oneoff
|
||||
wasitests::nightly_2022_10_18::host_fs::readlink
|
||||
wasitests::nightly_2022_10_18::host_fs::unix_open_special_files
|
||||
wasitests::nightly_2022_10_18::host_fs::writing
|
||||
|
||||
wasitests::nightly_2022_10_18::mem_fs::close_preopen_fd
|
||||
wasitests::nightly_2022_10_18::mem_fs::create_dir
|
||||
wasitests::nightly_2022_10_18::mem_fs::envvar
|
||||
@ -123,9 +124,187 @@ wasitests::nightly_2022_10_18::mem_fs::readlink
|
||||
wasitests::nightly_2022_10_18::mem_fs::unix_open_special_files
|
||||
wasitests::nightly_2022_10_18::mem_fs::writing
|
||||
|
||||
# These tests are failing in CI for some reason, but didn't fail on older compiler versions
|
||||
wasitests::nightly_2022_10_18::host_fs::path_symlink
|
||||
wasitests::nightly_2022_10_18::mem_fs::path_symlink
|
||||
wasitests::snapshot1::tmp_fs::close_preopen_fd
|
||||
wasitests::snapshot1::tmp_fs::create_dir
|
||||
wasitests::snapshot1::tmp_fs::envvar
|
||||
wasitests::snapshot1::tmp_fs::fd_allocate
|
||||
wasitests::snapshot1::tmp_fs::fd_close
|
||||
wasitests::snapshot1::tmp_fs::fd_pread
|
||||
wasitests::snapshot1::tmp_fs::fd_read
|
||||
wasitests::snapshot1::tmp_fs::poll_oneoff
|
||||
wasitests::snapshot1::tmp_fs::readlink
|
||||
wasitests::snapshot1::tmp_fs::unix_open_special_files
|
||||
wasitests::snapshot1::tmp_fs::writing
|
||||
wasitests::unstable::tmp_fs::close_preopen_fd
|
||||
wasitests::unstable::tmp_fs::create_dir
|
||||
wasitests::unstable::tmp_fs::envvar
|
||||
wasitests::unstable::tmp_fs::fd_allocate
|
||||
wasitests::unstable::tmp_fs::fd_close
|
||||
wasitests::unstable::tmp_fs::fd_pread
|
||||
wasitests::unstable::tmp_fs::fd_read
|
||||
wasitests::unstable::tmp_fs::poll_oneoff
|
||||
wasitests::unstable::tmp_fs::readlink
|
||||
wasitests::unstable::tmp_fs::unix_open_special_files
|
||||
wasitests::unstable::tmp_fs::writing
|
||||
wasitests::nightly_2022_10_18::tmp_fs::close_preopen_fd
|
||||
wasitests::nightly_2022_10_18::tmp_fs::create_dir
|
||||
wasitests::nightly_2022_10_18::tmp_fs::envvar
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_allocate
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_close
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_pread
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_read
|
||||
wasitests::nightly_2022_10_18::tmp_fs::poll_oneoff
|
||||
wasitests::nightly_2022_10_18::tmp_fs::readlink
|
||||
wasitests::nightly_2022_10_18::tmp_fs::unix_open_special_files
|
||||
wasitests::nightly_2022_10_18::tmp_fs::writing
|
||||
|
||||
wasitests::snapshot1::passthru_fs::close_preopen_fd
|
||||
wasitests::snapshot1::passthru_fs::create_dir
|
||||
wasitests::snapshot1::passthru_fs::envvar
|
||||
wasitests::snapshot1::passthru_fs::fd_allocate
|
||||
wasitests::snapshot1::passthru_fs::fd_close
|
||||
wasitests::snapshot1::passthru_fs::fd_pread
|
||||
wasitests::snapshot1::passthru_fs::fd_read
|
||||
wasitests::snapshot1::passthru_fs::poll_oneoff
|
||||
wasitests::snapshot1::passthru_fs::readlink
|
||||
wasitests::snapshot1::passthru_fs::unix_open_special_files
|
||||
wasitests::snapshot1::passthru_fs::writing
|
||||
wasitests::unstable::passthru_fs::close_preopen_fd
|
||||
wasitests::unstable::passthru_fs::create_dir
|
||||
wasitests::unstable::passthru_fs::envvar
|
||||
wasitests::unstable::passthru_fs::fd_allocate
|
||||
wasitests::unstable::passthru_fs::fd_close
|
||||
wasitests::unstable::passthru_fs::fd_pread
|
||||
wasitests::unstable::passthru_fs::fd_read
|
||||
wasitests::unstable::passthru_fs::poll_oneoff
|
||||
wasitests::unstable::passthru_fs::readlink
|
||||
wasitests::unstable::passthru_fs::unix_open_special_files
|
||||
wasitests::unstable::passthru_fs::writing
|
||||
wasitests::nightly_2022_10_18::passthru_fs::close_preopen_fd
|
||||
wasitests::nightly_2022_10_18::passthru_fs::create_dir
|
||||
wasitests::nightly_2022_10_18::passthru_fs::envvar
|
||||
wasitests::nightly_2022_10_18::passthru_fs::fd_allocate
|
||||
wasitests::nightly_2022_10_18::passthru_fs::fd_close
|
||||
wasitests::nightly_2022_10_18::passthru_fs::fd_pread
|
||||
wasitests::nightly_2022_10_18::passthru_fs::fd_read
|
||||
wasitests::nightly_2022_10_18::passthru_fs::poll_oneoff
|
||||
wasitests::nightly_2022_10_18::passthru_fs::readlink
|
||||
wasitests::nightly_2022_10_18::passthru_fs::unix_open_special_files
|
||||
wasitests::nightly_2022_10_18::passthru_fs::writing
|
||||
|
||||
wasitests::snapshot1::tmp_fs::close_preopen_fd
|
||||
wasitests::snapshot1::tmp_fs::create_dir
|
||||
wasitests::snapshot1::tmp_fs::envvar
|
||||
wasitests::snapshot1::tmp_fs::fd_allocate
|
||||
wasitests::snapshot1::tmp_fs::fd_close
|
||||
wasitests::snapshot1::tmp_fs::fd_pread
|
||||
wasitests::snapshot1::tmp_fs::fd_read
|
||||
wasitests::snapshot1::tmp_fs::poll_oneoff
|
||||
wasitests::snapshot1::tmp_fs::readlink
|
||||
wasitests::snapshot1::tmp_fs::unix_open_special_files
|
||||
wasitests::snapshot1::tmp_fs::writing
|
||||
wasitests::unstable::tmp_fs::close_preopen_fd
|
||||
wasitests::unstable::tmp_fs::create_dir
|
||||
wasitests::unstable::tmp_fs::envvar
|
||||
wasitests::unstable::tmp_fs::fd_allocate
|
||||
wasitests::unstable::tmp_fs::fd_close
|
||||
wasitests::unstable::tmp_fs::fd_pread
|
||||
wasitests::unstable::tmp_fs::fd_read
|
||||
wasitests::unstable::tmp_fs::poll_oneoff
|
||||
wasitests::unstable::tmp_fs::readlink
|
||||
wasitests::unstable::tmp_fs::unix_open_special_files
|
||||
wasitests::unstable::tmp_fs::writing
|
||||
wasitests::nightly_2022_10_18::tmp_fs::close_preopen_fd
|
||||
wasitests::nightly_2022_10_18::tmp_fs::create_dir
|
||||
wasitests::nightly_2022_10_18::tmp_fs::envvar
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_allocate
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_close
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_pread
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_read
|
||||
wasitests::nightly_2022_10_18::tmp_fs::poll_oneoff
|
||||
wasitests::nightly_2022_10_18::tmp_fs::readlink
|
||||
wasitests::nightly_2022_10_18::tmp_fs::unix_open_special_files
|
||||
wasitests::nightly_2022_10_18::tmp_fs::writing
|
||||
|
||||
wasitests::snapshot1::union_fs::close_preopen_fd
|
||||
wasitests::snapshot1::union_fs::create_dir
|
||||
wasitests::snapshot1::union_fs::envvar
|
||||
wasitests::snapshot1::union_fs::fd_allocate
|
||||
wasitests::snapshot1::union_fs::fd_close
|
||||
wasitests::snapshot1::union_fs::fd_pread
|
||||
wasitests::snapshot1::union_fs::fd_read
|
||||
wasitests::snapshot1::union_fs::poll_oneoff
|
||||
wasitests::snapshot1::union_fs::readlink
|
||||
wasitests::snapshot1::union_fs::unix_open_special_files
|
||||
wasitests::snapshot1::union_fs::writing
|
||||
wasitests::unstable::union_fs::close_preopen_fd
|
||||
wasitests::unstable::union_fs::create_dir
|
||||
wasitests::unstable::union_fs::envvar
|
||||
wasitests::unstable::union_fs::fd_allocate
|
||||
wasitests::unstable::union_fs::fd_close
|
||||
wasitests::unstable::union_fs::fd_pread
|
||||
wasitests::unstable::union_fs::fd_read
|
||||
wasitests::unstable::union_fs::poll_oneoff
|
||||
wasitests::unstable::union_fs::readlink
|
||||
wasitests::unstable::union_fs::unix_open_special_files
|
||||
wasitests::unstable::union_fs::writing
|
||||
wasitests::nightly_2022_10_18::union_fs::close_preopen_fd
|
||||
wasitests::nightly_2022_10_18::union_fs::create_dir
|
||||
wasitests::nightly_2022_10_18::union_fs::envvar
|
||||
wasitests::nightly_2022_10_18::union_fs::fd_allocate
|
||||
wasitests::nightly_2022_10_18::union_fs::fd_close
|
||||
wasitests::nightly_2022_10_18::union_fs::fd_pread
|
||||
wasitests::nightly_2022_10_18::union_fs::fd_read
|
||||
wasitests::nightly_2022_10_18::union_fs::poll_oneoff
|
||||
wasitests::nightly_2022_10_18::union_fs::readlink
|
||||
wasitests::nightly_2022_10_18::union_fs::unix_open_special_files
|
||||
wasitests::nightly_2022_10_18::union_fs::writing
|
||||
|
||||
wasitests::snapshot1::root_fs::close_preopen_fd
|
||||
wasitests::snapshot1::root_fs::create_dir
|
||||
wasitests::snapshot1::root_fs::envvar
|
||||
wasitests::snapshot1::root_fs::fd_allocate
|
||||
wasitests::snapshot1::root_fs::fd_close
|
||||
wasitests::snapshot1::root_fs::fd_pread
|
||||
wasitests::snapshot1::root_fs::fd_read
|
||||
wasitests::snapshot1::root_fs::poll_oneoff
|
||||
wasitests::snapshot1::root_fs::readlink
|
||||
wasitests::snapshot1::root_fs::unix_open_special_files
|
||||
wasitests::snapshot1::root_fs::writing
|
||||
wasitests::unstable::root_fs::close_preopen_fd
|
||||
wasitests::unstable::root_fs::create_dir
|
||||
wasitests::unstable::root_fs::envvar
|
||||
wasitests::unstable::root_fs::fd_allocate
|
||||
wasitests::unstable::root_fs::fd_close
|
||||
wasitests::unstable::root_fs::fd_pread
|
||||
wasitests::unstable::root_fs::fd_read
|
||||
wasitests::unstable::root_fs::poll_oneoff
|
||||
wasitests::unstable::root_fs::readlink
|
||||
wasitests::unstable::root_fs::unix_open_special_files
|
||||
wasitests::unstable::root_fs::writing
|
||||
wasitests::nightly_2022_10_18::root_fs::close_preopen_fd
|
||||
wasitests::nightly_2022_10_18::root_fs::create_dir
|
||||
wasitests::nightly_2022_10_18::root_fs::envvar
|
||||
wasitests::nightly_2022_10_18::root_fs::fd_allocate
|
||||
wasitests::nightly_2022_10_18::root_fs::fd_close
|
||||
wasitests::nightly_2022_10_18::root_fs::fd_pread
|
||||
wasitests::nightly_2022_10_18::root_fs::fd_read
|
||||
wasitests::nightly_2022_10_18::root_fs::poll_oneoff
|
||||
wasitests::nightly_2022_10_18::root_fs::readlink
|
||||
wasitests::nightly_2022_10_18::root_fs::unix_open_special_files
|
||||
wasitests::nightly_2022_10_18::root_fs::writing
|
||||
|
||||
# These tests are failing in CI for some reason, but didn't fail on older compiler versions
|
||||
wasitests::nightly_2022_10_18::passthru_fs::path_symlink
|
||||
wasitests::nightly_2022_10_18::root_fs::path_symlink
|
||||
wasitests::nightly_2022_10_18::tmp_fs::path_symlink
|
||||
wasitests::nightly_2022_10_18::union_fs::path_symlink
|
||||
wasitests::nightly_2022_10_18::mem_fs::path_symlink
|
||||
wasitests::nightly_2022_10_18::host_fs::path_symlink
|
||||
|
||||
wasitests::nightly_2022_10_18::mem_fs::fd_append
|
||||
wasitests::nightly_2022_10_18::host_fs::fd_append
|
||||
wasitests::nightly_2022_10_18::mem_fs::fd_append
|
||||
wasitests::nightly_2022_10_18::passthru_fs::fd_append
|
||||
wasitests::nightly_2022_10_18::root_fs::fd_append
|
||||
wasitests::nightly_2022_10_18::tmp_fs::fd_append
|
||||
wasitests::nightly_2022_10_18::union_fs::fd_append
|
@ -4,7 +4,9 @@ use std::io::{self, Read, Seek, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use wasmer::{FunctionEnv, Imports, Instance, Module, Store};
|
||||
use wasmer_vfs::{host_fs, mem_fs, FileSystem};
|
||||
use wasmer_vfs::{
|
||||
host_fs, mem_fs, passthru_fs, tmp_fs, union_fs, FileSystem, RootFileSystemBuilder,
|
||||
};
|
||||
use wasmer_wasi::types::wasi::{Filesize, Timestamp};
|
||||
use wasmer_wasi::{
|
||||
generate_import_object_from_env, get_wasi_version, FsError, VirtualFile,
|
||||
@ -20,6 +22,18 @@ pub enum WasiFileSystemKind {
|
||||
|
||||
/// Instruct the test runner to use `wasmer_vfs::mem_fs`.
|
||||
InMemory,
|
||||
|
||||
/// Instruct the test runner to use `wasmer_vfs::tmp_fs`
|
||||
Tmp,
|
||||
|
||||
/// Instruct the test runner to use `wasmer_vfs::passtru_fs`
|
||||
PassthruMemory,
|
||||
|
||||
/// Instruct the test runner to use `wasmer_vfs::union_fs<host_fs, mem_fs>`
|
||||
UnionHostMemory,
|
||||
|
||||
/// Instruct the test runner to use the TempFs returned by `wasmer_vfs::builder::RootFileSystemBuilder`
|
||||
RootFileSystemBuilder,
|
||||
}
|
||||
|
||||
/// Crate holding metadata parsed from the WASI WAST about the test to be run.
|
||||
@ -177,13 +191,47 @@ impl<'a> WasiTest<'a> {
|
||||
builder.set_fs(Box::new(fs));
|
||||
}
|
||||
|
||||
WasiFileSystemKind::InMemory => {
|
||||
let fs = mem_fs::FileSystem::default();
|
||||
other => {
|
||||
let fs: Box<dyn FileSystem> = match other {
|
||||
WasiFileSystemKind::InMemory => Box::new(mem_fs::FileSystem::default()),
|
||||
WasiFileSystemKind::Tmp => Box::new(tmp_fs::TmpFileSystem::default()),
|
||||
WasiFileSystemKind::PassthruMemory => {
|
||||
Box::new(passthru_fs::PassthruFileSystem::new(Box::new(
|
||||
mem_fs::FileSystem::default(),
|
||||
)))
|
||||
}
|
||||
WasiFileSystemKind::RootFileSystemBuilder => {
|
||||
Box::new(RootFileSystemBuilder::new().build())
|
||||
}
|
||||
WasiFileSystemKind::UnionHostMemory => {
|
||||
let a = mem_fs::FileSystem::default();
|
||||
let b = mem_fs::FileSystem::default();
|
||||
let c = mem_fs::FileSystem::default();
|
||||
let d = mem_fs::FileSystem::default();
|
||||
let e = mem_fs::FileSystem::default();
|
||||
let f = mem_fs::FileSystem::default();
|
||||
|
||||
let mut union = union_fs::UnionFileSystem::new();
|
||||
|
||||
union.mount("mem_fs", "/test_fs", false, Box::new(a), None);
|
||||
union.mount("mem_fs_2", "/snapshot1", false, Box::new(b), None);
|
||||
union.mount("mem_fs_3", "/tests", false, Box::new(c), None);
|
||||
union.mount("mem_fs_4", "/nightly_2022_10_18", false, Box::new(d), None);
|
||||
union.mount("mem_fs_5", "/unstable", false, Box::new(e), None);
|
||||
union.mount("mem_fs_6", "/.tmp_wasmer_wast_0", false, Box::new(f), None);
|
||||
|
||||
Box::new(union)
|
||||
}
|
||||
_ => {
|
||||
panic!("unexpected filesystem type {:?}", other);
|
||||
}
|
||||
};
|
||||
|
||||
let mut temp_dir_index: usize = 0;
|
||||
|
||||
let root = PathBuf::from("/");
|
||||
|
||||
map_host_fs_to_mem_fs(&fs, read_dir(BASE_TEST_DIR)?, &root)?;
|
||||
map_host_fs_to_mem_fs(&*fs, read_dir(BASE_TEST_DIR)?, &root)?;
|
||||
|
||||
for (alias, real_dir) in &self.mapped_dirs {
|
||||
let mut path = root.clone();
|
||||
@ -206,7 +254,7 @@ impl<'a> WasiTest<'a> {
|
||||
temp_dir_index += 1;
|
||||
}
|
||||
|
||||
builder.set_fs(Box::new(fs));
|
||||
builder.set_fs(fs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -640,7 +688,7 @@ impl VirtualFile for OutputCapturerer {
|
||||
/// because the host filesystem cannot be used. Instead, we are
|
||||
/// copying `BASE_TEST_DIR` to the `mem_fs`.
|
||||
fn map_host_fs_to_mem_fs(
|
||||
fs: &mem_fs::FileSystem,
|
||||
fs: &dyn FileSystem,
|
||||
directory_reader: ReadDir,
|
||||
path_prefix: &Path,
|
||||
) -> anyhow::Result<()> {
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user