diff --git a/lib/fs.js b/lib/fs.js index 0103500f0b522e..d4f5ee0909bcef 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -924,8 +924,12 @@ fs.renameSync = function(oldPath, newPath) { nullCheck(newPath); validatePath(oldPath, 'oldPath'); validatePath(newPath, 'newPath'); - return binding.rename(pathModule.toNamespacedPath(oldPath), - pathModule.toNamespacedPath(newPath)); + const ctx = { path: oldPath, dest: newPath }; + binding.rename(pathModule.toNamespacedPath(oldPath), + pathModule.toNamespacedPath(newPath), undefined, ctx); + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } }; fs.truncate = function(path, len, callback) { @@ -991,7 +995,11 @@ fs.ftruncateSync = function(fd, len = 0) { validateUint32(fd, 'fd'); validateLen(len); len = Math.max(0, len); - return binding.ftruncate(fd, len); + const ctx = {}; + binding.ftruncate(fd, len, undefined, ctx); + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } }; fs.rmdir = function(path, callback) { @@ -1021,7 +1029,11 @@ fs.fdatasync = function(fd, callback) { fs.fdatasyncSync = function(fd) { validateUint32(fd, 'fd'); - return binding.fdatasync(fd); + const ctx = {}; + binding.fdatasync(fd, undefined, ctx); + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } }; fs.fsync = function(fd, callback) { @@ -1033,7 +1045,11 @@ fs.fsync = function(fd, callback) { fs.fsyncSync = function(fd) { validateUint32(fd, 'fd'); - return binding.fsync(fd); + const ctx = {}; + binding.fsync(fd, undefined, ctx); + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } }; fs.mkdir = function(path, mode, callback) { @@ -1163,7 +1179,13 @@ fs.readlinkSync = function(path, options) { handleError((path = getPathFromURL(path))); nullCheck(path); validatePath(path, 'oldPath'); - return binding.readlink(pathModule.toNamespacedPath(path), options.encoding); + const ctx = { path }; + const result = binding.readlink(pathModule.toNamespacedPath(path), + options.encoding, undefined, ctx); + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } + return result; }; function preprocessSymlinkDestination(path, type, linkPath) { @@ -1220,6 +1242,7 @@ fs.symlink = function(target, path, type_, callback_) { const flags = stringToSymlinkType(type); const req = new FSReqWrap(); req.oncomplete = callback; + binding.symlink(preprocessSymlinkDestination(target, type, path), pathModule.toNamespacedPath(path), flags, req); }; @@ -1234,8 +1257,19 @@ fs.symlinkSync = function(target, path, type) { validatePath(target, 'target'); validatePath(path); const flags = stringToSymlinkType(type); - return binding.symlink(preprocessSymlinkDestination(target, type, path), - pathModule.toNamespacedPath(path), flags); + + const ctx = { path: target, dest: path }; + binding.symlink(preprocessSymlinkDestination(target, type, path), + pathModule.toNamespacedPath(path), flags, undefined, ctx); + + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } else if (ctx.error) { + // TODO(joyeecheung): this is an encoding error usually caused by memory + // problems. We need to figure out proper error code(s) for this. + Error.captureStackTrace(ctx.error); + throw ctx.error; + } }; fs.link = function(existingPath, newPath, callback) { @@ -1268,8 +1302,15 @@ fs.linkSync = function(existingPath, newPath) { nullCheck(newPath); validatePath(existingPath, 'existingPath'); validatePath(newPath, 'newPath'); - return binding.link(pathModule.toNamespacedPath(existingPath), - pathModule.toNamespacedPath(newPath)); + + const ctx = { path: existingPath, dest: newPath }; + const result = binding.link(pathModule.toNamespacedPath(existingPath), + pathModule.toNamespacedPath(newPath), + undefined, ctx); + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } + return result; }; fs.unlink = function(path, callback) { @@ -1287,7 +1328,11 @@ fs.unlinkSync = function(path) { handleError((path = getPathFromURL(path))); nullCheck(path); validatePath(path); - return binding.unlink(pathModule.toNamespacedPath(path)); + const ctx = { path }; + binding.unlink(pathModule.toNamespacedPath(path), undefined, ctx); + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } }; fs.fchmod = function(fd, mode, callback) { @@ -1963,7 +2008,10 @@ fs.realpathSync = function realpathSync(p, options) { if (ctx.errno !== undefined) { throw new errors.uvException(ctx); } - linkTarget = binding.readlink(baseLong); + linkTarget = binding.readlink(baseLong, undefined, undefined, ctx); + if (ctx.errno !== undefined) { + throw new errors.uvException(ctx); + } } resolvedLink = pathModule.resolve(previous, linkTarget); diff --git a/src/node_file.cc b/src/node_file.cc index 8cbfa1b1793269..93f54aed23f728 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -598,29 +598,34 @@ static void FStat(const FunctionCallbackInfo& args) { static void Symlink(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 3); + int argc = args.Length(); + CHECK_GE(argc, 4); BufferValue target(env->isolate(), args[0]); CHECK_NE(*target, nullptr); BufferValue path(env->isolate(), args[1]); CHECK_NE(*path, nullptr); - CHECK(args[2]->IsUint32()); - int flags = args[2]->Uint32Value(env->context()).ToChecked(); + CHECK(args[2]->IsInt32()); + int flags = args[2].As()->Value(); if (args[3]->IsObject()) { // symlink(target, path, flags, req) CHECK_EQ(args.Length(), 4); AsyncDestCall(env, args, "symlink", *path, path.length(), UTF8, AfterNoArgs, uv_fs_symlink, *target, *path, flags); - } else { // symlink(target, path, flags) - SYNC_DEST_CALL(symlink, *target, *path, *target, *path, flags) + } else { // symlink(target, path, flags, undefinec, ctx) + CHECK_EQ(argc, 5); + fs_req_wrap req_wrap; + SyncCall(env, args[4], &req_wrap, "symlink", + uv_fs_symlink, *target, *path, flags); } } static void Link(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 2); + int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue src(env->isolate(), args[0]); CHECK_NE(*src, nullptr); @@ -629,20 +634,22 @@ static void Link(const FunctionCallbackInfo& args) { CHECK_NE(*dest, nullptr); if (args[2]->IsObject()) { // link(src, dest, req) - CHECK_EQ(args.Length(), 3); + CHECK_EQ(argc, 3); AsyncDestCall(env, args, "link", *dest, dest.length(), UTF8, AfterNoArgs, uv_fs_link, *src, *dest); - } else { // link(src, dest) - SYNC_DEST_CALL(link, *src, *dest, *src, *dest) + } else { // link(src, dest, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + SyncCall(env, args[3], &req_wrap, "link", + uv_fs_link, *src, *dest); } } static void ReadLink(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - const int argc = args.Length(); - - CHECK_GE(argc, 1); + int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue path(env->isolate(), args[0]); CHECK_NE(*path, nullptr); @@ -650,12 +657,18 @@ static void ReadLink(const FunctionCallbackInfo& args) { const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8); if (args[2]->IsObject()) { // readlink(path, encoding, req) - CHECK_EQ(args.Length(), 3); + CHECK_EQ(argc, 3); AsyncCall(env, args, "readlink", encoding, AfterStringPtr, uv_fs_readlink, *path); - } else { - SYNC_CALL(readlink, *path, *path) - const char* link_path = static_cast(SYNC_REQ.ptr); + } else { // readlink(path, encoding, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + int err = SyncCall(env, args[3], &req_wrap, "readlink", + uv_fs_readlink, *path); + if (err) { + 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(), @@ -663,9 +676,11 @@ static void ReadLink(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()); } } @@ -673,86 +688,110 @@ static void ReadLink(const FunctionCallbackInfo& args) { static void Rename(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK_GE(args.Length(), 2); + int argc = args.Length(); + CHECK_GE(argc, 3); BufferValue old_path(env->isolate(), args[0]); CHECK_NE(*old_path, nullptr); BufferValue new_path(env->isolate(), args[1]); CHECK_NE(*new_path, nullptr); - if (args[2]->IsObject()) { - CHECK_EQ(args.Length(), 3); + if (args[2]->IsObject()) { // rename(old_path, new_path, req) + CHECK_EQ(argc, 3); AsyncDestCall(env, args, "rename", *new_path, new_path.length(), UTF8, AfterNoArgs, uv_fs_rename, *old_path, *new_path); - } else { - SYNC_DEST_CALL(rename, *old_path, *new_path, *old_path, *new_path) + } else { // rename(old_path, new_path, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + SyncCall(env, args[3], &req_wrap, "rename", + uv_fs_rename, *old_path, *new_path); } } static void FTruncate(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + const int argc = args.Length(); + CHECK_GE(argc, 3); + CHECK(args[0]->IsInt32()); - CHECK(args[1]->IsNumber()); + const int fd = args[0].As()->Value(); - int fd = args[0]->Int32Value(); - const int64_t len = args[1]->IntegerValue(); + CHECK(args[1]->IsNumber()); + const int64_t len = args[1].As()->Value(); - if (args[2]->IsObject()) { - CHECK_EQ(args.Length(), 3); + if (args[2]->IsObject()) { // ftruncate(fd, len, req) + CHECK_EQ(argc, 3); AsyncCall(env, args, "ftruncate", UTF8, AfterNoArgs, uv_fs_ftruncate, fd, len); - } else { - SYNC_CALL(ftruncate, 0, fd, len) + } else { // ftruncate(fd, len, undefined, ctx) + CHECK_EQ(argc, 4); + fs_req_wrap req_wrap; + SyncCall(env, args[3], &req_wrap, "ftruncate", + uv_fs_ftruncate, fd, len); } } static void Fdatasync(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsInt32()); + const int argc = args.Length(); + CHECK_GE(argc, 2); - int fd = args[0]->Int32Value(); + CHECK(args[0]->IsInt32()); + const int fd = args[0].As()->Value(); - if (args[1]->IsObject()) { - CHECK_EQ(args.Length(), 2); + if (args[1]->IsObject()) { // fdatasync(fd, req) + CHECK_EQ(argc, 2); AsyncCall(env, args, "fdatasync", UTF8, AfterNoArgs, uv_fs_fdatasync, fd); - } else { - SYNC_CALL(fdatasync, 0, fd) + } else { // fdatasync(fd, undefined, ctx) + CHECK_EQ(argc, 3); + fs_req_wrap req_wrap; + SyncCall(env, args[2], &req_wrap, "fdatasync", + uv_fs_fdatasync, fd); } } static void Fsync(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsInt32()); + const int argc = args.Length(); + CHECK_GE(argc, 2); - int fd = args[0]->Int32Value(); + CHECK(args[0]->IsInt32()); + const int fd = args[0].As()->Value(); - if (args[1]->IsObject()) { - CHECK_EQ(args.Length(), 2); + if (args[1]->IsObject()) { // fsync(fd, req) + CHECK_EQ(argc, 2); AsyncCall(env, args, "fsync", UTF8, AfterNoArgs, uv_fs_fsync, fd); - } else { - SYNC_CALL(fsync, 0, fd) + } else { // fsync(fd, undefined, ctx) + CHECK_EQ(argc, 3); + fs_req_wrap req_wrap; + SyncCall(env, args[2], &req_wrap, "fsync", + uv_fs_fsync, fd); } } static void Unlink(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); - if (args[1]->IsObject()) { - CHECK_EQ(args.Length(), 2); + if (args[1]->IsObject()) { // unlink(fd, req) + CHECK_EQ(argc, 2); AsyncCall(env, args, "unlink", UTF8, AfterNoArgs, uv_fs_unlink, *path); - } else { - SYNC_CALL(unlink, *path, *path) + } else { // unlink(fd, undefined, ctx) + CHECK_EQ(argc, 3); + fs_req_wrap req_wrap; + SyncCall(env, args[2], &req_wrap, "unlink", + uv_fs_unlink, *path); } } diff --git a/test/parallel/test-fs-error-messages.js b/test/parallel/test-fs-error-messages.js index ef38652d7a3df0..ba44c28d432983 100644 --- a/test/parallel/test-fs-error-messages.js +++ b/test/parallel/test-fs-error-messages.js @@ -455,3 +455,72 @@ function re(literals, ...values) { validateError ); } + +// ftruncate +{ + const validateError = (err) => { + assert.strictEqual(err.syscall, 'ftruncate'); + // Could be EBADF or EINVAL, depending on the platform + if (err.code === 'EBADF') { + assert.strictEqual(err.message, 'EBADF: bad file descriptor, ftruncate'); + assert.strictEqual(err.errno, uv.UV_EBADF); + } else { + assert.strictEqual(err.message, 'EINVAL: invalid argument, ftruncate'); + assert.strictEqual(err.errno, uv.UV_EINVAL); + assert.strictEqual(err.code, 'EINVAL'); + } + return true; + }; + + const fd = fs.openSync(existingFile, 'r'); + fs.closeSync(fd); + + fs.ftruncate(fd, 4, common.mustCall(validateError)); + + assert.throws( + () => fs.ftruncateSync(fd, 4), + validateError + ); +} + +// fdatasync +{ + const validateError = (err) => { + assert.strictEqual(err.message, 'EBADF: bad file descriptor, fdatasync'); + assert.strictEqual(err.errno, uv.UV_EBADF); + assert.strictEqual(err.code, 'EBADF'); + assert.strictEqual(err.syscall, 'fdatasync'); + return true; + }; + + const fd = fs.openSync(existingFile, 'r'); + fs.closeSync(fd); + + fs.fdatasync(fd, common.mustCall(validateError)); + + assert.throws( + () => fs.fdatasyncSync(fd), + validateError + ); +} + +// fsync +{ + const validateError = (err) => { + assert.strictEqual(err.message, 'EBADF: bad file descriptor, fsync'); + assert.strictEqual(err.errno, uv.UV_EBADF); + assert.strictEqual(err.code, 'EBADF'); + assert.strictEqual(err.syscall, 'fsync'); + return true; + }; + + const fd = fs.openSync(existingFile, 'r'); + fs.closeSync(fd); + + fs.fsync(fd, common.mustCall(validateError)); + + assert.throws( + () => fs.fsyncSync(fd), + validateError + ); +}