From a58d8604f8beb7dc6beb2a2245af653e33e6b636 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Mon, 31 Aug 2026 20:19:11 -0700 Subject: [PATCH] vfs: reject statfs for missing paths Validate mounted VFS paths before returning synthetic statfs data so fs.statfsSync() and fs.promises.statfs() report ENOENT for missing paths. Forward validation errors asynchronously to fs.statfs() callbacks. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol --- lib/fs.js | 8 +++++++- lib/internal/vfs/setup.js | 5 +++-- test/parallel/test-vfs-fs-promises.js | 5 +++++ test/parallel/test-vfs-fs-statSync.js | 17 ++++++++++++++++- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index ad90551f40f1..10ab1cb2ecae 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -2050,7 +2050,13 @@ function statfs(path, options = { __proto__: null, bigint: false }, callback) { const h = vfsState.handlers; if (h !== null) { - const result = h.statfsSync(path, options); + let result; + try { + result = h.statfsSync(path, options); + } catch (err) { + process.nextTick(callback, err); + return; + } if (result !== undefined) { process.nextTick(callback, null, result); return; diff --git a/lib/internal/vfs/setup.js b/lib/internal/vfs/setup.js index 3e6f246d794a..0b3f788b1f60 100644 --- a/lib/internal/vfs/setup.js +++ b/lib/internal/vfs/setup.js @@ -320,6 +320,7 @@ function findVFSWith(filename, syscall, fn) { const r = findVFS(filename); if (r === null) return undefined; if (r.vfs.existsSync(filename)) { + if (fn === undefined) return true; return fn(r.vfs, filename); } throw createENOENT(syscall, filename); @@ -432,7 +433,7 @@ function createVfsHandlers() { }, statfsSync(path, options) { const pathStr = toPathStr(path); - if (pathStr !== null && findVFSForPath(pathStr) !== null) { + if (pathStr !== null && findVFSWith(pathStr, 'statfs')) { if (options?.bigint) { return { type: 0n, bsize: 4096n, blocks: 0n, @@ -709,7 +710,7 @@ function createVfsHandlers() { vfsOp(path, (vfs, n) => vfs.promises.lutimes(n, atime, mtime).then(() => true)), statfs(path, options) { const pathStr = toPathStr(path); - if (pathStr !== null && findVFSForPath(pathStr) !== null) { + if (pathStr !== null && findVFSWith(pathStr, 'statfs')) { if (options?.bigint) { return { __proto__: null, diff --git a/test/parallel/test-vfs-fs-promises.js b/test/parallel/test-vfs-fs-promises.js index a5761d4ca5dd..71e924ca951a 100644 --- a/test/parallel/test-vfs-fs-promises.js +++ b/test/parallel/test-vfs-fs-promises.js @@ -31,6 +31,11 @@ const vfs = require('node:vfs'); // statfs const sfs = await fsp.statfs(p('src/hello.txt')); assert.strictEqual(typeof sfs.bsize, 'number'); + await assert.rejects(fsp.statfs(p('missing')), { + code: 'ENOENT', + syscall: 'statfs', + path: p('missing'), + }); // Path-based writes await fsp.writeFile(p('src/pw.txt'), 'pdata'); diff --git a/test/parallel/test-vfs-fs-statSync.js b/test/parallel/test-vfs-fs-statSync.js index 5a48c4e426fa..6e5d27c3059e 100644 --- a/test/parallel/test-vfs-fs-statSync.js +++ b/test/parallel/test-vfs-fs-statSync.js @@ -4,7 +4,7 @@ // fs.statSync / fs.lstatSync / fs.statfsSync dispatch through the VFS layer, // including the `throwIfNoEntry: false` option. -require('../common'); +const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); @@ -57,4 +57,19 @@ assert.strictEqual( assert.strictEqual(typeof s.bsize, 'bigint'); } +// statfsSync on a missing path throws ENOENT +assert.throws(() => fs.statfsSync(path.join(mountPoint, 'missing')), + { + code: 'ENOENT', + syscall: 'statfs', + path: path.join(mountPoint, 'missing'), + }); + +// Statfs on a missing path reports ENOENT through the callback +fs.statfs(path.join(mountPoint, 'missing'), common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOENT'); + assert.strictEqual(err.syscall, 'statfs'); + assert.strictEqual(err.path, path.join(mountPoint, 'missing')); +})); + myVfs.unmount();