diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2024-01-05 18:28:33 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-05 18:28:33 +0530 |
commit | df062d2c788fd76546d59c67452d8d0fe569c533 (patch) | |
tree | b82a837378469c3eb1456aeea40354601b040793 /ext/node | |
parent | bac51f66aa89dd9b79a3c4e844423c3a3399ea13 (diff) |
fix(ext/node): add fs.cp, fs.cpSync, promises.cp (#21745)
Fixes https://github.com/denoland/deno/issues/20803
Fixes https://github.com/denoland/deno/issues/21723
Performance: copying a 48GiB rust `target` folder (recursive)
| Platform | `deno` | `node v21.5` | Improvement |
| -------- | ------- | ------- | ------- |
| macOS (APFS) | 3.1secs | 127.99 secs | **42x** |
| Windows | 18.3secs | 67.2secs | **3.8x** |
Copying files with varying sizes:

Diffstat (limited to 'ext/node')
-rw-r--r-- | ext/node/lib.rs | 15 | ||||
-rw-r--r-- | ext/node/ops/fs.rs | 54 | ||||
-rw-r--r-- | ext/node/polyfills/_fs/_fs_cp.js | 41 | ||||
-rw-r--r-- | ext/node/polyfills/fs.ts | 6 |
4 files changed, 116 insertions, 0 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 2aac49754..de56285fd 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -63,6 +63,11 @@ pub trait NodePermissions { api_name: Option<&str>, ) -> Result<(), AnyError>; fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError>; + fn check_write_with_api_name( + &self, + path: &Path, + api_name: Option<&str>, + ) -> Result<(), AnyError>; } pub(crate) struct AllowAllNodePermissions; @@ -82,6 +87,13 @@ impl NodePermissions for AllowAllNodePermissions { ) -> Result<(), AnyError> { Ok(()) } + fn check_write_with_api_name( + &self, + _path: &Path, + _api_name: Option<&str>, + ) -> Result<(), AnyError> { + Ok(()) + } fn check_sys(&self, _kind: &str, _api_name: &str) -> Result<(), AnyError> { Ok(()) } @@ -238,6 +250,8 @@ deno_core::extension!(deno_node, ops::crypto::x509::op_node_x509_get_serial_number, ops::crypto::x509::op_node_x509_key_usage, ops::fs::op_node_fs_exists_sync<P>, + ops::fs::op_node_cp_sync<P>, + ops::fs::op_node_cp<P>, ops::winerror::op_node_sys_to_uv_error, ops::v8::op_v8_cached_data_version_tag, ops::v8::op_v8_get_heap_statistics, @@ -329,6 +343,7 @@ deno_core::extension!(deno_node, "_fs/_fs_common.ts", "_fs/_fs_constants.ts", "_fs/_fs_copy.ts", + "_fs/_fs_cp.js", "_fs/_fs_dir.ts", "_fs/_fs_dirent.ts", "_fs/_fs_exists.ts", diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index 8e4805f6c..c5ae2371e 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -1,6 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::cell::RefCell; +use std::path::Path; use std::path::PathBuf; +use std::rc::Rc; use deno_core::error::AnyError; use deno_core::op2; @@ -24,3 +27,54 @@ where let fs = state.borrow::<FileSystemRc>(); Ok(fs.lstat_sync(&path).is_ok()) } + +#[op2(fast)] +pub fn op_node_cp_sync<P>( + state: &mut OpState, + #[string] path: &str, + #[string] new_path: &str, +) -> Result<(), AnyError> +where + P: NodePermissions + 'static, +{ + let path = Path::new(path); + let new_path = Path::new(new_path); + + state + .borrow_mut::<P>() + .check_read_with_api_name(path, Some("node:fs.cpSync"))?; + state + .borrow_mut::<P>() + .check_write_with_api_name(new_path, Some("node:fs.cpSync"))?; + + let fs = state.borrow::<FileSystemRc>(); + fs.cp_sync(path, new_path)?; + Ok(()) +} + +#[op2(async)] +pub async fn op_node_cp<P>( + state: Rc<RefCell<OpState>>, + #[string] path: String, + #[string] new_path: String, +) -> Result<(), AnyError> +where + P: NodePermissions + 'static, +{ + let path = PathBuf::from(path); + let new_path = PathBuf::from(new_path); + + let fs = { + let mut state = state.borrow_mut(); + state + .borrow_mut::<P>() + .check_read_with_api_name(&path, Some("node:fs.cpSync"))?; + state + .borrow_mut::<P>() + .check_write_with_api_name(&new_path, Some("node:fs.cpSync"))?; + state.borrow::<FileSystemRc>().clone() + }; + + fs.cp_async(path, new_path).await?; + Ok(()) +} diff --git a/ext/node/polyfills/_fs/_fs_cp.js b/ext/node/polyfills/_fs/_fs_cp.js new file mode 100644 index 000000000..dbe327974 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_cp.js @@ -0,0 +1,41 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file prefer-primordials + +import { + getValidatedPath, + validateCpOptions, +} from "ext:deno_node/internal/fs/utils.mjs"; +import { promisify } from "ext:deno_node/internal/util.mjs"; + +const core = globalThis.__bootstrap.core; +const ops = core.ops; +const { op_node_cp } = core.ensureFastOps(); + +export function cpSync(src, dest, options) { + validateCpOptions(options); + const srcPath = getValidatedPath(src, "src"); + const destPath = getValidatedPath(dest, "dest"); + + ops.op_node_cp_sync(srcPath, destPath); +} + +export function cp(src, dest, options, callback) { + if (typeof options === "function") { + callback = options; + options = {}; + } + validateCpOptions(options); + const srcPath = getValidatedPath(src, "src"); + const destPath = getValidatedPath(dest, "dest"); + + op_node_cp( + srcPath, + destPath, + ).then( + (res) => callback(null, res), + (err) => callback(err, null), + ); +} + +export const cpPromise = promisify(cp); diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index 881f0c139..01ac9912e 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -18,6 +18,7 @@ import { copyFilePromise, copyFileSync, } from "ext:deno_node/_fs/_fs_copy.ts"; +import { cp, cpPromise, cpSync } from "ext:deno_node/_fs/_fs_cp.js"; import Dir from "ext:deno_node/_fs/_fs_dir.ts"; import Dirent from "ext:deno_node/_fs/_fs_dirent.ts"; import { exists, existsSync } from "ext:deno_node/_fs/_fs_exists.ts"; @@ -137,6 +138,7 @@ const { const promises = { access: accessPromise, copyFile: copyFilePromise, + cp: cpPromise, open: openPromise, opendir: opendirPromise, rename: renamePromise, @@ -179,6 +181,8 @@ export default { constants, copyFile, copyFileSync, + cp, + cpSync, createReadStream, createWriteStream, Dir, @@ -280,6 +284,8 @@ export { constants, copyFile, copyFileSync, + cp, + cpSync, createReadStream, createWriteStream, Dir, |