mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
## Summary Fixes #23292 `fs.access()` and `fs.accessSync()` threw EUNKNOWN (-134) when checking named pipes on Windows (paths like `\.\pipe\name`), but Node.js worked fine. **Repro:** ```ts // Server creates pipe at \.\pipe\bun-test import net from 'net'; const server = net.createServer(); server.listen('\\.\pipe\bun-test'); // Client tries to check if pipe exists import fs from 'fs'; fs.accessSync('\\.\pipe\bun-test', fs.constants.F_OK); // Error: EUNKNOWN: unknown error, access '\.\pipe\bun-test' ``` ## Root Cause The `osPathKernel32` function normalizes paths before passing to Windows APIs. The normalization logic treats a single `.` as a "current directory" component and removes it, so `\.\pipe\name` incorrectly became `\pipe\name` - an invalid path. ## Solution Detect Windows device paths (starting with `\.\` or `\?\`) and skip normalization for these special paths, preserving the device prefix. ## Test Plan - [x] Added regression test `test/regression/issue/23292.test.ts` - [x] Test fails with system bun (v1.3.3): 3 failures (EUNKNOWN) - [x] Test passes with fix: 4 pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
73 lines
1.9 KiB
TypeScript
73 lines
1.9 KiB
TypeScript
// https://github.com/oven-sh/bun/issues/23292
|
|
// fs.access() and fs.accessSync() should work with Windows named pipes
|
|
import { expect, test } from "bun:test";
|
|
import { isWindows } from "harness";
|
|
import { randomUUID } from "node:crypto";
|
|
import { once } from "node:events";
|
|
import fs from "node:fs";
|
|
import net from "node:net";
|
|
|
|
test.if(isWindows)("fs.accessSync should work with named pipes", async () => {
|
|
const pipeName = `\\\\.\\pipe\\bun-test-${randomUUID()}`;
|
|
|
|
const server = net.createServer();
|
|
server.listen(pipeName);
|
|
await once(server, "listening");
|
|
|
|
try {
|
|
// Should not throw - the pipe exists
|
|
fs.accessSync(pipeName, fs.constants.F_OK);
|
|
|
|
// Test with R_OK as well
|
|
fs.accessSync(pipeName, fs.constants.R_OK);
|
|
} finally {
|
|
server.close();
|
|
}
|
|
});
|
|
|
|
test.if(isWindows)("fs.access should work with named pipes", async () => {
|
|
const pipeName = `\\\\.\\pipe\\bun-test-${randomUUID()}`;
|
|
|
|
const server = net.createServer();
|
|
server.listen(pipeName);
|
|
await once(server, "listening");
|
|
|
|
try {
|
|
// Test fs.access with callback
|
|
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
fs.access(pipeName, fs.constants.F_OK, err => {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
await promise;
|
|
} finally {
|
|
server.close();
|
|
}
|
|
});
|
|
|
|
test.if(isWindows)("fs.promises.access should work with named pipes", async () => {
|
|
const pipeName = `\\\\.\\pipe\\bun-test-${randomUUID()}`;
|
|
|
|
const server = net.createServer();
|
|
server.listen(pipeName);
|
|
await once(server, "listening");
|
|
|
|
try {
|
|
// Should not throw - the pipe exists
|
|
await fs.promises.access(pipeName, fs.constants.F_OK);
|
|
} finally {
|
|
server.close();
|
|
}
|
|
});
|
|
|
|
test.if(isWindows)("fs.accessSync should throw ENOENT for non-existent named pipe", () => {
|
|
const pipeName = `\\\\.\\pipe\\bun-test-nonexistent-${randomUUID()}`;
|
|
|
|
expect(() => {
|
|
fs.accessSync(pipeName, fs.constants.F_OK);
|
|
}).toThrow();
|
|
});
|