process: migrate methods to throw errors with code

Migrate some methods from node.cc to JS in order to properly throw
errors with codes.

PR-URL: https://github.com/nodejs/node/pull/19973
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Michaël Zasso 2018-04-12 11:54:19 +02:00 committed by Ruben Bridgewater
parent e836128703
commit 2fd248f639
No known key found for this signature in database
GPG Key ID: F07496B3EB3C1762
10 changed files with 246 additions and 93 deletions

View File

@ -1607,6 +1607,11 @@ A string that contained unescaped characters was received.
An unhandled error occurred (for instance, when an `'error'` event is emitted
by an [`EventEmitter`][] but an `'error'` handler is not registered).
<a id="ERR_UNKNOWN_CREDENTIAL"></a>
### ERR_UNKNOWN_CREDENTIAL
A Unix group or user identifier that does not exist was passed.
<a id="ERR_UNKNOWN_ENCODING"></a>
### ERR_UNKNOWN_ENCODING

View File

@ -41,6 +41,7 @@
NativeModule.require('internal/process/warning').setup();
NativeModule.require('internal/process/next_tick').setup();
NativeModule.require('internal/process/stdio').setup();
NativeModule.require('internal/process/methods').setup();
const perf = process.binding('performance');
const {

View File

@ -1017,6 +1017,7 @@ E('ERR_UNHANDLED_ERROR',
if (err === undefined) return msg;
return `${msg} (${err})`;
}, Error);
E('ERR_UNKNOWN_CREDENTIAL', '%s identifier does not exist: %s', Error);
E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError);
// This should probably be a `TypeError`.

View File

@ -0,0 +1,137 @@
'use strict';
const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_UNKNOWN_CREDENTIAL
} = require('internal/errors').codes;
const {
validateUint32
} = require('internal/validators');
function setupProcessMethods() {
// Non-POSIX platforms like Windows don't have certain methods.
if (process.setgid !== undefined) {
setupPosixMethods();
}
const {
chdir: _chdir,
umask: _umask,
} = process;
process.chdir = chdir;
process.umask = umask;
function chdir(directory) {
if (typeof directory !== 'string') {
throw new ERR_INVALID_ARG_TYPE('directory', 'string', directory);
}
return _chdir(directory);
}
const octalReg = /^[0-7]+$/;
function umask(mask) {
if (typeof mask === 'undefined') {
return _umask(mask);
}
if (typeof mask === 'number') {
validateUint32(mask, 'mask');
return _umask(mask);
}
if (typeof mask === 'string') {
if (!octalReg.test(mask)) {
throw new ERR_INVALID_ARG_VALUE('mask', mask,
'must be an octal string');
}
const octal = Number.parseInt(mask, 8);
validateUint32(octal, 'mask');
return _umask(octal);
}
throw new ERR_INVALID_ARG_TYPE('mask', ['number', 'string', 'undefined'],
mask);
}
}
function setupPosixMethods() {
const {
initgroups: _initgroups,
setegid: _setegid,
seteuid: _seteuid,
setgid: _setgid,
setuid: _setuid,
setgroups: _setgroups
} = process;
process.initgroups = initgroups;
process.setegid = setegid;
process.seteuid = seteuid;
process.setgid = setgid;
process.setuid = setuid;
process.setgroups = setgroups;
function initgroups(user, extraGroup) {
validateId(user, 'user');
validateId(extraGroup, 'extraGroup');
// Result is 0 on success, 1 if user is unknown, 2 if group is unknown.
const result = _initgroups(user, extraGroup);
if (result === 1) {
throw new ERR_UNKNOWN_CREDENTIAL('User', user);
} else if (result === 2) {
throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
}
}
function setegid(id) {
return execId(id, 'Group', _setegid);
}
function seteuid(id) {
return execId(id, 'User', _seteuid);
}
function setgid(id) {
return execId(id, 'Group', _setgid);
}
function setuid(id) {
return execId(id, 'User', _setuid);
}
function setgroups(groups) {
if (!Array.isArray(groups)) {
throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
}
for (var i = 0; i < groups.length; i++) {
validateId(groups[i], `groups[${i}]`);
}
// Result is 0 on success. A positive integer indicates that the
// corresponding group was not found.
const result = _setgroups(groups);
if (result > 0) {
throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
}
}
function execId(id, type, method) {
validateId(id, 'id');
// Result is 0 on success, 1 if credential is unknown.
const result = method(id);
if (result === 1) {
throw new ERR_UNKNOWN_CREDENTIAL(type, id);
}
}
function validateId(id, name) {
if (typeof id === 'number') {
validateUint32(id, name);
} else if (typeof id !== 'string') {
throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id);
}
}
}
exports.setup = setupProcessMethods;

