use monty_types::{FileMode, MontyFileHandle, MontyObject, MontyPath, OsFunctionCall};
use super::{
common::MountContext, direct, error::MountError, mount_mode::MountMode, overlay,
path_security::normalize_virtual_path,
};
#[derive(Debug)]
pub(super) enum FsRequest {
Exists { path: MontyPath },
IsFile { path: MontyPath },
IsDir { path: MontyPath },
IsSymlink { path: MontyPath },
ReadText { path: MontyPath },
ReadBytes { path: MontyPath },
WriteText { path: MontyPath, data: String },
WriteBytes { path: MontyPath, data: Vec<u8> },
AppendText { path: MontyPath, data: String },
AppendBytes { path: MontyPath, data: Vec<u8> },
Mkdir {
path: MontyPath,
parents: bool,
exist_ok: bool,
},
Unlink { path: MontyPath },
Rmdir { path: MontyPath },
Iterdir { path: MontyPath },
Stat { path: MontyPath },
Rename { src: MontyPath, dst: MontyPath },
Resolve { path: MontyPath },
Absolute { path: MontyPath },
Open {
path: MontyPath,
mode: FileMode,
},
}
impl FsRequest {
#[must_use]
pub fn primary_path(&self) -> &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 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(path) => FsRequest::Exists { path },
OsFunctionCall::IsFile(path) => FsRequest::IsFile { path },
OsFunctionCall::IsDir(path) => FsRequest::IsDir { path },
OsFunctionCall::IsSymlink(path) => FsRequest::IsSymlink { path },
OsFunctionCall::ReadText(path) => FsRequest::ReadText { path },
OsFunctionCall::ReadBytes(path) => FsRequest::ReadBytes { path },
OsFunctionCall::WriteText(a) => FsRequest::WriteText {
path: a.path,
data: a.data,
},
OsFunctionCall::WriteBytes(a) => FsRequest::WriteBytes {
path: a.path,
data: a.data,
},
OsFunctionCall::AppendText(a) => FsRequest::AppendText {
path: a.path,
data: a.data,
},
OsFunctionCall::AppendBytes(a) => FsRequest::AppendBytes {
path: a.path,
data: a.data,
},
OsFunctionCall::Mkdir(a) => FsRequest::Mkdir {
path: a.path,
parents: a.parents,
exist_ok: a.exist_ok,
},
OsFunctionCall::Unlink(path) => FsRequest::Unlink { path },
OsFunctionCall::Rmdir(path) => FsRequest::Rmdir { path },
OsFunctionCall::Iterdir(path) => FsRequest::Iterdir { path },
OsFunctionCall::Stat(path) => FsRequest::Stat { path },
OsFunctionCall::Rename(a) => FsRequest::Rename { src: a.src, dst: a.dst },
OsFunctionCall::Resolve(path) => FsRequest::Resolve { path },
OsFunctionCall::Absolute(path) => FsRequest::Absolute { path },
OsFunctionCall::Open(a) => FsRequest::Open {
path: a.path,
mode: a.mode,
},
OsFunctionCall::Getenv(_)
| OsFunctionCall::GetEnviron
| OsFunctionCall::DateToday
| OsFunctionCall::DateTimeNow(_) => unreachable!("non-filesystem OS function 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,
})
}