From a623ee06f3a87df1362c3a66eda94ce34cc248b2 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Tue, 11 Jun 2024 03:04:05 +0200 Subject: [PATCH] chore(virtual-fs)!: Remove old stale WebcFileSystem The old WebcFileSystem in virt-fs is not used anymore, and was just for webc v1 support, which has long since been removed. This commit removes the implementation. The associated tests should be re-written to WebcVolumeFs, but preferably in a followup. --- lib/virtual-fs/src/lib.rs | 2 - lib/virtual-fs/src/overlay_fs.rs | 312 ++++++++++---------- lib/virtual-fs/src/webc_fs.rs | 488 ------------------------------- 3 files changed, 151 insertions(+), 651 deletions(-) delete mode 100644 lib/virtual-fs/src/webc_fs.rs diff --git a/lib/virtual-fs/src/lib.rs b/lib/virtual-fs/src/lib.rs index dd23826d89c..86ed6dcb154 100644 --- a/lib/virtual-fs/src/lib.rs +++ b/lib/virtual-fs/src/lib.rs @@ -47,8 +47,6 @@ mod static_file; pub mod static_fs; mod trace_fs; #[cfg(feature = "webc-fs")] -pub mod webc_fs; -#[cfg(feature = "webc-fs")] mod webc_volume_fs; pub mod limiter; diff --git a/lib/virtual-fs/src/overlay_fs.rs b/lib/virtual-fs/src/overlay_fs.rs index d140762c6e3..c91141bfdf8 100644 --- a/lib/virtual-fs/src/overlay_fs.rs +++ b/lib/virtual-fs/src/overlay_fs.rs @@ -1108,17 +1108,12 @@ fn should_continue(e: FsError) -> bool { #[cfg(test)] mod tests { - use std::{path::PathBuf, sync::Arc}; + use std::path::PathBuf; - use bytes::Bytes; - use tempfile::TempDir; use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; - use webc::v1::{ParseOptions, WebCOwned}; use super::*; - use crate::{mem_fs::FileSystem as MemFS, webc_fs::WebcFileSystem, RootFileSystemBuilder}; - - const PYTHON: &[u8] = include_bytes!("../../c-api/examples/assets/python-0.1.0.wasmer"); + use crate::mem_fs::FileSystem as MemFS; #[tokio::test] async fn remove_directory() { @@ -1256,160 +1251,6 @@ mod tests { ); } - #[tokio::test] - async fn wasi_runner_use_case() { - // Set up some dummy files on the host - let temp = TempDir::new().unwrap(); - let first = temp.path().join("first"); - let file_txt = first.join("file.txt"); - let second = temp.path().join("second"); - std::fs::create_dir_all(&first).unwrap(); - std::fs::write(&file_txt, b"First!").unwrap(); - std::fs::create_dir_all(&second).unwrap(); - // configure the union FS so things are saved in memory by default - // (initialized with a set of unix-like folders), but certain folders - // are first to the host. - let primary = RootFileSystemBuilder::new().build(); - let host_fs: Arc = - Arc::new(crate::host_fs::FileSystem::default()); - let first_dirs = [(&first, "/first"), (&second, "/second")]; - for (host, guest) in first_dirs { - primary - .mount(PathBuf::from(guest), &host_fs, host.clone()) - .unwrap(); - } - // Set up the secondary file systems - let webc = WebCOwned::parse(Bytes::from_static(PYTHON), &ParseOptions::default()).unwrap(); - let webc = WebcFileSystem::init_all(Arc::new(webc)); - - let fs = OverlayFileSystem::new(primary, [webc]); - - // We should get all the normal directories from rootfs (primary) - assert!(ops::is_dir(&fs, "/lib")); - assert!(ops::is_dir(&fs, "/bin")); - assert!(ops::is_file(&fs, "/dev/stdin")); - assert!(ops::is_file(&fs, "/dev/stdout")); - // We also want to see files from the WEBC volumes (secondary) - assert!(ops::is_dir(&fs, "/lib/python3.6")); - assert!(ops::is_file(&fs, "/lib/python3.6/collections/__init__.py")); - #[cfg(never)] - { - // files on a secondary fs aren't writable - // TODO(Michael-F-Bryan): re-enable this if/when we fix - // open_readonly_file_hack() - assert_eq!( - fs.new_open_options() - .append(true) - .open("/lib/python3.6/collections/__init__.py") - .unwrap_err(), - FsError::PermissionDenied, - ); - } - // you are allowed to create files that look like they are in a secondary - // folder, though - ops::touch(&fs, "/lib/python3.6/collections/something-else.py").unwrap(); - // But it'll be on the primary filesystem, not the secondary one - assert!(ops::is_file( - &fs.primary, - "/lib/python3.6/collections/something-else.py" - )); - assert!(!ops::is_file( - &fs.secondaries[0], - "/lib/python3.6/collections/something-else.py" - )); - // You can do the same thing with folders - fs.create_dir("/lib/python3.6/something-else".as_ref()) - .unwrap(); - assert!(ops::is_dir(&fs.primary, "/lib/python3.6/something-else")); - assert!(!ops::is_dir( - &fs.secondaries[0], - "/lib/python3.6/something-else" - )); - // It only works when you are directly inside an existing directory - // on the secondary filesystem, though - assert_eq!( - ops::touch(&fs, "/lib/python3.6/collections/this/doesnt/exist.txt").unwrap_err(), - FsError::EntryNotFound - ); - // you should also be able to read files mounted from the host - assert!(ops::is_dir(&fs, "/first")); - assert!(ops::is_file(&fs, "/first/file.txt")); - assert_eq!( - ops::read_to_string(&fs, "/first/file.txt").await.unwrap(), - "First!" - ); - // Overwriting them is fine and we'll see the changes on the host - ops::write(&fs, "/first/file.txt", "Updated").await.unwrap(); - assert_eq!(std::fs::read_to_string(&file_txt).unwrap(), "Updated"); - // The filesystem will see changes on the host that happened after it was - // set up - let another = second.join("another.txt"); - std::fs::write(&another, "asdf").unwrap(); - assert_eq!( - ops::read_to_string(&fs, "/second/another.txt") - .await - .unwrap(), - "asdf" - ); - } - - fn load_webc(bytes: &'static [u8]) -> WebcFileSystem { - let options = ParseOptions::default(); - let webc = WebCOwned::parse(bytes, &options).unwrap(); - WebcFileSystem::init_all(Arc::new(webc)) - } - - #[track_caller] - fn assert_same_directory_contents( - original: &dyn FileSystem, - path: impl AsRef, - candidate: &dyn FileSystem, - ) { - let path = path.as_ref(); - - let original_entries: Vec<_> = original - .read_dir(path) - .unwrap() - .map(|r| r.unwrap()) - .collect(); - let candidate_entries: Vec<_> = candidate - .read_dir(path) - .unwrap() - .map(|r| r.unwrap()) - .collect(); - - assert_eq!(original_entries, candidate_entries); - } - - #[tokio::test] - async fn absolute_and_relative_paths_are_passed_through() { - let python = Arc::new(load_webc(PYTHON)); - - // The underlying filesystem doesn't care about absolute/relative paths - assert_eq!(python.read_dir("/lib".as_ref()).unwrap().count(), 4); - assert_eq!(python.read_dir("lib".as_ref()).unwrap().count(), 4); - - // read_dir() should be passed through to the primary - let webc_primary = - OverlayFileSystem::new(Arc::clone(&python), [crate::EmptyFileSystem::default()]); - assert_same_directory_contents(&python, "/lib", &webc_primary); - assert_same_directory_contents(&python, "lib", &webc_primary); - - // read_dir() should also be passed through to the secondary - let webc_secondary = - OverlayFileSystem::new(crate::EmptyFileSystem::default(), [Arc::clone(&python)]); - assert_same_directory_contents(&python, "/lib", &webc_secondary); - assert_same_directory_contents(&python, "lib", &webc_secondary); - - // It should be fine to overlay the root fs on top of our webc file - let overlay_rootfs = OverlayFileSystem::new( - RootFileSystemBuilder::default().build(), - [Arc::clone(&python)], - ); - assert_same_directory_contents(&python, "/lib", &overlay_rootfs); - assert_same_directory_contents(&python, "lib", &overlay_rootfs); - } - #[tokio::test] async fn open_secondary_fs_files_in_write_mode() { let primary = MemFS::default(); @@ -1688,4 +1529,153 @@ mod tests { FsError::EntryNotFound ) } + + // OLD tests that used WebcFileSystem. + // Should be re-implemented with WebcVolumeFs + // #[tokio::test] + // async fn wasi_runner_use_case() { + // // Set up some dummy files on the host + // let temp = TempDir::new().unwrap(); + // let first = temp.path().join("first"); + // let file_txt = first.join("file.txt"); + // let second = temp.path().join("second"); + // std::fs::create_dir_all(&first).unwrap(); + // std::fs::write(&file_txt, b"First!").unwrap(); + // std::fs::create_dir_all(&second).unwrap(); + // // configure the union FS so things are saved in memory by default + // // (initialized with a set of unix-like folders), but certain folders + // // are first to the host. + // let primary = RootFileSystemBuilder::new().build(); + // let host_fs: Arc = + // Arc::new(crate::host_fs::FileSystem::default()); + // let first_dirs = [(&first, "/first"), (&second, "/second")]; + // for (host, guest) in first_dirs { + // primary + // .mount(PathBuf::from(guest), &host_fs, host.clone()) + // .unwrap(); + // } + // // Set up the secondary file systems + // let webc = WebCOwned::parse(Bytes::from_static(PYTHON), &ParseOptions::default()).unwrap(); + // let webc = WebcFileSystem::init_all(Arc::new(webc)); + // + // let fs = OverlayFileSystem::new(primary, [webc]); + // + // // We should get all the normal directories from rootfs (primary) + // assert!(ops::is_dir(&fs, "/lib")); + // assert!(ops::is_dir(&fs, "/bin")); + // assert!(ops::is_file(&fs, "/dev/stdin")); + // assert!(ops::is_file(&fs, "/dev/stdout")); + // // We also want to see files from the WEBC volumes (secondary) + // assert!(ops::is_dir(&fs, "/lib/python3.6")); + // assert!(ops::is_file(&fs, "/lib/python3.6/collections/__init__.py")); + // #[cfg(never)] + // { + // // files on a secondary fs aren't writable + // // TODO(Michael-F-Bryan): re-enable this if/when we fix + // // open_readonly_file_hack() + // assert_eq!( + // fs.new_open_options() + // .append(true) + // .open("/lib/python3.6/collections/__init__.py") + // .unwrap_err(), + // FsError::PermissionDenied, + // ); + // } + // // you are allowed to create files that look like they are in a secondary + // // folder, though + // ops::touch(&fs, "/lib/python3.6/collections/something-else.py").unwrap(); + // // But it'll be on the primary filesystem, not the secondary one + // assert!(ops::is_file( + // &fs.primary, + // "/lib/python3.6/collections/something-else.py" + // )); + // assert!(!ops::is_file( + // &fs.secondaries[0], + // "/lib/python3.6/collections/something-else.py" + // )); + // // You can do the same thing with folders + // fs.create_dir("/lib/python3.6/something-else".as_ref()) + // .unwrap(); + // assert!(ops::is_dir(&fs.primary, "/lib/python3.6/something-else")); + // assert!(!ops::is_dir( + // &fs.secondaries[0], + // "/lib/python3.6/something-else" + // )); + // // It only works when you are directly inside an existing directory + // // on the secondary filesystem, though + // assert_eq!( + // ops::touch(&fs, "/lib/python3.6/collections/this/doesnt/exist.txt").unwrap_err(), + // FsError::EntryNotFound + // ); + // // you should also be able to read files mounted from the host + // assert!(ops::is_dir(&fs, "/first")); + // assert!(ops::is_file(&fs, "/first/file.txt")); + // assert_eq!( + // ops::read_to_string(&fs, "/first/file.txt").await.unwrap(), + // "First!" + // ); + // // Overwriting them is fine and we'll see the changes on the host + // ops::write(&fs, "/first/file.txt", "Updated").await.unwrap(); + // assert_eq!(std::fs::read_to_string(&file_txt).unwrap(), "Updated"); + // // The filesystem will see changes on the host that happened after it was + // // set up + // let another = second.join("another.txt"); + // std::fs::write(&another, "asdf").unwrap(); + // assert_eq!( + // ops::read_to_string(&fs, "/second/another.txt") + // .await + // .unwrap(), + // "asdf" + // ); + // } + // + // #[tokio::test] + // async fn absolute_and_relative_paths_are_passed_through() { + // let python = Arc::new(load_webc(PYTHON)); + // + // // The underlying filesystem doesn't care about absolute/relative paths + // assert_eq!(python.read_dir("/lib".as_ref()).unwrap().count(), 4); + // assert_eq!(python.read_dir("lib".as_ref()).unwrap().count(), 4); + // + // // read_dir() should be passed through to the primary + // let webc_primary = + // OverlayFileSystem::new(Arc::clone(&python), [crate::EmptyFileSystem::default()]); + // assert_same_directory_contents(&python, "/lib", &webc_primary); + // assert_same_directory_contents(&python, "lib", &webc_primary); + // + // // read_dir() should also be passed through to the secondary + // let webc_secondary = + // OverlayFileSystem::new(crate::EmptyFileSystem::default(), [Arc::clone(&python)]); + // assert_same_directory_contents(&python, "/lib", &webc_secondary); + // assert_same_directory_contents(&python, "lib", &webc_secondary); + // + // // It should be fine to overlay the root fs on top of our webc file + // let overlay_rootfs = OverlayFileSystem::new( + // RootFileSystemBuilder::default().build(), + // [Arc::clone(&python)], + // ); + // assert_same_directory_contents(&python, "/lib", &overlay_rootfs); + // assert_same_directory_contents(&python, "lib", &overlay_rootfs); + // } + // #[track_caller] + // fn assert_same_directory_contents( + // original: &dyn FileSystem, + // path: impl AsRef, + // candidate: &dyn FileSystem, + // ) { + // let path = path.as_ref(); + // + // let original_entries: Vec<_> = original + // .read_dir(path) + // .unwrap() + // .map(|r| r.unwrap()) + // .collect(); + // let candidate_entries: Vec<_> = candidate + // .read_dir(path) + // .unwrap() + // .map(|r| r.unwrap()) + // .collect(); + // + // assert_eq!(original_entries, candidate_entries); + // } } diff --git a/lib/virtual-fs/src/webc_fs.rs b/lib/virtual-fs/src/webc_fs.rs deleted file mode 100644 index eba2387b673..00000000000 --- a/lib/virtual-fs/src/webc_fs.rs +++ /dev/null @@ -1,488 +0,0 @@ -use std::{ - convert::{TryFrom, TryInto}, - io::{self, Error as IoError, ErrorKind as IoErrorKind, SeekFrom}, - ops::Deref, - path::{Path, PathBuf}, - pin::Pin, - sync::Arc, - task::{Context, Poll}, -}; - -use anyhow::anyhow; -use futures::future::BoxFuture; -use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite}; -use webc::v1::{FsEntry, FsEntryType, OwnedFsEntryFile, WebC}; - -use crate::{ - mem_fs::FileSystem as MemFileSystem, FileOpener, FileSystem, FsError, Metadata, OpenOptions, - OpenOptionsConfig, ReadDir, VirtualFile, -}; - -/// Custom file system wrapper to map requested file paths -#[derive(Debug)] -pub struct WebcFileSystem -where - T: std::fmt::Debug + Send + Sync + 'static, -{ - pub webc: Arc, - pub memory: Arc, - top_level_dirs: Vec, - volumes: Vec>, -} - -impl WebcFileSystem -where - T: std::fmt::Debug + Send + Sync + 'static, - T: Deref>, -{ - pub fn init(webc: Arc, package: &str) -> Self { - let mut fs = Self { - webc: webc.clone(), - memory: Arc::new(MemFileSystem::default()), - top_level_dirs: Vec::new(), - volumes: Vec::new(), - }; - - for volume in webc.get_volumes_for_package(package) { - if let Some(vol_ref) = webc.volumes.get(&volume) { - fs.volumes.push(vol_ref.clone()); - } - for directory in webc.list_directories(&volume) { - fs.top_level_dirs.push(directory.clone()); - let _ = fs.create_dir(Path::new(&directory)); - } - } - fs - } - - pub fn init_all(webc: Arc) -> Self { - let mut fs = Self { - webc: webc.clone(), - memory: Arc::new(MemFileSystem::default()), - top_level_dirs: Vec::new(), - volumes: webc.volumes.clone().into_values().collect(), - }; - for (header, _) in webc.volumes.iter() { - for directory in webc.list_directories(header) { - fs.top_level_dirs.push(directory.clone()); - let _ = fs.create_dir(Path::new(&directory)); - } - } - fs - } - - pub fn top_level_dirs(&self) -> &Vec { - &self.top_level_dirs - } -} - -/// Custom file opener, returns a WebCFile -impl FileOpener for WebcFileSystem -where - T: std::fmt::Debug + Send + Sync + 'static, - T: Deref>, -{ - fn open( - &self, - path: &Path, - _conf: &OpenOptionsConfig, - ) -> Result, FsError> { - match get_volume_name_opt(path) { - Some(volume) => { - let file = self - .webc - .volumes - .get(&volume) - .ok_or(FsError::EntryNotFound)? - .get_file_entry(path.to_string_lossy().as_ref()) - .map_err(|_e| FsError::EntryNotFound)?; - - Ok(Box::new(WebCFile { - volume, - webc: self.webc.clone(), - path: path.to_path_buf(), - entry: file, - cursor: 0, - })) - } - None => { - for (volume, _) in self.webc.volumes.iter() { - let v = match self.webc.volumes.get(volume) { - Some(s) => s, - None => continue, // error - }; - - let entry = match v.get_file_entry(path.to_string_lossy().as_ref()) { - Ok(s) => s, - Err(_) => continue, // error - }; - - return Ok(Box::new(WebCFile { - volume: volume.clone(), - webc: self.webc.clone(), - path: path.to_path_buf(), - entry, - cursor: 0, - })); - } - self.memory.new_open_options().open(path) - } - } - } -} - -#[derive(Debug)] -struct WebCFile -where - T: std::fmt::Debug + Send + Sync + 'static, -{ - pub webc: Arc, - pub volume: String, - #[allow(dead_code)] - pub path: PathBuf, - pub entry: OwnedFsEntryFile, - pub cursor: u64, -} - -impl VirtualFile for WebCFile -where - T: std::fmt::Debug + Send + Sync + 'static, - T: Deref>, -{ - fn last_accessed(&self) -> u64 { - 0 - } - fn last_modified(&self) -> u64 { - 0 - } - fn created_time(&self) -> u64 { - 0 - } - fn size(&self) -> u64 { - self.entry.get_len() - } - fn set_len(&mut self, _new_size: u64) -> crate::Result<()> { - Ok(()) - } - fn unlink(&mut self) -> Result<(), FsError> { - Ok(()) - } - fn poll_read_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - let remaining = self.entry.get_len() - self.cursor; - Poll::Ready(Ok(remaining as usize)) - } - fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(0)) - } -} - -impl AsyncRead for WebCFile -where - T: std::fmt::Debug + Send + Sync + 'static, - T: Deref>, -{ - fn poll_read( - mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &mut tokio::io::ReadBuf<'_>, - ) -> Poll> { - let bytes = self - .webc - .volumes - .get(&self.volume) - .ok_or_else(|| { - IoError::new( - IoErrorKind::NotFound, - anyhow!("Unknown volume {:?}", self.volume), - ) - })? - .get_file_bytes(&self.entry) - .map_err(|e| IoError::new(IoErrorKind::NotFound, e))?; - - let start: usize = self.cursor.try_into().unwrap(); - let remaining = &bytes[start..]; - let bytes_read = remaining.len().min(buf.remaining()); - let bytes = &remaining[..bytes_read]; - - buf.put_slice(bytes); - self.cursor += u64::try_from(bytes_read).unwrap(); - - Poll::Ready(Ok(())) - } -} - -// WebC file is not writable, the FileOpener will return a MemoryFile for writing instead -// This code should never be executed (since writes are redirected to memory instead). -impl AsyncWrite for WebCFile -where - T: std::fmt::Debug + Send + Sync + 'static, -{ - fn poll_write( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Poll::Ready(Ok(buf.len())) - } - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -impl AsyncSeek for WebCFile -where - T: std::fmt::Debug + Send + Sync + 'static, - T: Deref>, -{ - fn start_seek(mut self: Pin<&mut Self>, pos: io::SeekFrom) -> io::Result<()> { - let self_size = self.size(); - match pos { - SeekFrom::Start(s) => { - self.cursor = s.min(self_size); - } - SeekFrom::End(e) => { - let self_size_i64 = self_size.try_into().unwrap_or(i64::MAX); - self.cursor = ((self_size_i64).saturating_add(e)) - .min(self_size_i64) - .try_into() - .unwrap_or(i64::MAX as u64); - } - SeekFrom::Current(c) => { - self.cursor = (self - .cursor - .saturating_add(c.try_into().unwrap_or(i64::MAX as u64))) - .min(self_size); - } - } - Ok(()) - } - fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(self.cursor)) - } -} - -fn get_volume_name_opt>(path: P) -> Option { - use std::path::Component::Normal; - if let Some(Normal(n)) = path.as_ref().components().next() { - if let Some(s) = n.to_str() { - if s.ends_with(':') { - return Some(s.replace(':', "")); - } - } - } - None -} - -#[allow(dead_code)] -fn get_volume_name>(path: P) -> String { - get_volume_name_opt(path).unwrap_or_else(|| "atom".to_string()) -} - -fn transform_into_read_dir(path: &Path, fs_entries: &[FsEntry<'_>]) -> crate::ReadDir { - let entries = fs_entries - .iter() - .map(|e| crate::DirEntry { - path: path.join(&*e.text), - metadata: Ok(crate::Metadata { - ft: translate_file_type(e.fs_type), - accessed: 0, - created: 0, - modified: 0, - len: e.get_len(), - }), - }) - .collect(); - - crate::ReadDir::new(entries) -} - -impl FileSystem for WebcFileSystem -where - T: std::fmt::Debug + Send + Sync + 'static, - T: Deref>, -{ - fn readlink(&self, _path: &Path) -> crate::Result { - Err(FsError::InvalidInput) - } - - fn read_dir(&self, path: &Path) -> Result { - let path = normalizes_path(path); - let read_dir_result = self - .volumes - .iter() - .filter_map(|v| v.read_dir(&path).ok()) - .next() - .map(|o| transform_into_read_dir(Path::new(&path), o.as_ref())) - .ok_or(FsError::EntryNotFound); - - match read_dir_result { - Ok(o) => Ok(o), - Err(_) => self.memory.read_dir(Path::new(&path)), - } - } - fn create_dir(&self, path: &Path) -> Result<(), FsError> { - let path = normalizes_path(path); - let result = self.memory.create_dir(Path::new(&path)); - result - } - fn remove_dir(&self, path: &Path) -> Result<(), FsError> { - let path = normalizes_path(path); - let result = self.memory.remove_dir(Path::new(&path)); - if self.volumes.iter().any(|v| v.get_file_entry(&path).is_ok()) { - Ok(()) - } else { - result - } - } - fn rename<'a>(&'a self, from: &'a Path, to: &'a Path) -> BoxFuture<'a, Result<(), FsError>> { - Box::pin(async { - let from = normalizes_path(from); - let to = normalizes_path(to); - let result = self.memory.rename(Path::new(&from), Path::new(&to)).await; - if self.volumes.iter().any(|v| v.get_file_entry(&from).is_ok()) { - Ok(()) - } else { - result - } - }) - } - fn metadata(&self, path: &Path) -> Result { - let path = normalizes_path(path); - if let Some(fs_entry) = self - .volumes - .iter() - .filter_map(|v| v.get_file_entry(&path).ok()) - .next() - { - Ok(Metadata { - ft: translate_file_type(FsEntryType::File), - accessed: 0, - created: 0, - modified: 0, - len: fs_entry.get_len(), - }) - } else if self - .volumes - .iter() - .filter_map(|v| v.read_dir(&path).ok()) - .next() - .is_some() - { - Ok(Metadata { - ft: translate_file_type(FsEntryType::Dir), - accessed: 0, - created: 0, - modified: 0, - len: 0, - }) - } else { - self.memory.metadata(Path::new(&path)) - } - } - fn remove_file(&self, path: &Path) -> Result<(), FsError> { - let path = normalizes_path(path); - let result = self.memory.remove_file(Path::new(&path)); - if self - .volumes - .iter() - .filter_map(|v| v.get_file_entry(&path).ok()) - .next() - .is_some() - { - Ok(()) - } else { - result - } - } - fn new_open_options(&self) -> OpenOptions { - OpenOptions::new(self) - } - fn symlink_metadata(&self, path: &Path) -> Result { - let path = normalizes_path(path); - if let Some(fs_entry) = self - .volumes - .iter() - .filter_map(|v| v.get_file_entry(&path).ok()) - .next() - { - Ok(Metadata { - ft: translate_file_type(FsEntryType::File), - accessed: 0, - created: 0, - modified: 0, - len: fs_entry.get_len(), - }) - } else if self - .volumes - .iter() - .filter_map(|v| v.read_dir(&path).ok()) - .next() - .is_some() - { - Ok(Metadata { - ft: translate_file_type(FsEntryType::Dir), - accessed: 0, - created: 0, - modified: 0, - len: 0, - }) - } else { - self.memory.symlink_metadata(Path::new(&path)) - } - } -} - -fn normalizes_path(path: &Path) -> String { - let path = format!("{}", path.display()); - if !path.starts_with('/') { - format!("/{path}") - } else { - path - } -} - -fn translate_file_type(f: FsEntryType) -> crate::FileType { - crate::FileType { - dir: f == FsEntryType::Dir, - file: f == FsEntryType::File, - symlink: false, - char_device: false, - block_device: false, - socket: false, - fifo: false, - } -} - -#[cfg(test)] -mod tests { - use bytes::Bytes; - use tokio::io::AsyncReadExt; - use webc::v1::{ParseOptions, WebCOwned}; - - use super::*; - - #[tokio::test] - async fn read_a_file_from_the_webc_fs() { - let webc: &[u8] = include_bytes!("../../c-api/examples/assets/python-0.1.0.wasmer"); - let options = ParseOptions::default(); - let webc = WebCOwned::parse(Bytes::from_static(webc), &options).unwrap(); - - let fs = WebcFileSystem::init_all(Arc::new(webc)); - - let mut f = fs - .new_open_options() - .read(true) - .open(Path::new("/lib/python3.6/collections/abc.py")) - .unwrap(); - - let mut abc_py = String::new(); - f.read_to_string(&mut abc_py).await.unwrap(); - assert_eq!( - abc_py, - "from _collections_abc import *\nfrom _collections_abc import __all__\n" - ); - } -}