use super::{File, Metadata, Permissions};
use async_recursion::async_recursion;
use bitflags::bitflags;
use parking_lot::Mutex as SyncMutex;
use std::{
collections::BTreeMap,
ffi::{OsStr, OsString},
fmt,
io::{self, Cursor, Error, ErrorKind, Result},
iter::Enumerate,
path::MAIN_SEPARATOR,
path::{Component, Path, PathBuf},
sync::{Arc, LazyLock},
vec::IntoIter,
};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
sync::{Mutex, RwLock},
};
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use super::meta_data::FileTime;
bitflags! {
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) struct FileFlags: u8 {
const DIR = 0b00000001;
const FILE = 0b00000010;
const SYM_LINK = 0b00000100;
}
}
type FileSystem = BTreeMap<OsString, Fd>;
pub(super) type Fd = Arc<RwLock<MemoryFd>>;
pub(super) type FileContent = Arc<SyncMutex<Cursor<Vec<u8>>>>;
pub(super) type RootDir = Arc<RwLock<MemoryDir>>;
static ROOT_DIR: LazyLock<RootDir> =
LazyLock::new(|| Arc::new(RwLock::new(MemoryDir::new_root())));
static FS_LOCK: LazyLock<tokio::sync::Mutex<()>> =
LazyLock::new(|| tokio::sync::Mutex::new(()));
pub(super) fn root_fs() -> RootDir {
Arc::clone(&ROOT_DIR)
}
pub(super) enum PathTarget {
Root(RootDir),
Descriptor(Fd),
}
impl From<Parent> for PathTarget {
fn from(value: Parent) -> Self {
match value {
Parent::Root(fs) => PathTarget::Root(fs),
Parent::Folder(fd) => PathTarget::Descriptor(fd),
}
}
}
#[derive(Debug)]
pub(super) enum Parent {
Root(RootDir),
Folder(Fd),
}
impl Clone for Parent {
fn clone(&self) -> Self {
match self {
Self::Root(root) => Self::Root(Arc::clone(root)),
Self::Folder(fd) => Self::Folder(Arc::clone(fd)),
}
}
}
impl Parent {
pub async fn name(&self) -> OsString {
match self {
Self::Root(fs) => {
let fs = fs.read().await;
fs.name.clone()
}
Self::Folder(fd) => {
let fd = fd.read().await;
fd.name().clone()
}
}
}
pub async fn mkdir(&mut self, name: OsString) -> Result<Fd> {
let fd = MemoryFd::Dir(MemoryDir::new_parent(
name.clone(),
Some(self.clone()),
));
let dir = Arc::new(RwLock::new(fd));
self.insert(name, Arc::clone(&dir)).await?;
Ok(dir)
}
pub async fn unlink(
&mut self,
path: impl AsRef<Path>,
) -> Result<Option<Fd>> {
match self {
Self::Root(fs) => {
let mut fs = fs.write().await;
Ok(fs.remove(path).await)
}
Self::Folder(fd) => {
let mut fd = fd.write().await;
match &mut *fd {
MemoryFd::Dir(dir) => Ok(dir.remove(path).await),
_ => Err(ErrorKind::PermissionDenied.into()),
}
}
}
}
pub async fn insert(&mut self, name: OsString, child: Fd) -> Result<()> {
match self {
Self::Root(fs) => {
let mut fs = fs.write().await;
fs.insert(name, child).await;
Ok(())
}
Self::Folder(fd) => {
let mut fd = fd.write().await;
match &mut *fd {
MemoryFd::Dir(dir) => {
dir.insert(name, child).await;
Ok(())
}
_ => Err(ErrorKind::PermissionDenied.into()),
}
}
}
}
async fn find_dir(&self, name: &OsStr) -> Option<Fd> {
match self {
Self::Root(fs) => {
let fs = fs.read().await;
fs.find_dir(name).await
}
Self::Folder(fd) => {
let mut fd = fd.write().await;
match &mut *fd {
MemoryFd::Dir(dir) => dir.find_dir(name).await,
_ => None,
}
}
}
}
}
#[derive(Default)]
pub(super) struct MemoryDir {
name: OsString,
parent: Option<Parent>,
permissions: Permissions,
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
time: FileTime,
files: FileSystem,
}
impl fmt::Debug for MemoryDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemoryDir")
.field("name", &self.name)
.field("permissions", &self.permissions)
.field("files", &self.files)
.finish()
}
}
impl MemoryDir {
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
fn new_root() -> Self {
Self {
name: OsString::from(MAIN_SEPARATOR.to_string()),
parent: None,
permissions: Default::default(),
time: Default::default(),
files: Default::default(),
}
}
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
fn new_root() -> Self {
Self {
name: OsString::from(MAIN_SEPARATOR.to_string()),
parent: None,
permissions: Default::default(),
files: Default::default(),
}
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub(super) fn new_parent(name: OsString, parent: Option<Parent>) -> Self {
Self {
name,
parent,
permissions: Default::default(),
time: Default::default(),
files: Default::default(),
}
}
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
pub(super) fn new_parent(name: OsString, parent: Option<Parent>) -> Self {
Self {
name,
parent,
permissions: Default::default(),
files: Default::default(),
}
}
pub fn files(&self) -> &FileSystem {
&self.files
}
pub async fn remove(&mut self, path: impl AsRef<Path>) -> Option<Fd> {
let _ = FS_LOCK.lock().await;
if let Some(name) = path.as_ref().file_name() {
self.files.remove(name)
} else {
None
}
}
pub async fn insert(&mut self, name: OsString, fd: Fd) {
let _ = FS_LOCK.lock().await;
self.files.insert(name, fd);
}
pub async fn find_dir(&self, name: &OsStr) -> Option<Fd> {
if let Some(child) = self.files.get(name) {
let is_dir = {
let fd = child.read().await;
matches!(&*fd, MemoryFd::Dir(_))
};
if is_dir {
Some(Arc::clone(child))
} else {
None
}
} else {
None
}
}
}
pub(super) struct MemoryFile {
name: OsString,
parent: Option<Parent>,
permissions: Permissions,
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
time: FileTime,
pub(super) contents: FileContent,
}
impl fmt::Debug for MemoryFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemoryFile")
.field("name", &self.name)
.field("permissions", &self.permissions)
.field("contents", &self.contents)
.finish()
}
}
impl MemoryFile {
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
fn new(name: OsString, parent: Option<Parent>) -> Self {
Self {
name,
parent,
permissions: Default::default(),
time: Default::default(),
contents: Arc::new(SyncMutex::new(Cursor::new(Vec::new()))),
}
}
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
fn new(name: OsString, parent: Option<Parent>) -> Self {
Self {
name,
parent,
permissions: Default::default(),
contents: Arc::new(SyncMutex::new(Cursor::new(Vec::new()))),
}
}
pub fn truncate(&self) {
let mut lock = self.contents.lock();
*lock = Cursor::new(Vec::new());
}
pub fn contents(&self) -> FileContent {
Arc::clone(&self.contents)
}
}
#[derive(Debug)]
pub(super) enum MemoryFd {
File(MemoryFile),
Dir(MemoryDir),
}
impl MemoryFd {
pub fn parent(&self) -> Option<&Parent> {
match self {
Self::File(fd) => fd.parent.as_ref(),
Self::Dir(fd) => fd.parent.as_ref(),
}
}
pub fn parent_mut(&mut self) -> Option<&mut Parent> {
match self {
Self::File(fd) => fd.parent.as_mut(),
Self::Dir(fd) => fd.parent.as_mut(),
}
}
pub fn name(&self) -> &OsString {
match self {
Self::File(fd) => &fd.name,
Self::Dir(fd) => &fd.name,
}
}
pub fn set_name(&mut self, name: OsString) {
match self {
Self::File(fd) => fd.name = name,
Self::Dir(fd) => fd.name = name,
}
}
pub async fn path(&self) -> PathBuf {
let mut parent = self.parent().cloned();
let mut components = vec![self.name().clone()];
while let Some(fd) = parent {
let name = fd.name().await;
components.push(name);
parent = match fd {
Parent::Root(_) => None,
Parent::Folder(fd) => {
let fd = fd.read().await;
fd.parent().cloned()
}
};
}
components.reverse();
let mut path = PathBuf::new();
for part in components {
path = path.join(part);
}
path
}
pub fn flags(&self) -> FileFlags {
match self {
Self::File(_) => FileFlags::FILE,
Self::Dir(_) => FileFlags::DIR,
}
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub(crate) fn time(&self) -> &FileTime {
match self {
Self::File(fd) => &fd.time,
Self::Dir(fd) => &fd.time,
}
}
pub fn set_permissions(&mut self, perm: Permissions) {
match self {
Self::File(fd) => fd.permissions = perm,
Self::Dir(fd) => fd.permissions = perm,
}
}
pub fn permissions(&self) -> &Permissions {
match self {
Self::File(fd) => &fd.permissions,
Self::Dir(fd) => &fd.permissions,
}
}
}
pub async fn copy(
from: impl AsRef<Path>,
to: impl AsRef<Path>,
) -> Result<()> {
async fn copy_fd(
fd: Fd,
buffer: Vec<u8>,
permissions: Permissions,
) -> Result<()> {
let mut fd = fd.write().await;
match &mut *fd {
MemoryFd::File(file) => {
let mut contents = file.contents.lock();
let buf = contents.get_mut();
*buf = buffer;
file.permissions = permissions;
Ok(())
}
_ => Err(ErrorKind::PermissionDenied.into()),
}
}
if let Some(target) = resolve(from.as_ref()).await {
match target {
PathTarget::Descriptor(fd) => {
if from.as_ref() == to.as_ref() {
return Ok(());
}
let result: Option<(Vec<u8>, Permissions)> = {
let fd = fd.read().await;
match &*fd {
MemoryFd::File(file) => {
let permissions = file.permissions;
let contents = file.contents.lock();
let buffer = contents.get_ref().clone();
Some((buffer, permissions))
}
_ => None,
}
};
if let Some((buffer, permissions)) = result {
if let Some(target) = resolve(to.as_ref()).await {
match target {
PathTarget::Descriptor(fd) => {
copy_fd(fd, buffer, permissions).await
}
PathTarget::Root(_) => {
Err(ErrorKind::PermissionDenied.into())
}
}
} else {
let fd = create_file(to, false).await?;
copy_fd(fd, buffer, permissions).await
}
} else {
Err(ErrorKind::PermissionDenied.into())
}
}
PathTarget::Root(_) => Err(ErrorKind::PermissionDenied.into()),
}
} else {
Err(ErrorKind::NotFound.into())
}
}
pub async fn write(
path: impl AsRef<Path>,
contents: impl AsRef<[u8]>,
) -> Result<()> {
let mut fd = File::create(&path).await?;
fd.write_all(contents.as_ref()).await?;
fd.flush().await?;
Ok(())
}
pub async fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
let mut fd = File::open(path.as_ref()).await?;
fd.read_to_end(&mut buffer).await?;
Ok(buffer)
}
pub async fn remove_file(path: impl AsRef<Path>) -> Result<()> {
let file = ensure_file(path.as_ref()).await?;
let mut fd = file.write().await;
if let Some(parent) = fd.parent_mut() {
parent.unlink(path).await?;
Ok(())
} else {
Err(ErrorKind::PermissionDenied.into())
}
}
pub async fn remove_dir(path: impl AsRef<Path>) -> Result<()> {
let dir = ensure_dir(path.as_ref()).await?;
let mut fd = dir.write().await;
match &*fd {
MemoryFd::Dir(dir) => {
if dir.files().is_empty() {
if let Some(parent) = fd.parent_mut() {
parent.unlink(path).await?;
}
Ok(())
} else {
Err(ErrorKind::PermissionDenied.into())
}
}
_ => Err(ErrorKind::PermissionDenied.into()),
}
}
pub async fn remove_dir_all(path: impl AsRef<Path>) -> Result<()> {
let dir = ensure_dir(path.as_ref()).await?;
let mut fd = dir.write().await;
if let Some(parent) = fd.parent_mut() {
parent.unlink(path).await?;
Ok(())
} else {
Err(ErrorKind::PermissionDenied.into())
}
}
pub async fn rename(
from: impl AsRef<Path>,
to: impl AsRef<Path>,
) -> Result<()> {
let file = resolve(from.as_ref()).await.ok_or_else(|| {
let err: io::Error = ErrorKind::NotFound.into();
err
})?;
let file = match file {
PathTarget::Descriptor(fd) => fd,
_ => return Err(ErrorKind::PermissionDenied.into()),
};
let mut fd = file.write().await;
let from_name = from.as_ref().file_name().ok_or_else(|| {
let err: io::Error = ErrorKind::PermissionDenied.into();
err
})?;
let to_name = to.as_ref().file_name().ok_or_else(|| {
let err: io::Error = ErrorKind::PermissionDenied.into();
err
})?;
fd.set_name(to_name.to_owned());
let source = {
let parent = fd.parent_mut().ok_or_else(|| {
let err: io::Error = ErrorKind::PermissionDenied.into();
err
})?;
parent.unlink(from_name).await?
};
if let Some(source) = source {
if let Some(target) = resolve(to.as_ref()).await {
match target {
PathTarget::Descriptor(to_fd) => {
let mut to_fd = to_fd.write().await;
if matches!(&*to_fd, MemoryFd::File(_)) {
if let Some(to_parent_fd) = to_fd.parent_mut() {
to_parent_fd
.insert(to_name.to_owned(), source)
.await?;
}
Ok(())
} else {
Err(ErrorKind::PermissionDenied.into())
}
}
_ => Err(ErrorKind::PermissionDenied.into()),
}
} else {
let has_parent = has_parent(to.as_ref());
if has_parent {
if let Some(target) = resolve_parent(to.as_ref()).await {
match target {
PathTarget::Descriptor(fd) => {
let mut to_parent_write = fd.write().await;
match &mut *to_parent_write {
MemoryFd::Dir(dir) => {
dir.insert(to_name.to_owned(), source)
.await;
}
_ => unreachable!(),
}
}
PathTarget::Root(dir) => {
let mut dir = dir.write().await;
dir.insert(to_name.to_owned(), source).await;
}
}
Ok(())
} else {
Err(ErrorKind::NotFound.into())
}
} else {
let root = root_fs();
let mut root = root.write().await;
root.insert(to_name.to_owned(), source).await;
Ok(())
}
}
} else {
Err(ErrorKind::NotFound.into())
}
}
pub async fn read_to_string(path: impl AsRef<Path>) -> Result<String> {
let contents = read(path).await?;
String::from_utf8(contents).map_err(|_| {
let err: Error = ErrorKind::InvalidData.into();
err
})
}
pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
if let Some(target) = resolve(path.as_ref().to_path_buf()).await {
match target {
PathTarget::Descriptor(fd) => {
let len = {
let fd = fd.read().await;
match &*fd {
MemoryFd::File(file) => {
let data = file.contents();
let data = data.lock();
(*data).get_ref().len() as u64
}
_ => 0u64,
}
};
Ok(new_metadata(fd, len).await)
}
PathTarget::Root(_) => {
unimplemented!("support root fs metadata");
}
}
} else {
Err(ErrorKind::NotFound.into())
}
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
async fn new_metadata(fd: Fd, len: u64) -> Metadata {
let fd = fd.read().await;
Metadata::new(*fd.permissions(), fd.flags(), len, *fd.time())
}
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
async fn new_metadata(fd: Fd, len: u64) -> Metadata {
let fd = fd.read().await;
Metadata::new(fd.permissions().clone(), fd.flags(), len)
}
pub async fn set_permissions(
path: impl AsRef<Path>,
perm: Permissions,
) -> Result<()> {
if let Some(target) = resolve(path.as_ref()).await {
match target {
PathTarget::Descriptor(fd) => {
let mut fd = fd.write().await;
fd.set_permissions(perm);
Ok(())
}
_ => Err(ErrorKind::PermissionDenied.into()),
}
} else {
Err(ErrorKind::NotFound.into())
}
}
pub async fn try_exists(path: impl AsRef<Path>) -> Result<bool> {
Ok(resolve(path).await.is_some())
}
pub async fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {
if let Some(target) = resolve(path.as_ref()).await {
match target {
PathTarget::Root(_) => {
Ok(PathBuf::from(MAIN_SEPARATOR.to_string()))
}
PathTarget::Descriptor(fd) => {
let fd = fd.read().await;
Ok(fd.path().await)
}
}
} else {
Err(ErrorKind::NotFound.into())
}
}
pub(super) async fn create_file(
path: impl AsRef<Path>,
truncate: bool,
) -> Result<Fd> {
let file_name = path.as_ref().file_name().ok_or_else(|| {
let err: io::Error = ErrorKind::PermissionDenied.into();
err
})?;
if let Some(target) = resolve(path.as_ref()).await {
match target {
PathTarget::Descriptor(file) => {
let mut file_fd = file.write().await;
if file_fd.parent().is_some() {
match &mut *file_fd {
MemoryFd::Dir(_) => {
Err(ErrorKind::PermissionDenied.into())
}
MemoryFd::File(fd) => {
if truncate {
fd.truncate();
}
Ok(Arc::clone(&file))
}
}
} else {
Err(ErrorKind::PermissionDenied.into())
}
}
_ => Err(ErrorKind::PermissionDenied.into()),
}
} else if let Some(target) = resolve_parent(path.as_ref()).await {
match target {
PathTarget::Descriptor(fd) => {
let mut parent_fd = fd.write().await;
match &mut *parent_fd {
MemoryFd::Dir(dir) => {
let new_file = MemoryFd::File(MemoryFile::new(
file_name.to_owned(),
Some(Parent::Folder(Arc::clone(&fd))),
));
dir.insert(
file_name.to_owned(),
Arc::new(RwLock::new(new_file)),
)
.await;
Ok(dir
.files()
.get(file_name)
.map(Arc::clone)
.unwrap())
}
MemoryFd::File(_) => {
Err(ErrorKind::PermissionDenied.into())
}
}
}
_ => unreachable!(),
}
} else {
let root = root_fs();
let new_file = MemoryFd::File(MemoryFile::new(
file_name.to_owned(),
Some(Parent::Root(Arc::clone(&root))),
));
{
let mut dir = root.write().await;
dir.insert(file_name.to_owned(), Arc::new(RwLock::new(new_file)))
.await;
}
let dir = root.read().await;
Ok(dir.files().get(file_name).map(Arc::clone).unwrap())
}
}
async fn ensure_file(path: impl AsRef<Path>) -> Result<Fd> {
if let Some(target) = resolve(path.as_ref().to_path_buf()).await {
match target {
PathTarget::Descriptor(fd) => {
let is_file = {
let fd = fd.read().await;
matches!(&*fd, MemoryFd::File(_))
};
if is_file {
Ok(fd)
} else {
Err(ErrorKind::PermissionDenied.into())
}
}
_ => Err(ErrorKind::PermissionDenied.into()),
}
} else {
Err(ErrorKind::NotFound.into())
}
}
async fn ensure_dir(path: impl AsRef<Path>) -> Result<Fd> {
if let Some(target) = resolve(path.as_ref().to_path_buf()).await {
match target {
PathTarget::Descriptor(fd) => {
let is_dir = {
let fd = fd.read().await;
matches!(&*fd, MemoryFd::Dir(_))
};
if is_dir {
Ok(fd)
} else {
Err(ErrorKind::PermissionDenied.into())
}
}
_ => Err(ErrorKind::PermissionDenied.into()),
}
} else {
Err(ErrorKind::NotFound.into())
}
}
pub(super) fn has_parent(path: impl AsRef<Path>) -> bool {
if let Some(parent) = path.as_ref().parent() {
!parent.as_os_str().is_empty()
} else {
false
}
}
#[async_recursion]
async fn walk(
target: Parent,
it: &mut Enumerate<IntoIter<Component>>,
length: usize,
parents: &mut Vec<Parent>,
) -> Option<PathTarget> {
if let Some((index, part)) = it.next() {
match part {
Component::RootDir => {
if length == 1 {
return Some(PathTarget::Root(root_fs()));
}
let root = root_fs();
parents.push(Parent::Root(Arc::clone(&root)));
return walk(Parent::Root(root), it, length, parents).await;
}
Component::CurDir | Component::Prefix(_) => {
return walk(target, it, length, parents).await;
}
Component::ParentDir => {
if parents.pop().is_some() {
if index == length - 1 {
if let Some(target) = parents.pop() {
return Some(target.into());
} else {
return None;
}
} else if let Some(last) = parents.last() {
return walk(last.clone(), it, length, parents).await;
}
} else {
return None;
}
}
Component::Normal(name) => {
if index == length - 1 {
return match target {
Parent::Root(fs) => {
let fs = fs.read().await;
fs.files().get(name).map(|fd| {
PathTarget::Descriptor(Arc::clone(fd))
})
}
Parent::Folder(fd) => {
let fd = fd.read().await;
match &*fd {
MemoryFd::Dir(dir) => {
dir.files().get(name).map(|fd| {
PathTarget::Descriptor(Arc::clone(fd))
})
}
_ => None,
}
}
};
} else if let Some(child) = target.find_dir(name).await {
parents.push(Parent::Folder(Arc::clone(&child)));
return walk(
Parent::Folder(Arc::clone(&child)),
it,
length,
parents,
)
.await;
} else {
return None;
}
}
}
}
None
}
async fn resolve_relative(
parent: Parent,
path: impl AsRef<Path>,
) -> Option<PathTarget> {
let components: Vec<Component> = path.as_ref().components().collect();
let length = components.len();
let mut it = components.into_iter().enumerate();
walk(parent.clone(), &mut it, length, &mut vec![parent]).await
}
pub(super) async fn resolve(path: impl AsRef<Path>) -> Option<PathTarget> {
resolve_relative(Parent::Root(root_fs()), path).await
}
pub(super) async fn resolve_parent(
path: impl AsRef<Path>,
) -> Option<PathTarget> {
if let Some(parent) = path.as_ref().parent() {
resolve(parent).await
} else {
None
}
}