From 1e3b56202dd282bd91013351c00d779d352838f9 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sat, 3 May 2025 22:07:30 +0100 Subject: [PATCH 01/13] fs: improve `cpSync` dest overriding performance move the logic in `cpSync` that overrides existing dest files from JavaScript to C++ increasing its performance --- lib/internal/fs/cp/cp-sync.js | 11 +++----- src/node_file.cc | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/lib/internal/fs/cp/cp-sync.js b/lib/internal/fs/cp/cp-sync.js index 1e922b7805fc8c..9e67ae6335ec46 100644 --- a/lib/internal/fs/cp/cp-sync.js +++ b/lib/internal/fs/cp/cp-sync.js @@ -81,15 +81,12 @@ function getStats(src, dest, opts) { function onFile(srcStat, destStat, src, dest, opts) { if (!destStat) return copyFile(srcStat, src, dest, opts); - return mayCopyFile(srcStat, src, dest, opts); -} -// TODO(@anonrig): Move this function to C++. -function mayCopyFile(srcStat, src, dest, opts) { if (opts.force) { - unlinkSync(dest); - return copyFile(srcStat, src, dest, opts); - } else if (opts.errorOnExist) { + return fsBinding.cpSyncOverrideFile(src, dest, opts.mode, opts.preserveTimestamps); + } + + if (opts.errorOnExist) { throw new ERR_FS_CP_EEXIST({ message: `${dest} already exists`, path: dest, diff --git a/src/node_file.cc b/src/node_file.cc index 56b7a94ecdfe42..54590ef3d60e05 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -42,6 +42,7 @@ #include "string_bytes.h" #include "uv.h" #include "v8-fast-api-calls.h" +#include #include @@ -3350,6 +3351,56 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { } } +static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + + CHECK_EQ(args.Length(), 4); // src, dest, mode, preserveTimestamps + + BufferValue src(isolate, args[0]); + CHECK_NOT_NULL(*src); + ToNamespacedPath(env, &src); + + BufferValue dest(isolate, args[1]); + CHECK_NOT_NULL(*dest); + ToNamespacedPath(env, &dest); + + int mode; + if (!GetValidFileMode(env, args[2], UV_FS_COPYFILE).To(&mode)) { + return; + } + + bool preserve_timestamps = args[3]->IsTrue(); + + THROW_IF_INSUFFICIENT_PERMISSIONS( + env, permission::PermissionScope::kFileSystemRead, src.ToStringView()); + THROW_IF_INSUFFICIENT_PERMISSIONS( + env, + permission::PermissionScope::kFileSystemWrite, + dest.ToStringView()); + + std::filesystem::remove(*dest); + + if (mode == 0) { + // if no mode is specified use the faster std::filesystem API + std::filesystem::copy_file(*src, *dest, std::filesystem::copy_options::skip_existing); + } else { + // if a mode is specified fallback to libuv instead + FSReqWrapSync req_wrap_sync("copyfile", *src, *dest); + SyncCallAndThrowOnError( + env, &req_wrap_sync, uv_fs_copyfile, *src, *dest, mode); + } + + if (preserve_timestamps) { + struct stat fileStat; + stat(*src, &fileStat); + struct utimbuf dest_times; + dest_times.actime = fileStat.st_atim.tv_sec; + dest_times.modtime = fileStat.st_mtime; + utime(*dest, &dest_times); + } +} + BindingData::FilePathIsFileReturnType BindingData::FilePathIsFile( Environment* env, const std::string& file_path) { THROW_IF_INSUFFICIENT_PERMISSIONS( @@ -3689,6 +3740,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data, SetMethod(isolate, target, "mkdtemp", Mkdtemp); SetMethod(isolate, target, "cpSyncCheckPaths", CpSyncCheckPaths); + SetMethod(isolate, target, "cpSyncOverrideFile", CpSyncOverrideFile); StatWatcher::CreatePerIsolateProperties(isolate_data, target); BindingData::CreatePerIsolateProperties(isolate_data, target); @@ -3801,6 +3853,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(CopyFile); registry->Register(CpSyncCheckPaths); + registry->Register(CpSyncOverrideFile); registry->Register(Chmod); registry->Register(FChmod); From ec520d8faa0d75afcd9428ef0f12dfb79647acbd Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 4 May 2025 14:48:16 +0100 Subject: [PATCH 02/13] fixup! fs: improve `cpSync` dest overriding performance add missing typing for `cpSyncOverrideFile` --- typings/internalBinding/fs.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/typings/internalBinding/fs.d.ts b/typings/internalBinding/fs.d.ts index e666725d39ac77..c413c6969fe888 100644 --- a/typings/internalBinding/fs.d.ts +++ b/typings/internalBinding/fs.d.ts @@ -77,6 +77,7 @@ declare namespace InternalFSBinding { function copyFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number, usePromises: typeof kUsePromises): Promise; function cpSyncCheckPaths(src: StringOrBuffer, dest: StringOrBuffer, dereference: boolean, recursive: boolean): void; + function cpSyncOverrideFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number, preserveTimestamps: boolean): void; function fchmod(fd: number, mode: number, req: FSReqCallback): void; function fchmod(fd: number, mode: number): void; @@ -260,6 +261,7 @@ export interface FsBinding { close: typeof InternalFSBinding.close; copyFile: typeof InternalFSBinding.copyFile; cpSyncCheckPaths: typeof InternalFSBinding.cpSyncCheckPaths; + cpSyncOverrideFile: typeof InternalFSBinding.cpSyncOverrideFile; fchmod: typeof InternalFSBinding.fchmod; fchown: typeof InternalFSBinding.fchown; fdatasync: typeof InternalFSBinding.fdatasync; From fe3a15dd22d15ac4f2edc8b00d8017225b9ffb1b Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 4 May 2025 14:49:33 +0100 Subject: [PATCH 03/13] fixup! fs: improve `cpSync` dest overriding performance fix cpp formatting --- src/node_file.cc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index 54590ef3d60e05..bede3a682b5a2a 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -37,12 +37,12 @@ #include "tracing/trace_event.h" +#include #include "req_wrap-inl.h" #include "stream_base-inl.h" #include "string_bytes.h" #include "uv.h" #include "v8-fast-api-calls.h" -#include #include @@ -3355,7 +3355,7 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); - CHECK_EQ(args.Length(), 4); // src, dest, mode, preserveTimestamps + CHECK_EQ(args.Length(), 4); // src, dest, mode, preserveTimestamps BufferValue src(isolate, args[0]); CHECK_NOT_NULL(*src); @@ -3373,22 +3373,21 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { bool preserve_timestamps = args[3]->IsTrue(); THROW_IF_INSUFFICIENT_PERMISSIONS( - env, permission::PermissionScope::kFileSystemRead, src.ToStringView()); + env, permission::PermissionScope::kFileSystemRead, src.ToStringView()); THROW_IF_INSUFFICIENT_PERMISSIONS( - env, - permission::PermissionScope::kFileSystemWrite, - dest.ToStringView()); + env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView()); std::filesystem::remove(*dest); if (mode == 0) { // if no mode is specified use the faster std::filesystem API - std::filesystem::copy_file(*src, *dest, std::filesystem::copy_options::skip_existing); + std::filesystem::copy_file( + *src, *dest, std::filesystem::copy_options::skip_existing); } else { // if a mode is specified fallback to libuv instead FSReqWrapSync req_wrap_sync("copyfile", *src, *dest); SyncCallAndThrowOnError( - env, &req_wrap_sync, uv_fs_copyfile, *src, *dest, mode); + env, &req_wrap_sync, uv_fs_copyfile, *src, *dest, mode); } if (preserve_timestamps) { From 59919baf33102aa272359e08354f1211136235a4 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 4 May 2025 14:59:48 +0100 Subject: [PATCH 04/13] fixup! fs: improve `cpSync` dest overriding performance use libuv for preserve_timestamps logic --- src/node_file.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index bede3a682b5a2a..bbde27ee085aeb 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3391,12 +3391,14 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { } if (preserve_timestamps) { - struct stat fileStat; - stat(*src, &fileStat); - struct utimbuf dest_times; - dest_times.actime = fileStat.st_atim.tv_sec; - dest_times.modtime = fileStat.st_mtime; - utime(*dest, &dest_times); + uv_fs_t req = uv_fs_t(); + uv_fs_stat(env->event_loop(), &req, *src, nullptr); + const uv_stat_t* const s = static_cast(req.ptr); + FSReqWrapSync req_wrap_sync("utime", *dest); + const double source_atime = s->st_atim.tv_sec + s->st_atim.tv_nsec / 1e9; + const double source_mtime = s->st_mtim.tv_sec + s->st_mtim.tv_nsec / 1e9; + SyncCallAndThrowOnError( + env, &req_wrap_sync, uv_fs_utime, *dest, source_atime, source_mtime); } } From 5263a50dad2f70ed98c9d4ad6769081f18df081c Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 4 May 2025 15:15:27 +0100 Subject: [PATCH 05/13] fixup! fs: improve `cpSync` dest overriding performance capture and throw fs errors --- src/node_file.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index bbde27ee085aeb..f39253155d56f6 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3377,12 +3377,20 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView()); - std::filesystem::remove(*dest); + std::error_code error; + + std::filesystem::remove(*dest, error); + if (error) { + return env->ThrowError(error.message().c_str()); + } if (mode == 0) { // if no mode is specified use the faster std::filesystem API std::filesystem::copy_file( - *src, *dest, std::filesystem::copy_options::skip_existing); + *src, *dest, std::filesystem::copy_options::skip_existing, error); + if (error) { + return env->ThrowError(error.message().c_str()); + } } else { // if a mode is specified fallback to libuv instead FSReqWrapSync req_wrap_sync("copyfile", *src, *dest); From c1bb55cc2d63e4aa9fe573898cdc073257d0dfff Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 4 May 2025 17:02:19 +0100 Subject: [PATCH 06/13] fixup! fs: improve `cpSync` dest overriding performance use libuv functions directly --- src/node_file.cc | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index f39253155d56f6..a69e045737d59e 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -44,6 +44,7 @@ #include "uv.h" #include "v8-fast-api-calls.h" +#include #include #if defined(__MINGW32__) || defined(_MSC_VER) @@ -3392,21 +3393,29 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { return env->ThrowError(error.message().c_str()); } } else { - // if a mode is specified fallback to libuv instead - FSReqWrapSync req_wrap_sync("copyfile", *src, *dest); - SyncCallAndThrowOnError( - env, &req_wrap_sync, uv_fs_copyfile, *src, *dest, mode); + uv_fs_t req; + int result = uv_fs_copyfile(nullptr, &req, *src, *dest, mode, nullptr); + if (is_uv_error(result)) { + return env->ThrowUVException(result, "copyfile", nullptr, *src, *dest); + } } if (preserve_timestamps) { - uv_fs_t req = uv_fs_t(); - uv_fs_stat(env->event_loop(), &req, *src, nullptr); + uv_fs_t req; + int result = uv_fs_stat(nullptr, &req, *src, nullptr); + if (is_uv_error(result)) { + return env->ThrowUVException(result, "stat", nullptr, *src); + } + const uv_stat_t* const s = static_cast(req.ptr); - FSReqWrapSync req_wrap_sync("utime", *dest); const double source_atime = s->st_atim.tv_sec + s->st_atim.tv_nsec / 1e9; const double source_mtime = s->st_mtim.tv_sec + s->st_mtim.tv_nsec / 1e9; - SyncCallAndThrowOnError( - env, &req_wrap_sync, uv_fs_utime, *dest, source_atime, source_mtime); + + int utime_result = + uv_fs_utime(nullptr, &req, *dest, source_atime, source_mtime, nullptr); + if (is_uv_error(utime_result)) { + return env->ThrowUVException(utime_result, "utime", nullptr, *dest); + } } } From 48776af3101a55ff6df89b1a73d1b1dcfa81d78a Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 5 May 2025 22:37:51 +0100 Subject: [PATCH 07/13] Apply suggestions from code review Co-authored-by: Yagiz Nizipli --- src/node_file.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node_file.cc b/src/node_file.cc index a69e045737d59e..1465488e4a5336 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -37,7 +37,6 @@ #include "tracing/trace_event.h" -#include #include "req_wrap-inl.h" #include "stream_base-inl.h" #include "string_bytes.h" @@ -3394,6 +3393,8 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { } } else { uv_fs_t req; + auto cleanup = + OnScopeLeave([&mkstemp_req]() { uv_fs_req_cleanup(&req); }); int result = uv_fs_copyfile(nullptr, &req, *src, *dest, mode, nullptr); if (is_uv_error(result)) { return env->ThrowUVException(result, "copyfile", nullptr, *src, *dest); From 30f5128d4aebee1247f0227f2278e32fcc80b3bd Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 5 May 2025 22:40:41 +0100 Subject: [PATCH 08/13] fix and add OnScopeLeave calls --- src/node_file.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/node_file.cc b/src/node_file.cc index 1465488e4a5336..504042633bc8f0 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3394,7 +3394,7 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { } else { uv_fs_t req; auto cleanup = - OnScopeLeave([&mkstemp_req]() { uv_fs_req_cleanup(&req); }); + OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); int result = uv_fs_copyfile(nullptr, &req, *src, *dest, mode, nullptr); if (is_uv_error(result)) { return env->ThrowUVException(result, "copyfile", nullptr, *src, *dest); @@ -3403,6 +3403,8 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { if (preserve_timestamps) { uv_fs_t req; + auto cleanup = + OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); int result = uv_fs_stat(nullptr, &req, *src, nullptr); if (is_uv_error(result)) { return env->ThrowUVException(result, "stat", nullptr, *src); From f2e3c4559f6ac8b119d1a8fa767e9ef5c945b770 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 5 May 2025 23:50:58 +0100 Subject: [PATCH 09/13] throw unlink errno error to match existing node behavior --- src/node_file.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/node_file.cc b/src/node_file.cc index 504042633bc8f0..5304a49572ed13 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3381,7 +3381,11 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { std::filesystem::remove(*dest, error); if (error) { - return env->ThrowError(error.message().c_str()); + auto dest_path = BufferValueToPath(dest); + auto dest_path_str = PathToString(dest_path); + std::string message = "operation not permitted, unlink"; + return env->ThrowErrnoException( + EPERM, "unlink", message.c_str(), dest_path.c_str()); } if (mode == 0) { From a9ed320e2e147d0dc7dc973bb86655ba1d8ec3d6 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 5 May 2025 23:56:38 +0100 Subject: [PATCH 10/13] use none flag instead of unnecessary skip_existing --- src/node_file.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_file.cc b/src/node_file.cc index 5304a49572ed13..e13f1b759b2dd2 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3391,7 +3391,7 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { if (mode == 0) { // if no mode is specified use the faster std::filesystem API std::filesystem::copy_file( - *src, *dest, std::filesystem::copy_options::skip_existing, error); + *src, *dest, std::filesystem::copy_options::none, error); if (error) { return env->ThrowError(error.message().c_str()); } From e30ef7f47760ea6dacc4f2db65deffba8235ac35 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 6 May 2025 00:07:54 +0100 Subject: [PATCH 11/13] format cpp code --- src/node_file.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index e13f1b759b2dd2..0e54701a118925 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3385,7 +3385,7 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { auto dest_path_str = PathToString(dest_path); std::string message = "operation not permitted, unlink"; return env->ThrowErrnoException( - EPERM, "unlink", message.c_str(), dest_path.c_str()); + EPERM, "unlink", message.c_str(), dest_path.c_str()); } if (mode == 0) { @@ -3397,8 +3397,7 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { } } else { uv_fs_t req; - auto cleanup = - OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); + auto cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); int result = uv_fs_copyfile(nullptr, &req, *src, *dest, mode, nullptr); if (is_uv_error(result)) { return env->ThrowUVException(result, "copyfile", nullptr, *src, *dest); @@ -3407,8 +3406,7 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { if (preserve_timestamps) { uv_fs_t req; - auto cleanup = - OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); + auto cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); int result = uv_fs_stat(nullptr, &req, *src, nullptr); if (is_uv_error(result)) { return env->ThrowUVException(result, "stat", nullptr, *src); From f0a3bd6bd0d0df46a72be46fc4a87f5713400302 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sat, 10 May 2025 16:05:29 +0100 Subject: [PATCH 12/13] fix EPERM throwing code --- src/node_file.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index 0e54701a118925..85e06cf0bed063 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3381,11 +3381,9 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { std::filesystem::remove(*dest, error); if (error) { - auto dest_path = BufferValueToPath(dest); - auto dest_path_str = PathToString(dest_path); std::string message = "operation not permitted, unlink"; return env->ThrowErrnoException( - EPERM, "unlink", message.c_str(), dest_path.c_str()); + EPERM, "unlink", message.c_str(), dest.out()); } if (mode == 0) { From 33cc59938e613c50c56a3d9572062f0ddf0307bc Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Tue, 13 May 2025 00:51:54 +0100 Subject: [PATCH 13/13] improve std code and properly throw std errors --- src/env-inl.h | 7 +++++++ src/env.h | 3 +++ src/node_file.cc | 17 ++++++----------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/env-inl.h b/src/env-inl.h index 9b8d8f6ab070ff..8e72e0500ee656 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -775,6 +775,13 @@ inline void Environment::ThrowError( isolate()->ThrowException(fun(OneByteString(isolate(), errmsg), {})); } +inline void Environment::ThrowStdErrException(std::error_code error_code, + const char* syscall, + const char* path) { + ThrowErrnoException( + error_code.value(), syscall, error_code.message().c_str(), path); +} + inline void Environment::ThrowErrnoException(int errorno, const char* syscall, const char* message, diff --git a/src/env.h b/src/env.h index b428f5937b96fd..657c619a329b48 100644 --- a/src/env.h +++ b/src/env.h @@ -830,6 +830,9 @@ class Environment final : public MemoryRetainer { inline void ThrowError(const char* errmsg); inline void ThrowTypeError(const char* errmsg); inline void ThrowRangeError(const char* errmsg); + inline void ThrowStdErrException(std::error_code error_code, + const char* syscall, + const char* path = nullptr); inline void ThrowErrnoException(int errorno, const char* syscall = nullptr, const char* message = nullptr, diff --git a/src/node_file.cc b/src/node_file.cc index 85e06cf0bed063..ba8a1c464d5799 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3379,26 +3379,21 @@ static void CpSyncOverrideFile(const FunctionCallbackInfo& args) { std::error_code error; - std::filesystem::remove(*dest, error); - if (error) { - std::string message = "operation not permitted, unlink"; - return env->ThrowErrnoException( - EPERM, "unlink", message.c_str(), dest.out()); + if (!std::filesystem::remove(*dest, error)) { + return env->ThrowStdErrException(error, "unlink", *dest); } if (mode == 0) { // if no mode is specified use the faster std::filesystem API - std::filesystem::copy_file( - *src, *dest, std::filesystem::copy_options::none, error); - if (error) { - return env->ThrowError(error.message().c_str()); + if (!std::filesystem::copy_file(*src, *dest, error)) { + return env->ThrowStdErrException(error, "cp", *dest); } } else { uv_fs_t req; auto cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); - int result = uv_fs_copyfile(nullptr, &req, *src, *dest, mode, nullptr); + auto result = uv_fs_copyfile(nullptr, &req, *src, *dest, mode, nullptr); if (is_uv_error(result)) { - return env->ThrowUVException(result, "copyfile", nullptr, *src, *dest); + return env->ThrowUVException(result, "cp", nullptr, *src, *dest); } }