summaryrefslogtreecommitdiff
path: root/ext/fs/lib.rs
diff options
context:
space:
mode:
authorLuca Casonato <hello@lcas.dev>2023-04-12 15:13:32 +0200
committerGitHub <noreply@github.com>2023-04-12 15:13:32 +0200
commitf90caa821c5a4acf28f7dec4071994ecf6f26e57 (patch)
treefa4af65399a16a9f2d17fa2b249f21be2c00b976 /ext/fs/lib.rs
parent0e3f62d4446ae7b9a64dacf7befcaecede118222 (diff)
refactor(ext/fs): abstract FS via FileSystem trait (#18599)
This commit abstracts out the specifics of the underlying system calls FS operations behind a new `FileSystem` and `File` trait in the `ext/fs` extension. This allows other embedders to re-use ext/fs, but substituting in a different FS backend. This is likely not the final form of these traits. Eventually they will be entirely `deno_core::Resource` agnostic, and will live in a seperate crate. --------- Co-authored-by: Bartek IwaƄczuk <biwanczuk@gmail.com>
Diffstat (limited to 'ext/fs/lib.rs')
-rw-r--r--ext/fs/lib.rs2484
1 files changed, 88 insertions, 2396 deletions
diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs
index c00f39599..c1418815c 100644
--- a/ext/fs/lib.rs
+++ b/ext/fs/lib.rs
@@ -1,74 +1,26 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-// Some deserializer fields are only used on Unix and Windows build fails without it
-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;
+mod interface;
+mod ops;
+mod std_fs;
+
+pub use crate::interface::File;
+pub use crate::interface::FileSystem;
+pub use crate::interface::FsDirEntry;
+pub use crate::interface::FsError;
+pub use crate::interface::FsFileType;
+pub use crate::interface::FsPermissions;
+pub use crate::interface::FsResult;
+pub use crate::interface::FsStat;
+pub use crate::interface::OpenOptions;
+use crate::ops::*;
+
+pub use crate::std_fs::StdFs;
+
use deno_core::OpState;
-use deno_core::ResourceId;
-use deno_core::ZeroCopyBuf;
-use deno_crypto::rand::thread_rng;
-use deno_crypto::rand::Rng;
-use deno_io::StdFileResource;
-use log::debug;
-use serde::Deserialize;
-use serde::Serialize;
-use std::borrow::Cow;
use std::cell::RefCell;
use std::convert::From;
-use std::env::current_dir;
-use std::env::set_current_dir;
-use std::env::temp_dir;
-use std::io;
-use std::io::Error;
-use std::io::Seek;
-use std::io::SeekFrom;
-use std::io::Write;
-use std::path::Path;
-use std::path::PathBuf;
use std::rc::Rc;
-use std::time::SystemTime;
-use std::time::UNIX_EPOCH;
-
-/// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows.
-fn canonicalize_path(path: &Path) -> Result<PathBuf, Error> {
- let mut canonicalized_path = path.canonicalize()?;
- if cfg!(windows) {
- canonicalized_path = PathBuf::from(
- canonicalized_path
- .display()
- .to_string()
- .trim_start_matches("\\\\?\\"),
- );
- }
- Ok(canonicalized_path)
-}
-
-/// A utility function to map OsStrings to Strings
-fn 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)
- })
-}
-
-#[cfg(unix)]
-pub fn get_nix_error_class(error: &nix::Error) -> &'static str {
- match error {
- nix::Error::ECHILD => "NotFound",
- nix::Error::EINVAL => "TypeError",
- nix::Error::ENOENT => "NotFound",
- nix::Error::ENOTTY => "BadResource",
- nix::Error::EPERM => "PermissionDenied",
- nix::Error::ESRCH => "NotFound",
- nix::Error::UnknownErrno => "Error",
- &nix::Error::ENOTSUP => unreachable!(),
- _ => "Error",
- }
-}
struct UnstableChecker {
pub unstable: bool,
@@ -87,2352 +39,92 @@ impl UnstableChecker {
}
/// Helper for checking unstable features. Used for sync ops.
-fn check_unstable(state: &OpState, api_name: &str) {
+pub(crate) fn check_unstable(state: &OpState, api_name: &str) {
state.borrow::<UnstableChecker>().check_unstable(api_name)
}
/// Helper for checking unstable features. Used for async ops.
-fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) {
+pub(crate) fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) {
let state = state.borrow();
state.borrow::<UnstableChecker>().check_unstable(api_name)
}
-pub trait FsPermissions {
- fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
- fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError>;
- fn check_read_blind(
- &mut self,
- p: &Path,
- display: &str,
- api_name: &str,
- ) -> Result<(), AnyError>;
- fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
- fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError>;
-}
-
-#[cfg(not(unix))]
-use deno_core::error::generic_error;
-#[cfg(not(unix))]
-use deno_core::error::not_supported;
-
deno_core::extension!(deno_fs,
- deps = [ deno_web, deno_io ],
- parameters = [P: FsPermissions],
+ deps = [ deno_web ],
+ parameters = [Fs: FileSystem, P: FsPermissions],
ops = [
- op_open_sync<P>,
- op_open_async<P>,
- op_write_file_sync<P>,
- op_write_file_async<P>,
- op_seek_sync,
- op_seek_async,
- op_fdatasync_sync,
- op_fdatasync_async,
- op_fsync_sync,
- op_fsync_async,
- op_fstat_sync,
- op_fstat_async,
- op_flock_sync,
- op_flock_async,
- op_funlock_sync,
- op_funlock_async,
- op_umask,
- op_chdir<P>,
- op_mkdir_sync<P>,
- op_mkdir_async<P>,
- op_chmod_sync<P>,
- op_chmod_async<P>,
- op_chown_sync<P>,
- op_chown_async<P>,
- op_remove_sync<P>,
- op_remove_async<P>,
- op_copy_file_sync<P>,
- op_copy_file_async<P>,
- op_stat_sync<P>,
- op_stat_async<P>,
- op_realpath_sync<P>,
- op_realpath_async<P>,
- op_read_dir_sync<P>,
- op_read_dir_async<P>,
- op_rename_sync<P>,
- op_rename_async<P>,
- op_link_sync<P>,
- op_link_async<P>,
- op_symlink_sync<P>,
- op_symlink_async<P>,
- op_read_link_sync<P>,
- op_read_link_async<P>,
- op_ftruncate_sync,
- op_ftruncate_async,
- op_truncate_sync<P>,
- op_truncate_async<P>,
- op_make_temp_dir_sync<P>,
- op_make_temp_dir_async<P>,
- op_make_temp_file_sync<P>,
- op_make_temp_file_async<P>,
- op_cwd<P>,
- op_futime_sync,
- op_futime_async,
- op_utime_sync<P>,
- op_utime_async<P>,
- op_readfile_sync<P>,
- op_readfile_text_sync<P>,
- op_readfile_async<P>,
- op_readfile_text_async<P>,
+ op_cwd<Fs, P>,
+ op_umask<Fs>,
+ op_chdir<Fs, P>,
+
+ op_open_sync<Fs, P>,
+ op_open_async<Fs, P>,
+ op_mkdir_sync<Fs, P>,
+ op_mkdir_async<Fs, P>,
+ op_chmod_sync<Fs, P>,
+ op_chmod_async<Fs, P>,
+ op_chown_sync<Fs, P>,
+ op_chown_async<Fs, P>,
+ op_remove_sync<Fs, P>,
+ op_remove_async<Fs, P>,
+ op_copy_file_sync<Fs, P>,
+ op_copy_file_async<Fs, P>,
+ op_stat_sync<Fs, P>,
+ op_stat_async<Fs, P>,
+ op_lstat_sync<Fs, P>,
+ op_lstat_async<Fs, P>,
+ op_realpath_sync<Fs, P>,
+ op_realpath_async<Fs, P>,
+ op_read_dir_sync<Fs, P>,
+ op_read_dir_async<Fs, P>,
+ op_rename_sync<Fs, P>,
+ op_rename_async<Fs, P>,
+ op_link_sync<Fs, P>,
+ op_link_async<Fs, P>,
+ op_symlink_sync<Fs, P>,
+ op_symlink_async<Fs, P>,
+ op_read_link_sync<Fs, P>,
+ op_read_link_async<Fs, P>,
+ op_truncate_sync<Fs, P>,
+ op_truncate_async<Fs, P>,
+ op_utime_sync<Fs, P>,
+ op_utime_async<Fs, P>,
+ op_make_temp_dir_sync<Fs, P>,
+ op_make_temp_dir_async<Fs, P>,
+ op_make_temp_file_sync<Fs, P>,
+ op_make_temp_file_async<Fs, P>,
+ op_write_file_sync<Fs, P>,
+ op_write_file_async<Fs, P>,
+ op_read_file_sync<Fs, P>,
+ op_read_file_async<Fs, P>,
+ op_read_file_text_sync<Fs, P>,
+ op_read_file_text_async<Fs, P>,
+
+ op_seek_sync<Fs>,
+ op_seek_async<Fs>,
+ op_fdatasync_sync<Fs>,
+ op_fdatasync_async<Fs>,
+ op_fsync_sync<Fs>,
+ op_fsync_async<Fs>,
+ op_fstat_sync<Fs>,
+ op_fstat_async<Fs>,
+ op_flock_sync<Fs>,
+ op_flock_async<Fs>,
+ op_funlock_sync<Fs>,
+ op_funlock_async<Fs>,
+ op_ftruncate_sync<Fs>,
+ op_ftruncate_async<Fs>,
+ op_futime_sync<Fs>,
+ op_futime_async<Fs>,
+
],
esm = [ "30_fs.js" ],
options = {
- unstable: bool
+ unstable: bool,
+ fs: Fs,
},
state = |state, options| {
state.put(UnstableChecker { unstable: options.unstable });
+ state.put(options.fs);
},
);
-
-fn default_err_mapper(err: Error, desc: String) -> AnyError {
- AnyError::new(Error::new(err.kind(), desc)).context(err)
-}
-
-#[derive(Deserialize, Default, Debug)]
-#[serde(rename_all = "camelCase")]
-#[serde(default)]
-pub struct OpenOptions {
- read: bool,
- write: bool,
- create: bool,
- truncate: bool,
- append: bool,
- create_new: bool,
-}
-
-#[inline]
-fn open_helper<P>(
- state: &mut OpState,
- path: &str,
- mode: Option<u32>,
- options: Option<&OpenOptions>,
- api_name: &str,
-) -> Result<(PathBuf, std::fs::OpenOptions), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(path).to_path_buf();
-
- let mut open_options = std::fs::OpenOptions::new();
-
- if let Some(mode) = mode {
- // mode only used if creating the file on Unix
- // if not specified, defaults to 0o666
- #[cfg(unix)]
- {
- use std::os::unix::fs::OpenOptionsExt;
- open_options.mode(mode & 0o777);
- }
- #[cfg(not(unix))]
- let _ = mode; // avoid unused warning
- }
-
- let permissions = state.borrow_mut::<P>();
-
- match options {
- None => {
- permissions.check_read(&path, api_name)?;
- open_options
- .read(true)
- .create(false)
- .write(false)
- .truncate(false)
- .append(false)
- .create_new(false);
- }
- Some(options) => {
- if options.read {
- permissions.check_read(&path, api_name)?;
- }
-
- if options.write || options.append {
- permissions.check_write(&path, api_name)?;
- }
-
- open_options
- .read(options.read)
- .create(options.create)
- .write(options.write)
- .truncate(options.truncate)
- .append(options.append)
- .create_new(options.create_new);
- }
- }
-
- Ok((path, open_options))
-}
-
-#[op]
-fn op_open_sync<P>(
- state: &mut OpState,
- path: String,
- options: Option<OpenOptions>,
- mode: Option<u32>,
-) -> Result<ResourceId, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let (path, open_options) =
- open_helper::<P>(state, &path, mode, options.as_ref(), "Deno.openSync()")?;
- let std_file = open_options.open(&path).map_err(|err| {
- default_err_mapper(err, format!("open '{}'", path.display()))
- })?;
- let resource = StdFileResource::fs_file(std_file);
- let rid = state.resource_table.add(resource);
- Ok(rid)
-}
-
-#[op]
-async fn op_open_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- options: Option<OpenOptions>,
- mode: Option<u32>,
-) -> Result<ResourceId, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let (path, open_options) = open_helper::<P>(
- &mut state.borrow_mut(),
- &path,
- mode,
- options.as_ref(),
- "Deno.open()",
- )?;
- let std_file = tokio::task::spawn_blocking(move || {
- open_options.open(&path).map_err(|err| {
- default_err_mapper(err, format!("open '{}'", path.display()))
- })
- })
- .await?;
- let resource = StdFileResource::fs_file(std_file?);
- let rid = state.borrow_mut().resource_table.add(resource);
- Ok(rid)
-}
-
-#[inline]
-fn write_open_options(
- create: bool,
- append: bool,
- create_new: bool,
-) -> OpenOptions {
- OpenOptions {
- read: false,
- write: true,
- create,
- truncate: !append,
- append,
- create_new,
- }
-}
-
-#[op]
-fn op_write_file_sync<P>(
- state: &mut OpState,
- path: String,
- mode: Option<u32>,
- append: bool,
- create: bool,
- create_new: bool,
- data: ZeroCopyBuf,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let (path, open_options) = open_helper::<P>(
- state,
- &path,
- mode,
- Some(&write_open_options(create, append, create_new)),
- "Deno.writeFileSync()",
- )?;
- write_file(&path, open_options, mode, data)
-}
-
-#[op]
-async fn op_write_file_async<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
- P: FsPermissions + 'static,
-{
- let (path, open_options) = open_helper::<P>(
- &mut state.borrow_mut(),
- &path,
- mode,
- Some(&write_open_options(create, append, create_new)),
- "Deno.writeFile()",
- )?;
-
- let write_future = tokio::task::spawn_blocking(move || {
- write_file(&path, open_options, mode, data)
- });
-
- let cancel_handle = cancel_rid.and_then(|rid| {
- state
- .borrow_mut()
- .resource_table
- .get::<CancelHandle>(rid)
- .ok()
- });
-
- if let Some(cancel_handle) = cancel_handle {
- let write_future_rv = write_future.or_cancel(cancel_handle).await;
-
- if let Some(cancel_rid) = cancel_rid {
- state.borrow_mut().resource_table.close(cancel_rid).ok();
- };
-
- return write_future_rv??;
- }
-
- write_future.await?
-}
-
-fn write_file(
- path: &Path,
- open_options: std::fs::OpenOptions,
- _mode: Option<u32>,
- data: ZeroCopyBuf,
-) -> Result<(), AnyError> {
- let mut std_file = open_options.open(path).map_err(|err| {
- default_err_mapper(err, format!("open '{}'", path.display()))
- })?;
-
- // need to chmod the file if it already exists and a mode is specified
- #[cfg(unix)]
- if let Some(mode) = _mode {
- use std::os::unix::fs::PermissionsExt;
- let permissions = PermissionsExt::from_mode(mode & 0o777);
- std_file.set_permissions(permissions).map_err(|err| {
- default_err_mapper(err, format!("chmod '{}'", path.display()))
- })?;
- }
-
- std_file.write_all(&data)?;
- Ok(())
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SeekArgs {
- rid: ResourceId,
- offset: i64,
- whence: i32,
-}
-
-fn seek_helper(args: SeekArgs) -> Result<(u32, SeekFrom), AnyError> {
- let rid = args.rid;
- let offset = args.offset;
- let whence = args.whence as u32;
- // Translate seek mode to Rust repr.
- 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((rid, seek_from))
-}
-
-#[op]
-fn op_seek_sync(state: &mut OpState, args: SeekArgs) -> Result<u64, AnyError> {
- let (rid, seek_from) = seek_helper(args)?;
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.seek(seek_from).map_err(AnyError::from)
- })
-}
-
-#[op]
-async fn op_seek_async(
- state: Rc<RefCell<OpState>>,
- args: SeekArgs,
-) -> Result<u64, AnyError> {
- let (rid, seek_from) = seek_helper(args)?;
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.seek(seek_from).map_err(AnyError::from)
- })
- .await
-}
-
-#[op]
-fn op_fdatasync_sync(
- state: &mut OpState,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.sync_data().map_err(AnyError::from)
- })
-}
-
-#[op]
-async fn op_fdatasync_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.sync_data().map_err(AnyError::from)
- })
- .await
-}
-
-#[op]
-fn op_fsync_sync(state: &mut OpState, rid: ResourceId) -> Result<(), AnyError> {
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.sync_all().map_err(AnyError::from)
- })
-}
-
-#[op]
-async fn op_fsync_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.sync_all().map_err(AnyError::from)
- })
- .await
-}
-
-#[op]
-fn op_fstat_sync(
- state: &mut OpState,
- rid: ResourceId,
- out_buf: &mut [u32],
-) -> Result<(), AnyError> {
- let metadata = StdFileResource::with_file(state, rid, |std_file| {
- std_file.metadata().map_err(AnyError::from)
- })?;
- let stat = get_stat(metadata);
- stat.write(out_buf);
- Ok(())
-}
-
-#[op]
-async fn op_fstat_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<FsStat, AnyError> {
- let metadata =
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.metadata().map_err(AnyError::from)
- })
- .await?;
- Ok(get_stat(metadata))
-}
-
-#[op]
-fn op_flock_sync(
- state: &mut OpState,
- rid: ResourceId,
- exclusive: bool,
-) -> Result<(), AnyError> {
- use fs3::FileExt;
- check_unstable(state, "Deno.flockSync");
-
- StdFileResource::with_file(state, rid, |std_file| {
- if exclusive {
- std_file.lock_exclusive()?;
- } else {
- std_file.lock_shared()?;
- }
- Ok(())
- })
-}
-
-#[op]
-async fn op_flock_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- exclusive: bool,
-) -> Result<(), AnyError> {
- use fs3::FileExt;
- check_unstable2(&state, "Deno.flock");
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- if exclusive {
- std_file.lock_exclusive()?;
- } else {
- std_file.lock_shared()?;
- }
- Ok(())
- })
- .await
-}
-
-#[op]
-fn op_funlock_sync(
- state: &mut OpState,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- use fs3::FileExt;
- check_unstable(state, "Deno.funlockSync");
-
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.unlock()?;
- Ok(())
- })
-}
-
-#[op]
-async fn op_funlock_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<(), AnyError> {
- use fs3::FileExt;
- check_unstable2(&state, "Deno.funlock");
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.unlock()?;
- Ok(())
- })
- .await
-}
-
-#[op]
-fn op_umask(state: &mut OpState, mask: Option<u32>) -> Result<u32, AnyError> {
- check_unstable(state, "Deno.umask");
- // TODO implement umask for Windows
- // see https://github.com/nodejs/node/blob/master/src/node_process_methods.cc
- // and https://docs.microsoft.com/fr-fr/cpp/c-runtime-library/reference/umask?view=vs-2019
- #[cfg(not(unix))]
- {
- let _ = mask; // avoid unused warning.
- Err(not_supported())
- }
- #[cfg(unix)]
- {
- use nix::sys::stat::mode_t;
- use nix::sys::stat::umask;
- use nix::sys::stat::Mode;
- let r = if let Some(mask) = mask {
- // If mask provided, return previous.
- umask(Mode::from_bits_truncate(mask as mode_t))
- } else {
- // If no mask provided, we query the current. Requires two syscalls.
- let prev = umask(Mode::from_bits_truncate(0o777));
- let _ = umask(prev);
- prev
- };
- #[cfg(target_os = "linux")]
- {
- Ok(r.bits())
- }
- #[cfg(target_os = "macos")]
- {
- Ok(r.bits() as u32)
- }
- }
-}
-
-#[op]
-fn op_chdir<P>(state: &mut OpState, directory: &str) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let d = PathBuf::from(&directory);
- state.borrow_mut::<P>().check_read(&d, "Deno.chdir()")?;
- set_current_dir(&d)
- .map_err(|err| default_err_mapper(err, format!("chdir '{directory}'")))?;
- Ok(())
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct MkdirArgs {
- path: String,
- recursive: bool,
- mode: Option<u32>,
-}
-
-#[op]
-fn op_mkdir_sync<P>(
- state: &mut OpState,
- args: MkdirArgs,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&args.path).to_path_buf();
- let mode = args.mode.unwrap_or(0o777) & 0o777;
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.mkdirSync()")?;
- debug!("op_mkdir {} {:o} {}", path.display(), mode, args.recursive);
- let mut builder = std::fs::DirBuilder::new();
- builder.recursive(args.recursive);
- #[cfg(unix)]
- {
- use std::os::unix::fs::DirBuilderExt;
- builder.mode(mode);
- }
- builder.create(&path).map_err(|err| {
- default_err_mapper(err, format!("mkdir '{}'", path.display()))
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_mkdir_async<P>(
- state: Rc<RefCell<OpState>>,
- args: MkdirArgs,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&args.path).to_path_buf();
- let mode = args.mode.unwrap_or(0o777) & 0o777;
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- debug!("op_mkdir {} {:o} {}", path.display(), mode, args.recursive);
- let mut builder = std::fs::DirBuilder::new();
- builder.recursive(args.recursive);
- #[cfg(unix)]
- {
- use std::os::unix::fs::DirBuilderExt;
- builder.mode(mode);
- }
- builder.create(&path).map_err(|err| {
- default_err_mapper(err, format!("mkdir '{}'", path.display()))
- })?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_chmod_sync<P>(
- state: &mut OpState,
- path: &str,
- mode: u32,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(path);
- let mode = mode & 0o777;
-
- state
- .borrow_mut::<P>()
- .check_write(path, "Deno.chmodSync()")?;
- raw_chmod(path, mode)
-}
-
-#[op]
-async fn op_chmod_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- mode: u32,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&path).to_path_buf();
- let mode = mode & 0o777;
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
- }
-
- tokio::task::spawn_blocking(move || raw_chmod(&path, mode))
- .await
- .unwrap()
-}
-
-fn raw_chmod(path: &Path, _raw_mode: u32) -> Result<(), AnyError> {
- let err_mapper =
- |err| default_err_mapper(err, format!("chmod '{}'", path.display()));
- #[cfg(unix)]
- {
- use std::os::unix::fs::PermissionsExt;
- let permissions = PermissionsExt::from_mode(_raw_mode);
- std::fs::set_permissions(path, permissions).map_err(err_mapper)?;
- Ok(())
- }
- // TODO Implement chmod for Windows (#4357)
- #[cfg(not(unix))]
- {
- // Still check file/dir exists on Windows
- let _metadata = std::fs::metadata(path).map_err(err_mapper)?;
- Err(not_supported())
- }
-}
-
-#[op]
-fn op_chown_sync<P>(
- state: &mut OpState,
- path: &str,
- #[cfg_attr(windows, allow(unused_variables))] uid: Option<u32>,
- #[cfg_attr(windows, allow(unused_variables))] gid: Option<u32>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(path).to_path_buf();
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.chownSync()")?;
- #[cfg(unix)]
- {
- use nix::unistd::chown;
- use nix::unistd::Gid;
- use nix::unistd::Uid;
- let nix_uid = uid.map(Uid::from_raw);
- let nix_gid = gid.map(Gid::from_raw);
- chown(&path, nix_uid, nix_gid).map_err(|err| {
- custom_error(
- get_nix_error_class(&err),
- format!("{}, chown '{}'", err.desc(), path.display()),
- )
- })?;
- Ok(())
- }
- // TODO Implement chown for Windows
- #[cfg(not(unix))]
- {
- Err(generic_error("Not implemented"))
- }
-}
-
-#[op]
-async fn op_chown_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- #[cfg_attr(windows, allow(unused_variables))] uid: Option<u32>,
- #[cfg_attr(windows, allow(unused_variables))] gid: Option<u32>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&path).to_path_buf();
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- #[cfg(unix)]
- {
- use nix::unistd::chown;
- use nix::unistd::Gid;
- use nix::unistd::Uid;
- let nix_uid = uid.map(Uid::from_raw);
- let nix_gid = gid.map(Gid::from_raw);
- chown(&path, nix_uid, nix_gid).map_err(|err| {
- custom_error(
- get_nix_error_class(&err),
- format!("{}, chown '{}'", err.desc(), path.display()),
- )
- })?;
- Ok(())
- }
- // TODO Implement chown for Windows
- #[cfg(not(unix))]
- Err(not_supported())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_remove_sync<P>(
- state: &mut OpState,
- path: &str,
- recursive: bool,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(path);
-
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.removeSync()")?;
-
- #[cfg(not(unix))]
- use std::os::windows::prelude::MetadataExt;
-
- let err_mapper =
- |err| default_err_mapper(err, format!("remove '{}'", path.display()));
- let metadata = std::fs::symlink_metadata(&path).map_err(err_mapper)?;
-
- let file_type = metadata.file_type();
- if file_type.is_file() {
- std::fs::remove_file(&path).map_err(err_mapper)?;
- } else if recursive {
- std::fs::remove_dir_all(&path).map_err(err_mapper)?;
- } else if file_type.is_symlink() {
- #[cfg(unix)]
- std::fs::remove_file(&path).map_err(err_mapper)?;
- #[cfg(not(unix))]
- {
- use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
- if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 {
- std::fs::remove_dir(&path).map_err(err_mapper)?;
- } else {
- std::fs::remove_file(&path).map_err(err_mapper)?;
- }
- }
- } else if file_type.is_dir() {
- std::fs::remove_dir(&path).map_err(err_mapper)?;
- } else {
- // pipes, sockets, etc...
- std::fs::remove_file(&path).map_err(err_mapper)?;
- }
- Ok(())
-}
-
-#[op]
-async fn op_remove_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- recursive: bool,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- {
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.remove()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- #[cfg(not(unix))]
- use std::os::windows::prelude::MetadataExt;
- let err_mapper =
- |err| default_err_mapper(err, format!("remove '{}'", path.display()));
- let metadata = std::fs::symlink_metadata(&path).map_err(err_mapper)?;
-
- debug!("op_remove_async {} {}", path.display(), recursive);
- let file_type = metadata.file_type();
- if file_type.is_file() {
- std::fs::remove_file(&path).map_err(err_mapper)?;
- } else if recursive {
- std::fs::remove_dir_all(&path).map_err(err_mapper)?;
- } else if file_type.is_symlink() {
- #[cfg(unix)]
- std::fs::remove_file(&path).map_err(err_mapper)?;
- #[cfg(not(unix))]
- {
- use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
- if metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0 {
- std::fs::remove_dir(&path).map_err(err_mapper)?;
- } else {
- std::fs::remove_file(&path).map_err(err_mapper)?;
- }
- }
- } else if file_type.is_dir() {
- std::fs::remove_dir(&path).map_err(err_mapper)?;
- } else {
- // pipes, sockets, etc...
- std::fs::remove_file(&path).map_err(err_mapper)?;
- }
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_copy_file_sync<P>(
- state: &mut OpState,
- from: &str,
- to: &str,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let from_path = PathBuf::from(from);
- let to_path = PathBuf::from(to);
-
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&from_path, "Deno.copyFileSync()")?;
- permissions.check_write(&to_path, "Deno.copyFileSync()")?;
-
- // On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput
- // See https://github.com/rust-lang/rust/issues/54800
- // Once the issue is resolved, we should remove this workaround.
- if cfg!(unix) && !from_path.is_file() {
- return Err(custom_error(
- "NotFound",
- format!(
- "File not found, copy '{}' -> '{}'",
- from_path.display(),
- to_path.display()
- ),
- ));
- }
-
- let err_mapper = |err| {
- default_err_mapper(
- err,
- format!("copy '{}' -> '{}'", from_path.display(), to_path.display()),
- )
- };
-
- #[cfg(target_os = "macos")]
- {
- use libc::clonefile;
- use libc::stat;
- use libc::unlink;
- use std::ffi::CString;
- use std::io::Read;
- use std::os::unix::fs::OpenOptionsExt;
- use std::os::unix::fs::PermissionsExt;
-
- let from = CString::new(from).unwrap();
- let to = CString::new(to).unwrap();
-
- // SAFETY: `from` and `to` are valid C strings.
- // std::fs::copy does open() + fcopyfile() on macOS. We try to use
- // clonefile() instead, which is more efficient.
- unsafe {
- let mut st = std::mem::zeroed();
- let ret = stat(from.as_ptr(), &mut st);
- if ret != 0 {
- return Err(err_mapper(Error::last_os_error()));
- }
-
- if st.st_size > 128 * 1024 {
- // Try unlink. If it fails, we are going to try clonefile() anyway.
- let _ = unlink(to.as_ptr());
- // Matches rust stdlib behavior for io::copy.
- // https://github.com/rust-lang/rust/blob/3fdd578d72a24d4efc2fe2ad18eec3b6ba72271e/library/std/src/sys/unix/fs.rs#L1613-L1616
- if clonefile(from.as_ptr(), to.as_ptr(), 0) == 0 {
- return Ok(());
- }
- } else {
- // Do a regular copy. fcopyfile() is an overkill for < 128KB
- // files.
- let mut buf = [0u8; 128 * 1024];
- let mut from_file =
- std::fs::File::open(&from_path).map_err(err_mapper)?;
- let perm = from_file.metadata().map_err(err_mapper)?.permissions();
-
- let mut to_file = std::fs::OpenOptions::new()
- // create the file with the correct mode right away
- .mode(perm.mode())
- .write(true)
- .create(true)
- .truncate(true)
- .open(&to_path)
- .map_err(err_mapper)?;
- let writer_metadata = to_file.metadata()?;
- if writer_metadata.is_file() {
- // Set the correct file permissions, in case the file already existed.
- // Don't set the permissions on already existing non-files like
- // pipes/FIFOs or device nodes.
- to_file.set_permissions(perm)?;
- }
- loop {
- let nread = from_file.read(&mut buf).map_err(err_mapper)?;
- if nread == 0 {
- break;
- }
- to_file.write_all(&buf[..nread]).map_err(err_mapper)?;
- }
- return Ok(());
- }
- }
-
- // clonefile() failed, fall back to std::fs::copy().
- }
-
- // returns size of from as u64 (we ignore)
- std::fs::copy(&from_path, &to_path).map_err(err_mapper)?;
- Ok(())
-}
-
-#[op]
-async fn op_copy_file_async<P>(
- state: Rc<RefCell<OpState>>,
- from: String,
- to: String,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let from = PathBuf::from(&from);
- let to = PathBuf::from(&to);
-
- {
- let mut state = state.borrow_mut();
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&from, "Deno.copyFile()")?;
- permissions.check_write(&to, "Deno.copyFile()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- // On *nix, Rust reports non-existent `from` as ErrorKind::InvalidInput
- // See https://github.com/rust-lang/rust/issues/54800
- // Once the issue is resolved, we should remove this workaround.
- if cfg!(unix) && !from.is_file() {
- return Err(custom_error(
- "NotFound",
- format!(
- "File not found, copy '{}' -> '{}'",
- from.display(),
- to.display()
- ),
- ));
- }
- // returns size of from as u64 (we ignore)
- std::fs::copy(&from, &to).map_err(|err| {
- default_err_mapper(
- err,
- format!("copy '{}' -> '{}'", from.display(), to.display()),
- )
- })?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-fn to_msec(maybe_time: Result<SystemTime, io::Error>) -> (u64, bool) {
- match maybe_time {
- Ok(time) => (
- time
- .duration_since(UNIX_EPOCH)
- .map(|t| t.as_millis() as u64)
- .unwrap_or_else(|err| err.duration().as_millis() as u64),
- true,
- ),
- Err(_) => (0, false),
- }
-}
-
-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 FsStat {
- 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,
- }
-}
-
-#[inline(always)]
-fn get_stat(metadata: std::fs::Metadata) -> FsStat {
- // Unix stat member (number types only). 0 if not on unix.
- macro_rules! usm {
- ($member:ident) => {{
- #[cfg(unix)]
- {
- metadata.$member()
- }
- #[cfg(not(unix))]
- {
- 0
- }
- }};
- }
-
- #[cfg(unix)]
- use std::os::unix::fs::MetadataExt;
- let (mtime, mtime_set) = to_msec(metadata.modified());
- let (atime, atime_set) = to_msec(metadata.accessed());
- let (birthtime, birthtime_set) = to_msec(metadata.created());
-
- FsStat {
- is_file: metadata.is_file(),
- is_directory: metadata.is_dir(),
- is_symlink: metadata.file_type().is_symlink(),
- size: metadata.len(),
- // In milliseconds, like JavaScript. Available on both Unix or Windows.
- mtime_set,
- mtime,
- atime_set,
- atime,
- birthtime_set,
- birthtime,
- // Following are only valid under Unix.
- dev: usm!(dev),
- ino: usm!(ino),
- mode: usm!(mode),
- nlink: usm!(nlink),
- uid: usm!(uid),
- gid: usm!(gid),
- rdev: usm!(rdev),
- blksize: usm!(blksize),
- blocks: usm!(blocks),
- }
-}
-
-#[cfg(windows)]
-#[inline(always)]
-fn get_stat2(metadata: std::fs::Metadata, dev: u64) -> FsStat {
- let (mtime, mtime_set) = to_msec(metadata.modified());
- let (atime, atime_set) = to_msec(metadata.accessed());
- let (birthtime, birthtime_set) = to_msec(metadata.created());
-
- FsStat {
- is_file: metadata.is_file(),
- is_directory: metadata.is_dir(),
- is_symlink: metadata.file_type().is_symlink(),
- size: metadata.len(),
- mtime_set,
- mtime,
- atime_set,
- atime,
- birthtime_set,
- birthtime,
- dev,
- ino: 0,
- mode: 0,
- nlink: 0,
- uid: 0,
- gid: 0,
- rdev: 0,
- blksize: 0,
- blocks: 0,
- }
-}
-
-#[cfg(not(windows))]
-#[inline(always)]
-fn get_stat2(metadata: std::fs::Metadata) -> FsStat {
- #[cfg(unix)]
- use std::os::unix::fs::MetadataExt;
- let (mtime, mtime_set) = to_msec(metadata.modified());
- let (atime, atime_set) = to_msec(metadata.accessed());
- let (birthtime, birthtime_set) = to_msec(metadata.created());
-
- FsStat {
- is_file: metadata.is_file(),
- is_directory: metadata.is_dir(),
- is_symlink: metadata.file_type().is_symlink(),
- size: metadata.len(),
- mtime_set,
- mtime,
- atime_set,
- atime,
- birthtime_set,
- birthtime,
- dev: metadata.dev(),
- ino: metadata.ino(),
- mode: metadata.mode(),
- nlink: metadata.nlink(),
- uid: metadata.uid(),
- gid: metadata.gid(),
- rdev: metadata.rdev(),
- blksize: metadata.blksize(),
- blocks: metadata.blocks(),
- }
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct StatArgs {
- path: String,
- lstat: bool,
-}
-
-#[cfg(not(windows))]
-fn do_stat(path: PathBuf, lstat: bool) -> Result<FsStat, AnyError> {
- let err_mapper =
- |err| default_err_mapper(err, format!("stat '{}'", path.display()));
- let metadata = if lstat {
- std::fs::symlink_metadata(&path).map_err(err_mapper)?
- } else {
- std::fs::metadata(&path).map_err(err_mapper)?
- };
-
- Ok(get_stat2(metadata))
-}
-
-#[cfg(windows)]
-fn do_stat(path: PathBuf, lstat: bool) -> Result<FsStat, AnyError> {
- use std::os::windows::prelude::OsStrExt;
-
- use winapi::um::fileapi::CreateFileW;
- use winapi::um::fileapi::OPEN_EXISTING;
- use winapi::um::handleapi::CloseHandle;
- use winapi::um::handleapi::INVALID_HANDLE_VALUE;
- use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS;
- use winapi::um::winbase::FILE_FLAG_OPEN_REPARSE_POINT;
- use winapi::um::winnt::FILE_SHARE_DELETE;
- use winapi::um::winnt::FILE_SHARE_READ;
- use winapi::um::winnt::FILE_SHARE_WRITE;
-
- let err_mapper =
- |err| default_err_mapper(err, format!("stat '{}'", path.display()));
- let metadata = if lstat {
- std::fs::symlink_metadata(&path).map_err(err_mapper)?
- } else {
- std::fs::metadata(&path).map_err(err_mapper)?
- };
-
- let (p, file_flags) = if lstat {
- (
- path,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
- )
- } else {
- (path.canonicalize()?, FILE_FLAG_BACKUP_SEMANTICS)
- };
- // SAFETY: winapi calls
- unsafe {
- let mut path: Vec<_> = p.as_os_str().encode_wide().collect();
- path.push(0);
- let file_handle = CreateFileW(
- path.as_ptr(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
- std::ptr::null_mut(),
- OPEN_EXISTING,
- file_flags,
- std::ptr::null_mut(),
- );
- if file_handle == INVALID_HANDLE_VALUE {
- return Err(std::io::Error::last_os_error().into());
- }
-
- let result = get_dev(file_handle);
- CloseHandle(file_handle);
- let dev = result?;
-
- Ok(get_stat2(metadata, dev))
- }
-}
-
-#[cfg(windows)]
-use winapi::um::fileapi::GetFileInformationByHandle;
-#[cfg(windows)]
-use winapi::um::fileapi::BY_HANDLE_FILE_INFORMATION;
-
-#[cfg(windows)]
-unsafe fn get_dev(
- handle: winapi::shared::ntdef::HANDLE,
-) -> std::io::Result<u64> {
- use winapi::shared::minwindef::FALSE;
-
- let info = {
- let mut info =
- std::mem::MaybeUninit::<BY_HANDLE_FILE_INFORMATION>::zeroed();
- if GetFileInformationByHandle(handle, info.as_mut_ptr()) == FALSE {
- return Err(std::io::Error::last_os_error());
- }
-
- info.assume_init()
- };
-
- Ok(info.dwVolumeSerialNumber as u64)
-}
-
-#[op]
-fn op_stat_sync<P>(
- state: &mut OpState,
- path: &str,
- lstat: bool,
- out_buf: &mut [u32],
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(path);
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.statSync()")?;
-
- let stat = do_stat(path, lstat)?;
- stat.write(out_buf);
-
- Ok(())
-}
-
-#[op]
-async fn op_stat_async<P>(
- state: Rc<RefCell<OpState>>,
- args: StatArgs,
-) -> Result<FsStat, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&args.path);
- let lstat = args.lstat;
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_read(&path, "Deno.stat()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- debug!("op_stat_async {} {}", path.display(), lstat);
- do_stat(path, lstat)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_realpath_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.realPathSync()")?;
- if path.is_relative() {
- permissions.check_read_blind(
- &current_dir()?,
- "CWD",
- "Deno.realPathSync()",
- )?;
- }
-
- debug!("op_realpath_sync {}", path.display());
- // corresponds to the realpath on Unix and
- // CreateFile and GetFinalPathNameByHandle on Windows
- let realpath = canonicalize_path(&path).map_err(|error| {
- default_err_mapper(error, format!("op_realpath_sync '{}'", path.display()))
- })?;
- let realpath_str = into_string(realpath.into_os_string())?;
- Ok(realpath_str)
-}
-
-#[op]
-async fn op_realpath_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- {
- let mut state = state.borrow_mut();
- let permissions = state.borrow_mut::<P>();
- permissions.check_read(&path, "Deno.realPath()")?;
- if path.is_relative() {
- permissions.check_read_blind(
- &current_dir()?,
- "CWD",
- "Deno.realPath()",
- )?;
- }
- }
-
- tokio::task::spawn_blocking(move || {
- debug!("op_realpath_async {}", path.display());
- // corresponds to the realpath on Unix and
- // CreateFile and GetFinalPathNameByHandle on Windows
- let realpath = canonicalize_path(&path).map_err(|error| {
- default_err_mapper(
- error,
- format!("op_realpath_async '{}'", path.display()),
- )
- })?;
- let realpath_str = into_string(realpath.into_os_string())?;
- Ok(realpath_str)
- })
- .await
- .unwrap()
-}
-
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct DirEntry {
- name: String,
- is_file: bool,
- is_directory: bool,
- is_symlink: bool,
-}
-
-#[op]
-fn op_read_dir_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<Vec<DirEntry>, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.readDirSync()")?;
-
- debug!("op_read_dir_sync {}", path.display());
-
- let entries: Vec<_> = std::fs::read_dir(&path)
- .map_err(|err| {
- default_err_mapper(err, format!("readdir '{}'", path.display()))
- })?
- .filter_map(|entry| {
- let entry = entry.unwrap();
- // Not all filenames can be encoded as UTF-8. Skip those for now.
- if let Ok(name) = into_string(entry.file_name()) {
- Some(DirEntry {
- name,
- is_file: entry
- .file_type()
- .map(|file_type| file_type.is_file())
- .unwrap_or(false),
- is_directory: entry
- .file_type()
- .map(|file_type| file_type.is_dir())
- .unwrap_or(false),
- is_symlink: entry
- .file_type()
- .map(|file_type| file_type.is_symlink())
- .unwrap_or(false),
- })
- } else {
- None
- }
- })
- .collect();
-
- Ok(entries)
-}
-
-#[op]
-async fn op_read_dir_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
-) -> Result<Vec<DirEntry>, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
- {
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.readDir()")?;
- }
- tokio::task::spawn_blocking(move || {
- debug!("op_read_dir_async {}", path.display());
-
- let entries: Vec<_> = std::fs::read_dir(&path)
- .map_err(|err| {
- default_err_mapper(err, format!("readdir '{}'", path.display()))
- })?
- .filter_map(|entry| {
- let entry = entry.unwrap();
- // Not all filenames can be encoded as UTF-8. Skip those for now.
- if let Ok(name) = into_string(entry.file_name()) {
- Some(DirEntry {
- name,
- is_file: entry
- .file_type()
- .map(|file_type| file_type.is_file())
- .unwrap_or(false),
- is_directory: entry
- .file_type()
- .map(|file_type| file_type.is_dir())
- .unwrap_or(false),
- is_symlink: entry
- .file_type()
- .map(|file_type| file_type.is_symlink())
- .unwrap_or(false),
- })
- } else {
- None
- }
- })
- .collect();
-
- Ok(entries)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_rename_sync<P>(
- state: &mut OpState,
- oldpath: &str,
- newpath: &str,
-) -> Result<(), AnyError>
-where
- 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()")?;
-
- std::fs::rename(&oldpath, &newpath).map_err(|err| {
- default_err_mapper(
- err,
- format!("rename '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_rename_async<P>(
- state: Rc<RefCell<OpState>>,
- oldpath: String,
- newpath: String,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(&oldpath);
- let newpath = PathBuf::from(&newpath);
- {
- 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()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- std::fs::rename(&oldpath, &newpath).map_err(|err| {
- default_err_mapper(
- err,
- format!("rename '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- })?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_link_sync<P>(
- state: &mut OpState,
- oldpath: &str,
- newpath: &str,
-) -> Result<(), AnyError>
-where
- 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()")?;
-
- std::fs::hard_link(&oldpath, &newpath).map_err(|err| {
- default_err_mapper(
- err,
- format!("link '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_link_async<P>(
- state: Rc<RefCell<OpState>>,
- oldpath: String,
- newpath: String,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(&oldpath);
- let newpath = PathBuf::from(&newpath);
-
- {
- 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()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- let err_mapper = |err| {
- default_err_mapper(
- err,
- format!("link '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- };
- std::fs::hard_link(&oldpath, &newpath).map_err(err_mapper)?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_symlink_sync<P>(
- state: &mut OpState,
- oldpath: &str,
- newpath: &str,
- _type: Option<String>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(oldpath);
- let newpath = PathBuf::from(newpath);
-
- state
- .borrow_mut::<P>()
- .check_write_all("Deno.symlinkSync()")?;
- state
- .borrow_mut::<P>()
- .check_read_all("Deno.symlinkSync()")?;
-
- let err_mapper = |err| {
- default_err_mapper(
- err,
- format!("symlink '{}' -> '{}'", oldpath.display(), newpath.display()),
- )
- };
-
- #[cfg(unix)]
- {
- use std::os::unix::fs::symlink;
- symlink(&oldpath, &newpath).map_err(err_mapper)?;
- Ok(())
- }
- #[cfg(not(unix))]
- {
- use std::os::windows::fs::symlink_dir;
- use std::os::windows::fs::symlink_file;
-
- match _type {
- Some(ty) => match ty.as_ref() {
- "file" => symlink_file(&oldpath, &newpath).map_err(err_mapper)?,
- "dir" => symlink_dir(&oldpath, &newpath).map_err(err_mapper)?,
- _ => return Err(type_error("unsupported type")),
- },
- None => {
- let old_meta = std::fs::metadata(&oldpath);
- match old_meta {
- Ok(metadata) => {
- if metadata.is_file() {
- symlink_file(&oldpath, &newpath).map_err(err_mapper)?
- } else if metadata.is_dir() {
- symlink_dir(&oldpath, &newpath).map_err(err_mapper)?
- }
- }
- Err(_) => return Err(type_error("you must pass a `options` argument for non-existent target path in windows".to_string())),
- }
- }
- };
- Ok(())
- }
-}
-
-#[op]
-async fn op_symlink_async<P>(
- state: Rc<RefCell<OpState>>,
- oldpath: String,
- newpath: String,
- _type: Option<String>,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let oldpath = PathBuf::from(&oldpath);
- let newpath = PathBuf::from(&newpath);
-
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write_all("Deno.symlink()")?;
- state.borrow_mut::<P>().check_read_all("Deno.symlink()")?;
- }
-
- tokio::task::spawn_blocking(move || {
- let err_mapper = |err| default_err_mapper(err, format!(
- "symlink '{}' -> '{}'",
- oldpath.display(),
- newpath.display()
- ));
-
- #[cfg(unix)]
- {
- use std::os::unix::fs::symlink;
- symlink(&oldpath, &newpath).map_err(err_mapper)?;
- Ok(())
- }
- #[cfg(not(unix))]
- {
- use std::os::windows::fs::{symlink_dir, symlink_file};
-
- match _type {
- Some(ty) => match ty.as_ref() {
- "file" => symlink_file(&oldpath, &newpath).map_err(err_mapper)?,
- "dir" => symlink_dir(&oldpath, &newpath).map_err(err_mapper)?,
- _ => return Err(type_error("unsupported type")),
- },
- None => {
- let old_meta = std::fs::metadata(&oldpath);
- match old_meta {
- Ok(metadata) => {
- if metadata.is_file() {
- symlink_file(&oldpath, &newpath).map_err(err_mapper)?
- } else if metadata.is_dir() {
- symlink_dir(&oldpath, &newpath).map_err(err_mapper)?
- }
- }
- Err(_) => return Err(type_error("you must pass a `options` argument for non-existent target path in windows".to_string())),
- }
- }
- };
- Ok(())
- }
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_read_link_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.readLink()")?;
-
- debug!("op_read_link_value {}", path.display());
- let target = std::fs::read_link(&path)
- .map_err(|err| {
- default_err_mapper(err, format!("readlink '{}'", path.display()))
- })?
- .into_os_string();
- let targetstr = into_string(target)?;
- Ok(targetstr)
-}
-
-#[op]
-async fn op_read_link_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
- {
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_read(&path, "Deno.readLink()")?;
- }
- tokio::task::spawn_blocking(move || {
- debug!("op_read_link_async {}", path.display());
- let target = std::fs::read_link(&path)
- .map_err(|err| {
- default_err_mapper(err, format!("readlink '{}'", path.display()))
- })?
- .into_os_string();
- let targetstr = into_string(target)?;
- Ok(targetstr)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_ftruncate_sync(
- state: &mut OpState,
- rid: u32,
- len: i32,
-) -> Result<(), AnyError> {
- let len = len as u64;
- StdFileResource::with_file(state, rid, |std_file| {
- std_file.set_len(len).map_err(AnyError::from)
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_ftruncate_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- len: i32,
-) -> Result<(), AnyError> {
- let len = len as u64;
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- std_file.set_len(len)?;
- Ok(())
- })
- .await
-}
-
-#[op]
-fn op_truncate_sync<P>(
- state: &mut OpState,
- path: &str,
- len: u64,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(path);
-
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.truncateSync()")?;
-
- debug!("op_truncate_sync {} {}", path.display(), len);
- let err_mapper =
- |err| default_err_mapper(err, format!("truncate '{}'", path.display()));
- let f = std::fs::OpenOptions::new()
- .write(true)
- .open(&path)
- .map_err(err_mapper)?;
- f.set_len(len).map_err(err_mapper)?;
- Ok(())
-}
-
-#[op]
-async fn op_truncate_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- len: u64,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
-
- {
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_write(&path, "Deno.truncate()")?;
- }
- tokio::task::spawn_blocking(move || {
- debug!("op_truncate_async {} {}", path.display(), len);
- let err_mapper =
- |err| default_err_mapper(err, format!("truncate '{}'", path.display()));
- let f = std::fs::OpenOptions::new()
- .write(true)
- .open(&path)
- .map_err(err_mapper)?;
- f.set_len(len).map_err(err_mapper)?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-fn make_temp(
- dir: Option<&Path>,
- prefix: Option<&str>,
- suffix: Option<&str>,
- is_dir: bool,
-) -> std::io::Result<PathBuf> {
- let prefix_ = prefix.unwrap_or("");
- let suffix_ = suffix.unwrap_or("");
- let mut buf: PathBuf = match dir {
- Some(p) => p.to_path_buf(),
- None => temp_dir(),
- }
- .join("_");
- let mut rng = thread_rng();
- const MAX_TRIES: u32 = 10;
- for _ in 0..MAX_TRIES {
- let unique = rng.gen::<u32>();
- buf.set_file_name(format!("{prefix_}{unique:08x}{suffix_}"));
- let r = if is_dir {
- #[allow(unused_mut)]
- let mut builder = std::fs::DirBuilder::new();
- #[cfg(unix)]
- {
- use std::os::unix::fs::DirBuilderExt;
- builder.mode(0o700);
- }
- builder.create(buf.as_path())
- } else {
- let mut open_options = std::fs::OpenOptions::new();
- open_options.write(true).create_new(true);
- #[cfg(unix)]
- {
- use std::os::unix::fs::OpenOptionsExt;
- open_options.mode(0o600);
- }
- open_options.open(buf.as_path()).map(drop)
- };
- match r {
- Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue,
- Ok(_) => return Ok(buf),
- Err(e) => return Err(e),
- }
- }
- Err(io::Error::new(
- io::ErrorKind::AlreadyExists,
- "too many temp files exist",
- ))
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct MakeTempArgs {
- dir: Option<String>,
- prefix: Option<String>,
- suffix: Option<String>,
-}
-
-#[op]
-fn op_make_temp_dir_sync<P>(
- state: &mut OpState,
- args: MakeTempArgs,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let dir = args.dir.map(|s| PathBuf::from(&s));
- let prefix = args.prefix.map(String::from);
- let suffix = args.suffix.map(String::from);
-
- state.borrow_mut::<P>().check_write(
- dir.clone().unwrap_or_else(temp_dir).as_path(),
- "Deno.makeTempDirSync()",
- )?;
-
- // TODO(piscisaureus): use byte vector for paths, not a string.
- // See https://github.com/denoland/deno/issues/627.
- // We can't assume that paths are always valid utf8 strings.
- let path = make_temp(
- // Converting Option<String> to Option<&str>
- dir.as_deref(),
- prefix.as_deref(),
- suffix.as_deref(),
- true,
- )?;
- let path_str = into_string(path.into_os_string())?;
-
- Ok(path_str)
-}
-
-#[op]
-async fn op_make_temp_dir_async<P>(
- state: Rc<RefCell<OpState>>,
- args: MakeTempArgs,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let dir = args.dir.map(|s| PathBuf::from(&s));
- let prefix = args.prefix.map(String::from);
- let suffix = args.suffix.map(String::from);
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(
- dir.clone().unwrap_or_else(temp_dir).as_path(),
- "Deno.makeTempDir()",
- )?;
- }
- tokio::task::spawn_blocking(move || {
- // TODO(piscisaureus): use byte vector for paths, not a string.
- // See https://github.com/denoland/deno/issues/627.
- // We can't assume that paths are always valid utf8 strings.
- let path = make_temp(
- // Converting Option<String> to Option<&str>
- dir.as_deref(),
- prefix.as_deref(),
- suffix.as_deref(),
- true,
- )?;
- let path_str = into_string(path.into_os_string())?;
-
- Ok(path_str)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_make_temp_file_sync<P>(
- state: &mut OpState,
- args: MakeTempArgs,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let dir = args.dir.map(|s| PathBuf::from(&s));
- let prefix = args.prefix.map(String::from);
- let suffix = args.suffix.map(String::from);
-
- state.borrow_mut::<P>().check_write(
- dir.clone().unwrap_or_else(temp_dir).as_path(),
- "Deno.makeTempFileSync()",
- )?;
-
- // TODO(piscisaureus): use byte vector for paths, not a string.
- // See https://github.com/denoland/deno/issues/627.
- // We can't assume that paths are always valid utf8 strings.
- let path = make_temp(
- // Converting Option<String> to Option<&str>
- dir.as_deref(),
- prefix.as_deref(),
- suffix.as_deref(),
- false,
- )?;
- let path_str = into_string(path.into_os_string())?;
-
- Ok(path_str)
-}
-
-#[op]
-async fn op_make_temp_file_async<P>(
- state: Rc<RefCell<OpState>>,
- args: MakeTempArgs,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let dir = args.dir.map(|s| PathBuf::from(&s));
- let prefix = args.prefix.map(String::from);
- let suffix = args.suffix.map(String::from);
- {
- let mut state = state.borrow_mut();
- state.borrow_mut::<P>().check_write(
- dir.clone().unwrap_or_else(temp_dir).as_path(),
- "Deno.makeTempFile()",
- )?;
- }
- tokio::task::spawn_blocking(move || {
- // TODO(piscisaureus): use byte vector for paths, not a string.
- // See https://github.com/denoland/deno/issues/627.
- // We can't assume that paths are always valid utf8 strings.
- let path = make_temp(
- // Converting Option<String> to Option<&str>
- dir.as_deref(),
- prefix.as_deref(),
- suffix.as_deref(),
- false,
- )?;
- let path_str = into_string(path.into_os_string())?;
-
- Ok(path_str)
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_futime_sync(
- state: &mut OpState,
- rid: ResourceId,
- atime_secs: i64,
- atime_nanos: u32,
- mtime_secs: i64,
- mtime_nanos: u32,
-) -> Result<(), AnyError> {
- let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
- let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
-
- StdFileResource::with_file(state, rid, |std_file| {
- filetime::set_file_handle_times(std_file, Some(atime), Some(mtime))
- .map_err(AnyError::from)
- })?;
-
- Ok(())
-}
-
-#[op]
-async fn op_futime_async(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
- atime_secs: i64,
- atime_nanos: u32,
- mtime_secs: i64,
- mtime_nanos: u32,
-) -> Result<(), AnyError> {
- let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
- let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
-
- StdFileResource::with_file_blocking_task(state, rid, move |std_file| {
- filetime::set_file_handle_times(std_file, Some(atime), Some(mtime))?;
- Ok(())
- })
- .await
-}
-
-#[op]
-fn op_utime_sync<P>(
- state: &mut OpState,
- path: &str,
- atime_secs: i64,
- atime_nanos: u32,
- mtime_secs: i64,
- mtime_nanos: u32,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(path);
- let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
- let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
-
- state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
- filetime::set_file_times(&path, atime, mtime).map_err(|err| {
- default_err_mapper(err, format!("utime '{}'", path.display()))
- })?;
- Ok(())
-}
-
-#[op]
-async fn op_utime_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- atime_secs: i64,
- atime_nanos: u32,
- mtime_secs: i64,
- mtime_nanos: u32,
-) -> Result<(), AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = PathBuf::from(&path);
- let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos);
- let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos);
-
- state
- .borrow_mut()
- .borrow_mut::<P>()
- .check_write(&path, "Deno.utime()")?;
-
- tokio::task::spawn_blocking(move || {
- filetime::set_file_times(&path, atime, mtime).map_err(|err| {
- default_err_mapper(err, format!("utime '{}'", path.display()))
- })?;
- Ok(())
- })
- .await
- .unwrap()
-}
-
-#[op]
-fn op_cwd<P>(state: &mut OpState) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = current_dir()?;
- state
- .borrow_mut::<P>()
- .check_read_blind(&path, "CWD", "Deno.cwd()")?;
- let path_str = into_string(path.into_os_string())?;
- Ok(path_str)
-}
-
-#[op]
-fn op_readfile_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<ZeroCopyBuf, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&path);
- state
- .borrow_mut::<P>()
- .check_read(path, "Deno.readFileSync()")?;
- Ok(
- std::fs::read(path)
- .map_err(|err| {
- default_err_mapper(err, format!("readfile '{}'", path.display()))
- })?
- .into(),
- )
-}
-
-#[op]
-fn op_readfile_text_sync<P>(
- state: &mut OpState,
- path: String,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- let path = Path::new(&path);
- state
- .borrow_mut::<P>()
- .check_read(path, "Deno.readTextFileSync()")?;
- Ok(string_from_utf8_lossy(std::fs::read(path).map_err(
- |err| default_err_mapper(err, format!("readfile '{}'", path.display())),
- )?))
-}
-
-#[op]
-async fn op_readfile_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- cancel_rid: Option<ResourceId>,
-) -> Result<ZeroCopyBuf, AnyError>
-where
- P: FsPermissions + 'static,
-{
- {
- let path = Path::new(&path);
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_read(path, "Deno.readFile()")?;
- }
-
- let read_future = tokio::task::spawn_blocking(move || {
- let path = Path::new(&path);
- std::fs::read(path).map(ZeroCopyBuf::from).map_err(|err| {
- default_err_mapper(err, format!("readfile '{}'", path.display()))
- })
- });
-
- let cancel_handle = cancel_rid.and_then(|rid| {
- state
- .borrow_mut()
- .resource_table
- .get::<CancelHandle>(rid)
- .ok()
- });
-
- if let Some(cancel_handle) = cancel_handle {
- let read_future_rv = read_future.or_cancel(cancel_handle).await;
-
- if let Some(cancel_rid) = cancel_rid {
- state.borrow_mut().resource_table.close(cancel_rid).ok();
- };
-
- return read_future_rv??;
- }
-
- read_future.await?
-}
-
-#[op]
-async fn op_readfile_text_async<P>(
- state: Rc<RefCell<OpState>>,
- path: String,
- cancel_rid: Option<ResourceId>,
-) -> Result<String, AnyError>
-where
- P: FsPermissions + 'static,
-{
- {
- let path = Path::new(&path);
- let mut state = state.borrow_mut();
- state
- .borrow_mut::<P>()
- .check_read(path, "Deno.readTextFile()")?;
- }
-
- let read_future = tokio::task::spawn_blocking(move || {
- let path = Path::new(&path);
- Ok(string_from_utf8_lossy(std::fs::read(path).map_err(
- |err| default_err_mapper(err, format!("readfile '{}'", path.display())),
- )?))
- });
-
- let cancel_handle = cancel_rid.and_then(|rid| {
- state
- .borrow_mut()
- .resource_table
- .get::<CancelHandle>(rid)
- .ok()
- });
-
- if let Some(cancel_handle) = cancel_handle {
- let read_future_rv = read_future.or_cancel(cancel_handle).await;
-
- if let Some(cancel_rid) = cancel_rid {
- state.borrow_mut().resource_table.close(cancel_rid).ok();
- };
-
- return read_future_rv??;
- }
-
- read_future.await?
-}
-
-// 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) },
- }
-}