Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 9 additions & 30 deletions src/errno/windows_errno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,22 +377,16 @@ pub use bun_core::S as s;
// getErrno
// ──────────────────────────────────────────────────────────────────────────

/// `get_errno(rc)` — `rc` is ignored;
/// `get_errno(rc)` — `rc` is ignored: the caller has already seen the call
/// fail, and this reads `GetLastError()` (which is also `WSAGetLastError()`).
/// A non-zero code with no row in the Win32→errno table is `UNKNOWN`, like
/// libuv's `uv_translate_sys_error`, never `SUCCESS`.
/// NTSTATUS callers use `windows::translate_ntstatus_to_errno` directly.
pub fn get_errno<T>(_rc: T) -> E {
if let Some(sys) = Win32Error::get().to_system_errno() {
return sys.to_e();
match Win32Error::get() {
Win32Error::SUCCESS => E::SUCCESS,
err => err.to_e(),
}

// `wsa_get_last_error()` returns `Option<SystemErrno>` (already routed
// through the Win32Error→errno switch). An unmapped non-zero WSA code
// yields `None` there and falls through to `SUCCESS` — it must NOT surface
// as `E::UNKNOWN` (which `Win32ErrorExt::to_e`'s `unwrap_or` would do).
if let Some(wsa) = windows::wsa_get_last_error() {
return wsa.to_e();
}

E::SUCCESS
}

// ──────────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -844,19 +838,13 @@ pub mod windows {
/// `NTSTATUS` — `enum(u32) { …, _ }`. Same provenance as `Win32Error`.
pub use bun_windows_sys::NTSTATUS;

use bun_windows_sys::ws2_32::WSAGetLastError;

/// Extension trait for `Win32Error` → `SystemErrno`/`E` mapping.
/// `bun_windows_sys` is tier-0 and cannot name `SystemErrno`, so
/// `to_system_errno()` surfaces here as an extension method instead.
pub trait Win32ErrorExt: Copy {
fn to_system_errno(self) -> Option<SystemErrno>;
/// Convenience: Win32 error → `E`, falling back to `E::UNKNOWN` for
/// codes not in the Win32→errno table.
///
/// **Note:** NOT appropriate where unmapped codes must fall through
/// to `SUCCESS` (e.g. the WSA path of `get_errno`); those callers
/// must use `to_system_errno()` and choose their own fallback.
/// Win32 error → `E`, falling back to `E::UNKNOWN` for codes not in
/// the Win32→errno table (including `SUCCESS` — check for it first).
#[inline]
fn to_e(self) -> E {
self.to_system_errno()
Expand All @@ -871,15 +859,6 @@ pub mod windows {
}
}

/// Feeds the raw WSA code (`c_int`) through the Win32→errno switch.
/// Returns `Some(SUCCESS)` for `0` and `None` for any non-zero code with
/// no mapping (e.g. `WSANOTINITIALISED`/`WSAEDISCON`); callers that need
/// a success-on-unmapped fallthrough (`getErrno`) rely on that `None`.
#[inline]
pub(crate) fn wsa_get_last_error() -> Option<SystemErrno> {
SystemErrno::init_c_int(WSAGetLastError())
}

/// Moved DOWN so `bun_errno` owns the only NTSTATUS→`E` mapping (cycle-break).
pub fn translate_ntstatus_to_errno(err: NTSTATUS) -> E {
match err {
Expand Down
12 changes: 12 additions & 0 deletions test/js/node/fs/fs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,18 @@ describe("copyFileSync", () => {
}).toThrow();
});

// A share that does not exist makes CopyFileW fail with ERROR_BAD_NET_NAME,
// a Win32 code with no errno mapping.
it.if(isWindows)("fails when CopyFileW fails with an unmapped Win32 error", async () => {
const src = import.meta.path;
const dest = "\\\\localhost\\bun-test-no-such-share$\\copyFileSync.js";
const expected = expect.objectContaining({ syscall: "copyfile", errno: -4094, path: src, dest });
expect(() => copyFileSync(src, dest)).toThrow(expected);
await expect(promisify(fs.copyFile)(src, dest)).rejects.toThrow(expected);
await expect(fs.promises.copyFile(src, dest)).rejects.toThrow(expected);
expect(existsSync(dest)).toBe(false);
});

if (process.platform === "linux") {
describe("should work when copyFileRange is not available", () => {
it("on large files", () => {
Expand Down
Loading