use super::{
common::MountContext, direct, error::MountError, mount_mode::MountMode, overlay,
path_security::normalize_virtual_path,
};
use crate::{MontyFileHandle, MontyObject, os::OsFunctionCall, types::file::FileMode};
#[derive(Clone, Copy, Debug)]
pub(super) enum FsRequest<'a> {
Exists { path: &'a str },
IsFile { path: &'a str },
IsDir { path: &'a str },
IsSymlink { path: &'a str },
ReadText { path: &'a str },
ReadBytes { path: &'a str },
WriteText { path: &'a str, data: &'a str },
WriteBytes { path: &'a str, data: &'a [u8] },
AppendText { path: &'a str, data: &'a str },
AppendBytes { path: &'a str, data: &'a [u8] },
Mkdir {
path: &'a str,
parents: bool,
exist_ok: bool,
},
Unlink { path: &'a str },
Rmdir { path: &'a str },
Iterdir { path: &'a str },
Stat { path: &'a str },
Rename { src: &'a str, dst: &'a str },
Resolve { path: &'a str },
Absolute { path: &'a str },
Open {
path: &'a str,
mode: FileMode,
},
}
impl<'a> FsRequest<'a> {
#[must_use]
pub fn primary_path(self) -> &'a str {
match self {
Self::Exists { path }
| Self::IsFile { path }
| Self::IsDir { path }
| Self::IsSymlink { path }
| Self::ReadText { path }
| Self::ReadBytes { path }
| Self::WriteText { path, .. }
| Self::WriteBytes { path, .. }
| Self::AppendText { path, .. }
| Self::AppendBytes { path, .. }
| Self::Mkdir { path, .. }
| Self::Unlink { path }
| Self::Rmdir { path }
| Self::Iterdir { path }
| Self::Stat { path }
| Self::Resolve { path }
| Self::Absolute { path }
| Self::Open { path, .. }
| Self::Rename { src: path, .. } => path,
}
}
#[must_use]
pub fn rename_destination(self) -> Option<&'a str> {
match self {
Self::Rename { dst, .. } => Some(dst),
_ => None,
}
}
#[must_use]
pub fn is_write(self) -> bool {
match self {
Self::WriteText { .. }
| Self::WriteBytes { .. }
| Self::AppendText { .. }
| Self::AppendBytes { .. }
| Self::Mkdir { .. }
| Self::Unlink { .. }
| Self::Rmdir { .. }
| Self::Rename { .. } => true,
Self::Open { mode, .. } => mode.create(),
_ => false,
}
}
}
pub(super) fn fs_request_from_call(call: &OsFunctionCall) -> FsRequest<'_> {
match call {
OsFunctionCall::Exists(p) => FsRequest::Exists { path: p.as_str() },
OsFunctionCall::IsFile(p) => FsRequest::IsFile { path: p.as_str() },
OsFunctionCall::IsDir(p) => FsRequest::IsDir { path: p.as_str() },
OsFunctionCall::IsSymlink(p) => FsRequest::IsSymlink { path: p.as_str() },
OsFunctionCall::ReadText(p) => FsRequest::ReadText { path: p.as_str() },
OsFunctionCall::ReadBytes(p) => FsRequest::ReadBytes { path: p.as_str() },
OsFunctionCall::WriteText(a) => FsRequest::WriteText {
path: a.path.as_str(),
data: a.data.as_str(),
},
OsFunctionCall::WriteBytes(a) => FsRequest::WriteBytes {
path: a.path.as_str(),
data: a.data.as_slice(),
},
OsFunctionCall::AppendText(a) => FsRequest::AppendText {
path: a.path.as_str(),
data: a.data.as_str(),
},
OsFunctionCall::AppendBytes(a) => FsRequest::AppendBytes {
path: a.path.as_str(),
data: a.data.as_slice(),
},
OsFunctionCall::Mkdir(a) => FsRequest::Mkdir {
path: a.path.as_str(),
parents: a.parents,
exist_ok: a.exist_ok,
},
OsFunctionCall::Unlink(p) => FsRequest::Unlink { path: p.as_str() },
OsFunctionCall::Rmdir(p) => FsRequest::Rmdir { path: p.as_str() },
OsFunctionCall::Iterdir(p) => FsRequest::Iterdir { path: p.as_str() },
OsFunctionCall::Stat(p) => FsRequest::Stat { path: p.as_str() },
OsFunctionCall::Rename(a) => FsRequest::Rename {
src: a.src.as_str(),
dst: a.dst.as_str(),
},
OsFunctionCall::Resolve(p) => FsRequest::Resolve { path: p.as_str() },
OsFunctionCall::Absolute(p) => FsRequest::Absolute { path: p.as_str() },
OsFunctionCall::Open(a) => FsRequest::Open {
path: a.path.as_str(),
mode: a.mode,
},
OsFunctionCall::Getenv(_)
| OsFunctionCall::GetEnviron
| OsFunctionCall::DateToday
| OsFunctionCall::DateTimeNow(_) => unreachable!("non-filesystem OS function reached filesystem parser"),
OsFunctionCall::Used => unreachable!("OsFunctionCall::Used reached filesystem parser"),
}
}
pub(super) fn execute(
request: FsRequest<'_>,
ctx: &mut MountContext<'_>,
mode: &mut MountMode,
) -> Result<MontyObject, MountError> {
if request.is_write() && matches!(mode, MountMode::ReadOnly) {
Err(MountError::ReadOnly(request.primary_path().to_owned()))
} else {
match mode {
MountMode::ReadWrite | MountMode::ReadOnly => direct::execute(request, ctx),
MountMode::OverlayMemory(state) => overlay::execute(request, ctx, state),
}
}
}
pub(super) fn file_handle_result(path: &str, mode: FileMode) -> MontyObject {
MontyObject::FileHandle(MontyFileHandle {
path: normalize_virtual_path(path),
mode,
position: 0,
})
}