View File

@ -118,6 +118,7 @@
'lib/internal/net.js',
'lib/internal/os.js',
'lib/internal/process/esm_loader.js',
'lib/internal/process/methods.js',
'lib/internal/process/next_tick.js',
'lib/internal/process/promises.js',
'lib/internal/process/stdio.js',

View File

@ -164,6 +164,7 @@ using v8::ScriptOrigin;
using v8::SealHandleScope;
using v8::String;
using v8::TryCatch;
using v8::Uint32;
using v8::Uint32Array;
using v8::Undefined;
using v8::V8;
@ -1580,10 +1581,8 @@ static void Abort(const FunctionCallbackInfo<Value>& args) {
static void Chdir(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (args.Length() != 1 || !args[0]->IsString()) {
return env->ThrowTypeError("Bad argument.");
}
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsString());
node::Utf8Value path(args.GetIsolate(), args[0]);
int err = uv_chdir(*path);
if (err) {
@ -1616,32 +1615,16 @@ static void Cwd(const FunctionCallbackInfo<Value>& args) {
static void Umask(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
uint32_t old;
if (args.Length() < 1 || args[0]->IsUndefined()) {
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsUndefined() || args[0]->IsUint32());
if (args[0]->IsUndefined()) {
old = umask(0);
umask(static_cast<mode_t>(old));
} else if (!args[0]->IsInt32() && !args[0]->IsString()) {
return env->ThrowTypeError("argument must be an integer or octal string.");
} else {
int oct;
if (args[0]->IsInt32()) {
oct = args[0]->Uint32Value();
} else {
oct = 0;
node::Utf8Value str(env->isolate(), args[0]);
// Parse the octal string.
for (size_t i = 0; i < str.length(); i++) {
char c = (*str)[i];
if (c > '7' || c < '0') {
return env->ThrowTypeError("invalid octal string");
}
oct *= 8;
oct += c - '0';
}
}
int oct = args[0].As<Uint32>()->Value();
old = umask(static_cast<mode_t>(oct));
}
@ -1779,18 +1762,18 @@ static void GetEGid(const FunctionCallbackInfo<Value>& args) {
static void SetGid(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsUint32() && !args[0]->IsString()) {
return env->ThrowTypeError("setgid argument must be a number or a string");
}
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsUint32() || args[0]->IsString());
gid_t gid = gid_by_name(env->isolate(), args[0]);
if (gid == gid_not_found) {
return env->ThrowError("setgid group id does not exist");
}
if (setgid(gid)) {
return env->ThrowErrnoException(errno, "setgid");
// Tells JS to throw ERR_INVALID_CREDENTIAL
args.GetReturnValue().Set(1);
} else if (setgid(gid)) {
env->ThrowErrnoException(errno, "setgid");
} else {
args.GetReturnValue().Set(0);
}
}
@ -1798,18 +1781,18 @@ static void SetGid(const FunctionCallbackInfo<Value>& args) {
static void SetEGid(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsUint32() && !args[0]->IsString()) {
return env->ThrowTypeError("setegid argument must be a number or string");
}
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsUint32() || args[0]->IsString());
gid_t gid = gid_by_name(env->isolate(), args[0]);
if (gid == gid_not_found) {
return env->ThrowError("setegid group id does not exist");
}
if (setegid(gid)) {
return env->ThrowErrnoException(errno, "setegid");
// Tells JS to throw ERR_INVALID_CREDENTIAL
args.GetReturnValue().Set(1);
} else if (setegid(gid)) {
env->ThrowErrnoException(errno, "setegid");
} else {
args.GetReturnValue().Set(0);
}
}
@ -1817,18 +1800,18 @@ static void SetEGid(const FunctionCallbackInfo<Value>& args) {
static void SetUid(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsUint32() && !args[0]->IsString()) {
return env->ThrowTypeError("setuid argument must be a number or a string");
}
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsUint32() || args[0]->IsString());
uid_t uid = uid_by_name(env->isolate(), args[0]);
if (uid == uid_not_found) {
return env->ThrowError("setuid user id does not exist");
}
if (setuid(uid)) {
return env->ThrowErrnoException(errno, "setuid");
// Tells JS to throw ERR_INVALID_CREDENTIAL
args.GetReturnValue().Set(1);
} else if (setuid(uid)) {
env->ThrowErrnoException(errno, "setuid");
} else {
args.GetReturnValue().Set(0);
}
}
@ -1836,18 +1819,18 @@ static void SetUid(const FunctionCallbackInfo<Value>& args) {
static void SetEUid(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsUint32() && !args[0]->IsString()) {
return env->ThrowTypeError("seteuid argument must be a number or string");
}
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsUint32() || args[0]->IsString());
uid_t uid = uid_by_name(env->isolate(), args[0]);
if (uid == uid_not_found) {
return env->ThrowError("seteuid user id does not exist");
}
if (seteuid(uid)) {
return env->ThrowErrnoException(errno, "seteuid");
// Tells JS to throw ERR_INVALID_CREDENTIAL
args.GetReturnValue().Set(1);
} else if (seteuid(uid)) {
env->ThrowErrnoException(errno, "seteuid");
} else {
args.GetReturnValue().Set(0);
}
}
@ -1893,9 +1876,8 @@ static void GetGroups(const FunctionCallbackInfo<Value>& args) {
static void SetGroups(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsArray()) {
return env->ThrowTypeError("argument 1 must be an array");
}
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsArray());
Local<Array> groups_list = args[0].As<Array>();
size_t size = groups_list->Length();
@ -1906,7 +1888,9 @@ static void SetGroups(const FunctionCallbackInfo<Value>& args) {
if (gid == gid_not_found) {
delete[] groups;
return env->ThrowError("group name not found");
// Tells JS to throw ERR_INVALID_CREDENTIAL
args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
return;
}
groups[i] = gid;
@ -1918,19 +1902,17 @@ static void SetGroups(const FunctionCallbackInfo<Value>& args) {
if (rc == -1) {
return env->ThrowErrnoException(errno, "setgroups");
}
args.GetReturnValue().Set(0);
}
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsUint32() && !args[0]->IsString()) {
return env->ThrowTypeError("argument 1 must be a number or a string");
}
if (!args[1]->IsUint32() && !args[1]->IsString()) {
return env->ThrowTypeError("argument 2 must be a number or a string");
}
CHECK_EQ(args.Length(), 2);
CHECK(args[0]->IsUint32() || args[0]->IsString());
CHECK(args[1]->IsUint32() || args[1]->IsString());
node::Utf8Value arg0(env->isolate(), args[0]);
gid_t extra_group;
@ -1946,7 +1928,8 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
}
if (user == nullptr) {
return env->ThrowError("initgroups user not found");
// Tells JS to throw ERR_INVALID_CREDENTIAL
return args.GetReturnValue().Set(1);
}
extra_group = gid_by_name(env->isolate(), args[1]);
@ -1954,7 +1937,8 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
if (extra_group == gid_not_found) {
if (must_free)
free(user);
return env->ThrowError("initgroups extra group not found");
// Tells JS to throw ERR_INVALID_CREDENTIAL
return args.GetReturnValue().Set(2);
}
int rc = initgroups(user, extra_group);
@ -1966,6 +1950,8 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
if (rc) {
return env->ThrowErrnoException(errno, "initgroups");
}
args.GetReturnValue().Set(0);
}
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)

