summaryrefslogtreecommitdiff
path: root/ext/fs/ops.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/fs/ops.rs')
-rw-r--r--ext/fs/ops.rs397
1 files changed, 253 insertions, 144 deletions
diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs
index b13d3a7d1..e3a511f8e 100644
--- a/ext/fs/ops.rs
+++ b/ext/fs/ops.rs
@@ -1,6 +1,8 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::cell::RefCell;
+use std::error::Error;
+use std::fmt::Formatter;
use std::io;
use std::io::SeekFrom;
use std::path::Path;
@@ -8,10 +10,13 @@ use std::path::PathBuf;
use std::path::StripPrefixError;
use std::rc::Rc;
-use deno_core::anyhow::bail;
-use deno_core::error::custom_error;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
+use crate::interface::AccessCheckFn;
+use crate::interface::FileSystemRc;
+use crate::interface::FsDirEntry;
+use crate::interface::FsFileType;
+use crate::FsPermissions;
+use crate::OpenOptions;
+use boxed_error::Boxed;
use deno_core::op2;
use deno_core::CancelFuture;
use deno_core::CancelHandle;
@@ -22,17 +27,76 @@ use deno_core::ToJsBuffer;
use deno_io::fs::FileResource;
use deno_io::fs::FsError;
use deno_io::fs::FsStat;
+use deno_permissions::PermissionCheckError;
use rand::rngs::ThreadRng;
use rand::thread_rng;
use rand::Rng;
use serde::Serialize;
-use crate::interface::AccessCheckFn;
-use crate::interface::FileSystemRc;
-use crate::interface::FsDirEntry;
-use crate::interface::FsFileType;
-use crate::FsPermissions;
-use crate::OpenOptions;
+#[derive(Debug, Boxed)]
+pub struct FsOpsError(pub Box<FsOpsErrorKind>);
+
+#[derive(Debug, thiserror::Error)]
+pub enum FsOpsErrorKind {
+ #[error("{0}")]
+ Io(#[source] std::io::Error),
+ #[error("{0}")]
+ OperationError(#[source] OperationError),
+ #[error(transparent)]
+ Permission(#[from] PermissionCheckError),
+ #[error(transparent)]
+ Resource(deno_core::error::AnyError),
+ #[error("File name or path {0:?} is not valid UTF-8")]
+ InvalidUtf8(std::ffi::OsString),
+ #[error("{0}")]
+ StripPrefix(#[from] StripPrefixError),
+ #[error("{0}")]
+ Canceled(#[from] deno_core::Canceled),
+ #[error("Invalid seek mode: {0}")]
+ InvalidSeekMode(i32),
+ #[error("Invalid control character in prefix or suffix: {0:?}")]
+ InvalidControlCharacter(String),
+ #[error("Invalid character in prefix or suffix: {0:?}")]
+ InvalidCharacter(String),
+ #[cfg(windows)]
+ #[error("Invalid trailing character in suffix")]
+ InvalidTrailingCharacter,
+ #[error("Requires {err} access to {path}, {}", print_not_capable_info(*.standalone, .err))]
+ NotCapableAccess {
+ // NotCapable
+ standalone: bool,
+ err: &'static str,
+ path: String,
+ },
+ #[error("permission denied: {0}")]
+ NotCapable(&'static str), // NotCapable
+ #[error(transparent)]
+ Other(deno_core::error::AnyError),
+}
+
+impl From<FsError> for FsOpsError {
+ fn from(err: FsError) -> Self {
+ match err {
+ FsError::Io(err) => FsOpsErrorKind::Io(err),
+ FsError::FileBusy => {
+ FsOpsErrorKind::Other(deno_core::error::resource_unavailable())
+ }
+ FsError::NotSupported => {
+ FsOpsErrorKind::Other(deno_core::error::not_supported())
+ }
+ FsError::NotCapable(err) => FsOpsErrorKind::NotCapable(err),
+ }
+ .into_box()
+ }
+}
+
+fn print_not_capable_info(standalone: bool, err: &'static str) -> String {
+ if standalone {
+ format!("specify the required permissions during compilation using `deno compile --allow-{err}`")
+ } else {
+ format!("run again with the --allow-{err} flag")
+ }
+}
fn sync_permission_check<'a, P: FsPermissions + 'static>(
permissions: &'a mut P,
@@ -58,7 +122,7 @@ fn map_permission_error(
operation: &'static str,
error: FsError,
path: &Path,
-) -> AnyError {
+) -> FsOpsError {
match error {
FsError::NotCapable(err) => {
let path = format!("{path:?}");
@@ -67,14 +131,13 @@ fn map_permission_error(
} else {
(path.as_str(), "")
};
- let msg = if deno_permissions::is_standalone() {
- format!(
- "Requires {err} access to {path}{truncated}, specify the required permissions during compilation using `deno compile --allow-{err}`")
- } else {
- format!(
- "Requires {err} access to {path}{truncated}, run again with the --allow-{err} flag")
- };
- custom_error("NotCapable", msg)
+
+ FsOpsErrorKind::NotCapableAccess {
+ standalone: deno_permissions::is_standalone(),
+ err,
+ path: format!("{path}{truncated}"),
+ }
+ .into_box()
}
err => Err::<(), _>(err)
.context_path(operation, path)
@@ -85,7 +148,7 @@ fn map_permission_error(
#[op2]
#[string]
-pub fn op_fs_cwd<P>(state: &mut OpState) -> Result<String, AnyError>
+pub fn op_fs_cwd<P>(state: &mut OpState) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -102,7 +165,7 @@ where
pub fn op_fs_chdir<P>(
state: &mut OpState,
#[string] directory: &str,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -119,7 +182,7 @@ where
pub fn op_fs_umask(
state: &mut OpState,
mask: Option<u32>,
-) -> Result<u32, AnyError>
+) -> Result<u32, FsOpsError>
where
{
state.borrow::<FileSystemRc>().umask(mask).context("umask")
@@ -131,7 +194,7 @@ pub fn op_fs_open_sync<P>(
state: &mut OpState,
#[string] path: String,
#[serde] options: Option<OpenOptions>,
-) -> Result<ResourceId, AnyError>
+) -> Result<ResourceId, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -158,7 +221,7 @@ pub async fn op_fs_open_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
#[serde] options: Option<OpenOptions>,
-) -> Result<ResourceId, AnyError>
+) -> Result<ResourceId, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -186,7 +249,7 @@ pub fn op_fs_mkdir_sync<P>(
#[string] path: String,
recursive: bool,
mode: Option<u32>,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -209,7 +272,7 @@ pub async fn op_fs_mkdir_async<P>(
#[string] path: String,
recursive: bool,
mode: Option<u32>,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -233,7 +296,7 @@ pub fn op_fs_chmod_sync<P>(
state: &mut OpState,
#[string] path: String,
mode: u32,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -250,7 +313,7 @@ pub async fn op_fs_chmod_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
mode: u32,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -271,7 +334,7 @@ pub fn op_fs_chown_sync<P>(
#[string] path: String,
uid: Option<u32>,
gid: Option<u32>,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -290,7 +353,7 @@ pub async fn op_fs_chown_async<P>(
#[string] path: String,
uid: Option<u32>,
gid: Option<u32>,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -310,7 +373,7 @@ pub fn op_fs_remove_sync<P>(
state: &mut OpState,
#[string] path: &str,
recursive: bool,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -330,7 +393,7 @@ pub async fn op_fs_remove_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
recursive: bool,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -361,7 +424,7 @@ pub fn op_fs_copy_file_sync<P>(
state: &mut OpState,
#[string] from: &str,
#[string] to: &str,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -381,7 +444,7 @@ pub async fn op_fs_copy_file_async<P>(
state: Rc<RefCell<OpState>>,
#[string] from: String,
#[string] to: String,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -405,7 +468,7 @@ pub fn op_fs_stat_sync<P>(
state: &mut OpState,
#[string] path: String,
#[buffer] stat_out_buf: &mut [u32],
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -424,7 +487,7 @@ where
pub async fn op_fs_stat_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
-) -> Result<SerializableStat, AnyError>
+) -> Result<SerializableStat, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -446,7 +509,7 @@ pub fn op_fs_lstat_sync<P>(
state: &mut OpState,
#[string] path: String,
#[buffer] stat_out_buf: &mut [u32],
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -465,7 +528,7 @@ where
pub async fn op_fs_lstat_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
-) -> Result<SerializableStat, AnyError>
+) -> Result<SerializableStat, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -487,7 +550,7 @@ where
pub fn op_fs_realpath_sync<P>(
state: &mut OpState,
#[string] path: String,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -510,7 +573,7 @@ where
pub async fn op_fs_realpath_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -538,7 +601,7 @@ where
pub fn op_fs_read_dir_sync<P>(
state: &mut OpState,
#[string] path: String,
-) -> Result<Vec<FsDirEntry>, AnyError>
+) -> Result<Vec<FsDirEntry>, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -557,7 +620,7 @@ where
pub async fn op_fs_read_dir_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
-) -> Result<Vec<FsDirEntry>, AnyError>
+) -> Result<Vec<FsDirEntry>, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -582,7 +645,7 @@ pub fn op_fs_rename_sync<P>(
state: &mut OpState,
#[string] oldpath: String,
#[string] newpath: String,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -603,7 +666,7 @@ pub async fn op_fs_rename_async<P>(
state: Rc<RefCell<OpState>>,
#[string] oldpath: String,
#[string] newpath: String,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -628,7 +691,7 @@ pub fn op_fs_link_sync<P>(
state: &mut OpState,
#[string] oldpath: &str,
#[string] newpath: &str,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -650,7 +713,7 @@ pub async fn op_fs_link_async<P>(
state: Rc<RefCell<OpState>>,
#[string] oldpath: String,
#[string] newpath: String,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -677,7 +740,7 @@ pub fn op_fs_symlink_sync<P>(
#[string] oldpath: &str,
#[string] newpath: &str,
#[serde] file_type: Option<FsFileType>,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -701,7 +764,7 @@ pub async fn op_fs_symlink_async<P>(
#[string] oldpath: String,
#[string] newpath: String,
#[serde] file_type: Option<FsFileType>,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -728,7 +791,7 @@ where
pub fn op_fs_read_link_sync<P>(
state: &mut OpState,
#[string] path: String,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -748,7 +811,7 @@ where
pub async fn op_fs_read_link_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -773,7 +836,7 @@ pub fn op_fs_truncate_sync<P>(
state: &mut OpState,
#[string] path: &str,
#[number] len: u64,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -793,7 +856,7 @@ pub async fn op_fs_truncate_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
#[number] len: u64,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -820,7 +883,7 @@ pub fn op_fs_utime_sync<P>(
#[smi] atime_nanos: u32,
#[number] mtime_secs: i64,
#[smi] mtime_nanos: u32,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -841,7 +904,7 @@ pub async fn op_fs_utime_async<P>(
#[smi] atime_nanos: u32,
#[number] mtime_secs: i64,
#[smi] mtime_nanos: u32,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -871,7 +934,7 @@ pub fn op_fs_make_temp_dir_sync<P>(
#[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -913,7 +976,7 @@ pub async fn op_fs_make_temp_dir_async<P>(
#[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -959,7 +1022,7 @@ pub fn op_fs_make_temp_file_sync<P>(
#[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1007,7 +1070,7 @@ pub async fn op_fs_make_temp_file_async<P>(
#[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1069,7 +1132,7 @@ fn make_temp_check_sync<P>(
state: &mut OpState,
dir: Option<&str>,
api_name: &str,
-) -> Result<(PathBuf, FileSystemRc), AnyError>
+) -> Result<(PathBuf, FileSystemRc), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1091,7 +1154,7 @@ fn make_temp_check_async<P>(
state: Rc<RefCell<OpState>>,
dir: Option<&str>,
api_name: &str,
-) -> Result<(PathBuf, FileSystemRc), AnyError>
+) -> Result<(PathBuf, FileSystemRc), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1116,10 +1179,12 @@ where
fn validate_temporary_filename_component(
component: &str,
#[allow(unused_variables)] suffix: bool,
-) -> Result<(), AnyError> {
+) -> Result<(), FsOpsError> {
// Ban ASCII and Unicode control characters: these will often fail
if let Some(c) = component.matches(|c: char| c.is_control()).next() {
- bail!("Invalid control character in prefix or suffix: {:?}", c);
+ return Err(
+ FsOpsErrorKind::InvalidControlCharacter(c.to_string()).into_box(),
+ );
}
// Windows has the most restrictive filenames. As temp files aren't normal files, we just
// use this set of banned characters for all platforms because wildcard-like files can also
@@ -1135,13 +1200,13 @@ fn validate_temporary_filename_component(
.matches(|c: char| "<>:\"/\\|?*".contains(c))
.next()
{
- bail!("Invalid character in prefix or suffix: {:?}", c);
+ return Err(FsOpsErrorKind::InvalidCharacter(c.to_string()).into_box());
}
// This check is only for Windows
#[cfg(windows)]
if suffix && component.ends_with(|c: char| ". ".contains(c)) {
- bail!("Invalid trailing character in suffix");
+ return Err(FsOpsErrorKind::InvalidTrailingCharacter.into_box());
}
Ok(())
@@ -1152,7 +1217,7 @@ fn tmp_name(
dir: &Path,
prefix: Option<&str>,
suffix: Option<&str>,
-) -> Result<PathBuf, AnyError> {
+) -> Result<PathBuf, FsOpsError> {
let prefix = prefix.unwrap_or("");
validate_temporary_filename_component(prefix, false)?;
let suffix = suffix.unwrap_or("");
@@ -1179,7 +1244,7 @@ pub fn op_fs_write_file_sync<P>(
create: bool,
create_new: bool,
#[buffer] data: JsBuffer,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1207,7 +1272,7 @@ pub async fn op_fs_write_file_async<P>(
create_new: bool,
#[buffer] data: JsBuffer,
#[smi] cancel_rid: Option<ResourceId>,
-) -> Result<(), AnyError>
+) -> Result<(), FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1255,7 +1320,7 @@ where
pub fn op_fs_read_file_sync<P>(
state: &mut OpState,
#[string] path: String,
-) -> Result<ToJsBuffer, AnyError>
+) -> Result<ToJsBuffer, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1277,7 +1342,7 @@ pub async fn op_fs_read_file_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
#[smi] cancel_rid: Option<ResourceId>,
-) -> Result<ToJsBuffer, AnyError>
+) -> Result<ToJsBuffer, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1318,7 +1383,7 @@ where
pub fn op_fs_read_file_text_sync<P>(
state: &mut OpState,
#[string] path: String,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1340,7 +1405,7 @@ pub async fn op_fs_read_file_text_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
#[smi] cancel_rid: Option<ResourceId>,
-) -> Result<String, AnyError>
+) -> Result<String, FsOpsError>
where
P: FsPermissions + 'static,
{
@@ -1377,13 +1442,13 @@ where
Ok(str)
}
-fn to_seek_from(offset: i64, whence: i32) -> Result<SeekFrom, AnyError> {
+fn to_seek_from(offset: i64, whence: i32) -> Result<SeekFrom, FsOpsError> {
let seek_from = match whence {
0 => SeekFrom::Start(offset as u64),
1 => SeekFrom::Current(offset),
2 => SeekFrom::End(offset),
_ => {
- return Err(type_error(format!("Invalid seek mode: {whence}")));
+ return Err(FsOpsErrorKind::InvalidSeekMode(whence).into_box());
}
};
Ok(seek_from)
@@ -1396,9 +1461,10 @@ pub fn op_fs_seek_sync(
#[smi] rid: ResourceId,
#[number] offset: i64,
#[smi] whence: i32,
-) -> Result<u64, AnyError> {
+) -> Result<u64, FsOpsError> {
let pos = to_seek_from(offset, whence)?;
- let file = FileResource::get_file(state, rid)?;
+ let file =
+ FileResource::get_file(state, rid).map_err(FsOpsErrorKind::Resource)?;
let cursor = file.seek_sync(pos)?;
Ok(cursor)
}
@@ -1410,9 +1476,10 @@ pub async fn op_fs_seek_async(
#[smi] rid: ResourceId,
#[number] offset: i64,
#[smi] whence: i32,
-) -> Result<u64, AnyError> {
+) -> Result<u64, FsOpsError> {
let pos = to_seek_from(offset, whence)?;
- let file = FileResource::get_file(&state.borrow(), rid)?;
+ let file = FileResource::get_file(&state.borrow(), rid)
+ .map_err(FsOpsErrorKind::Resource)?;
let cursor = file.seek_async(pos).await?;
Ok(cursor)
}
@@ -1421,8 +1488,9 @@ pub async fn op_fs_seek_async(
pub fn op_fs_file_sync_data_sync(
state: &mut OpState,
#[smi] rid: ResourceId,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(state, rid)?;
+) -> Result<(), FsOpsError> {
+ let file =
+ FileResource::get_file(state, rid).map_err(FsOpsErrorKind::Resource)?;
file.datasync_sync()?;
Ok(())
}
@@ -1431,8 +1499,9 @@ pub fn op_fs_file_sync_data_sync(
pub async fn op_fs_file_sync_data_async(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(&state.borrow(), rid)?;
+) -> Result<(), FsOpsError> {
+ let file = FileResource::get_file(&state.borrow(), rid)
+ .map_err(FsOpsErrorKind::Resource)?;
file.datasync_async().await?;
Ok(())
}
@@ -1441,8 +1510,9 @@ pub async fn op_fs_file_sync_data_async(
pub fn op_fs_file_sync_sync(
state: &mut OpState,
#[smi] rid: ResourceId,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(state, rid)?;
+) -> Result<(), FsOpsError> {
+ let file =
+ FileResource::get_file(state, rid).map_err(FsOpsErrorKind::Resource)?;
file.sync_sync()?;
Ok(())
}
@@ -1451,8 +1521,9 @@ pub fn op_fs_file_sync_sync(
pub async fn op_fs_file_sync_async(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(&state.borrow(), rid)?;
+) -> Result<(), FsOpsError> {
+ let file = FileResource::get_file(&state.borrow(), rid)
+ .map_err(FsOpsErrorKind::Resource)?;
file.sync_async().await?;
Ok(())
}
@@ -1462,8 +1533,9 @@ pub fn op_fs_file_stat_sync(
state: &mut OpState,
#[smi] rid: ResourceId,
#[buffer] stat_out_buf: &mut [u32],
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(state, rid)?;
+) -> Result<(), FsOpsError> {
+ let file =
+ FileResource::get_file(state, rid).map_err(FsOpsErrorKind::Resource)?;
let stat = file.stat_sync()?;
let serializable_stat = SerializableStat::from(stat);
serializable_stat.write(stat_out_buf);
@@ -1475,8 +1547,9 @@ pub fn op_fs_file_stat_sync(
pub async fn op_fs_file_stat_async(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<SerializableStat, AnyError> {
- let file = FileResource::get_file(&state.borrow(), rid)?;
+) -> Result<SerializableStat, FsOpsError> {
+ let file = FileResource::get_file(&state.borrow(), rid)
+ .map_err(FsOpsErrorKind::Resource)?;
let stat = file.stat_async().await?;
Ok(stat.into())
}
@@ -1486,8 +1559,9 @@ pub fn op_fs_flock_sync(
state: &mut OpState,
#[smi] rid: ResourceId,
exclusive: bool,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(state, rid)?;
+) -> Result<(), FsOpsError> {
+ let file =
+ FileResource::get_file(state, rid).map_err(FsOpsErrorKind::Resource)?;
file.lock_sync(exclusive)?;
Ok(())
}
@@ -1497,8 +1571,9 @@ pub async fn op_fs_flock_async(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
exclusive: bool,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(&state.borrow(), rid)?;
+) -> Result<(), FsOpsError> {
+ let file = FileResource::get_file(&state.borrow(), rid)
+ .map_err(FsOpsErrorKind::Resource)?;
file.lock_async(exclusive).await?;
Ok(())
}
@@ -1507,8 +1582,9 @@ pub async fn op_fs_flock_async(
pub fn op_fs_funlock_sync(
state: &mut OpState,
#[smi] rid: ResourceId,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(state, rid)?;
+) -> Result<(), FsOpsError> {
+ let file =
+ FileResource::get_file(state, rid).map_err(FsOpsErrorKind::Resource)?;
file.unlock_sync()?;
Ok(())
}
@@ -1517,8 +1593,9 @@ pub fn op_fs_funlock_sync(
pub async fn op_fs_funlock_async(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(&state.borrow(), rid)?;
+) -> Result<(), FsOpsError> {
+ let file = FileResource::get_file(&state.borrow(), rid)
+ .map_err(FsOpsErrorKind::Resource)?;
file.unlock_async().await?;
Ok(())
}
@@ -1528,8 +1605,9 @@ pub fn op_fs_ftruncate_sync(
state: &mut OpState,
#[smi] rid: ResourceId,
#[number] len: u64,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(state, rid)?;
+) -> Result<(), FsOpsError> {
+ let file =
+ FileResource::get_file(state, rid).map_err(FsOpsErrorKind::Resource)?;
file.truncate_sync(len)?;
Ok(())
}
@@ -1539,8 +1617,9 @@ pub async fn op_fs_file_truncate_async(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
#[number] len: u64,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(&state.borrow(), rid)?;
+) -> Result<(), FsOpsError> {
+ let file = FileResource::get_file(&state.borrow(), rid)
+ .map_err(FsOpsErrorKind::Resource)?;
file.truncate_async(len).await?;
Ok(())
}
@@ -1553,8 +1632,9 @@ pub fn op_fs_futime_sync(
#[smi] atime_nanos: u32,
#[number] mtime_secs: i64,
#[smi] mtime_nanos: u32,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(state, rid)?;
+) -> Result<(), FsOpsError> {
+ let file =
+ FileResource::get_file(state, rid).map_err(FsOpsErrorKind::Resource)?;
file.utime_sync(atime_secs, atime_nanos, mtime_secs, mtime_nanos)?;
Ok(())
}
@@ -1567,42 +1647,64 @@ pub async fn op_fs_futime_async(
#[smi] atime_nanos: u32,
#[number] mtime_secs: i64,
#[smi] mtime_nanos: u32,
-) -> Result<(), AnyError> {
- let file = FileResource::get_file(&state.borrow(), rid)?;
+) -> Result<(), FsOpsError> {
+ let file = FileResource::get_file(&state.borrow(), rid)
+ .map_err(FsOpsErrorKind::Resource)?;
file
.utime_async(atime_secs, atime_nanos, mtime_secs, mtime_nanos)
.await?;
Ok(())
}
-trait WithContext {
- fn context<E: Into<Box<dyn std::error::Error + Send + Sync>>>(
- self,
- desc: E,
- ) -> AnyError;
+#[derive(Debug)]
+pub struct OperationError {
+ operation: &'static str,
+ kind: OperationErrorKind,
+ pub err: FsError,
}
-impl WithContext for FsError {
- fn context<E: Into<Box<dyn std::error::Error + Send + Sync>>>(
- self,
- desc: E,
- ) -> AnyError {
- match self {
- FsError::Io(io) => {
- AnyError::new(io::Error::new(io.kind(), desc)).context(io)
+impl std::fmt::Display for OperationError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ if let FsError::Io(e) = &self.err {
+ std::fmt::Display::fmt(&e, f)?;
+ f.write_str(": ")?;
+ }
+
+ f.write_str(self.operation)?;
+
+ match &self.kind {
+ OperationErrorKind::Bare => Ok(()),
+ OperationErrorKind::WithPath(path) => write!(f, " '{}'", path.display()),
+ OperationErrorKind::WithTwoPaths(from, to) => {
+ write!(f, " '{}' -> '{}'", from.display(), to.display())
}
- _ => self.into(),
}
}
}
+impl std::error::Error for OperationError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ if let FsError::Io(err) = &self.err {
+ Some(err)
+ } else {
+ None
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum OperationErrorKind {
+ Bare,
+ WithPath(PathBuf),
+ WithTwoPaths(PathBuf, PathBuf),
+}
+
trait MapErrContext {
type R;
- fn context_fn<F, E>(self, f: F) -> Self::R
+ fn context_fn<F>(self, f: F) -> Self::R
where
- F: FnOnce() -> E,
- E: Into<Box<dyn std::error::Error + Send + Sync>>;
+ F: FnOnce(FsError) -> OperationError;
fn context(self, desc: &'static str) -> Self::R;
@@ -1617,25 +1719,29 @@ trait MapErrContext {
}
impl<T> MapErrContext for Result<T, FsError> {
- type R = Result<T, AnyError>;
+ type R = Result<T, FsOpsError>;
- fn context_fn<F, E>(self, f: F) -> Self::R
+ fn context_fn<F>(self, f: F) -> Self::R
where
- F: FnOnce() -> E,
- E: Into<Box<dyn std::error::Error + Send + Sync>>,
+ F: FnOnce(FsError) -> OperationError,
{
- self.map_err(|err| {
- let message = f();
- err.context(message)
- })
+ self.map_err(|err| FsOpsErrorKind::OperationError(f(err)).into_box())
}
- fn context(self, desc: &'static str) -> Self::R {
- self.context_fn(move || desc)
+ fn context(self, operation: &'static str) -> Self::R {
+ self.context_fn(move |err| OperationError {
+ operation,
+ kind: OperationErrorKind::Bare,
+ err,
+ })
}
fn context_path(self, operation: &'static str, path: &Path) -> Self::R {
- self.context_fn(|| format!("{operation} '{}'", path.display()))
+ self.context_fn(|err| OperationError {
+ operation,
+ kind: OperationErrorKind::WithPath(path.to_path_buf()),
+ err,
+ })
}
fn context_two_path(
@@ -1644,21 +1750,20 @@ impl<T> MapErrContext for Result<T, FsError> {
oldpath: &Path,
newpath: &Path,
) -> Self::R {
- self.context_fn(|| {
- format!(
- "{operation} '{}' -> '{}'",
- oldpath.display(),
- newpath.display()
- )
+ self.context_fn(|err| OperationError {
+ operation,
+ kind: OperationErrorKind::WithTwoPaths(
+ oldpath.to_path_buf(),
+ newpath.to_path_buf(),
+ ),
+ err,
})
}
}
-fn path_into_string(s: std::ffi::OsString) -> Result<String, AnyError> {
- s.into_string().map_err(|s| {
- let message = format!("File name or path {s:?} is not valid UTF-8");
- custom_error("InvalidData", message)
- })
+fn path_into_string(s: std::ffi::OsString) -> Result<String, FsOpsError> {
+ s.into_string()
+ .map_err(|e| FsOpsErrorKind::InvalidUtf8(e).into_box())
}
macro_rules! create_struct_writer {
@@ -1699,6 +1804,8 @@ create_struct_writer! {
atime: u64,
birthtime_set: bool,
birthtime: u64,
+ ctime_set: bool,
+ ctime: u64,
// Following are only valid under Unix.
dev: u64,
ino: u64,
@@ -1730,6 +1837,8 @@ impl From<FsStat> for SerializableStat {
atime: stat.atime.unwrap_or(0),
birthtime_set: stat.birthtime.is_some(),
birthtime: stat.birthtime.unwrap_or(0),
+ ctime_set: stat.ctime.is_some(),
+ ctime: stat.ctime.unwrap_or(0),
dev: stat.dev,
ino: stat.ino,