Skip to content

Commit 4eb45b8

Browse files
committed
fs: throw copyFileSync errors in JS
PR-URL: #18871 Refs: #18106 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
1 parent d2dc2a5 commit 4eb45b8

File tree

4 files changed

+112
-30
lines changed

4 files changed

+112
-30
lines changed

lib/fs.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,7 +1859,7 @@ fs.copyFile = function(src, dest, flags, callback) {
18591859
callback = flags;
18601860
flags = 0;
18611861
} else if (typeof callback !== 'function') {
1862-
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'callback', 'Function');
1862+
throw new errors.TypeError('ERR_INVALID_CALLBACK');
18631863
}
18641864

18651865
src = getPathFromURL(src);
@@ -1882,10 +1882,13 @@ fs.copyFileSync = function(src, dest, flags) {
18821882
validatePath(src, 'src');
18831883
validatePath(dest, 'dest');
18841884

1885+
const ctx = { path: src, dest }; // non-prefixed
1886+
18851887
src = pathModule._makeLong(src);
18861888
dest = pathModule._makeLong(dest);
18871889
flags = flags | 0;
1888-
binding.copyFile(src, dest, flags);
1890+
binding.copyFile(src, dest, flags, undefined, ctx);
1891+
handleErrorFromBinding(ctx);
18891892
};
18901893

18911894

src/node_file.cc

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,21 +1232,28 @@ static void OpenFileHandle(const FunctionCallbackInfo<Value>& args) {
12321232
static void CopyFile(const FunctionCallbackInfo<Value>& args) {
12331233
Environment* env = Environment::GetCurrent(args);
12341234

1235-
CHECK_GE(args.Length(), 3);
1236-
CHECK(args[2]->IsInt32());
1235+
const int argc = args.Length();
1236+
CHECK_GE(argc, 3);
12371237

12381238
BufferValue src(env->isolate(), args[0]);
12391239
CHECK_NE(*src, nullptr);
1240+
12401241
BufferValue dest(env->isolate(), args[1]);
12411242
CHECK_NE(*dest, nullptr);
1242-
int flags = args[2]->Int32Value();
1243+
1244+
CHECK(args[2]->IsInt32());
1245+
const int flags = args[2].As<Int32>()->Value();
12431246

12441247
FSReqBase* req_wrap = GetReqWrap(env, args[3]);
1245-
if (req_wrap != nullptr) {
1246-
AsyncCall(env, req_wrap, args, "copyfile", UTF8, AfterNoArgs,
1247-
uv_fs_copyfile, *src, *dest, flags);
1248-
} else {
1249-
SYNC_DEST_CALL(copyfile, *src, *dest, *src, *dest, flags)
1248+
if (req_wrap != nullptr) { // copyFile(src, dest, flags, req)
1249+
AsyncDestCall(env, req_wrap, args, "copyfile",
1250+
*dest, dest.length(), UTF8, AfterNoArgs,
1251+
uv_fs_copyfile, *src, *dest, flags);
1252+
} else { // copyFile(src, dest, flags, undefined, ctx)
1253+
CHECK_EQ(argc, 5);
1254+
fs_req_wrap req_wrap;
1255+
SyncCall(env, args[4], &req_wrap, "copyfile",
1256+
uv_fs_copyfile, *src, *dest, flags);
12501257
}
12511258
}
12521259

test/parallel/test-fs-copyfile.js

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const fixtures = require('../common/fixtures');
44
const tmpdir = require('../common/tmpdir');
55
const assert = require('assert');
66
const fs = require('fs');
7+
const uv = process.binding('uv');
78
const path = require('path');
89
const src = fixtures.path('a.js');
910
const dest = path.join(tmpdir.path, 'copyfile.out');
@@ -37,15 +38,6 @@ verify(src, dest);
3738
fs.copyFileSync(src, dest, 0);
3839
verify(src, dest);
3940

40-
// Throws if destination exists and the COPYFILE_EXCL flag is provided.
41-
assert.throws(() => {
42-
fs.copyFileSync(src, dest, COPYFILE_EXCL);
43-
}, /^Error: EEXIST|ENOENT:.+, copyfile/);
44-
45-
// Throws if the source does not exist.
46-
assert.throws(() => {
47-
fs.copyFileSync(`${src}__does_not_exist`, dest, COPYFILE_EXCL);
48-
}, /^Error: ENOENT: no such file or directory, copyfile/);
4941

5042
// Copies asynchronously.
5143
fs.unlinkSync(dest);
@@ -55,19 +47,30 @@ fs.copyFile(src, dest, common.mustCall((err) => {
5547

5648
// Copy asynchronously with flags.
5749
fs.copyFile(src, dest, COPYFILE_EXCL, common.mustCall((err) => {
58-
assert(
59-
/^Error: EEXIST: file already exists, copyfile/.test(err.toString())
60-
);
50+
if (err.code === 'ENOENT') { // Could be ENOENT or EEXIST
51+
assert.strictEqual(err.message,
52+
'ENOENT: no such file or directory, copyfile ' +
53+
`'${src}' -> '${dest}'`);
54+
assert.strictEqual(err.errno, uv.UV_ENOENT);
55+
assert.strictEqual(err.code, 'ENOENT');
56+
assert.strictEqual(err.syscall, 'copyfile');
57+
} else {
58+
assert.strictEqual(err.message,
59+
'EEXIST: file already exists, copyfile ' +
60+
`'${src}' -> '${dest}'`);
61+
assert.strictEqual(err.errno, uv.UV_EEXIST);
62+
assert.strictEqual(err.code, 'EEXIST');
63+
assert.strictEqual(err.syscall, 'copyfile');
64+
}
6165
}));
6266
}));
6367

6468
// Throws if callback is not a function.
6569
common.expectsError(() => {
6670
fs.copyFile(src, dest, 0, 0);
6771
}, {
68-
code: 'ERR_INVALID_ARG_TYPE',
69-
type: TypeError,
70-
message: 'The "callback" argument must be of type Function'
72+
code: 'ERR_INVALID_CALLBACK',
73+
type: TypeError
7174
});
7275

7376
// Throws if the source path is not a string.
@@ -101,8 +104,3 @@ common.expectsError(() => {
101104
}
102105
);
103106
});
104-
105-
// Errors if invalid flags are provided.
106-
assert.throws(() => {
107-
fs.copyFileSync(src, dest, -1);
108-
}, /^Error: EINVAL: invalid argument, copyfile/);

