diff --git a/lib/fs.js b/lib/fs.js index 558e9ffa19..e48b79e440 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -81,6 +81,16 @@ function throwOptionsError(options) { 'but got ' + typeof options + ' instead'); } +function isFD(fd) { + return Number.isInteger(fd) && fd >= 0 && fd <= 0xFFFFFFFF; +} + +function sanitizeFD(fd) { + if (!isFD(fd)) + throw new TypeError('file descriptor must be a unsigned 32-bit integer'); + return fd; +} + // Ensure that callbacks run in the global context. Only use this function // for callbacks that are passed to the binding layer, callbacks that are // invoked from JS already run in the proper scope. @@ -112,10 +122,6 @@ function nullCheck(path, callback) { return true; } -function isFd(path) { - return (path >>> 0) === path; -} - // Static method to set the stats properties on a Stats object. fs.Stats = function( dev, @@ -257,7 +263,7 @@ fs.readFile = function(path, options, callback) { return; var context = new ReadFileContext(callback, encoding); - context.isUserFd = isFd(path); // file descriptor ownership + context.isUserFd = isFD(path); // file descriptor ownership var req = new FSReqWrap(); req.context = context; req.oncomplete = readFileAfterOpen; @@ -473,7 +479,7 @@ fs.readFileSync = function(path, options) { assertEncoding(encoding); var flag = options.flag || 'r'; - var isUserFd = isFd(path); // file descriptor ownership + var isUserFd = isFD(path); // file descriptor ownership var fd = isUserFd ? path : fs.openSync(path, flag, 0o666); var st = tryStatSync(fd, isUserFd); @@ -570,12 +576,12 @@ Object.defineProperty(exports, '_stringToFlags', { fs.close = function(fd, callback) { var req = new FSReqWrap(); - req.oncomplete = makeCallback(arguments[arguments.length - 1]); - binding.close(fd, req); + req.oncomplete = makeCallback(callback); + binding.close(sanitizeFD(fd), req); }; fs.closeSync = function(fd) { - return binding.close(fd); + return binding.close(sanitizeFD(fd)); }; function modeNum(m, def) { @@ -612,6 +618,7 @@ fs.openSync = function(path, flags, mode) { var readWarned = false; fs.read = function(fd, buffer, offset, length, position, callback) { callback = makeCallback(arguments[arguments.length - 1]); + fd = sanitizeFD(fd); if (!(buffer instanceof Buffer)) { // legacy string interface (fd, length, position, encoding, callback) readWarned = printDeprecation('fs.read\'s legacy String interface ' + @@ -672,6 +679,7 @@ fs.readSync = function(fd, buffer, offset, length, position) { var legacy = false; var encoding; + fd = sanitizeFD(fd); if (!(buffer instanceof Buffer)) { // legacy string interface (fd, length, position, encoding, callback) readSyncWarned = printDeprecation('fs.readSync\'s legacy String interface' + @@ -713,6 +721,7 @@ fs.readSync = function(fd, buffer, offset, length, position) { // fs.write(fd, string[, position[, encoding]], callback); fs.write = function(fd, buffer, offset, length, position, callback) { callback = makeCallback(arguments[arguments.length - 1]); + fd = sanitizeFD(fd); function wrapper(err, written) { // Retain a reference to buffer so that it can't be GC'ed too soon. callback(err, written || 0, buffer); @@ -748,6 +757,7 @@ fs.write = function(fd, buffer, offset, length, position, callback) { // OR // fs.writeSync(fd, string[, position[, encoding]]); fs.writeSync = function(fd, buffer, offset, length, position) { + fd = sanitizeFD(fd); if (buffer instanceof Buffer) { if (position === undefined) position = null; @@ -779,14 +789,18 @@ fs.renameSync = function(oldPath, newPath) { }; fs.truncate = function(path, len, callback) { - if (typeof path === 'number') { + callback = makeCallback(arguments[arguments.length - 1]); + + if (isFD(path)) return fs.ftruncate(path, len, callback); - } - callback = makeCallback(arguments[arguments.length - 1]); + if (typeof path !== 'string') + throw new TypeError('path must be a string'); - if (typeof len === 'function' || len === undefined) { + if (typeof len === 'function' || len == undefined) { len = 0; + } else if (!Number.isInteger(len) || len < 0) { + throw new TypeError('length must be a positive integer'); } fs.open(path, 'r+', function(er, fd) { @@ -802,13 +816,18 @@ fs.truncate = function(path, len, callback) { }; fs.truncateSync = function(path, len) { - if (typeof path === 'number') { - // legacy + if (isFD(path)) return fs.ftruncateSync(path, len); - } - if (len === undefined) { + + if (typeof path !== 'string') + throw new TypeError('path must be a string'); + + if (len === undefined || len === null) { len = 0; + } else if (!Number.isInteger(len) || len < 0) { + throw new TypeError('length must be a positive integer'); } + // allow error to be thrown, but still close fd. var fd = fs.openSync(path, 'r+'); var ret; @@ -822,18 +841,30 @@ fs.truncateSync = function(path, len) { }; fs.ftruncate = function(fd, len, callback) { - if (typeof len === 'function' || len === undefined) { + callback = makeCallback(arguments[arguments.length - 1]); + + fd = sanitizeFD(fd); + + if (typeof len === 'function' || len == undefined) { len = 0; + } else if (!Number.isInteger(len) || len < 0) { + throw new TypeError('length must be a positive integer'); } + var req = new FSReqWrap(); - req.oncomplete = makeCallback(arguments[arguments.length - 1]); + req.oncomplete = callback; binding.ftruncate(fd, len, req); }; fs.ftruncateSync = function(fd, len) { - if (len === undefined) { + fd = sanitizeFD(fd); + + if (len === undefined || len === null) { len = 0; + } else if (!Number.isInteger(len) || len < 0) { + throw new TypeError('length must be a positive integer'); } + return binding.ftruncate(fd, len); }; @@ -853,21 +884,21 @@ fs.rmdirSync = function(path) { fs.fdatasync = function(fd, callback) { var req = new FSReqWrap(); req.oncomplete = makeCallback(callback); - binding.fdatasync(fd, req); + binding.fdatasync(sanitizeFD(fd), req); }; fs.fdatasyncSync = function(fd) { - return binding.fdatasync(fd); + return binding.fdatasync(sanitizeFD(fd)); }; fs.fsync = function(fd, callback) { var req = new FSReqWrap(); - req.oncomplete = makeCallback(arguments[arguments.length - 1]); - binding.fsync(fd, req); + req.oncomplete = makeCallback(callback); + binding.fsync(sanitizeFD(fd), req); }; fs.fsyncSync = function(fd) { - return binding.fsync(fd); + return binding.fsync(sanitizeFD(fd)); }; fs.mkdir = function(path, mode, callback) { @@ -915,8 +946,8 @@ fs.readdirSync = function(path, options) { fs.fstat = function(fd, callback) { var req = new FSReqWrap(); - req.oncomplete = makeCallback(arguments[arguments.length - 1]); - binding.fstat(fd, req); + req.oncomplete = makeCallback(callback); + binding.fstat(sanitizeFD(fd), req); }; fs.lstat = function(path, callback) { @@ -936,7 +967,7 @@ fs.stat = function(path, callback) { }; fs.fstatSync = function(fd) { - return binding.fstat(fd); + return binding.fstat(sanitizeFD(fd)); }; fs.lstatSync = function(path) { @@ -1053,11 +1084,11 @@ fs.unlinkSync = function(path) { fs.fchmod = function(fd, mode, callback) { var req = new FSReqWrap(); req.oncomplete = makeCallback(arguments[arguments.length - 1]); - binding.fchmod(fd, modeNum(mode), req); + binding.fchmod(sanitizeFD(fd), modeNum(mode), req); }; fs.fchmodSync = function(fd, mode) { - return binding.fchmod(fd, modeNum(mode)); + return binding.fchmod(sanitizeFD(fd), modeNum(mode)); }; if (constants.hasOwnProperty('O_SYMLINK')) { @@ -1136,11 +1167,11 @@ if (constants.hasOwnProperty('O_SYMLINK')) { fs.fchown = function(fd, uid, gid, callback) { var req = new FSReqWrap(); req.oncomplete = makeCallback(arguments[arguments.length - 1]); - binding.fchown(fd, uid, gid, req); + binding.fchown(sanitizeFD(fd), uid, gid, req); }; fs.fchownSync = function(fd, uid, gid) { - return binding.fchown(fd, uid, gid); + return binding.fchown(sanitizeFD(fd), uid, gid); }; fs.chown = function(path, uid, gid, callback) { @@ -1197,6 +1228,7 @@ fs.utimesSync = function(path, atime, mtime) { fs.futimes = function(fd, atime, mtime, callback) { callback = makeCallback(arguments[arguments.length - 1]); + fd = sanitizeFD(fd); atime = toUnixTimestamp(atime); mtime = toUnixTimestamp(mtime); var req = new FSReqWrap(); @@ -1205,6 +1237,7 @@ fs.futimes = function(fd, atime, mtime, callback) { }; fs.futimesSync = function(fd, atime, mtime) { + fd = sanitizeFD(fd); atime = toUnixTimestamp(atime); mtime = toUnixTimestamp(mtime); binding.futimes(fd, atime, mtime); @@ -1257,7 +1290,7 @@ fs.writeFile = function(path, data, options, callback) { var flag = options.flag || 'w'; - if (isFd(path)) { + if (isFD(path)) { writeFd(path, true); return; } @@ -1291,7 +1324,7 @@ fs.writeFileSync = function(path, data, options) { assertEncoding(options.encoding); var flag = options.flag || 'w'; - var isUserFd = isFd(path); // file descriptor ownership + var isUserFd = isFD(path); // file descriptor ownership var fd = isUserFd ? path : fs.openSync(path, flag, options.mode); if (!(data instanceof Buffer)) { @@ -1329,7 +1362,7 @@ fs.appendFile = function(path, data, options, callback) { options = util._extend({ flag: 'a' }, options); // force append behavior when using a supplied file descriptor - if (isFd(path)) + if (isFD(path)) options.flag = 'a'; fs.writeFile(path, data, options, callback); @@ -1348,7 +1381,7 @@ fs.appendFileSync = function(path, data, options) { options = util._extend({ flag: 'a' }, options); // force append behavior when using a supplied file descriptor - if (isFd(path)) + if (isFD(path)) options.flag = 'a'; fs.writeFileSync(path, data, options); @@ -1932,7 +1965,7 @@ function SyncWriteStream(fd, options) { options = options || {}; - this.fd = fd; + this.fd = sanitizeFD(fd); this.writable = true; this.readable = false; this.autoClose = options.autoClose === undefined ? true : options.autoClose; diff --git a/src/node_file.cc b/src/node_file.cc index 1fdef68ca6..a51bcabba3 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -49,6 +49,11 @@ using v8::Value; #define GET_OFFSET(a) ((a)->IsNumber() ? (a)->IntegerValue() : -1) +static int SanitizeFD(Local fd) { + CHECK(fd->IsUint32() && "file descriptor must be a unsigned 32-bit integer"); + return fd->Uint32Value(); +} + class FSReqWrap: public ReqWrap { public: enum Ownership { COPY, MOVE }; @@ -406,10 +411,8 @@ static void Close(const FunctionCallbackInfo& args) { if (args.Length() < 1) return TYPE_ERROR("fd is required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be a file descriptor"); - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); if (args[1]->IsObject()) { ASYNC_CALL(close, args[1], UTF8, fd) @@ -641,10 +644,8 @@ static void FStat(const FunctionCallbackInfo& args) { if (args.Length() < 1) return TYPE_ERROR("fd is required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be a file descriptor"); - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); if (args[1]->IsObject()) { ASYNC_CALL(fstat, args[1], UTF8, fd) @@ -772,21 +773,10 @@ static void FTruncate(const FunctionCallbackInfo& args) { if (args.Length() < 2) return TYPE_ERROR("fd and length are required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be a file descriptor"); - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); - // FIXME(bnoordhuis) It's questionable to reject non-ints here but still - // allow implicit coercion from null or undefined to zero. Probably best - // handled in lib/fs.js. Local len_v(args[1]); - if (!len_v->IsUndefined() && - !len_v->IsNull() && - !IsInt64(len_v->NumberValue())) { - return env->ThrowTypeError("Not an integer"); - } - const int64_t len = len_v->IntegerValue(); if (args[2]->IsObject()) { @@ -801,10 +791,8 @@ static void Fdatasync(const FunctionCallbackInfo& args) { if (args.Length() < 1) return TYPE_ERROR("fd is required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be a file descriptor"); - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); if (args[1]->IsObject()) { ASYNC_CALL(fdatasync, args[1], UTF8, fd) @@ -818,10 +806,8 @@ static void Fsync(const FunctionCallbackInfo& args) { if (args.Length() < 1) return TYPE_ERROR("fd is required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be a file descriptor"); - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); if (args[1]->IsObject()) { ASYNC_CALL(fsync, args[1], UTF8, fd) @@ -1024,12 +1010,8 @@ static void Open(const FunctionCallbackInfo& args) { static void WriteBuffer(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsInt32()) - return env->ThrowTypeError("First argument must be file descriptor"); - CHECK(Buffer::HasInstance(args[1])); - - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); Local obj = args[1].As(); const char* buf = Buffer::Data(obj); size_t buffer_length = Buffer::Length(obj); @@ -1071,10 +1053,8 @@ static void WriteBuffer(const FunctionCallbackInfo& args) { static void WriteBuffers(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsInt32()); CHECK(args[1]->IsArray()); - - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); Local chunks = args[1].As(); int64_t pos = GET_OFFSET(args[2]); Local req = args[3]; @@ -1111,12 +1091,9 @@ static void WriteBuffers(const FunctionCallbackInfo& args) { static void WriteString(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsInt32()) - return env->ThrowTypeError("First argument must be file descriptor"); - Local req; Local string = args[1]; - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); char* buf = nullptr; int64_t pos; size_t len; @@ -1191,12 +1168,10 @@ static void Read(const FunctionCallbackInfo& args) { if (args.Length() < 2) return TYPE_ERROR("fd and buffer are required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be a file descriptor"); if (!Buffer::HasInstance(args[1])) return TYPE_ERROR("Second argument needs to be a buffer"); - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); Local req; @@ -1267,12 +1242,10 @@ static void FChmod(const FunctionCallbackInfo& args) { if (args.Length() < 2) return TYPE_ERROR("fd and mode are required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be a file descriptor"); if (!args[1]->IsInt32()) return TYPE_ERROR("mode must be an integer"); - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); int mode = static_cast(args[1]->Int32Value()); if (args[2]->IsObject()) { @@ -1328,14 +1301,12 @@ static void FChown(const FunctionCallbackInfo& args) { return TYPE_ERROR("uid required"); if (len < 3) return TYPE_ERROR("gid required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be an int"); if (!args[1]->IsUint32()) return TYPE_ERROR("uid must be an unsigned int"); if (!args[2]->IsUint32()) return TYPE_ERROR("gid must be an unsigned int"); - int fd = args[0]->Int32Value(); + int fd = SanitizeFD(args[0]); uv_uid_t uid = static_cast(args[1]->Uint32Value()); uv_gid_t gid = static_cast(args[2]->Uint32Value()); @@ -1385,14 +1356,12 @@ static void FUTimes(const FunctionCallbackInfo& args) { return TYPE_ERROR("atime required"); if (len < 3) return TYPE_ERROR("mtime required"); - if (!args[0]->IsInt32()) - return TYPE_ERROR("fd must be an int"); if (!args[1]->IsNumber()) return TYPE_ERROR("atime must be a number"); if (!args[2]->IsNumber()) return TYPE_ERROR("mtime must be a number"); - const int fd = args[0]->Int32Value(); + const int fd = SanitizeFD(args[0]); const double atime = static_cast(args[1]->NumberValue()); const double mtime = static_cast(args[2]->NumberValue()); diff --git a/test/parallel/test-fs-truncate-fd.js b/test/parallel/test-fs-truncate-fd.js index 2514b80f09..29b03284a2 100644 --- a/test/parallel/test-fs-truncate-fd.js +++ b/test/parallel/test-fs-truncate-fd.js @@ -1,21 +1,71 @@ 'use strict'; -var common = require('../common'); -var assert = require('assert'); -var path = require('path'); -var fs = require('fs'); -var tmp = common.tmpDir; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + common.refreshTmpDir(); -var filename = path.resolve(tmp, 'truncate-file.txt'); +const fds = []; +const filename = path.resolve(common.tmpDir, 'truncate-file.txt'); fs.writeFileSync(filename, 'hello world', 'utf8'); -var fd = fs.openSync(filename, 'r+'); +const fd = fs.openSync(filename, 'r+'); +fds.push(fd); fs.truncate(fd, 5, common.mustCall(function(err) { - assert.ok(!err); + assert.ifError(err); assert.equal(fs.readFileSync(filename, 'utf8'), 'hello'); })); -process.on('exit', function() { - fs.closeSync(fd); - fs.unlinkSync(filename); - console.log('ok'); -}); +{ + // test partial truncation of a file + const fileName = path.resolve(common.tmpDir, 'truncate-file-1.txt'); + console.log(fileName); + fs.writeFileSync(fileName, 'hello world', 'utf8'); + const fd = fs.openSync(fileName, 'r+'); + fds.push(fd); + + fs.truncate(fd, 5, common.mustCall(function(err) { + assert.ifError(err); + assert.strictEqual(fs.readFileSync(fileName, 'utf8'), 'hello'); + })); +} + +{ + // make sure numbers as strings are not treated as fds with sync version + const fileName = path.resolve(common.tmpDir, 'truncate-file-2.txt'); + console.log(fileName); + fs.writeFileSync(fileName, 'One'); + const fd = fs.openSync(fileName, 'r'); + fds.push(fd); + + const fdFileName = path.resolve(common.tmpDir, '' + fd); + fs.writeFileSync(fdFileName, 'Two'); + assert.strictEqual(fs.readFileSync(fileName).toString(), 'One'); + assert.strictEqual(fs.readFileSync(fdFileName).toString(), 'Two'); + + fs.truncateSync(fdFileName); + assert.strictEqual(fs.readFileSync(fileName).toString(), 'One'); + assert.strictEqual(fs.readFileSync(fdFileName).toString(), ''); +} + +{ + // make sure numbers as strings are not treated as fds with async version + const fileName = path.resolve(common.tmpDir, 'truncate-file-3.txt'); + console.log(fileName); + fs.writeFileSync(fileName, 'One'); + const fd = fs.openSync(fileName, 'r'); + fds.push(fd); + + const fdFileName = path.resolve(common.tmpDir, '' + fd); + fs.writeFileSync(fdFileName, 'Two'); + assert.strictEqual(fs.readFileSync(fileName).toString(), 'One'); + assert.strictEqual(fs.readFileSync(fdFileName).toString(), 'Two'); + + fs.truncate(fdFileName, common.mustCall(function(err) { + assert.ifError(err); + assert.strictEqual(fs.readFileSync(fileName).toString(), 'One'); + assert.strictEqual(fs.readFileSync(fdFileName).toString(), ''); + })); +} + +process.on('exit', () => fds.forEach((fd) => fs.closeSync(fd))); diff --git a/test/parallel/test-fs-truncate.js b/test/parallel/test-fs-truncate.js index ab0148a246..be986937b3 100644 --- a/test/parallel/test-fs-truncate.js +++ b/test/parallel/test-fs-truncate.js @@ -24,6 +24,55 @@ fs.truncateSync(filename); stat = fs.statSync(filename); assert.equal(stat.size, 0); +// path must be a string +assert.throws(function() { + fs.truncateSync({}); +}, /path must be a string/); + +assert.throws(function() { + fs.truncateSync([]); +}, /path must be a string/); + +// Even invalid file descriptors are not allowed +assert.throws(function() { + fs.truncateSync(-1); +}, /path must be a string/); + +assert.throws(function() { + fs.truncateSync(NaN); +}, /path must be a string/); + +assert.throws(function() { + fs.truncateSync(Infinity); +}, /path must be a string/); + +// Invalid lengths will also fail +assert.throws(function() { + fs.truncateSync('', ''); +}, /length must be a positive integer/); + +assert.throws(function() { + fs.truncateSync('', -1); +}, /length must be a positive integer/); + +assert.throws(function() { + fs.truncateSync('', NaN); +}, /length must be a positive integer/); + +assert.throws(function() { + fs.truncateSync('', Infinity); +}, /length must be a positive integer/); + +// null is a special case which will also be treated as zero length +fs.writeFileSync(filename, data); + +stat = fs.statSync(filename); +assert.equal(stat.size, 1024 * 16); + +fs.truncateSync(filename, null); +stat = fs.statSync(filename); +assert.equal(stat.size, 0); + // ftruncateSync fs.writeFileSync(filename, data); var fd = fs.openSync(filename, 'r+'); @@ -41,6 +90,51 @@ assert.equal(stat.size, 0); fs.closeSync(fd); +// file descriptor must be a unsigned 32-bit integer +assert.throws(function() { + fs.ftruncateSync({}); +}, /file descriptor must be a unsigned 32-bit integer/); + +// Even invalid file descriptors are not allowed +assert.throws(function() { + fs.ftruncateSync(-1); +}, /file descriptor must be a unsigned 32-bit integer/); + +assert.throws(function() { + fs.ftruncateSync(NaN); +}, /file descriptor must be a unsigned 32-bit integer/); + +assert.throws(function() { + fs.ftruncateSync(Infinity); +}, /file descriptor must be a unsigned 32-bit integer/); + +// Invalid lengths will also fail +assert.throws(function() { + fs.ftruncateSync(1, ''); +}, /length must be a positive integer/); + +assert.throws(function() { + fs.ftruncateSync(1, -1); +}, /length must be a positive integer/); + +assert.throws(function() { + fs.ftruncateSync(1, NaN); +}, /length must be a positive integer/); + +assert.throws(function() { + fs.ftruncateSync(1, Infinity); +}, /length must be a positive integer/); + +// null is a special case which will also be treated as zero length +fs.writeFileSync(filename, data); +fd = fs.openSync(filename, 'r+'); + +fs.ftruncateSync(fd, null); +stat = fs.statSync(filename); +assert.equal(stat.size, 0); + +fs.closeSync(fd); + // async tests testTruncate(common.mustCall(function(er) { if (er) throw er; diff --git a/test/parallel/test-fs-utimes.js b/test/parallel/test-fs-utimes.js index f245a7962d..eecc56ee4d 100644 --- a/test/parallel/test-fs-utimes.js +++ b/test/parallel/test-fs-utimes.js @@ -76,15 +76,6 @@ function runTest(atime, mtime, callback) { } expect_errno('utimesSync', 'foobarbaz', err, 'ENOENT'); tests_run++; - - err = undefined; - try { - fs.futimesSync(-1, atime, mtime); - } catch (ex) { - err = ex; - } - expect_errno('futimesSync', -1, err, 'EBADF'); - tests_run++; } // @@ -105,13 +96,8 @@ function runTest(atime, mtime, callback) { fs.futimes(fd, atime, mtime, function(err) { expect_ok('futimes', fd, err, atime, mtime); - - fs.futimes(-1, atime, mtime, function(err) { - expect_errno('futimes', -1, err, 'EBADF'); - syncTests(); - callback(); - }); - tests_run++; + syncTests(); + callback(); }); tests_run++; }); @@ -137,6 +123,56 @@ runTest(new Date('1982-09-10 13:37'), new Date('1982-09-10 13:37'), function() { }); }); +// validate if the file descriptors are in the valid range +assert.throws(() => fs.futimes(-1, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync(-1), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes('-1', () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync('-1'), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes(0xFFFFFFFF + 1, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync(0xFFFFFFFF + 1), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes('0x100000000', () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync('0x100000000'), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +// invalid file descriptors also should throw +assert.throws(() => fs.futimes(null, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync(null), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes(undefined, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync(undefined), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes(NaN, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync(NaN), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes({}, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync({}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes([], () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync([]), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes(() => true, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync(() => true), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes(() => /RegEx/, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync(() => /RegEx/), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimes(() => 3.14, () => {}), + /TypeError: file descriptor must be a unsigned 32-bit integer/); +assert.throws(() => fs.futimesSync(() => 3.14), + /TypeError: file descriptor must be a unsigned 32-bit integer/); process.on('exit', function() { console.log('Tests run / ok:', tests_run, '/', tests_ok); diff --git a/test/parallel/test-fs-write-no-fd.js b/test/parallel/test-fs-write-no-fd.js index 0aaec4b733..eb59e46a83 100644 --- a/test/parallel/test-fs-write-no-fd.js +++ b/test/parallel/test-fs-write-no-fd.js @@ -1,12 +1,13 @@ 'use strict'; + require('../common'); const fs = require('fs'); const assert = require('assert'); assert.throws(function() { - fs.write(null, Buffer.allocUnsafe(1), 0, 1); -}, /TypeError/); + fs.write(null, Buffer.allocUnsafe(1), 0, 1, () => {}); +}, /TypeError: file descriptor must be a unsigned 32-bit integer/); assert.throws(function() { - fs.write(null, '1', 0, 1); -}, /TypeError/); + fs.write(undefined, '1', 0, 1, () => {}); +}, /TypeError: file descriptor must be a unsigned 32-bit integer/);