View File

@ -1,6 +1,6 @@
'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
@ -33,10 +33,9 @@ process.chdir('..');
assert.strictEqual(process.cwd().normalize(),
path.resolve(tmpdir.path).normalize());
const errMessage = /^TypeError: Bad argument\.$/;
assert.throws(function() { process.chdir({}); },
errMessage, 'Bad argument.');
assert.throws(function() { process.chdir(); },
errMessage, 'Bad argument.');
assert.throws(function() { process.chdir('x', 'y'); },
errMessage, 'Bad argument.');
const err = {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "directory" argument must be of type string/
};
common.expectsError(function() { process.chdir({}); }, err);
common.expectsError(function() { process.chdir(); }, err);

View File

@ -13,11 +13,18 @@ if (common.isWindows) {
assert.throws(() => {
process.seteuid({});
}, /^TypeError: seteuid argument must be a number or string$/);
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "id" argument must be one of type number or string. ' +
'Received type object'
});
assert.throws(() => {
process.seteuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf');
}, /^Error: seteuid user id does not exist$/);
process.seteuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveyb');
}, {
code: 'ERR_UNKNOWN_CREDENTIAL',
message: 'User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb'
});
// If we're not running as super user...
if (process.getuid() !== 0) {
@ -27,11 +34,11 @@ if (process.getuid() !== 0) {
assert.throws(() => {
process.setegid('nobody');
}, /^Error: (?:EPERM, .+|setegid group id does not exist)$/);
}, /(?:EPERM, .+|Group identifier does not exist: nobody)$/);
assert.throws(() => {
process.seteuid('nobody');
}, /^Error: (?:EPERM, .+|seteuid user id does not exist)$/);
}, /^Error: (?:EPERM, .+|User identifier does not exist: nobody)$/);
return;
}
@ -41,7 +48,7 @@ const oldgid = process.getegid();
try {
process.setegid('nobody');
} catch (err) {
if (err.message !== 'setegid group id does not exist') {
if (err.message !== 'Group identifier does not exist: nobody') {
throw err;
} else {
process.setegid('nogroup');

View File

@ -35,11 +35,18 @@ if (common.isWindows) {
assert.throws(() => {
process.setuid({});
}, /^TypeError: setuid argument must be a number or a string$/);
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "id" argument must be one of type ' +
'number or string. Received type object'
});
assert.throws(() => {
process.setuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf');
}, /^Error: setuid user id does not exist$/);
process.setuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveyb');
}, {
code: 'ERR_UNKNOWN_CREDENTIAL',
message: 'User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb'
});
// If we're not running as super user...
if (process.getuid() !== 0) {
@ -49,12 +56,12 @@ if (process.getuid() !== 0) {
assert.throws(
() => { process.setgid('nobody'); },
/^Error: (?:EPERM, .+|setgid group id does not exist)$/
/(?:EPERM, .+|Group identifier does not exist: nobody)$/
);
assert.throws(
() => { process.setuid('nobody'); },
/^Error: (?:EPERM, .+|setuid user id does not exist)$/
/(?:EPERM, .+|User identifier does not exist: nobody)$/
);
return;
}

View File

@ -43,8 +43,17 @@ assert.strictEqual(old, process.umask());
assert.throws(() => {
process.umask({});
}, /argument must be an integer or octal string/);
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "mask" argument must be one of type number, string, or ' +
'undefined. Received type object'
});
assert.throws(() => {
process.umask('123x');
}, /invalid octal string/);
['123x', 'abc', '999'].forEach((value) => {
assert.throws(() => {
process.umask(value);
}, {
code: 'ERR_INVALID_ARG_VALUE',
message: `The argument 'mask' must be an octal string. Received '${value}'`
});
});