diff --git a/doc/api/fs.md b/doc/api/fs.md index d0348d45c93..9a405b92149 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1766,7 +1766,7 @@ changes: --> * `fd` {integer} -* `len` {integer} **Default:** `0` +* `len` {integer} **Default:** `0n` * `callback` {Function} * `err` {Error} @@ -1805,7 +1805,7 @@ console.log(fs.readFileSync('temp.txt', 'utf8')); const fd = fs.openSync('temp.txt', 'r+'); // truncate the file to 10 bytes, whereas the actual size is 7 bytes -fs.ftruncate(fd, 10, (err) => { +fs.ftruncate(fd, 10n, (err) => { assert.ifError(err); console.log(fs.readFileSync('temp.txt')); }); @@ -1821,7 +1821,7 @@ added: v0.8.6 --> * `fd` {integer} -* `len` {integer} **Default:** `0` +* `len` {integer} **Default:** `0n` Returns `undefined`. @@ -2904,7 +2904,7 @@ changes: --> * `path` {string|Buffer|URL} -* `len` {integer} **Default:** `0` +* `len` {integer} **Default:** `0n` * `callback` {Function} * `err` {Error} @@ -2921,7 +2921,7 @@ added: v0.8.6 --> * `path` {string|Buffer|URL} -* `len` {integer} **Default:** `0` +* `len` {integer} **Default:** `0n` Synchronous truncate(2). Returns `undefined`. A file descriptor can also be passed as the first argument. In this case, `fs.ftruncateSync()` is called. @@ -3277,8 +3277,9 @@ Write `buffer` to the file specified by `fd`. an integer specifying the number of bytes to write. `position` refers to the offset from the beginning of the file where this data -should be written. If `typeof position !== 'number'`, the data will be written -at the current position. See pwrite(2). +should be written. If `typeof position !== 'number'` or +`typeof position !== 'bigint', the data will be written at the current position. +See pwrite(2). The callback will be given three arguments `(err, bytesWritten, buffer)` where `bytesWritten` specifies how many _bytes_ were written from `buffer`. @@ -3323,8 +3324,9 @@ Write `string` to the file specified by `fd`. If `string` is not a string, then the value will be coerced to one. `position` refers to the offset from the beginning of the file where this data -should be written. If `typeof position !== 'number'` the data will be written at -the current position. See pwrite(2). +should be written. If `typeof position !== 'number'` or +`typeof position !== 'bigint'`, the data will be written at the current +position. See pwrite(2). `encoding` is the expected string encoding. @@ -3649,7 +3651,7 @@ success. -* `len` {integer} **Default:** `0` +* `len` {integer} **Default:** `0n` * Returns: {Promise} Truncates the file then resolves the `Promise` with no arguments upon success. @@ -3747,8 +3749,9 @@ a reference to the `buffer` written. an integer specifying the number of bytes to write. `position` refers to the offset from the beginning of the file where this data -should be written. If `typeof position !== 'number'`, the data will be written -at the current position. See pwrite(2). +should be written. If `typeof position !== 'number'` or +`typeof position !== 'bigint'`, the data will be written at the current +position. See pwrite(2). It is unsafe to use `filehandle.write()` multiple times on the same file without waiting for the `Promise` to be resolved (or rejected). For this @@ -4196,7 +4199,7 @@ added: v10.0.0 --> * `path` {string|Buffer|URL} -* `len` {integer} **Default:** `0` +* `len` {integer} **Default:** `0n` * Returns: {Promise} Truncates the `path` then resolves the `Promise` with no arguments upon diff --git a/lib/fs.js b/lib/fs.js index bc7642447cb..b7687864279 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -448,6 +448,7 @@ function read(fd, buffer, offset, length, position, callback) { offset |= 0; length |= 0; + position = BigInt(position); if (length === 0) { return process.nextTick(function tick() { @@ -462,8 +463,9 @@ function read(fd, buffer, offset, length, position, callback) { validateOffsetLengthRead(offset, length, buffer.length); - if (!Number.isSafeInteger(position)) - position = -1; + // can be seen as the same as position > 0 && position < 2n ** 64n. Just a little faster? + if (position !== BigInt.asUintN(64, position)) + position = -1n; function wrapper(err, bytesRead) { // Retain a reference to buffer so that it can't be GC'ed too soon. @@ -485,6 +487,7 @@ function readSync(fd, buffer, offset, length, position) { offset |= 0; length |= 0; + position = BigInt(position || 0) if (length === 0) { return 0; @@ -497,8 +500,9 @@ function readSync(fd, buffer, offset, length, position) { validateOffsetLengthRead(offset, length, buffer.length); - if (!Number.isSafeInteger(position)) - position = -1; + // can be seen as the same as position > 0 && position < 2n ** 64n. Just a little faster? + if (position !== BigInt.asUintN(64, position)) + position = -1n; const ctx = {}; const result = binding.read(fd, buffer, offset, length, position, @@ -528,8 +532,14 @@ function write(fd, buffer, offset, length, position, callback) { offset = 0; if (typeof length !== 'number') length = buffer.length - offset; - if (typeof position !== 'number') + if (typeof position !== 'number' && typeof position !== 'bigint') position = null; + else + position = BigInt(position) + + if (position && position !== BigInt.asUintN(64, position)) + position = -1n; + validateOffsetLengthWrite(offset, length, buffer.byteLength); return binding.writeBuffer(fd, buffer, offset, length, position, req); } @@ -567,6 +577,14 @@ function writeSync(fd, buffer, offset, length, position) { offset = 0; if (typeof length !== 'number') length = buffer.length - offset; + if (typeof position !== 'number' && typeof position !== 'bigint') + position = null; + else + position = BigInt(position) + + if (position && position !== BigInt.asUintN(64, position)) + position = -1n; + validateOffsetLengthWrite(offset, length, buffer.byteLength); result = binding.writeBuffer(fd, buffer, offset, length, position, undefined, ctx); @@ -613,12 +631,16 @@ function truncate(path, len, callback) { } if (typeof len === 'function') { callback = len; - len = 0; + len = 0n; } else if (len === undefined) { - len = 0; + len = 0n; + } else { + len = BigInt(len) + if (len !== BigInt.asUintN(64, len)) + len = 0n } - validateInteger(len, 'len'); + callback = maybeCallback(callback); fs.open(path, 'r+', function(er, fd) { if (er) return callback(er); @@ -639,8 +661,13 @@ function truncateSync(path, len) { return fs.ftruncateSync(path, len); } if (len === undefined) { - len = 0; + len = 0n; + } else { + len = BigInt(len) + if (len !== BigInt.asUintN(64, len)) + len = 0n } + // allow error to be thrown, but still close fd. const fd = fs.openSync(path, 'r+'); let ret; @@ -653,23 +680,27 @@ function truncateSync(path, len) { return ret; } -function ftruncate(fd, len = 0, callback) { +function ftruncate(fd, len = 0n, callback) { if (typeof len === 'function') { callback = len; - len = 0; + len = 0n; + } else { + len = BigInt(len) + if (len !== BigInt.asUintN(64, len)) + len = 0n } + validateUint32(fd, 'fd'); - validateInteger(len, 'len'); - len = Math.max(0, len); const req = new FSReqWrap(); req.oncomplete = makeCallback(callback); binding.ftruncate(fd, len, req); } -function ftruncateSync(fd, len = 0) { +function ftruncateSync(fd, len = 0n) { validateUint32(fd, 'fd'); - validateInteger(len, 'len'); - len = Math.max(0, len); + len = BigInt(len) + if (len !== BigInt.asUintN(64, len)) + len = 0n const ctx = {}; binding.ftruncate(fd, len, undefined, ctx); handleErrorFromBinding(ctx); diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index f38a3e59bef..5a903dcb376 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -205,6 +205,7 @@ async function read(handle, buffer, offset, length, position) { offset |= 0; length |= 0; + position = BigInt(position || 0); if (length === 0) return { bytesRead: length, buffer }; @@ -216,8 +217,9 @@ async function read(handle, buffer, offset, length, position) { validateOffsetLengthRead(offset, length, buffer.length); - if (!Number.isSafeInteger(position)) - position = -1; + // can be seen as the same as position > 0 && position < 2n ** 64n. Just a little faster? + if (position !== BigInt.asUintN(64, position)) + position = -1n; const bytesRead = (await binding.read(handle.fd, buffer, offset, length, position, kUsePromises)) || 0; @@ -236,8 +238,14 @@ async function write(handle, buffer, offset, length, position) { offset = 0; if (typeof length !== 'number') length = buffer.length - offset; - if (typeof position !== 'number') + if (typeof position !== 'number' && typeof position !== 'bigint') position = null; + else + position = BigInt(position) + + if (position && position !== BigInt.asUintN(64, position)) + position = -1n; + validateOffsetLengthWrite(offset, length, buffer.byteLength); const bytesWritten = (await binding.writeBuffer(handle.fd, buffer, offset, @@ -262,14 +270,15 @@ async function rename(oldPath, newPath) { kUsePromises); } -async function truncate(path, len = 0) { +async function truncate(path, len = 0n) { return ftruncate(await open(path, 'r+'), len); } -async function ftruncate(handle, len = 0) { +async function ftruncate(handle, len = 0n) { validateFileHandle(handle); - validateInteger(len, 'len'); - len = Math.max(0, len); + len = BigInt(len) + if (len !== BigInt.asUintN(64, len)) + len = 0n return binding.ftruncate(handle.fd, len, kUsePromises); } diff --git a/src/node_file.cc b/src/node_file.cc index 16f2506d1f2..eb068f30a53 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -50,6 +50,7 @@ namespace fs { using v8::Array; using v8::BigUint64Array; +using v8::BigInt; using v8::Context; using v8::EscapableHandleScope; using v8::Float64Array; @@ -1042,8 +1043,8 @@ static void FTruncate(const FunctionCallbackInfo& args) { CHECK(args[0]->IsInt32()); const int fd = args[0].As()->Value(); - CHECK(args[1]->IsNumber()); - const int64_t len = args[1].As()->Value(); + CHECK(args[1]->IsBigInt()); + const int64_t len = args[1].As()->Uint64Value(); FSReqBase* req_wrap_async = GetReqWrap(env, args[2]); if (req_wrap_async != nullptr) { @@ -1428,7 +1429,8 @@ static void WriteBuffer(const FunctionCallbackInfo& args) { CHECK_LE(len, buffer_length); CHECK_GE(off + len, off); - const int64_t pos = GET_OFFSET(args[4]); + CHECK(args[4]->IsBigInt()); + const int64_t pos = args[4].As()->Uint64Value(); char* buf = buffer_data + off; uv_buf_t uvbuf = uv_buf_init(const_cast(buf), len); @@ -1468,7 +1470,8 @@ static void WriteBuffers(const FunctionCallbackInfo& args) { CHECK(args[1]->IsArray()); Local chunks = args[1].As(); - int64_t pos = GET_OFFSET(args[2]); + CHECK(args[2]->IsBigInt()); + int64_t pos = args[2].As()->Uint64Value(); MaybeStackBuffer iovs(chunks->Length()); @@ -1511,7 +1514,8 @@ static void WriteString(const FunctionCallbackInfo& args) { CHECK(args[0]->IsInt32()); const int fd = args[0].As()->Value(); - const int64_t pos = GET_OFFSET(args[2]); + CHECK(args[2]->IsBigInt()); + const int64_t pos = args[2].As()->Uint64Value(); const auto enc = ParseEncoding(env->isolate(), args[3], UTF8); @@ -1624,8 +1628,8 @@ static void Read(const FunctionCallbackInfo& args) { const size_t len = static_cast(args[3].As()->Value()); CHECK(Buffer::IsWithinBounds(off, len, buffer_length)); - CHECK(args[4]->IsNumber()); - const int64_t pos = args[4].As()->Value(); + CHECK(args[4]->IsBigInt()); + const int64_t pos = args[4].As()->Uint64Value(); char* buf = buffer_data + off; uv_buf_t uvbuf = uv_buf_init(const_cast(buf), len);