mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
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: ![image](https://github.com/denoland/deno/assets/34997667/58932652-6f7a-47f5-8504-896dc9ab4ddc)
This commit is contained in:
parent
7f68ea0e14
commit
1fcace13b9
11 changed files with 350 additions and 2 deletions
47
Cargo.lock
generated
47
Cargo.lock
generated
|
@ -757,6 +757,28 @@ dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -769,9 +791,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.16"
|
version = "0.8.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
@ -1298,6 +1320,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"nix 0.26.2",
|
"nix 0.26.2",
|
||||||
"rand",
|
"rand",
|
||||||
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
"winapi",
|
"winapi",
|
||||||
|
@ -4668,6 +4691,26 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
|
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
|
@ -175,6 +175,17 @@ impl FileSystem for DenoCompileFileSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cp_sync(&self, from: &Path, to: &Path) -> FsResult<()> {
|
||||||
|
self.error_if_in_vfs(to)?;
|
||||||
|
|
||||||
|
RealFs.cp_sync(from, to)
|
||||||
|
}
|
||||||
|
async fn cp_async(&self, from: PathBuf, to: PathBuf) -> FsResult<()> {
|
||||||
|
self.error_if_in_vfs(&to)?;
|
||||||
|
|
||||||
|
RealFs.cp_async(from, to).await
|
||||||
|
}
|
||||||
|
|
||||||
fn stat_sync(&self, path: &Path) -> FsResult<FsStat> {
|
fn stat_sync(&self, path: &Path) -> FsResult<FsStat> {
|
||||||
if self.0.is_path_within(path) {
|
if self.0.is_path_within(path) {
|
||||||
Ok(self.0.stat(path)?)
|
Ok(self.0.stat(path)?)
|
||||||
|
|
|
@ -25,6 +25,7 @@ fs3.workspace = true
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
|
rayon = "1.8.0"
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,9 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
|
||||||
newpath: PathBuf,
|
newpath: PathBuf,
|
||||||
) -> FsResult<()>;
|
) -> FsResult<()>;
|
||||||
|
|
||||||
|
fn cp_sync(&self, path: &Path, new_path: &Path) -> FsResult<()>;
|
||||||
|
async fn cp_async(&self, path: PathBuf, new_path: PathBuf) -> FsResult<()>;
|
||||||
|
|
||||||
fn stat_sync(&self, path: &Path) -> FsResult<FsStat>;
|
fn stat_sync(&self, path: &Path) -> FsResult<FsStat>;
|
||||||
async fn stat_async(&self, path: PathBuf) -> FsResult<FsStat>;
|
async fn stat_async(&self, path: PathBuf) -> FsResult<FsStat>;
|
||||||
|
|
||||||
|
|
158
ext/fs/std_fs.rs
158
ext/fs/std_fs.rs
|
@ -150,6 +150,13 @@ impl FileSystem for RealFs {
|
||||||
spawn_blocking(move || copy_file(&from, &to)).await?
|
spawn_blocking(move || copy_file(&from, &to)).await?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cp_sync(&self, fro: &Path, to: &Path) -> FsResult<()> {
|
||||||
|
cp(fro, to)
|
||||||
|
}
|
||||||
|
async fn cp_async(&self, fro: PathBuf, to: PathBuf) -> FsResult<()> {
|
||||||
|
spawn_blocking(move || cp(&fro, &to)).await?
|
||||||
|
}
|
||||||
|
|
||||||
fn stat_sync(&self, path: &Path) -> FsResult<FsStat> {
|
fn stat_sync(&self, path: &Path) -> FsResult<FsStat> {
|
||||||
stat(path).map(Into::into)
|
stat(path).map(Into::into)
|
||||||
}
|
}
|
||||||
|
@ -469,6 +476,157 @@ fn copy_file(from: &Path, to: &Path) -> FsResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cp(from: &Path, to: &Path) -> FsResult<()> {
|
||||||
|
fn cp_(source_meta: fs::Metadata, from: &Path, to: &Path) -> FsResult<()> {
|
||||||
|
use rayon::prelude::IntoParallelIterator;
|
||||||
|
use rayon::prelude::ParallelIterator;
|
||||||
|
|
||||||
|
let ty = source_meta.file_type();
|
||||||
|
if ty.is_dir() {
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut builder = fs::DirBuilder::new();
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::DirBuilderExt;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
builder.mode(fs::symlink_metadata(from)?.permissions().mode());
|
||||||
|
}
|
||||||
|
builder.create(to)?;
|
||||||
|
|
||||||
|
let mut entries: Vec<_> = fs::read_dir(from)?
|
||||||
|
.map(|res| res.map(|e| e.file_name()))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
entries.shrink_to_fit();
|
||||||
|
entries
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|file_name| {
|
||||||
|
cp_(
|
||||||
|
fs::symlink_metadata(from.join(&file_name)).unwrap(),
|
||||||
|
&from.join(&file_name),
|
||||||
|
&to.join(&file_name),
|
||||||
|
)
|
||||||
|
.map_err(|err| {
|
||||||
|
io::Error::new(
|
||||||
|
err.kind(),
|
||||||
|
format!(
|
||||||
|
"failed to copy '{}' to '{}': {:?}",
|
||||||
|
from.join(&file_name).display(),
|
||||||
|
to.join(&file_name).display(),
|
||||||
|
err
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
} else if ty.is_symlink() {
|
||||||
|
let from = std::fs::read_link(from)?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
std::os::unix::fs::symlink(from, to)?;
|
||||||
|
#[cfg(windows)]
|
||||||
|
std::os::windows::fs::symlink_file(from, to)?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
if ty.is_socket() {
|
||||||
|
return Err(
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"sockets cannot be copied",
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copy_file(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
// Just clonefile()
|
||||||
|
use libc::clonefile;
|
||||||
|
use libc::unlink;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
|
let from_str = CString::new(from.as_os_str().as_bytes()).unwrap();
|
||||||
|
let to_str = CString::new(to.as_os_str().as_bytes()).unwrap();
|
||||||
|
|
||||||
|
// SAFETY: `from` and `to` are valid C strings.
|
||||||
|
unsafe {
|
||||||
|
// Try unlink. If it fails, we are going to try clonefile() anyway.
|
||||||
|
let _ = unlink(to_str.as_ptr());
|
||||||
|
|
||||||
|
if clonefile(from_str.as_ptr(), to_str.as_ptr(), 0) == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let source_meta = fs::symlink_metadata(from)?;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_identical(
|
||||||
|
source_meta: &fs::Metadata,
|
||||||
|
dest_meta: &fs::Metadata,
|
||||||
|
) -> bool {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
source_meta.ino() == dest_meta.ino()
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use std::os::windows::fs::MetadataExt;
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information
|
||||||
|
//
|
||||||
|
// The identifier (low and high parts) and the volume serial number uniquely identify a file on a single computer.
|
||||||
|
// To determine whether two open handles represent the same file, combine the identifier and the volume serial
|
||||||
|
// number for each file and compare them.
|
||||||
|
//
|
||||||
|
// Use this code once file_index() and volume_serial_number() is stabalized
|
||||||
|
// See: https://github.com/rust-lang/rust/issues/63010
|
||||||
|
//
|
||||||
|
// source_meta.file_index() == dest_meta.file_index()
|
||||||
|
// && source_meta.volume_serial_number()
|
||||||
|
// == dest_meta.volume_serial_number()
|
||||||
|
source_meta.last_write_time() == dest_meta.last_write_time()
|
||||||
|
&& source_meta.creation_time() == dest_meta.creation_time()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match (fs::metadata(to), fs::symlink_metadata(to)) {
|
||||||
|
(Ok(m), _) if m.is_dir() => cp_(
|
||||||
|
source_meta,
|
||||||
|
from,
|
||||||
|
&to.join(from.file_name().ok_or_else(|| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"the source path is not a valid file",
|
||||||
|
)
|
||||||
|
})?),
|
||||||
|
)?,
|
||||||
|
(_, Ok(m)) if is_identical(&source_meta, &m) => {
|
||||||
|
return Err(
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"the source and destination are the same file",
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => cp_(source_meta, from, to)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn stat(path: &Path) -> FsResult<FsStat> {
|
fn stat(path: &Path) -> FsResult<FsStat> {
|
||||||
let metadata = fs::metadata(path)?;
|
let metadata = fs::metadata(path)?;
|
||||||
|
|
|
@ -63,6 +63,11 @@ pub trait NodePermissions {
|
||||||
api_name: Option<&str>,
|
api_name: Option<&str>,
|
||||||
) -> Result<(), AnyError>;
|
) -> Result<(), AnyError>;
|
||||||
fn check_sys(&self, kind: &str, api_name: &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;
|
pub(crate) struct AllowAllNodePermissions;
|
||||||
|
@ -82,6 +87,13 @@ impl NodePermissions for AllowAllNodePermissions {
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
Ok(())
|
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> {
|
fn check_sys(&self, _kind: &str, _api_name: &str) -> Result<(), AnyError> {
|
||||||
Ok(())
|
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_get_serial_number,
|
||||||
ops::crypto::x509::op_node_x509_key_usage,
|
ops::crypto::x509::op_node_x509_key_usage,
|
||||||
ops::fs::op_node_fs_exists_sync<P>,
|
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::winerror::op_node_sys_to_uv_error,
|
||||||
ops::v8::op_v8_cached_data_version_tag,
|
ops::v8::op_v8_cached_data_version_tag,
|
||||||
ops::v8::op_v8_get_heap_statistics,
|
ops::v8::op_v8_get_heap_statistics,
|
||||||
|
@ -329,6 +343,7 @@ deno_core::extension!(deno_node,
|
||||||
"_fs/_fs_common.ts",
|
"_fs/_fs_common.ts",
|
||||||
"_fs/_fs_constants.ts",
|
"_fs/_fs_constants.ts",
|
||||||
"_fs/_fs_copy.ts",
|
"_fs/_fs_copy.ts",
|
||||||
|
"_fs/_fs_cp.js",
|
||||||
"_fs/_fs_dir.ts",
|
"_fs/_fs_dir.ts",
|
||||||
"_fs/_fs_dirent.ts",
|
"_fs/_fs_dirent.ts",
|
||||||
"_fs/_fs_exists.ts",
|
"_fs/_fs_exists.ts",
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// 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::path::PathBuf;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::op2;
|
use deno_core::op2;
|
||||||
|
@ -24,3 +27,54 @@ where
|
||||||
let fs = state.borrow::<FileSystemRc>();
|
let fs = state.borrow::<FileSystemRc>();
|
||||||
Ok(fs.lstat_sync(&path).is_ok())
|
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(())
|
||||||
|
}
|
||||||
|
|
41
ext/node/polyfills/_fs/_fs_cp.js
Normal file
41
ext/node/polyfills/_fs/_fs_cp.js
Normal file
|
@ -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);
|
|
@ -18,6 +18,7 @@ import {
|
||||||
copyFilePromise,
|
copyFilePromise,
|
||||||
copyFileSync,
|
copyFileSync,
|
||||||
} from "ext:deno_node/_fs/_fs_copy.ts";
|
} 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 Dir from "ext:deno_node/_fs/_fs_dir.ts";
|
||||||
import Dirent from "ext:deno_node/_fs/_fs_dirent.ts";
|
import Dirent from "ext:deno_node/_fs/_fs_dirent.ts";
|
||||||
import { exists, existsSync } from "ext:deno_node/_fs/_fs_exists.ts";
|
import { exists, existsSync } from "ext:deno_node/_fs/_fs_exists.ts";
|
||||||
|
@ -137,6 +138,7 @@ const {
|
||||||
const promises = {
|
const promises = {
|
||||||
access: accessPromise,
|
access: accessPromise,
|
||||||
copyFile: copyFilePromise,
|
copyFile: copyFilePromise,
|
||||||
|
cp: cpPromise,
|
||||||
open: openPromise,
|
open: openPromise,
|
||||||
opendir: opendirPromise,
|
opendir: opendirPromise,
|
||||||
rename: renamePromise,
|
rename: renamePromise,
|
||||||
|
@ -179,6 +181,8 @@ export default {
|
||||||
constants,
|
constants,
|
||||||
copyFile,
|
copyFile,
|
||||||
copyFileSync,
|
copyFileSync,
|
||||||
|
cp,
|
||||||
|
cpSync,
|
||||||
createReadStream,
|
createReadStream,
|
||||||
createWriteStream,
|
createWriteStream,
|
||||||
Dir,
|
Dir,
|
||||||
|
@ -280,6 +284,8 @@ export {
|
||||||
constants,
|
constants,
|
||||||
copyFile,
|
copyFile,
|
||||||
copyFileSync,
|
copyFileSync,
|
||||||
|
cp,
|
||||||
|
cpSync,
|
||||||
createReadStream,
|
createReadStream,
|
||||||
createWriteStream,
|
createWriteStream,
|
||||||
Dir,
|
Dir,
|
||||||
|
|
|
@ -1384,6 +1384,15 @@ impl deno_node::NodePermissions for PermissionsContainer {
|
||||||
self.0.lock().read.check(path, api_name)
|
self.0.lock().read.check(path, api_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn check_write_with_api_name(
|
||||||
|
&self,
|
||||||
|
path: &Path,
|
||||||
|
api_name: Option<&str>,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
self.0.lock().write.check(path, api_name)
|
||||||
|
}
|
||||||
|
|
||||||
fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
|
fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
|
||||||
self.0.lock().sys.check(kind, Some(api_name))
|
self.0.lock().sys.check(kind, Some(api_name))
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,13 @@ impl deno_node::NodePermissions for Permissions {
|
||||||
) -> Result<(), deno_core::error::AnyError> {
|
) -> Result<(), deno_core::error::AnyError> {
|
||||||
unreachable!("snapshotting!")
|
unreachable!("snapshotting!")
|
||||||
}
|
}
|
||||||
|
fn check_write_with_api_name(
|
||||||
|
&self,
|
||||||
|
_p: &Path,
|
||||||
|
_api_name: Option<&str>,
|
||||||
|
) -> Result<(), deno_core::error::AnyError> {
|
||||||
|
unreachable!("snapshotting!")
|
||||||
|
}
|
||||||
fn check_sys(
|
fn check_sys(
|
||||||
&self,
|
&self,
|
||||||
_kind: &str,
|
_kind: &str,
|
||||||
|
|
Loading…
Reference in a new issue