fs: allow position parameter to be a BigInt in read and readSync

Fixes: https://github.com/nodejs/node/issues/36185

PR-URL: https://github.com/nodejs/node/pull/36190
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Zeyu Yang <himself65@outlook.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
raisinten
2020-11-20 19:25:41 +05:30
committed by Antoine du Hamel
parent 99580bd311
commit 72b678a0b3
4 changed files with 127 additions and 10 deletions

View File

@@ -2931,7 +2931,7 @@ changes:
* `buffer` {Buffer|TypedArray|DataView}
* `offset` {integer}
* `length` {integer}
* `position` {integer}
* `position` {integer|bigint}
* `callback` {Function}
* `err` {Error}
* `bytesRead` {integer}
@@ -2976,7 +2976,7 @@ changes:
* `buffer` {Buffer|TypedArray|DataView} **Default:** `Buffer.alloc(16384)`
* `offset` {integer} **Default:** `0`
* `length` {integer} **Default:** `buffer.length`
* `position` {integer} **Default:** `null`
* `position` {integer|bigint} **Default:** `null`
* `callback` {Function}
* `err` {Error}
* `bytesRead` {integer}
@@ -3273,7 +3273,7 @@ changes:
* `buffer` {Buffer|TypedArray|DataView}
* `offset` {integer}
* `length` {integer}
* `position` {integer}
* `position` {integer|bigint}
* Returns: {number}
Returns the number of `bytesRead`.
@@ -3300,7 +3300,7 @@ changes:
* `options` {Object}
* `offset` {integer} **Default:** `0`
* `length` {integer} **Default:** `buffer.length`
* `position` {integer} **Default:** `null`
* `position` {integer|bigint} **Default:** `null`
* Returns: {number}
Returns the number of `bytesRead`.

View File

@@ -37,7 +37,6 @@ const {
BigIntPrototypeToString,
MathMax,
Number,
NumberIsSafeInteger,
ObjectCreate,
ObjectDefineProperties,
ObjectDefineProperty,
@@ -75,7 +74,8 @@ const {
ERR_FS_FILE_TOO_LARGE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_ARG_TYPE,
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM,
ERR_OUT_OF_RANGE,
},
hideStackFrames,
uvErrmapGet,
@@ -545,9 +545,23 @@ function read(fd, buffer, offset, length, position, callback) {
validateOffsetLengthRead(offset, length, buffer.byteLength);
if (!NumberIsSafeInteger(position))
if (position == null)
position = -1;
if (typeof position === 'number') {
validateInteger(position, 'position');
} else if (typeof position === 'bigint') {
if (!(position >= -(2n ** 63n) && position <= 2n ** 63n - 1n)) {
throw new ERR_OUT_OF_RANGE('position',
`>= ${-(2n ** 63n)} && <= ${2n ** 63n - 1n}`,
position);
}
} else {
throw new ERR_INVALID_ARG_TYPE('position',
['integer', 'bigint'],
position);
}
function wrapper(err, bytesRead) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback(err, bytesRead || 0, buffer);
@@ -597,9 +611,23 @@ function readSync(fd, buffer, offset, length, position) {
validateOffsetLengthRead(offset, length, buffer.byteLength);
if (!NumberIsSafeInteger(position))
if (position == null)
position = -1;
if (typeof position === 'number') {
validateInteger(position, 'position');
} else if (typeof position === 'bigint') {
if (!(position >= -(2n ** 63n) && position <= 2n ** 63n - 1n)) {
throw new ERR_OUT_OF_RANGE('position',
`>= ${-(2n ** 63n)} && <= ${2n ** 63n - 1n}`,
position);
}
} else {
throw new ERR_INVALID_ARG_TYPE('position',
['integer', 'bigint'],
position);
}
const ctx = {};
const result = binding.read(fd, buffer, offset, length, position,
undefined, ctx);

View File

@@ -51,6 +51,7 @@ namespace node {
namespace fs {
using v8::Array;
using v8::BigInt;
using v8::Boolean;
using v8::Context;
using v8::EscapableHandleScope;
@@ -2038,8 +2039,10 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value());
CHECK(Buffer::IsWithinBounds(off, len, buffer_length));
CHECK(IsSafeJsInt(args[4]));
const int64_t pos = args[4].As<Integer>()->Value();
CHECK(IsSafeJsInt(args[4]) || args[4]->IsBigInt());
const int64_t pos = args[4]->IsNumber() ?
args[4].As<Integer>()->Value() :
args[4].As<BigInt>()->Int64Value();
char* buf = buffer_data + off;
uv_buf_t uvbuf = uv_buf_init(buf, len);

View File

@@ -76,6 +76,47 @@ assert.throws(() => {
'It must be >= 0. Received -1'
});
[true, () => {}, {}, ''].forEach((value) => {
assert.throws(() => {
fs.read(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
value,
common.mustNotCall());
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
});
[0.5, 2 ** 53, 2n ** 63n].forEach((value) => {
assert.throws(() => {
fs.read(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
value,
common.mustNotCall());
}, {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError'
});
});
fs.read(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
0n,
common.mustCall());
fs.read(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
2n ** 53n - 1n,
common.mustCall());
assert.throws(
() => fs.readSync(fd, expected.length, 0, 'utf-8'),
@@ -151,3 +192,48 @@ assert.throws(() => {
message: 'The value of "length" is out of range. ' +
'It must be <= 4. Received 5'
});
[true, () => {}, {}, ''].forEach((value) => {
assert.throws(() => {
fs.readSync(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
value);
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
});
[0.5, 2 ** 53, 2n ** 63n].forEach((value) => {
assert.throws(() => {
fs.readSync(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
value);
}, {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError'
});
});
fs.readSync(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
0n);
try {
fs.readSync(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
2n ** 53n - 1n);
} catch (err) {
// On systems where max file size is below 2^53-1, we'd expect a EFBIG error.
// This is not using `assert.throws` because the above call should not raise
// any error on systems that allows file of that size.
if (err.code !== 'EFBIG') throw err;
}