test/parallel/test-fs-error-messages.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const existingFile = fixtures.path('exit.js');
3030
const existingFile2 = fixtures.path('create-file.js');
3131
const existingDir = fixtures.path('empty');
3232
const existingDir2 = fixtures.path('keys');
33+
const { COPYFILE_EXCL } = fs.constants;
3334
const uv = process.binding('uv');
3435

3536
// Template tag function for escaping special characters in strings so that:
@@ -634,3 +635,76 @@ if (!common.isAIX) {
634635
validateError
635636
);
636637
}
638+
639+
// copyFile with invalid flags
640+
{
641+
const validateError = (err) => {
642+
assert.strictEqual(err.message,
643+
'EINVAL: invalid argument, copyfile ' +
644+
`'${existingFile}' -> '${nonexistentFile}'`);
645+
assert.strictEqual(err.errno, uv.UV_EINVAL);
646+
assert.strictEqual(err.code, 'EINVAL');
647+
assert.strictEqual(err.syscall, 'copyfile');
648+
return true;
649+
};
650+
651+
// TODO(joyeecheung): test fs.copyFile() when uv_fs_copyfile does not
652+
// keep the loop open when the flags are invalid.
653+
// See https://github.com/libuv/libuv/pull/1747
654+
655+
assert.throws(
656+
() => fs.copyFileSync(existingFile, nonexistentFile, -1),
657+
validateError
658+
);
659+
}
660+
661+
// copyFile: destination exists but the COPYFILE_EXCL flag is provided.
662+
{
663+
const validateError = (err) => {
664+
if (err.code === 'ENOENT') { // Could be ENOENT or EEXIST
665+
assert.strictEqual(err.message,
666+
'ENOENT: no such file or directory, copyfile ' +
667+
`'${existingFile}' -> '${existingFile2}'`);
668+
assert.strictEqual(err.errno, uv.UV_ENOENT);
669+
assert.strictEqual(err.code, 'ENOENT');
670+
assert.strictEqual(err.syscall, 'copyfile');
671+
} else {
672+
assert.strictEqual(err.message,
673+
'EEXIST: file already exists, copyfile ' +
674+
`'${existingFile}' -> '${existingFile2}'`);
675+
assert.strictEqual(err.errno, uv.UV_EEXIST);
676+
assert.strictEqual(err.code, 'EEXIST');
677+
assert.strictEqual(err.syscall, 'copyfile');
678+
}
679+
return true;
680+
};
681+
682+
fs.copyFile(existingFile, existingFile2, COPYFILE_EXCL,
683+
common.mustCall(validateError));
684+
685+
assert.throws(
686+
() => fs.copyFileSync(existingFile, existingFile2, COPYFILE_EXCL),
687+
validateError
688+
);
689+
}
690+
691+
// copyFile: the source does not exist.
692+
{
693+
const validateError = (err) => {
694+
assert.strictEqual(err.message,
695+
'ENOENT: no such file or directory, copyfile ' +
696+
`'${nonexistentFile}' -> '${existingFile2}'`);
697+
assert.strictEqual(err.errno, uv.UV_ENOENT);
698+
assert.strictEqual(err.code, 'ENOENT');
699+
assert.strictEqual(err.syscall, 'copyfile');
700+
return true;
701+
};
702+
703+
fs.copyFile(nonexistentFile, existingFile2, COPYFILE_EXCL,
704+
common.mustCall(validateError));
705+
706+
assert.throws(
707+
() => fs.copyFileSync(nonexistentFile, existingFile2, COPYFILE_EXCL),
708+
validateError
709+
);
710+
}

0 commit comments

Comments
 (0)