diff --git a/lib/fs.js b/lib/fs.js index 0b17885329ab92..45c09972a6032e 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -575,8 +575,12 @@ fs.openSync = function(path, flags, mode) { validatePath(path); validateUint32(mode, 'mode'); - return binding.open(pathModule.toNamespacedPath(path), - stringToFlags(flags), mode); + const ctx = { path }; + const result = binding.open(pathModule.toNamespacedPath(path), + stringToFlags(flags), mode, + undefined, ctx); + handleErrorFromBinding(ctx); + return result; }; fs.read = function(fd, buffer, offset, length, position, callback) { @@ -802,7 +806,9 @@ fs.rmdir = function(path, callback) { fs.rmdirSync = function(path) { path = getPathFromURL(path); validatePath(path); - return binding.rmdir(pathModule.toNamespacedPath(path)); + const ctx = { path }; + binding.rmdir(pathModule.toNamespacedPath(path), undefined, ctx); + handleErrorFromBinding(ctx); }; fs.fdatasync = function(fd, callback) { @@ -851,7 +857,9 @@ fs.mkdirSync = function(path, mode) { validatePath(path); mode = modeNum(mode, 0o777); validateUint32(mode, 'mode'); - return binding.mkdir(pathModule.toNamespacedPath(path), mode); + const ctx = { path }; + binding.mkdir(pathModule.toNamespacedPath(path), mode, undefined, ctx); + handleErrorFromBinding(ctx); }; fs.readdir = function(path, options, callback) { @@ -869,7 +877,11 @@ fs.readdirSync = function(path, options) { options = getOptions(options, {}); path = getPathFromURL(path); validatePath(path); - return binding.readdir(pathModule.toNamespacedPath(path), options.encoding); + const ctx = { path }; + const result = binding.readdir(pathModule.toNamespacedPath(path), + options.encoding, undefined, ctx); + handleErrorFromBinding(ctx); + return result; }; fs.fstat = function(fd, callback) { @@ -1096,7 +1108,9 @@ fs.chmodSync = function(path, mode) { validatePath(path); mode = modeNum(mode); validateUint32(mode, 'mode'); - return binding.chmod(pathModule.toNamespacedPath(path), mode); + const ctx = { path }; + binding.chmod(pathModule.toNamespacedPath(path), mode, undefined, ctx); + handleErrorFromBinding(ctx); }; if (constants.O_SYMLINK !== undefined) { @@ -1164,7 +1178,9 @@ fs.chownSync = function(path, uid, gid) { validatePath(path); validateUint32(uid, 'uid'); validateUint32(gid, 'gid'); - return binding.chown(pathModule.toNamespacedPath(path), uid, gid); + const ctx = { path }; + binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); + handleErrorFromBinding(ctx); }; // exported for unit tests, not for public consumption @@ -1186,9 +1202,11 @@ fs.utimes = function(path, atime, mtime, callback) { fs.utimesSync = function(path, atime, mtime) { path = getPathFromURL(path); validatePath(path); + const ctx = { path }; binding.utimes(pathModule.toNamespacedPath(path), - toUnixTimestamp(atime), - toUnixTimestamp(mtime)); + toUnixTimestamp(atime), toUnixTimestamp(mtime), + undefined, ctx); + handleErrorFromBinding(ctx); }; fs.futimes = function(fd, atime, mtime, callback) { @@ -1699,7 +1717,10 @@ fs.realpathSync.native = function(path, options) { options = getOptions(options, {}); path = getPathFromURL(path); validatePath(path); - return binding.realpath(path, options.encoding); + const ctx = { path }; + const result = binding.realpath(path, options.encoding, undefined, ctx); + handleErrorFromBinding(ctx); + return result; }; @@ -1872,7 +1893,12 @@ fs.mkdtempSync = function(prefix, options) { prefix); } nullCheck(prefix, 'prefix'); - return binding.mkdtemp(`${prefix}XXXXXX`, options.encoding); + const path = `${prefix}XXXXXX`; + const ctx = { path }; + const result = binding.mkdtemp(path, options.encoding, + undefined, ctx); + handleErrorFromBinding(ctx); + return result; }; @@ -1887,7 +1913,7 @@ fs.copyFile = function(src, dest, flags, callback) { callback = flags; flags = 0; } else if (typeof callback !== 'function') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'callback', 'Function'); + throw new errors.TypeError('ERR_INVALID_CALLBACK'); } src = getPathFromURL(src); @@ -1910,10 +1936,13 @@ fs.copyFileSync = function(src, dest, flags) { validatePath(src, 'src'); validatePath(dest, 'dest'); + const ctx = { path: src, dest }; // non-prefixed + src = pathModule._makeLong(src); dest = pathModule._makeLong(dest); flags = flags | 0; - binding.copyFile(src, dest, flags); + binding.copyFile(src, dest, flags, undefined, ctx); + handleErrorFromBinding(ctx); }; diff --git a/src/node_file.cc b/src/node_file.cc index c9c7570779bcbe..17dfd1f9603724 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -102,6 +102,7 @@ using v8::ObjectTemplate; using v8::Promise; using v8::String; using v8::Symbol; +using v8::Uint32; using v8::Undefined; using v8::Value; @@ -1005,55 +1006,73 @@ static void Unlink(const FunctionCallbackInfo& args) { static void RMDir(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 1); + const int argc = args.Length(); + CHECK_GE(argc, 2); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); - FSReqBase* req_wrap = GetReqWrap(env, args[1]); + FSReqBase* req_wrap = GetReqWrap(env, args[1]); // rmdir(path, req) if (req_wrap != nullptr) { AsyncCall(env, req_wrap, args, "rmdir", UTF8, AfterNoArgs, uv_fs_rmdir, *path); - } else { - SYNC_CALL(rmdir, *path, *path) + } else { // rmdir(path, undefined, ctx) + CHECK_EQ(argc, 3); + fs_req_wrap req_wrap; + SyncCall(env, args[2], &req_wrap, "rmdir", + uv_fs_rmdir, *path); } } static void MKDir(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 2); - CHECK(args[1]->IsInt32()); + const int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); - int mode = static_cast(args[1]->Int32Value()); + CHECK(args[1]->IsInt32()); + const int mode = args[1].As()->Value(); FSReqBase* req_wrap = GetReqWrap(env, args[2]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // mkdir(path, mode, req) AsyncCall(env, req_wrap, args, "mkdir", UTF8, AfterNoArgs, uv_fs_mkdir, *path, mode); - } else { - SYNC_CALL(mkdir, *path, *path, mode) + } else { // mkdir(path, mode, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + SyncCall(env, args[3], &req_wrap, "mkdir", + uv_fs_mkdir, *path, mode); } } static void RealPath(const FunctionCallbackInfo& args) { - CHECK_GE(args.Length(), 2); Environment* env = Environment::GetCurrent(args); + + const int argc = args.Length(); + CHECK_GE(argc, 3); + BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8); FSReqBase* req_wrap = GetReqWrap(env, args[2]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // realpath(path, encoding, req) AsyncCall(env, req_wrap, args, "realpath", encoding, AfterStringPtr, uv_fs_realpath, *path); - } else { - SYNC_CALL(realpath, *path, *path); - const char* link_path = static_cast(SYNC_REQ.ptr); + } else { // realpath(path, encoding, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + int err = SyncCall(env, args[3], &req_wrap, "realpath", + uv_fs_realpath, *path); + if (err < 0) { + return; // syscall failed, no need to continue, error info is in ctx + } + + const char* link_path = static_cast(req_wrap.req.ptr); Local error; MaybeLocal rc = StringBytes::Encode(env->isolate(), @@ -1061,9 +1080,11 @@ static void RealPath(const FunctionCallbackInfo& args) { encoding, &error); if (rc.IsEmpty()) { - env->isolate()->ThrowException(error); + Local ctx = args[3].As(); + ctx->Set(env->context(), env->error_string(), error).FromJust(); return; } + args.GetReturnValue().Set(rc.ToLocalChecked()); } } @@ -1071,7 +1092,8 @@ static void RealPath(const FunctionCallbackInfo& args) { static void ReadDir(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 1); + const int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); @@ -1079,13 +1101,19 @@ static void ReadDir(const FunctionCallbackInfo& args) { const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8); FSReqBase* req_wrap = GetReqWrap(env, args[2]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // readdir(path, encoding, req) AsyncCall(env, req_wrap, args, "scandir", encoding, AfterScanDir, uv_fs_scandir, *path, 0 /*flags*/); - } else { - SYNC_CALL(scandir, *path, *path, 0 /*flags*/) + } else { // readdir(path, encoding, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + int err = SyncCall(env, args[3], &req_wrap, "scandir", + uv_fs_scandir, *path, 0 /*flags*/); + if (err < 0) { + return; // syscall failed, no need to continue, error info is in ctx + } - CHECK_GE(SYNC_REQ.result, 0); + CHECK_GE(req_wrap.req.result, 0); int r; Local names = Array::New(env->isolate(), 0); Local fn = env->push_values_to_array_function(); @@ -1095,11 +1123,17 @@ static void ReadDir(const FunctionCallbackInfo& args) { for (int i = 0; ; i++) { uv_dirent_t ent; - r = uv_fs_scandir_next(&SYNC_REQ, &ent); + r = uv_fs_scandir_next(&(req_wrap.req), &ent); if (r == UV_EOF) break; - if (r != 0) - return env->ThrowUVException(r, "readdir", "", *path); + if (r != 0) { + Local ctx = args[3].As(); + ctx->Set(env->context(), env->errno_string(), + Integer::New(env->isolate(), r)).FromJust(); + ctx->Set(env->context(), env->syscall_string(), + OneByteString(env->isolate(), "readdir")).FromJust(); + return; + } Local error; MaybeLocal filename = StringBytes::Encode(env->isolate(), @@ -1107,7 +1141,8 @@ static void ReadDir(const FunctionCallbackInfo& args) { encoding, &error); if (filename.IsEmpty()) { - env->isolate()->ThrowException(error); + Local ctx = args[3].As(); + ctx->Set(env->context(), env->error_string(), error).FromJust(); return; } @@ -1136,50 +1171,61 @@ static void ReadDir(const FunctionCallbackInfo& args) { static void Open(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Local context = env->context(); - CHECK_GE(args.Length(), 3); - CHECK(args[1]->IsInt32()); - CHECK(args[2]->IsInt32()); + const int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); - int flags = args[1]->Int32Value(context).ToChecked(); - int mode = args[2]->Int32Value(context).ToChecked(); + CHECK(args[1]->IsInt32()); + const int flags = args[1].As()->Value(); + + CHECK(args[2]->IsInt32()); + const int mode = args[2].As()->Value(); FSReqBase* req_wrap = GetReqWrap(env, args[3]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // open(path, flags, mode, req) AsyncCall(env, req_wrap, args, "open", UTF8, AfterInteger, uv_fs_open, *path, flags, mode); - } else { - SYNC_CALL(open, *path, *path, flags, mode) - args.GetReturnValue().Set(SYNC_RESULT); + } else { // open(path, flags, mode, undefined, ctx) + CHECK_EQ(argc, 5); + fs_req_wrap req_wrap; + int result = SyncCall(env, args[4], &req_wrap, "open", + uv_fs_open, *path, flags, mode); + args.GetReturnValue().Set(result); } } static void OpenFileHandle(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Local context = env->context(); - CHECK_GE(args.Length(), 3); - CHECK(args[1]->IsInt32()); - CHECK(args[2]->IsInt32()); + const int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); - int flags = args[1]->Int32Value(context).ToChecked(); - int mode = args[2]->Int32Value(context).ToChecked(); + CHECK(args[1]->IsInt32()); + const int flags = args[1].As()->Value(); + + CHECK(args[2]->IsInt32()); + const int mode = args[2].As()->Value(); FSReqBase* req_wrap = GetReqWrap(env, args[3]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // openFileHandle(path, flags, mode, req) AsyncCall(env, req_wrap, args, "open", UTF8, AfterOpenFileHandle, uv_fs_open, *path, flags, mode); - } else { - SYNC_CALL(open, *path, *path, flags, mode) + } else { // openFileHandle(path, flags, mode, undefined, ctx) + CHECK_EQ(argc, 5); + fs_req_wrap req_wrap; + int result = SyncCall(env, args[4], &req_wrap, "open", + uv_fs_open, *path, flags, mode); + if (result < 0) { + return; // syscall failed, no need to continue, error info is in ctx + } HandleScope scope(env->isolate()); - FileHandle* fd = new FileHandle(env, SYNC_RESULT); + FileHandle* fd = new FileHandle(env, result); args.GetReturnValue().Set(fd->object()); } } @@ -1187,21 +1233,28 @@ static void OpenFileHandle(const FunctionCallbackInfo& args) { static void CopyFile(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 3); - CHECK(args[2]->IsInt32()); + const int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue src(env->isolate(), args[0]); CHECK_NE(*src, nullptr); + BufferValue dest(env->isolate(), args[1]); CHECK_NE(*dest, nullptr); - int flags = args[2]->Int32Value(); + + CHECK(args[2]->IsInt32()); + const int flags = args[2].As()->Value(); FSReqBase* req_wrap = GetReqWrap(env, args[3]); - if (req_wrap != nullptr) { - AsyncCall(env, req_wrap, args, "copyfile", UTF8, AfterNoArgs, - uv_fs_copyfile, *src, *dest, flags); - } else { - SYNC_DEST_CALL(copyfile, *src, *dest, *src, *dest, flags) + if (req_wrap != nullptr) { // copyFile(src, dest, flags, req) + AsyncDestCall(env, req_wrap, args, "copyfile", + *dest, dest.length(), UTF8, AfterNoArgs, + uv_fs_copyfile, *src, *dest, flags); + } else { // copyFile(src, dest, flags, undefined, ctx) + CHECK_EQ(argc, 5); + fs_req_wrap req_wrap; + SyncCall(env, args[4], &req_wrap, "copyfile", + uv_fs_copyfile, *src, *dest, flags); } } @@ -1414,20 +1467,24 @@ static void Read(const FunctionCallbackInfo& args) { static void Chmod(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 2); - CHECK(args[1]->IsInt32()); + const int argc = args.Length(); + CHECK_GE(argc, 2); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); - int mode = static_cast(args[1]->Int32Value()); + CHECK(args[1]->IsInt32()); + int mode = args[1].As()->Value(); FSReqBase* req_wrap = GetReqWrap(env, args[2]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // chmod(path, mode, req) AsyncCall(env, req_wrap, args, "chmod", UTF8, AfterNoArgs, uv_fs_chmod, *path, mode); - } else { - SYNC_CALL(chmod, *path, *path, mode); + } else { // chmod(path, mode, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + SyncCall(env, args[3], &req_wrap, "chmod", + uv_fs_chmod, *path, mode); } } @@ -1460,23 +1517,27 @@ static void FChmod(const FunctionCallbackInfo& args) { static void Chown(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - int len = args.Length(); - CHECK_GE(len, 3); - CHECK(args[1]->IsUint32()); - CHECK(args[2]->IsUint32()); + const int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); - uv_uid_t uid = static_cast(args[1]->Uint32Value()); - uv_gid_t gid = static_cast(args[2]->Uint32Value()); + CHECK(args[1]->IsUint32()); + const uv_uid_t uid = static_cast(args[1].As()->Value()); + + CHECK(args[2]->IsUint32()); + const uv_gid_t gid = static_cast(args[2].As()->Value()); FSReqBase* req_wrap = GetReqWrap(env, args[3]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // chown(path, uid, gid, req) AsyncCall(env, req_wrap, args, "chown", UTF8, AfterNoArgs, uv_fs_chown, *path, uid, gid); - } else { - SYNC_CALL(chown, *path, *path, uid, gid); + } else { // chown(path, uid, gid, undefined, ctx) + CHECK_EQ(argc, 5); + fs_req_wrap req_wrap; + SyncCall(env, args[4], &req_wrap, "chown", + uv_fs_chown, *path, uid, gid); } } @@ -1508,22 +1569,27 @@ static void FChown(const FunctionCallbackInfo& args) { static void UTimes(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 3); - CHECK(args[1]->IsNumber()); - CHECK(args[2]->IsNumber()); + const int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); - const double atime = static_cast(args[1]->NumberValue()); - const double mtime = static_cast(args[2]->NumberValue()); + CHECK(args[1]->IsNumber()); + const double atime = args[1].As()->Value(); + + CHECK(args[2]->IsNumber()); + const double mtime = args[2].As()->Value(); FSReqBase* req_wrap = GetReqWrap(env, args[3]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // utimes(path, atime, mtime, req) AsyncCall(env, req_wrap, args, "utime", UTF8, AfterNoArgs, uv_fs_utime, *path, atime, mtime); - } else { - SYNC_CALL(utime, *path, *path, atime, mtime); + } else { // utimes(path, atime, mtime, undefined, ctx) + CHECK_EQ(argc, 5); + fs_req_wrap req_wrap; + SyncCall(env, args[4], &req_wrap, "utime", + uv_fs_utime, *path, atime, mtime); } } @@ -1550,7 +1616,8 @@ static void FUTimes(const FunctionCallbackInfo& args) { static void Mkdtemp(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 2); + const int argc = args.Length(); + CHECK_GE(argc, 2); BufferValue tmpl(env->isolate(), args[0]); CHECK_NE(*tmpl, nullptr); @@ -1558,18 +1625,22 @@ static void Mkdtemp(const FunctionCallbackInfo& args) { const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8); FSReqBase* req_wrap = GetReqWrap(env, args[2]); - if (req_wrap != nullptr) { + if (req_wrap != nullptr) { // mkdtemp(tmpl, encoding, req) AsyncCall(env, req_wrap, args, "mkdtemp", encoding, AfterStringPath, uv_fs_mkdtemp, *tmpl); - } else { - SYNC_CALL(mkdtemp, *tmpl, *tmpl); - const char* path = static_cast(SYNC_REQ.path); + } else { // mkdtemp(tmpl, encoding, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + SyncCall(env, args[3], &req_wrap, "mkdtemp", + uv_fs_mkdtemp, *tmpl); + const char* path = static_cast(req_wrap.req.path); Local error; MaybeLocal rc = StringBytes::Encode(env->isolate(), path, encoding, &error); if (rc.IsEmpty()) { - env->isolate()->ThrowException(error); + Local ctx = args[3].As(); + ctx->Set(env->context(), env->error_string(), error).FromJust(); return; } args.GetReturnValue().Set(rc.ToLocalChecked()); diff --git a/test/parallel/test-fs-copyfile.js b/test/parallel/test-fs-copyfile.js index 8b910ba046ec48..21e0838148b5ef 100644 --- a/test/parallel/test-fs-copyfile.js +++ b/test/parallel/test-fs-copyfile.js @@ -4,6 +4,7 @@ const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); const assert = require('assert'); const fs = require('fs'); +const uv = process.binding('uv'); const path = require('path'); const src = fixtures.path('a.js'); const dest = path.join(tmpdir.path, 'copyfile.out'); @@ -37,15 +38,6 @@ verify(src, dest); fs.copyFileSync(src, dest, 0); verify(src, dest); -// Throws if destination exists and the COPYFILE_EXCL flag is provided. -assert.throws(() => { - fs.copyFileSync(src, dest, COPYFILE_EXCL); -}, /^Error: EEXIST|ENOENT:.+, copyfile/); - -// Throws if the source does not exist. -assert.throws(() => { - fs.copyFileSync(`${src}__does_not_exist`, dest, COPYFILE_EXCL); -}, /^Error: ENOENT: no such file or directory, copyfile/); // Copies asynchronously. fs.unlinkSync(dest); @@ -55,9 +47,21 @@ fs.copyFile(src, dest, common.mustCall((err) => { // Copy asynchronously with flags. fs.copyFile(src, dest, COPYFILE_EXCL, common.mustCall((err) => { - assert( - /^Error: EEXIST: file already exists, copyfile/.test(err.toString()) - ); + if (err.code === 'ENOENT') { // Could be ENOENT or EEXIST + assert.strictEqual(err.message, + 'ENOENT: no such file or directory, copyfile ' + + `'${src}' -> '${dest}'`); + assert.strictEqual(err.errno, uv.UV_ENOENT); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'copyfile'); + } else { + assert.strictEqual(err.message, + 'EEXIST: file already exists, copyfile ' + + `'${src}' -> '${dest}'`); + assert.strictEqual(err.errno, uv.UV_EEXIST); + assert.strictEqual(err.code, 'EEXIST'); + assert.strictEqual(err.syscall, 'copyfile'); + } })); })); @@ -65,9 +69,8 @@ fs.copyFile(src, dest, common.mustCall((err) => { common.expectsError(() => { fs.copyFile(src, dest, 0, 0); }, { - code: 'ERR_INVALID_ARG_TYPE', - type: TypeError, - message: 'The "callback" argument must be of type Function' + code: 'ERR_INVALID_CALLBACK', + type: TypeError }); // Throws if the source path is not a string. @@ -101,8 +104,3 @@ common.expectsError(() => { } ); }); - -// Errors if invalid flags are provided. -assert.throws(() => { - fs.copyFileSync(src, dest, -1); -}, /^Error: EINVAL: invalid argument, copyfile/); diff --git a/test/parallel/test-fs-error-messages.js b/test/parallel/test-fs-error-messages.js index ba44c28d432983..b96f19800c2a14 100644 --- a/test/parallel/test-fs-error-messages.js +++ b/test/parallel/test-fs-error-messages.js @@ -25,10 +25,12 @@ const fixtures = require('../common/fixtures'); const assert = require('assert'); const fs = require('fs'); const nonexistentFile = fixtures.path('non-existent'); +const nonexistentDir = fixtures.path('non-existent', 'foo', 'bar'); const existingFile = fixtures.path('exit.js'); const existingFile2 = fixtures.path('create-file.js'); const existingDir = fixtures.path('empty'); const existingDir2 = fixtures.path('keys'); +const { COPYFILE_EXCL } = fs.constants; const uv = process.binding('uv'); // Template tag function for escaping special characters in strings so that: @@ -126,6 +128,27 @@ function re(literals, ...values) { ); } +// native realpath +{ + const validateError = (err) => { + assert.strictEqual(nonexistentFile, err.path); + assert.strictEqual( + err.message, + `ENOENT: no such file or directory, realpath '${nonexistentFile}'`); + assert.strictEqual(err.errno, uv.UV_ENOENT); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'realpath'); + return true; + }; + + fs.realpath.native(nonexistentFile, common.mustCall(validateError)); + + assert.throws( + () => fs.realpathSync.native(nonexistentFile), + validateError + ); +} + // readlink { const validateError = (err) => { @@ -524,3 +547,147 @@ function re(literals, ...values) { validateError ); } + +// chown +if (!common.isWindows) { + const validateError = (err) => { + assert.strictEqual(nonexistentFile, err.path); + assert.strictEqual( + err.message, + `ENOENT: no such file or directory, chown '${nonexistentFile}'`); + assert.strictEqual(err.errno, uv.UV_ENOENT); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'chown'); + return true; + }; + + fs.chown(nonexistentFile, process.getuid(), process.getgid(), + common.mustCall(validateError)); + + assert.throws( + () => fs.chownSync(nonexistentFile, + process.getuid(), process.getgid()), + validateError + ); +} + +// utimes +if (!common.isAIX) { + const validateError = (err) => { + assert.strictEqual(nonexistentFile, err.path); + assert.strictEqual( + err.message, + `ENOENT: no such file or directory, utime '${nonexistentFile}'`); + assert.strictEqual(err.errno, uv.UV_ENOENT); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'utime'); + return true; + }; + + fs.utimes(nonexistentFile, new Date(), new Date(), + common.mustCall(validateError)); + + assert.throws( + () => fs.utimesSync(nonexistentFile, new Date(), new Date()), + validateError + ); +} + +// mkdtemp +{ + const validateError = (err) => { + const pathPrefix = new RegExp('^' + re`${nonexistentDir}`); + assert(pathPrefix.test(err.path), + `Expect ${err.path} to match ${pathPrefix}`); + + const prefix = new RegExp('^ENOENT: no such file or directory, mkdtemp ' + + re`'${nonexistentDir}`); + assert(prefix.test(err.message), + `Expect ${err.message} to match ${prefix}`); + + assert.strictEqual(err.errno, uv.UV_ENOENT); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'mkdtemp'); + return true; + }; + + fs.mkdtemp(nonexistentDir, common.mustCall(validateError)); + + assert.throws( + () => fs.mkdtempSync(nonexistentDir), + validateError + ); +} + +// copyFile with invalid flags +{ + const validateError = (err) => { + assert.strictEqual(err.message, + 'EINVAL: invalid argument, copyfile ' + + `'${existingFile}' -> '${nonexistentFile}'`); + assert.strictEqual(err.errno, uv.UV_EINVAL); + assert.strictEqual(err.code, 'EINVAL'); + assert.strictEqual(err.syscall, 'copyfile'); + return true; + }; + + // TODO(joyeecheung): test fs.copyFile() when uv_fs_copyfile does not + // keep the loop open when the flags are invalid. + // See https://github.com/libuv/libuv/pull/1747 + + assert.throws( + () => fs.copyFileSync(existingFile, nonexistentFile, -1), + validateError + ); +} + +// copyFile: destination exists but the COPYFILE_EXCL flag is provided. +{ + const validateError = (err) => { + if (err.code === 'ENOENT') { // Could be ENOENT or EEXIST + assert.strictEqual(err.message, + 'ENOENT: no such file or directory, copyfile ' + + `'${existingFile}' -> '${existingFile2}'`); + assert.strictEqual(err.errno, uv.UV_ENOENT); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'copyfile'); + } else { + assert.strictEqual(err.message, + 'EEXIST: file already exists, copyfile ' + + `'${existingFile}' -> '${existingFile2}'`); + assert.strictEqual(err.errno, uv.UV_EEXIST); + assert.strictEqual(err.code, 'EEXIST'); + assert.strictEqual(err.syscall, 'copyfile'); + } + return true; + }; + + fs.copyFile(existingFile, existingFile2, COPYFILE_EXCL, + common.mustCall(validateError)); + + assert.throws( + () => fs.copyFileSync(existingFile, existingFile2, COPYFILE_EXCL), + validateError + ); +} + +// copyFile: the source does not exist. +{ + const validateError = (err) => { + assert.strictEqual(err.message, + 'ENOENT: no such file or directory, copyfile ' + + `'${nonexistentFile}' -> '${existingFile2}'`); + assert.strictEqual(err.errno, uv.UV_ENOENT); + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'copyfile'); + return true; + }; + + fs.copyFile(nonexistentFile, existingFile2, COPYFILE_EXCL, + common.mustCall(validateError)); + + assert.throws( + () => fs.copyFileSync(nonexistentFile, existingFile2, COPYFILE_EXCL), + validateError + ); +} diff --git a/test/parallel/test-fs-filehandle.js b/test/parallel/test-fs-filehandle.js index 8b7de1a35b3ab2..761193b6d2993e 100644 --- a/test/parallel/test-fs-filehandle.js +++ b/test/parallel/test-fs-filehandle.js @@ -2,6 +2,7 @@ 'use strict'; const common = require('../common'); +const assert = require('assert'); const path = require('path'); const fs = process.binding('fs'); const { stringToFlags } = require('internal/fs'); @@ -11,8 +12,10 @@ const { stringToFlags } = require('internal/fs'); let fdnum; { + const ctx = {}; fdnum = fs.openFileHandle(path.toNamespacedPath(__filename), - stringToFlags('r'), 0o666).fd; + stringToFlags('r'), 0o666, undefined, ctx).fd; + assert.strictEqual(ctx.errno, undefined); } common.expectWarning(