diff --git a/doc/api/fs.md b/doc/api/fs.md index ef61b0cc1e7634..2b2fbbb5aca793 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -638,7 +638,7 @@ The numeric identifier of the device containing the file. The file system specific "Inode" number for the file. -*Note*: The `number` version is unreliable on Windows as values often overflow. +*Note*: The `number` version is unreliable as values often overflow. ### stats.mode diff --git a/src/node_file.h b/src/node_file.h index 7a8d604136aee9..d96177f23db5d6 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -194,22 +194,23 @@ constexpr uint64_t ToNative(uv_timespec_t ts) { template constexpr void FillStatsArray(AliasedBuffer* fields, const uv_stat_t* s, const size_t offset = 0) { - fields->SetValue(offset + 0, gsl::narrow(s->st_dev)); - fields->SetValue(offset + 1, gsl::narrow(s->st_mode)); - fields->SetValue(offset + 2, gsl::narrow(s->st_nlink)); - fields->SetValue(offset + 3, gsl::narrow(s->st_uid)); - fields->SetValue(offset + 4, gsl::narrow(s->st_gid)); - fields->SetValue(offset + 5, gsl::narrow(s->st_rdev)); + fields->SetValue(offset + 0, gsl::narrow_cast(s->st_dev)); + fields->SetValue(offset + 1, gsl::narrow_cast(s->st_mode)); + fields->SetValue(offset + 2, gsl::narrow_cast(s->st_nlink)); + fields->SetValue(offset + 3, gsl::narrow_cast(s->st_uid)); + fields->SetValue(offset + 4, gsl::narrow_cast(s->st_gid)); + fields->SetValue(offset + 5, gsl::narrow_cast(s->st_rdev)); #if defined(__POSIX__) - fields->SetValue(offset + 6, gsl::narrow(s->st_blksize)); - fields->SetValue(offset + 7, gsl::narrow(s->st_ino)); - fields->SetValue(offset + 8, gsl::narrow(s->st_size)); - fields->SetValue(offset + 9, gsl::narrow(s->st_blocks)); + fields->SetValue(offset + 6, gsl::narrow_cast(s->st_blksize)); + // Using the noop `narrow_cast` since this overflows. + fields->SetValue(offset + 7, gsl::narrow_cast(s->st_ino)); + fields->SetValue(offset + 8, gsl::narrow_cast(s->st_size)); + fields->SetValue(offset + 9, gsl::narrow_cast(s->st_blocks)); #else fields->SetValue(offset + 6, 0); - // This overflows on Windows for NativeT == double + // Using the noop `narrow_cast` since this overflows. fields->SetValue(offset + 7, gsl::narrow_cast(s->st_ino)); - fields->SetValue(offset + 8, gsl::narrow(s->st_size)); + fields->SetValue(offset + 8, gsl::narrow_cast(s->st_size)); fields->SetValue(offset + 9, 0); #endif diff --git a/test/known_issues/test-fs-stat-ino-overflow-on-windows.js b/test/known_issues/test-fs-stat-ino-overflow-on-windows.js index c95fb15c0ba773..4a4d3ea298aacf 100644 --- a/test/known_issues/test-fs-stat-ino-overflow-on-windows.js +++ b/test/known_issues/test-fs-stat-ino-overflow-on-windows.js @@ -10,7 +10,6 @@ const fs = require('fs'); const promiseFs = require('fs').promises; const path = require('path'); const tmpdir = require('../common/tmpdir'); -const { isDate } = require('util').types; tmpdir.refresh(); @@ -23,8 +22,25 @@ function getFilename() { } function verifyStats(bigintStats, numStats) { - assert.ok(Number.isSafeInteger(numStats.ino)); - assert.strictEqual(bigintStats.ino, BigInt(numStats.ino)); + const keys = [ + 'dev', 'mode', 'nlink', 'uid', + 'gid', 'rdev', 'ino', 'size', + ]; + if (!common.isWindows) { + keys.push('blocks', 'blksize'); + } + for (const key of keys) { + const nVal = numStats[key]; + const bVal = bigintStats[key]; + assert.ok( + Number.isSafeInteger(nVal), + `numStats.${key}: ${nVal} is not a safe integer` + ); + assert.strictEqual( + bigintStats[key], BigInt(numStats[key]), + `bigintStats.${key}: ${bVal} is not equal to numStats.${key}: ${nVal}` + ); + } } { diff --git a/test/parallel/test-fs-stat-ino-overflow.js b/test/parallel/test-fs-stat-ino-overflow.js new file mode 100644 index 00000000000000..73010aee793531 --- /dev/null +++ b/test/parallel/test-fs-stat-ino-overflow.js @@ -0,0 +1,106 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const fs = require('fs'); +const promiseFs = require('fs').promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +let testIndex = 0; + +function getFilename() { + const filename = path.join(tmpdir.path, `test-file-${++testIndex}`); + fs.writeFileSync(filename, 'test'); + return filename; +} + +function verifyStats(bigintStats, numStats) { + const keys = [ + 'dev', 'mode', 'nlink', 'uid', 'gid', 'rdev', 'size', + ]; + if (!common.isWindows) { + keys.push('blocks', 'blksize', 'ino'); + } + for (const key of keys) { + const nVal = numStats[key]; + const bVal = bigintStats[key]; + assert.strictEqual( + bigintStats[key], BigInt(numStats[key]), + `bigintStats.${key}: ${bVal} is not equal to numStats.${key}: ${nVal}` + ); + assert.ok( + Number.isSafeInteger(nVal), + `numStats.${key}: ${nVal} is not a safe integer` + ); + } +} + +{ + const filename = getFilename(); + const bigintStats = fs.statSync(filename, { bigint: true }); + const numStats = fs.statSync(filename); + verifyStats(bigintStats, numStats); +} + +{ + const filename = __filename; + const bigintStats = fs.statSync(filename, { bigint: true }); + const numStats = fs.statSync(filename); + verifyStats(bigintStats, numStats); +} + +{ + const filename = __dirname; + const bigintStats = fs.statSync(filename, { bigint: true }); + const numStats = fs.statSync(filename); + verifyStats(bigintStats, numStats); +} + +{ + const filename = getFilename(); + const fd = fs.openSync(filename, 'r'); + const bigintStats = fs.fstatSync(fd, { bigint: true }); + const numStats = fs.fstatSync(fd); + verifyStats(bigintStats, numStats); + fs.closeSync(fd); +} + +{ + const filename = getFilename(); + fs.stat(filename, { bigint: true }, (err, bigintStats) => { + fs.stat(filename, (err, numStats) => { + verifyStats(bigintStats, numStats); + }); + }); +} + +{ + const filename = getFilename(); + const fd = fs.openSync(filename, 'r'); + fs.fstat(fd, { bigint: true }, (err, bigintStats) => { + fs.fstat(fd, (err, numStats) => { + verifyStats(bigintStats, numStats); + fs.closeSync(fd); + }); + }); +} + +(async function() { + const filename = getFilename(); + const bigintStats = await promiseFs.stat(filename, { bigint: true }); + const numStats = await promiseFs.stat(filename); + verifyStats(bigintStats, numStats); +})(); + +(async function() { + const filename = getFilename(); + const handle = await promiseFs.open(filename, 'r'); + const bigintStats = await handle.stat({ bigint: true }); + const numStats = await handle.stat(); + verifyStats(bigintStats, numStats); + await handle.close(); +})();