From 1d5a85cca08ceae18d5663bdd51f62fb3a8b5847 Mon Sep 17 00:00:00 2001 From: David Michon Date: Wed, 30 Apr 2025 17:39:49 +0000 Subject: [PATCH 1/2] [node-core-library] Fix FileSystem.isErrnoException --- .../fix-node-errno_2025-04-30-17-39.json | 10 ++++++++++ libraries/node-core-library/src/FileSystem.ts | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 common/changes/@rushstack/node-core-library/fix-node-errno_2025-04-30-17-39.json diff --git a/common/changes/@rushstack/node-core-library/fix-node-errno_2025-04-30-17-39.json b/common/changes/@rushstack/node-core-library/fix-node-errno_2025-04-30-17-39.json new file mode 100644 index 00000000000..bbeb22b0650 --- /dev/null +++ b/common/changes/@rushstack/node-core-library/fix-node-errno_2025-04-30-17-39.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/node-core-library", + "comment": "Fix a bug in `FileSystem.isErrnoException` that failed to identify errors if the underlying method was invoked using only a file descriptor, e.g. for `fs.readSync`.", + "type": "patch" + } + ], + "packageName": "@rushstack/node-core-library" +} \ No newline at end of file diff --git a/libraries/node-core-library/src/FileSystem.ts b/libraries/node-core-library/src/FileSystem.ts index 9f22dacef11..8572d8696c4 100644 --- a/libraries/node-core-library/src/FileSystem.ts +++ b/libraries/node-core-library/src/FileSystem.ts @@ -1508,10 +1508,11 @@ export class FileSystem { */ public static isErrnoException(error: Error): error is NodeJS.ErrnoException { const typedError: NodeJS.ErrnoException = error; + // Don't check for `path` because the syscall may not have a path. + // For example, when invoked with a file descriptor. return ( typeof typedError.code === 'string' && typeof typedError.errno === 'number' && - typeof typedError.path === 'string' && typeof typedError.syscall === 'string' ); } From b35c7501e8fabdb8926af50c95faf86deda3ef01 Mon Sep 17 00:00:00 2001 From: David Michon Date: Wed, 30 Apr 2025 17:53:35 +0000 Subject: [PATCH 2/2] Add unit tests --- .../src/test/FileSystem.test.ts | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/libraries/node-core-library/src/test/FileSystem.test.ts b/libraries/node-core-library/src/test/FileSystem.test.ts index 9789d3d3a6d..0712f630c38 100644 --- a/libraries/node-core-library/src/test/FileSystem.test.ts +++ b/libraries/node-core-library/src/test/FileSystem.test.ts @@ -1,25 +1,55 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import fs from 'node:fs'; + import { FileSystem } from '../FileSystem'; import { PosixModeBits } from '../PosixModeBits'; -// The PosixModeBits are intended to be used with bitwise operations. -/* eslint-disable no-bitwise */ +describe(FileSystem.name, () => { + test(FileSystem.formatPosixModeBits.name, () => { + // The PosixModeBits are intended to be used with bitwise operations. + /* eslint-disable no-bitwise */ + let modeBits: number = PosixModeBits.AllRead | PosixModeBits.AllWrite; + + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rw-rw-'); + + modeBits |= PosixModeBits.GroupExecute; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrw-'); -test('PosixModeBits tests', () => { - let modeBits: number = PosixModeBits.AllRead | PosixModeBits.AllWrite; + // Add the group execute bit + modeBits |= PosixModeBits.OthersExecute; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrwx'); - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rw-rw-'); + // Add the group execute bit + modeBits &= ~PosixModeBits.AllWrite; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-r--r-xr-x'); + /* eslint-enable no-bitwise */ + }); - modeBits |= PosixModeBits.GroupExecute; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrw-'); + describe(FileSystem.isErrnoException.name, () => { + test('Should return false for a non-ErrnoException', () => { + const error: Error = new Error('Test error'); + expect(FileSystem.isErrnoException(error)).toBe(false); + }); - // Add the group execute bit - modeBits |= PosixModeBits.OthersExecute; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrwx'); + test('Should return true for an error on a path call', () => { + expect.assertions(1); + try { + fs.openSync(`${__dirname}/nonexistent.txt`, 'r'); + } catch (error) { + expect(FileSystem.isErrnoException(error)).toBe(true); + } + }); - // Add the group execute bit - modeBits &= ~PosixModeBits.AllWrite; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-r--r-xr-x'); + test('Should return true for an error on a file descriptor call', () => { + const buffer: Buffer = Buffer.allocUnsafeSlow(1024); + expect.assertions(1); + try { + fs.readSync(11, buffer, 0, buffer.length, -1); + } catch (error) { + expect(FileSystem.isErrnoException(error)).toBe(true); + } + }); + }); });