Skip to content

Commit

Permalink
path: update win32 toNamespacedPath to support device namespace paths
Browse files Browse the repository at this point in the history
  • Loading branch information
EarlyRiser42 committed Aug 14, 2024
1 parent 880c446 commit 5000736
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 0 deletions.
33 changes: 33 additions & 0 deletions lib/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const {
ArrayPrototypeJoin,
ArrayPrototypeSlice,
FunctionPrototypeBind,
ObjectValues,
StringPrototypeCharCodeAt,
StringPrototypeIndexOf,
StringPrototypeLastIndexOf,
Expand Down Expand Up @@ -180,6 +181,25 @@ function glob(path, pattern, windows) {
});
}

// Regular expressions to identify special device names in Windows.
// Ref: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
// COM to AUX (e.g., COM1, LPT1, NUL, CON, PRN, AUX) are reserved OS device names.
// Paths like C:\path\to\COM1 map to \\.\COM1, referencing hardware or system streams.
// Ref: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
const windowDevicePatterns = {
comPortRegex: /([\\/])?(COM\d+)$/i,
lptPortRegex: /([\\/])?(LPT\d+)$/i,
nulDeviceRegex: /([\\/])?(NUL)$/i,
conDeviceRegex: /([\\/])?(CON)$/i,
prnDeviceRegex: /([\\/])?(PRN)$/i,
auxDeviceRegex: /([\\/])?(AUX)$/i,
physicalDriveRegex: /^(PHYSICALDRIVE\d+)$/i,
pipeRegex: /^(PIPE\\.+)$/i,
mailslotRegex: /^(MAILSLOT\\.+)$/i,
tapeDriveRegex: /^(TAPE\d+)$/i,
changerDeviceRegex: /^(CHANGER\d+)$/i,
};

const win32 = {
/**
* path.resolve([from ...], to)
Expand Down Expand Up @@ -687,6 +707,19 @@ const win32 = {
if (typeof path !== 'string' || path.length === 0)
return path;

// Check if the path matches any device pattern
if (ObjectValues(windowDevicePatterns).some((pattern) => pattern.test(path))) {
let deviceName;
if (windowDevicePatterns.pipeRegex.test(path) || windowDevicePatterns.mailslotRegex.test(path)) {
// If the path starts with PIPE\ or MAILSLOT\, keep it as is
deviceName = path;
} else {
// Extract the last component after the last slash or backslash
deviceName = path.split(/[\\/]/).pop();
}
return `\\\\.\\${deviceName}`;
}

const resolvedPath = win32.resolve(path);

if (resolvedPath.length <= 2)
Expand Down
26 changes: 26 additions & 0 deletions test/parallel/test-path-makelong.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,34 @@ if (common.isWindows) {
assert.strictEqual(path.toNamespacedPath(
'\\\\?\\UNC\\someserver\\someshare\\somefile'),
'\\\\?\\UNC\\someserver\\someshare\\somefile');
// Device name tests
assert.strictEqual(path.toNamespacedPath('C:\\path\\COM1'),
'\\\\.\\COM1');
assert.strictEqual(path.toNamespacedPath('COM1'),
'\\\\.\\COM1');
assert.strictEqual(path.toNamespacedPath('LPT1'),
'\\\\.\\LPT1');
assert.strictEqual(path.toNamespacedPath('C:\\LPT1'),
'\\\\.\\LPT1');
assert.strictEqual(path.toNamespacedPath('PhysicalDrive0'),
'\\\\.\\PhysicalDrive0');
assert.strictEqual(path.toNamespacedPath('pipe\\mypipe'),
'\\\\.\\pipe\\mypipe');
assert.strictEqual(path.toNamespacedPath('MAILSLOT\\mySlot'),
'\\\\.\\MAILSLOT\\mySlot');
assert.strictEqual(path.toNamespacedPath('NUL'),
'\\\\.\\NUL');
assert.strictEqual(path.toNamespacedPath('Tape0'),
'\\\\.\\Tape0');
assert.strictEqual(path.toNamespacedPath('Changer0'),
'\\\\.\\Changer0');
// Test cases for inputs with "\\.\" prefix
assert.strictEqual(path.toNamespacedPath('\\\\.\\pipe\\somepipe'),
'\\\\.\\pipe\\somepipe');
assert.strictEqual(path.toNamespacedPath('\\\\.\\COM1'),
'\\\\.\\COM1');
assert.strictEqual(path.toNamespacedPath('\\\\.\\LPT1'),
'\\\\.\\LPT1');
}

assert.strictEqual(path.toNamespacedPath(''), '');
Expand Down

0 comments on commit 5000736

Please sign in to comment.