fix(jsonl): bind MoveFileExW with the Win32 BOOL ABI
Declare MoveFileExW's return value as Koffi int and model it as a numeric 32-bit Win32 BOOL. The previous Koffi bool declaration represented a one-byte C boolean and could read the native return register with the wrong ABI. Test zero and nonzero results explicitly and assert the binding result type while preserving the existing write-through flags, error translation, and durable directory race behavior.
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
import { mkdtemp, rm, stat } from 'node:fs/promises'
|
||||
import { basename, join, parse, resolve, toNamespacedPath } from 'node:path'
|
||||
|
||||
type MoveFileExW = (existing: string, replacement: string, flags: number) => boolean
|
||||
type MoveFileExW = (existing: string, replacement: string, flags: number) => number
|
||||
type GetLastError = () => number
|
||||
|
||||
interface Win32Bindings {
|
||||
@@ -44,7 +44,7 @@ async function win32(): Promise<Win32Bindings> {
|
||||
const koffi = (await import('koffi')).default
|
||||
const kernel32 = koffi.load('kernel32.dll')
|
||||
bindings = {
|
||||
moveFileExW: kernel32.func('__stdcall', 'MoveFileExW', 'bool', ['str16', 'str16', 'uint']) as MoveFileExW,
|
||||
moveFileExW: kernel32.func('__stdcall', 'MoveFileExW', 'int', ['str16', 'str16', 'uint']) as MoveFileExW,
|
||||
getLastError: kernel32.func('__stdcall', 'GetLastError', 'uint', []) as GetLastError,
|
||||
}
|
||||
return bindings
|
||||
@@ -113,7 +113,7 @@ async function assertDirectory(path: string): Promise<boolean> {
|
||||
export async function publishNewFileWin32(existing: string, replacement: string): Promise<void> {
|
||||
const api = await win32()
|
||||
const ok = api.moveFileExW(toNamespacedPath(existing), toNamespacedPath(replacement), MOVEFILE_WRITE_THROUGH)
|
||||
if (!ok) throw win32Error('MoveFileExW', api.getLastError(), existing, replacement)
|
||||
if (ok === 0) throw win32Error('MoveFileExW', api.getLastError(), existing, replacement)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,7 +19,7 @@ const ERROR_FILE_EXISTS = 80
|
||||
const ERROR_INVALID_NAME = 123
|
||||
const ERROR_ALREADY_EXISTS = 183
|
||||
|
||||
type MoveFileExW = (existing: string, replacement: string, flags: number, setLastError: (code: number) => void) => boolean
|
||||
type MoveFileExW = (existing: string, replacement: string, flags: number, setLastError: (code: number) => void) => number
|
||||
|
||||
const roots: string[] = []
|
||||
|
||||
@@ -42,14 +42,15 @@ async function importWithMove(moveFileExW: MoveFileExW): Promise<typeof import('
|
||||
const setLastError = (code: number): void => { lastError = code }
|
||||
const move: MoveFileExW = (existing, replacement, flags, setError) => {
|
||||
const ok = moveFileExW(existing, replacement, flags, setError)
|
||||
lastError = ok ? 0 : lastError
|
||||
lastError = ok === 0 ? lastError : 0
|
||||
return ok
|
||||
}
|
||||
return {
|
||||
default: {
|
||||
load: () => ({
|
||||
func: (_convention: string, name: string) => {
|
||||
func: (_convention: string, name: string, result: string) => {
|
||||
if (name === 'MoveFileExW') return (existing: string, replacement: string, flags: number) => {
|
||||
expect(result).toBe('int')
|
||||
const ok = move(existing, replacement, flags, setLastError)
|
||||
return ok
|
||||
}
|
||||
@@ -68,7 +69,7 @@ async function importWithError(code: number): Promise<typeof import('../src/win3
|
||||
default: {
|
||||
load: () => ({
|
||||
func: (_convention: string, name: string) => {
|
||||
if (name === 'MoveFileExW') return () => false
|
||||
if (name === 'MoveFileExW') return () => 0
|
||||
return () => code
|
||||
},
|
||||
}),
|
||||
@@ -82,10 +83,10 @@ async function importWithFilesystemMove(): Promise<typeof import('../src/win32.t
|
||||
expect(flags).toBe(MOVEFILE_WRITE_THROUGH)
|
||||
const from = stripNamespace(existing)
|
||||
const to = stripNamespace(replacement)
|
||||
if (!existsSync(from)) { setLastError(ERROR_FILE_NOT_FOUND); return false }
|
||||
if (existsSync(to)) { setLastError(ERROR_ALREADY_EXISTS); return false }
|
||||
if (!existsSync(from)) { setLastError(ERROR_FILE_NOT_FOUND); return 0 }
|
||||
if (existsSync(to)) { setLastError(ERROR_ALREADY_EXISTS); return 0 }
|
||||
renameSync(from, to)
|
||||
return true
|
||||
return 1
|
||||
})
|
||||
}
|
||||
|
||||
@@ -135,12 +136,12 @@ describe('Windows durable namespace helpers', () => {
|
||||
if (to === raced) {
|
||||
mkdirSync(to)
|
||||
setLastError(ERROR_ALREADY_EXISTS)
|
||||
return false
|
||||
return 0
|
||||
}
|
||||
if (!existsSync(from)) { setLastError(ERROR_FILE_NOT_FOUND); return false }
|
||||
if (existsSync(to)) { setLastError(ERROR_ALREADY_EXISTS); return false }
|
||||
if (!existsSync(from)) { setLastError(ERROR_FILE_NOT_FOUND); return 0 }
|
||||
if (existsSync(to)) { setLastError(ERROR_ALREADY_EXISTS); return 0 }
|
||||
renameSync(from, to)
|
||||
return true
|
||||
return 1
|
||||
})
|
||||
|
||||
await ensureDurableDirectoryWin32(join(root, 'a', 'b'))
|
||||
|
||||
Reference in New Issue
Block a user