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.rs1712
1 files changed, 1712 insertions, 0 deletions
diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs
new file mode 100644
index 000000000..c1381d89c
--- /dev/null
+++ b/ext/fs/ops.rs
@@ -0,0 +1,1712 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::io;
+use std::io::SeekFrom;
+use std::path::Path;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+use deno_core::error::custom_error;
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::op;
+use deno_core::CancelFuture;
+use deno_core::CancelHandle;
+use deno_core::OpState;
+use deno_core::ResourceId;
+use deno_core::ZeroCopyBuf;
+use rand::rngs::ThreadRng;
+use rand::thread_rng;
+use rand::Rng;
+use serde::Serialize;
+
+use crate::check_unstable;
+use crate::check_unstable2;
+use crate::interface::FsDirEntry;
+use crate::interface::FsError;
+use crate::interface::FsFileType;
+use crate::interface::FsStat;
+use crate::File;
+use crate::FileSystem;
+use crate::FsPermissions;
+use crate::OpenOptions;
+
+#[op]
+pub fn op_cwd<Fs, P>(state: &mut OpState) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let fs = state.borrow::<Fs>();
+ let path = fs.cwd()?;
+ state
+ .borrow_mut::<P>()
+ .check_read_blind(&path, "CWD", "Deno.cwd()")?;
+ let path_str = path_into_string(path.into_os_string())?;
+ Ok(path_str)
+}
+
+#[op]
+fn op_chdir<Fs, P>(state: &mut OpState, directory: &str) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let d = PathBuf::from(&directory);
+ state.borrow_mut::<P>().check_read(&d, "Deno.chdir()")?;
+ state.borrow::<Fs>().chdir(&d).context_path("chdir", &d)
+}
+
+#[op]
+fn op_umask<Fs>(state: &mut OpState, mask: Option<u32>) -> Result<u32, AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ check_unstable(state, "Deno.umask");
+ state.borrow::<Fs>().umask(mask).context("umask")
+}
+
+#[op]
+fn op_open_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+ options: Option<OpenOptions>,
+) -> Result<ResourceId, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let options = options.unwrap_or_else(OpenOptions::read);
+ let permissions = state.borrow_mut::<P>();
+ options.check(permissions, &path, "Deno.openSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ let file = fs.open_sync(&path, options).context_path("open", &path)?;
+
+ let rid = state.resource_table.add(file);
+ Ok(rid)
+}
+
+#[op]
+async fn op_open_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ options: Option<OpenOptions>,
+) -> Result<ResourceId, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let options = options.unwrap_or_else(OpenOptions::read);
+ let fs = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ options.check(permissions, &path, "Deno.open()")?;
+ state.borrow::<Fs>().clone()
+ };
+ let file = fs
+ .open_async(path.clone(), options)
+ .await
+ .context_path("open", &path)?;
+
+ let rid = state.borrow_mut().resource_table.add(file);
+ Ok(rid)
+}
+
+#[op]
+fn op_mkdir_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+ recursive: bool,
+ mode: Option<u32>,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let mode = mode.unwrap_or(0o777) & 0o777;
+
+ state
+ .borrow_mut::<P>()
+ .check_write(&path, "Deno.mkdirSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ fs.mkdir_sync(&path, recursive, mode)
+ .context_path("mkdir", &path)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_mkdir_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ recursive: bool,
+ mode: Option<u32>,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let mode = mode.unwrap_or(0o777) & 0o777;
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ fs.mkdir_async(path.clone(), recursive, mode)
+ .await
+ .context_path("mkdir", &path)?;
+
+ Ok(())
+}
+
+#[op]
+fn op_chmod_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+ mode: u32,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ state
+ .borrow_mut::<P>()
+ .check_write(&path, "Deno.chmodSync()")?;
+ let fs = state.borrow::<Fs>();
+ fs.chmod_sync(&path, mode).context_path("chmod", &path)?;
+ Ok(())
+}
+
+#[op]
+async fn op_chmod_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ mode: u32,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ let fs = {
+ let mut state = state.borrow_mut();
+ state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
+ state.borrow::<Fs>().clone()
+ };
+ fs.chmod_async(path.clone(), mode)
+ .await
+ .context_path("chmod", &path)?;
+ Ok(())
+}
+
+#[op]
+fn op_chown_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+ uid: Option<u32>,
+ gid: Option<u32>,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ state
+ .borrow_mut::<P>()
+ .check_write(&path, "Deno.chownSync()")?;
+ let fs = state.borrow::<Fs>();
+ fs.chown_sync(&path, uid, gid)
+ .context_path("chown", &path)?;
+ Ok(())
+}
+
+#[op]
+async fn op_chown_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ uid: Option<u32>,
+ gid: Option<u32>,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ let fs = {
+ let mut state = state.borrow_mut();
+ state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
+ state.borrow::<Fs>().clone()
+ };
+ fs.chown_async(path.clone(), uid, gid)
+ .await
+ .context_path("chown", &path)?;
+ Ok(())
+}
+
+#[op]
+fn op_remove_sync<Fs, P>(
+ state: &mut OpState,
+ path: &str,
+ recursive: bool,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ state
+ .borrow_mut::<P>()
+ .check_write(&path, "Deno.removeSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ fs.remove_sync(&path, recursive)
+ .context_path("remove", &path)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_remove_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ recursive: bool,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ state
+ .borrow_mut::<P>()
+ .check_write(&path, "Deno.remove()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ fs.remove_async(path.clone(), recursive)
+ .await
+ .context_path("remove", &path)?;
+
+ Ok(())
+}
+
+#[op]
+fn op_copy_file_sync<Fs, P>(
+ state: &mut OpState,
+ from: &str,
+ to: &str,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let from = PathBuf::from(from);
+ let to = PathBuf::from(to);
+
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&from, "Deno.copyFileSync()")?;
+ permissions.check_write(&to, "Deno.copyFileSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ fs.copy_file_sync(&from, &to)
+ .context_two_path("copy", &from, &to)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_copy_file_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ from: String,
+ to: String,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let from = PathBuf::from(from);
+ let to = PathBuf::from(to);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&from, "Deno.copyFile()")?;
+ permissions.check_write(&to, "Deno.copyFile()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ fs.copy_file_async(from.clone(), to.clone())
+ .await
+ .context_two_path("copy", &from, &to)?;
+
+ Ok(())
+}
+
+#[op]
+fn op_stat_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+ stat_out_buf: &mut [u32],
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ state
+ .borrow_mut::<P>()
+ .check_read(&path, "Deno.statSync()")?;
+ let fs = state.borrow::<Fs>();
+ let stat = fs.stat_sync(&path).context_path("stat", &path)?;
+ let serializable_stat = SerializableStat::from(stat);
+ serializable_stat.write(stat_out_buf);
+ Ok(())
+}
+
+#[op]
+async fn op_stat_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+) -> Result<SerializableStat, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ let fs = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&path, "Deno.stat()")?;
+ state.borrow::<Fs>().clone()
+ };
+ let stat = fs
+ .stat_async(path.clone())
+ .await
+ .context_path("stat", &path)?;
+ Ok(SerializableStat::from(stat))
+}
+
+#[op]
+fn op_lstat_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+ stat_out_buf: &mut [u32],
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ state
+ .borrow_mut::<P>()
+ .check_read(&path, "Deno.lstatSync()")?;
+ let fs = state.borrow::<Fs>();
+ let stat = fs.lstat_sync(&path).context_path("lstat", &path)?;
+ let serializable_stat = SerializableStat::from(stat);
+ serializable_stat.write(stat_out_buf);
+ Ok(())
+}
+
+#[op]
+async fn op_lstat_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+) -> Result<SerializableStat, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ let fs = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&path, "Deno.lstat()")?;
+ state.borrow::<Fs>().clone()
+ };
+ let stat = fs
+ .lstat_async(path.clone())
+ .await
+ .context_path("lstat", &path)?;
+ Ok(SerializableStat::from(stat))
+}
+
+#[op]
+fn op_realpath_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let fs = state.borrow::<Fs>().clone();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&path, "Deno.realPathSync()")?;
+ if path.is_relative() {
+ permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPathSync()")?;
+ }
+
+ let resolved_path =
+ fs.realpath_sync(&path).context_path("realpath", &path)?;
+
+ let path_string = path_into_string(resolved_path.into_os_string())?;
+ Ok(path_string)
+}
+
+#[op]
+async fn op_realpath_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let fs;
+ {
+ let mut state = state.borrow_mut();
+ fs = state.borrow::<Fs>().clone();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&path, "Deno.realPath()")?;
+ if path.is_relative() {
+ permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPath()")?;
+ }
+ }
+ let resolved_path = fs
+ .realpath_async(path.clone())
+ .await
+ .context_path("realpath", &path)?;
+
+ let path_string = path_into_string(resolved_path.into_os_string())?;
+ Ok(path_string)
+}
+
+#[op]
+fn op_read_dir_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+) -> Result<Vec<FsDirEntry>, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ state
+ .borrow_mut::<P>()
+ .check_read(&path, "Deno.readDirSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ let entries = fs.read_dir_sync(&path).context_path("readdir", &path)?;
+
+ Ok(entries)
+}
+
+#[op]
+async fn op_read_dir_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+) -> Result<Vec<FsDirEntry>, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ state
+ .borrow_mut::<P>()
+ .check_read(&path, "Deno.readDir()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ let entries = fs
+ .read_dir_async(path.clone())
+ .await
+ .context_path("readdir", &path)?;
+
+ Ok(entries)
+}
+
+#[op]
+fn op_rename_sync<Fs, P>(
+ state: &mut OpState,
+ oldpath: String,
+ newpath: String,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let oldpath = PathBuf::from(oldpath);
+ let newpath = PathBuf::from(newpath);
+
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&oldpath, "Deno.renameSync()")?;
+ permissions.check_write(&oldpath, "Deno.renameSync()")?;
+ permissions.check_write(&newpath, "Deno.renameSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ fs.rename_sync(&oldpath, &newpath)
+ .context_two_path("rename", &oldpath, &newpath)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_rename_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ oldpath: String,
+ newpath: String,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let oldpath = PathBuf::from(oldpath);
+ let newpath = PathBuf::from(newpath);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&oldpath, "Deno.rename()")?;
+ permissions.check_write(&oldpath, "Deno.rename()")?;
+ permissions.check_write(&newpath, "Deno.rename()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ fs.rename_async(oldpath.clone(), newpath.clone())
+ .await
+ .context_two_path("rename", &oldpath, &newpath)?;
+
+ Ok(())
+}
+
+#[op]
+fn op_link_sync<Fs, P>(
+ state: &mut OpState,
+ oldpath: &str,
+ newpath: &str,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let oldpath = PathBuf::from(oldpath);
+ let newpath = PathBuf::from(newpath);
+
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&oldpath, "Deno.linkSync()")?;
+ permissions.check_write(&oldpath, "Deno.linkSync()")?;
+ permissions.check_read(&newpath, "Deno.linkSync()")?;
+ permissions.check_write(&newpath, "Deno.linkSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ fs.link_sync(&oldpath, &newpath)
+ .context_two_path("link", &oldpath, &newpath)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_link_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ oldpath: String,
+ newpath: String,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let oldpath = PathBuf::from(&oldpath);
+ let newpath = PathBuf::from(&newpath);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&oldpath, "Deno.link()")?;
+ permissions.check_write(&oldpath, "Deno.link()")?;
+ permissions.check_read(&newpath, "Deno.link()")?;
+ permissions.check_write(&newpath, "Deno.link()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ fs.link_async(oldpath.clone(), newpath.clone())
+ .await
+ .context_two_path("link", &oldpath, &newpath)?;
+
+ Ok(())
+}
+
+#[op]
+fn op_symlink_sync<Fs, P>(
+ state: &mut OpState,
+ oldpath: &str,
+ newpath: &str,
+ file_type: Option<FsFileType>,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let oldpath = PathBuf::from(oldpath);
+ let newpath = PathBuf::from(newpath);
+
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_write_all("Deno.symlinkSync()")?;
+ permissions.check_read_all("Deno.symlinkSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ fs.symlink_sync(&oldpath, &newpath, file_type)
+ .context_two_path("symlink", &oldpath, &newpath)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_symlink_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ oldpath: String,
+ newpath: String,
+ file_type: Option<FsFileType>,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let oldpath = PathBuf::from(&oldpath);
+ let newpath = PathBuf::from(&newpath);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_write_all("Deno.symlink()")?;
+ permissions.check_read_all("Deno.symlink()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ fs.symlink_async(oldpath.clone(), newpath.clone(), file_type)
+ .await
+ .context_two_path("symlink", &oldpath, &newpath)?;
+
+ Ok(())
+}
+
+#[op]
+fn op_read_link_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ state
+ .borrow_mut::<P>()
+ .check_read(&path, "Deno.readLink()")?;
+
+ let fs = state.borrow::<Fs>();
+
+ let target = fs.read_link_sync(&path).context_path("readlink", &path)?;
+ let target_string = path_into_string(target.into_os_string())?;
+ Ok(target_string)
+}
+
+#[op]
+async fn op_read_link_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ state
+ .borrow_mut::<P>()
+ .check_read(&path, "Deno.readLink()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ let target = fs
+ .read_link_async(path.clone())
+ .await
+ .context_path("readlink", &path)?;
+ let target_string = path_into_string(target.into_os_string())?;
+ Ok(target_string)
+}
+
+#[op]
+fn op_truncate_sync<Fs, P>(
+ state: &mut OpState,
+ path: &str,
+ len: u64,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ state
+ .borrow_mut::<P>()
+ .check_write(&path, "Deno.truncateSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ fs.truncate_sync(&path, len)
+ .context_path("truncate", &path)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_truncate_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ len: u64,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ state
+ .borrow_mut::<P>()
+ .check_write(&path, "Deno.truncate()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ fs.truncate_async(path.clone(), len)
+ .await
+ .context_path("truncate", &path)?;
+
+ Ok(())
+}
+
+#[op]
+fn op_utime_sync<Fs, P>(
+ state: &mut OpState,
+ path: &str,
+ atime_secs: i64,
+ atime_nanos: u32,
+ mtime_secs: i64,
+ mtime_nanos: u32,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
+
+ let fs = state.borrow::<Fs>();
+ fs.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
+ .context_path("utime", &path)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_utime_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ atime_secs: i64,
+ atime_nanos: u32,
+ mtime_secs: i64,
+ mtime_nanos: u32,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
+ state.borrow::<Fs>().clone()
+ };
+
+ fs.utime_async(
+ path.clone(),
+ atime_secs,
+ atime_nanos,
+ mtime_secs,
+ mtime_nanos,
+ )
+ .await
+ .context_path("utime", &path)?;
+
+ Ok(())
+}
+
+#[op]
+fn op_make_temp_dir_sync<Fs, P>(
+ state: &mut OpState,
+ dir: Option<String>,
+ prefix: Option<String>,
+ suffix: Option<String>,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let (dir, fs) = make_temp_check_sync::<Fs, P>(state, dir)?;
+
+ let mut rng = thread_rng();
+
+ const MAX_TRIES: u32 = 10;
+ for _ in 0..MAX_TRIES {
+ let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
+ match fs.mkdir_sync(&path, false, 0o700) {
+ Ok(_) => return path_into_string(path.into_os_string()),
+ Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
+ continue;
+ }
+ Err(e) => return Err(e).context("tmpdir"),
+ }
+ }
+
+ Err(FsError::Io(io::Error::new(
+ io::ErrorKind::AlreadyExists,
+ "too many temp dirs exist",
+ )))
+ .context("tmpdir")
+}
+
+#[op]
+async fn op_make_temp_dir_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ dir: Option<String>,
+ prefix: Option<String>,
+ suffix: Option<String>,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let (dir, fs) = make_temp_check_async::<Fs, P>(state, dir)?;
+
+ let mut rng = thread_rng();
+
+ const MAX_TRIES: u32 = 10;
+ for _ in 0..MAX_TRIES {
+ let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
+ match fs.clone().mkdir_async(path.clone(), false, 0o700).await {
+ Ok(_) => return path_into_string(path.into_os_string()),
+ Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
+ continue;
+ }
+ Err(e) => return Err(e).context("tmpdir"),
+ }
+ }
+
+ Err(FsError::Io(io::Error::new(
+ io::ErrorKind::AlreadyExists,
+ "too many temp dirs exist",
+ )))
+ .context("tmpdir")
+}
+
+#[op]
+fn op_make_temp_file_sync<Fs, P>(
+ state: &mut OpState,
+ dir: Option<String>,
+ prefix: Option<String>,
+ suffix: Option<String>,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let (dir, fs) = make_temp_check_sync::<Fs, P>(state, dir)?;
+
+ let open_opts = OpenOptions {
+ write: true,
+ create_new: true,
+ mode: Some(0o600),
+ ..Default::default()
+ };
+
+ let mut rng = thread_rng();
+
+ const MAX_TRIES: u32 = 10;
+ for _ in 0..MAX_TRIES {
+ let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
+ match fs.open_sync(&path, open_opts) {
+ Ok(_) => return path_into_string(path.into_os_string()),
+ Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
+ continue;
+ }
+ Err(e) => return Err(e).context("tmpfile"),
+ }
+ }
+
+ Err(FsError::Io(io::Error::new(
+ io::ErrorKind::AlreadyExists,
+ "too many temp files exist",
+ )))
+ .context("tmpfile")
+}
+
+#[op]
+async fn op_make_temp_file_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ dir: Option<String>,
+ prefix: Option<String>,
+ suffix: Option<String>,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let (dir, fs) = make_temp_check_async::<Fs, P>(state, dir)?;
+
+ let open_opts = OpenOptions {
+ write: true,
+ create_new: true,
+ mode: Some(0o600),
+ ..Default::default()
+ };
+
+ let mut rng = thread_rng();
+
+ const MAX_TRIES: u32 = 10;
+ for _ in 0..MAX_TRIES {
+ let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
+ match fs.clone().open_async(path.clone(), open_opts).await {
+ Ok(_) => return path_into_string(path.into_os_string()),
+ Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
+ continue;
+ }
+ Err(e) => return Err(e).context("tmpfile"),
+ }
+ }
+ Err(FsError::Io(io::Error::new(
+ io::ErrorKind::AlreadyExists,
+ "too many temp files exist",
+ )))
+ .context("tmpfile")
+}
+
+fn make_temp_check_sync<Fs, P>(
+ state: &mut OpState,
+ dir: Option<String>,
+) -> Result<(PathBuf, Fs), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let fs = state.borrow::<Fs>().clone();
+ let dir = match dir {
+ Some(dir) => {
+ let dir = PathBuf::from(dir);
+ state
+ .borrow_mut::<P>()
+ .check_write(&dir, "Deno.makeTempFile()")?;
+ dir
+ }
+ None => {
+ let dir = fs.tmp_dir().context("tmpdir")?;
+ state.borrow_mut::<P>().check_write_blind(
+ &dir,
+ "TMP",
+ "Deno.makeTempFile()",
+ )?;
+ dir
+ }
+ };
+ Ok((dir, fs))
+}
+
+fn make_temp_check_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ dir: Option<String>,
+) -> Result<(PathBuf, Fs), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let mut state = state.borrow_mut();
+ let fs = state.borrow::<Fs>().clone();
+ let dir = match dir {
+ Some(dir) => {
+ let dir = PathBuf::from(dir);
+ state
+ .borrow_mut::<P>()
+ .check_write(&dir, "Deno.makeTempFile()")?;
+ dir
+ }
+ None => {
+ let dir = fs.tmp_dir().context("tmpdir")?;
+ state.borrow_mut::<P>().check_write_blind(
+ &dir,
+ "TMP",
+ "Deno.makeTempFile()",
+ )?;
+ dir
+ }
+ };
+ Ok((dir, fs))
+}
+
+fn tmp_name(
+ rng: &mut ThreadRng,
+ dir: &Path,
+ prefix: Option<&str>,
+ suffix: Option<&str>,
+) -> Result<PathBuf, AnyError> {
+ let prefix = prefix.unwrap_or("");
+ let suffix = suffix.unwrap_or("");
+
+ let mut path = dir.join("_");
+
+ let unique = rng.gen::<u32>();
+ path.set_file_name(format!("{prefix}{unique:08x}{suffix}"));
+
+ Ok(path)
+}
+
+#[op]
+fn op_write_file_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+ mode: Option<u32>,
+ append: bool,
+ create: bool,
+ create_new: bool,
+ data: ZeroCopyBuf,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let permissions = state.borrow_mut::<P>();
+ let options = OpenOptions::write(create, append, create_new, mode);
+ options.check(permissions, &path, "Deno.writeFileSync()")?;
+
+ let fs = state.borrow::<Fs>();
+
+ fs.write_file_sync(&path, options, &data)
+ .context_path("writefile", &path)?;
+
+ Ok(())
+}
+
+#[op]
+async fn op_write_file_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ mode: Option<u32>,
+ append: bool,
+ create: bool,
+ create_new: bool,
+ data: ZeroCopyBuf,
+ cancel_rid: Option<ResourceId>,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let options = OpenOptions::write(create, append, create_new, mode);
+
+ let (fs, cancel_handle) = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ options.check(permissions, &path, "Deno.writeFile()")?;
+ let cancel_handle = cancel_rid
+ .and_then(|rid| state.resource_table.get::<CancelHandle>(rid).ok());
+ (state.borrow::<Fs>().clone(), cancel_handle)
+ };
+
+ let fut = fs.write_file_async(path.clone(), options, data.to_vec());
+
+ if let Some(cancel_handle) = cancel_handle {
+ let res = fut.or_cancel(cancel_handle).await;
+
+ if let Some(cancel_rid) = cancel_rid {
+ state.borrow_mut().resource_table.close(cancel_rid).ok();
+ };
+
+ res?.context_path("writefile", &path)?;
+ } else {
+ fut.await.context_path("writefile", &path)?;
+ }
+
+ Ok(())
+}
+
+#[op]
+fn op_read_file_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+) -> Result<ZeroCopyBuf, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&path, "Deno.readFileSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ let buf = fs.read_file_sync(path).context("readfile")?;
+
+ Ok(buf.into())
+}
+
+#[op]
+async fn op_read_file_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ cancel_rid: Option<ResourceId>,
+) -> Result<ZeroCopyBuf, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let (fs, cancel_handle) = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&path, "Deno.readFile()")?;
+ let cancel_handle = cancel_rid
+ .and_then(|rid| state.resource_table.get::<CancelHandle>(rid).ok());
+ (state.borrow::<Fs>().clone(), cancel_handle)
+ };
+
+ let fut = fs.read_file_async(path.clone());
+
+ let buf = if let Some(cancel_handle) = cancel_handle {
+ let res = fut.or_cancel(cancel_handle).await;
+
+ if let Some(cancel_rid) = cancel_rid {
+ state.borrow_mut().resource_table.close(cancel_rid).ok();
+ };
+
+ res?.context_path("readfile", &path)?
+ } else {
+ fut.await.context_path("readfile", &path)?
+ };
+
+ Ok(buf.into())
+}
+
+#[op]
+fn op_read_file_text_sync<Fs, P>(
+ state: &mut OpState,
+ path: String,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&path, "Deno.readFileSync()")?;
+
+ let fs = state.borrow::<Fs>();
+ let buf = fs.read_file_sync(path).context("readfile")?;
+
+ Ok(string_from_utf8_lossy(buf))
+}
+
+#[op]
+async fn op_read_file_text_async<Fs, P>(
+ state: Rc<RefCell<OpState>>,
+ path: String,
+ cancel_rid: Option<ResourceId>,
+) -> Result<String, AnyError>
+where
+ Fs: FileSystem + 'static,
+ P: FsPermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let (fs, cancel_handle) = {
+ let mut state = state.borrow_mut();
+ let permissions = state.borrow_mut::<P>();
+ permissions.check_read(&path, "Deno.readFile()")?;
+ let cancel_handle = cancel_rid
+ .and_then(|rid| state.resource_table.get::<CancelHandle>(rid).ok());
+ (state.borrow::<Fs>().clone(), cancel_handle)
+ };
+
+ let fut = fs.read_file_async(path.clone());
+
+ let buf = if let Some(cancel_handle) = cancel_handle {
+ let res = fut.or_cancel(cancel_handle).await;
+
+ if let Some(cancel_rid) = cancel_rid {
+ state.borrow_mut().resource_table.close(cancel_rid).ok();
+ };
+
+ res?.context_path("readfile", &path)?
+ } else {
+ fut.await.context_path("readfile", &path)?
+ };
+
+ Ok(string_from_utf8_lossy(buf))
+}
+
+// Like String::from_utf8_lossy but operates on owned values
+fn string_from_utf8_lossy(buf: Vec<u8>) -> String {
+ match String::from_utf8_lossy(&buf) {
+ // buf contained non-utf8 chars than have been patched
+ Cow::Owned(s) => s,
+ // SAFETY: if Borrowed then the buf only contains utf8 chars,
+ // we do this instead of .into_owned() to avoid copying the input buf
+ Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(buf) },
+ }
+}
+
+fn to_seek_from(offset: i64, whence: i32) -> Result<SeekFrom, AnyError> {
+ 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}")));
+ }
+ };
+ Ok(seek_from)
+}
+
+#[op]
+fn op_seek_sync<Fs>(
+ state: &mut OpState,
+ rid: ResourceId,
+ offset: i64,
+ whence: i32,
+) -> Result<u64, AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let pos = to_seek_from(offset, whence)?;
+ let file = state.resource_table.get::<Fs::File>(rid)?;
+ let cursor = file.seek_sync(pos)?;
+ Ok(cursor)
+}
+
+#[op]
+async fn op_seek_async<Fs>(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+ offset: i64,
+ whence: i32,
+) -> Result<u64, AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let pos = to_seek_from(offset, whence)?;
+ let file = state.borrow().resource_table.get::<Fs::File>(rid)?;
+ let cursor = file.seek_async(pos).await?;
+ Ok(cursor)
+}
+
+#[op]
+fn op_fdatasync_sync<Fs>(
+ state: &mut OpState,
+ rid: ResourceId,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.resource_table.get::<Fs::File>(rid)?;
+ file.datasync_sync()?;
+ Ok(())
+}
+
+#[op]
+async fn op_fdatasync_async<Fs>(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.borrow().resource_table.get::<Fs::File>(rid)?;
+ file.datasync_async().await?;
+ Ok(())
+}
+
+#[op]
+fn op_fsync_sync<Fs>(
+ state: &mut OpState,
+ rid: ResourceId,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.resource_table.get::<Fs::File>(rid)?;
+ file.sync_sync()?;
+ Ok(())
+}
+
+#[op]
+async fn op_fsync_async<Fs>(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.borrow().resource_table.get::<Fs::File>(rid)?;
+ file.sync_async().await?;
+ Ok(())
+}
+
+#[op]
+fn op_fstat_sync<Fs>(
+ state: &mut OpState,
+ rid: ResourceId,
+ stat_out_buf: &mut [u32],
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.resource_table.get::<Fs::File>(rid)?;
+ let stat = file.stat_sync()?;
+ let serializable_stat = SerializableStat::from(stat);
+ serializable_stat.write(stat_out_buf);
+ Ok(())
+}
+
+#[op]
+async fn op_fstat_async<Fs>(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+) -> Result<SerializableStat, AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.borrow().resource_table.get::<Fs::File>(rid)?;
+ let stat = file.stat_async().await?;
+ Ok(stat.into())
+}
+
+#[op]
+fn op_flock_sync<Fs>(
+ state: &mut OpState,
+ rid: ResourceId,
+ exclusive: bool,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ check_unstable(state, "Deno.flockSync");
+ let file = state.resource_table.get::<Fs::File>(rid)?;
+ file.lock_sync(exclusive)?;
+ Ok(())
+}
+
+#[op]
+async fn op_flock_async<Fs>(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+ exclusive: bool,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ check_unstable2(&state, "Deno.flock");
+ let file = state.borrow().resource_table.get::<Fs::File>(rid)?;
+ file.lock_async(exclusive).await?;
+ Ok(())
+}
+
+#[op]
+fn op_funlock_sync<Fs>(
+ state: &mut OpState,
+ rid: ResourceId,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ check_unstable(state, "Deno.funlockSync");
+ let file = state.resource_table.get::<Fs::File>(rid)?;
+ file.unlock_sync()?;
+ Ok(())
+}
+
+#[op]
+async fn op_funlock_async<Fs>(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ check_unstable2(&state, "Deno.funlock");
+ let file = state.borrow().resource_table.get::<Fs::File>(rid)?;
+ file.unlock_async().await?;
+ Ok(())
+}
+
+#[op]
+fn op_ftruncate_sync<Fs>(
+ state: &mut OpState,
+ rid: ResourceId,
+ len: u64,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.resource_table.get::<Fs::File>(rid)?;
+ file.truncate_sync(len)?;
+ Ok(())
+}
+
+#[op]
+async fn op_ftruncate_async<Fs>(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+ len: u64,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.borrow().resource_table.get::<Fs::File>(rid)?;
+ file.truncate_async(len).await?;
+ Ok(())
+}
+
+#[op]
+fn op_futime_sync<Fs>(
+ state: &mut OpState,
+ rid: ResourceId,
+ atime_secs: i64,
+ atime_nanos: u32,
+ mtime_secs: i64,
+ mtime_nanos: u32,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.resource_table.get::<Fs::File>(rid)?;
+ file.utime_sync(atime_secs, atime_nanos, mtime_secs, mtime_nanos)?;
+ Ok(())
+}
+
+#[op]
+async fn op_futime_async<Fs>(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+ atime_secs: i64,
+ atime_nanos: u32,
+ mtime_secs: i64,
+ mtime_nanos: u32,
+) -> Result<(), AnyError>
+where
+ Fs: FileSystem + 'static,
+{
+ let file = state.borrow().resource_table.get::<Fs::File>(rid)?;
+ 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;
+}
+
+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)
+ }
+ _ => self.into(),
+ }
+ }
+}
+
+trait MapErrContext {
+ type R;
+
+ fn context_fn<F, E>(self, f: F) -> Self::R
+ where
+ F: FnOnce() -> E,
+ E: Into<Box<dyn std::error::Error + Send + Sync>>;
+
+ fn context(self, desc: &'static str) -> Self::R;
+
+ fn context_path(self, operation: &'static str, path: &Path) -> Self::R;
+
+ fn context_two_path(
+ self,
+ operation: &'static str,
+ from: &Path,
+ to: &Path,
+ ) -> Self::R;
+}
+
+impl<T> MapErrContext for Result<T, FsError> {
+ type R = Result<T, AnyError>;
+
+ fn context_fn<F, E>(self, f: F) -> Self::R
+ where
+ F: FnOnce() -> E,
+ E: Into<Box<dyn std::error::Error + Send + Sync>>,
+ {
+ self.map_err(|err| {
+ let message = f();
+ err.context(message)
+ })
+ }
+
+ fn context(self, desc: &'static str) -> Self::R {
+ self.context_fn(move || desc)
+ }
+
+ fn context_path(self, operation: &'static str, path: &Path) -> Self::R {
+ self.context_fn(|| format!("{operation} '{}'", path.display()))
+ }
+
+ fn context_two_path(
+ self,
+ operation: &'static str,
+ oldpath: &Path,
+ newpath: &Path,
+ ) -> Self::R {
+ self.context_fn(|| {
+ format!(
+ "{operation} '{}' -> '{}'",
+ oldpath.display(),
+ newpath.display()
+ )
+ })
+ }
+}
+
+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)
+ })
+}
+
+macro_rules! create_struct_writer {
+ (pub struct $name:ident { $($field:ident: $type:ty),* $(,)? }) => {
+ impl $name {
+ fn write(self, buf: &mut [u32]) {
+ let mut offset = 0;
+ $(
+ let value = self.$field as u64;
+ buf[offset] = value as u32;
+ buf[offset + 1] = (value >> 32) as u32;
+ #[allow(unused_assignments)]
+ {
+ offset += 2;
+ }
+ )*
+ }
+ }
+
+ #[derive(Serialize)]
+ #[serde(rename_all = "camelCase")]
+ struct $name {
+ $($field: $type),*
+ }
+ };
+}
+
+create_struct_writer! {
+ pub struct SerializableStat {
+ is_file: bool,
+ is_directory: bool,
+ is_symlink: bool,
+ size: u64,
+ // In milliseconds, like JavaScript. Available on both Unix or Windows.
+ mtime_set: bool,
+ mtime: u64,
+ atime_set: bool,
+ atime: u64,
+ birthtime_set: bool,
+ birthtime: u64,
+ // Following are only valid under Unix.
+ dev: u64,
+ ino: u64,
+ mode: u32,
+ nlink: u64,
+ uid: u32,
+ gid: u32,
+ rdev: u64,
+ blksize: u64,
+ blocks: u64,
+ }
+}
+
+impl From<FsStat> for SerializableStat {
+ fn from(stat: FsStat) -> Self {
+ SerializableStat {
+ is_file: stat.is_file,
+ is_directory: stat.is_directory,
+ is_symlink: stat.is_symlink,
+ size: stat.size,
+
+ mtime_set: stat.mtime.is_some(),
+ mtime: stat.mtime.unwrap_or(0),
+ atime_set: stat.atime.is_some(),
+ atime: stat.atime.unwrap_or(0),
+ birthtime_set: stat.birthtime.is_some(),
+ birthtime: stat.birthtime.unwrap_or(0),
+
+ dev: stat.dev,
+ ino: stat.ino,
+ mode: stat.mode,
+ nlink: stat.nlink,
+ uid: stat.uid,
+ gid: stat.gid,
+ rdev: stat.rdev,
+ blksize: stat.blksize,
+ blocks: stat.blocks,
+ }
+ }
+}