diff --git a/lib/internal/fs/cp/cp-sync.js b/lib/internal/fs/cp/cp-sync.js index 03fcae9b7cdbda..c26335ee4bc353 100644 --- a/lib/internal/fs/cp/cp-sync.js +++ b/lib/internal/fs/cp/cp-sync.js @@ -22,8 +22,6 @@ const { chmodSync, copyFileSync, lstatSync, - mkdirSync, - opendirSync, readlinkSync, statSync, symlinkSync, @@ -33,7 +31,6 @@ const { const { dirname, isAbsolute, - join, resolve, } = require('path'); const { isPromise } = require('util/types'); @@ -65,7 +62,13 @@ function getStats(src, dest, opts) { const destStat = statSyncFn(dest, { bigint: true, throwIfNoEntry: false }); if (srcStat.isDirectory() && opts.recursive) { - return onDir(srcStat, destStat, src, dest, opts); + return fsBinding.cpSyncCopyDir(src, dest, + opts.force, + opts.dereference, + opts.errorOnExist, + opts.verbatimSymlinks, + opts.preserveTimestamps, + opts.filter); } else if (srcStat.isFile() || srcStat.isCharacterDevice() || srcStat.isBlockDevice()) { @@ -131,60 +134,6 @@ function setDestTimestamps(src, dest) { return utimesSync(dest, updatedSrcStat.atime, updatedSrcStat.mtime); } -// TODO(@anonrig): Move this function to C++. -function onDir(srcStat, destStat, src, dest, opts) { - if (!destStat) return copyDir(src, dest, opts, true, srcStat.mode); - return copyDir(src, dest, opts); -} - -function copyDir(src, dest, opts, mkDir, srcMode) { - if (!opts.filter) { - // The caller didn't provide a js filter function, in this case - // we can run the whole function faster in C++ - // TODO(dario-piotrowicz): look into making cpSyncCopyDir also accept the potential filter function - return fsBinding.cpSyncCopyDir(src, dest, - opts.force, - opts.dereference, - opts.errorOnExist, - opts.verbatimSymlinks, - opts.preserveTimestamps); - } - - if (mkDir) { - mkdirSync(dest); - } - - const dir = opendirSync(src); - - try { - let dirent; - - while ((dirent = dir.readSync()) !== null) { - const { name } = dirent; - const srcItem = join(src, name); - const destItem = join(dest, name); - let shouldCopy = true; - - if (opts.filter) { - shouldCopy = opts.filter(srcItem, destItem); - if (isPromise(shouldCopy)) { - throw new ERR_INVALID_RETURN_VALUE('boolean', 'filter', shouldCopy); - } - } - - if (shouldCopy) { - getStats(srcItem, destItem, opts); - } - } - } finally { - dir.closeSync(); - - if (srcMode !== undefined) { - setDestMode(dest, srcMode); - } - } -} - // TODO(@anonrig): Move this function to C++. function onLink(destStat, src, dest, verbatimSymlinks) { let resolvedSrc = readlinkSync(src); diff --git a/src/node_file.cc b/src/node_file.cc index 3d8ce43e88d961..ccce9907ce02b8 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3428,8 +3428,9 @@ bool isInsideDir(const std::filesystem::path& src, } static void CpSyncCopyDir(const FunctionCallbackInfo& args) { - CHECK_EQ(args.Length(), 7); // src, dest, force, dereference, errorOnExist, - // verbatimSymlinks, preserveTimestamps + CHECK_EQ(args.Length(), + 8); // src, dest, force, dereference, errorOnExist, + // verbatimSymlinks, preserveTimestamps, filterFunction Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); @@ -3448,6 +3449,40 @@ static void CpSyncCopyDir(const FunctionCallbackInfo& args) { bool verbatim_symlinks = args[5]->IsTrue(); bool preserve_timestamps = args[6]->IsTrue(); + std::optional> + filter_fn; + + if (args[7]->IsFunction()) { + Local args_filter_fn = args[7].As(); + + filter_fn = [env, args_filter_fn](std::string_view src, + std::string_view dest) -> bool { + Local src_arg; + Local dest_arg; + + if (!String::NewFromUtf8( + env->isolate(), src.data(), v8::NewStringType::kNormal) + .ToLocal(&src_arg) || + !String::NewFromUtf8( + env->isolate(), dest.data(), v8::NewStringType::kNormal) + .ToLocal(&dest_arg)) { + // if for some reason we fail to load the src or dest strings + // just skip the filtering function and allow the copy + return true; + } + + Local argv[] = {src_arg, dest_arg}; + + Local result; + if (!args_filter_fn->Call(env->context(), Null(env->isolate()), 2, argv) + .ToLocal(&result)) { + // if the call failed for whatever reason allow the copy + return true; + } + return result->BooleanValue(env->isolate()); + }; + } + std::error_code error; std::filesystem::create_directories(*dest, error); if (error) { @@ -3473,11 +3508,19 @@ static void CpSyncCopyDir(const FunctionCallbackInfo& args) { force, error_on_exist, dereference, - &isolate](std::filesystem::path src, - std::filesystem::path dest) { + &isolate, + &filter_fn](std::filesystem::path src, + std::filesystem::path dest) { std::error_code error; for (auto dir_entry : std::filesystem::directory_iterator(src)) { + auto dir_entry_path_str = PathToString(dir_entry.path()); auto dest_file_path = dest / dir_entry.path().filename(); + auto dest_file_path_str = PathToString(dest_file_path); + + if (filter_fn && !(*filter_fn)(dir_entry_path_str, dest_file_path_str)) { + continue; + } + auto dest_str = PathToString(dest); if (dir_entry.is_symlink()) { @@ -3541,7 +3584,6 @@ static void CpSyncCopyDir(const FunctionCallbackInfo& args) { } } else if (std::filesystem::is_regular_file(dest_file_path)) { if (!dereference || (!force && error_on_exist)) { - auto dest_file_path_str = PathToString(dest_file_path); env->ThrowStdErrException( std::make_error_code(std::errc::file_exists), "cp",