From 18f96e156e6a522bacb786fdbe7d68758b53da14 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:53:04 -0700 Subject: [PATCH] fix(windows): restore fresh-profile startup after durable fsync (#14173) Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> Co-authored-by: DHTheOne <238933622+DHTheOne@users.noreply.github.com> Co-authored-by: Anton Tupitsyn <70199858+PLUTONYY@users.noreply.github.com> Co-authored-by: 7loop <48346764+7loop@users.noreply.github.com> (cherry picked from commit e8044b1b30756ead7e6fd61a8d96c74f3b72b14d) --- .github/workflows/pr.yml | 8 +++- src/shared/secure-file-fsync-flags.test.ts | 52 ++++++++++++++++++++++ src/shared/secure-file.ts | 11 +++-- 3 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 src/shared/secure-file-fsync-flags.test.ts diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 50eea7fc6d2..89e9e2e6123 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -424,8 +424,12 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Test Windows PowerShell CLI boundary - run: pnpm exec vitest run --config config/vitest.config.ts src/main/cli/wsl-cli-powershell-boundary.test.ts + - name: Test Windows-specific boundaries + run: >- + pnpm exec vitest run --config config/vitest.config.ts + src/main/cli/wsl-cli-powershell-boundary.test.ts + src/main/orca-profiles/profile-index-store.test.ts + src/shared/secure-file-fsync-flags.test.ts - name: Build package inputs run: pnpm run build:release diff --git a/src/shared/secure-file-fsync-flags.test.ts b/src/shared/secure-file-fsync-flags.test.ts new file mode 100644 index 00000000000..1c167bffdad --- /dev/null +++ b/src/shared/secure-file-fsync-flags.test.ts @@ -0,0 +1,52 @@ +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import type * as NodeFs from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' + +const openedPaths = vi.hoisted(() => [] as { path: string; flags: string | number }[]) + +vi.mock('node:fs', async () => { + const actual = await vi.importActual('node:fs') + return { + ...actual, + openSync: (path: NodeFs.PathLike, flags: string | number, mode?: NodeFs.Mode) => { + openedPaths.push({ path: String(path), flags }) + return actual.openSync(path, flags, mode) + } + } +}) + +import { bestEffortFsyncDirectorySync, fsyncFileSync } from './secure-file' + +const createdPaths: string[] = [] + +afterEach(() => { + openedPaths.length = 0 + for (const path of createdPaths.splice(0)) { + rmSync(path, { recursive: true, force: true }) + } +}) + +describe('secure file fsync flags', () => { + it('opens files read/write before fsync', () => { + const directory = mkdtempSync(join(tmpdir(), 'orca-file-fsync-')) + createdPaths.push(directory) + const path = join(directory, 'record.json') + writeFileSync(path, '{}') + + fsyncFileSync(path) + + expect(openedPaths).toEqual([{ path, flags: 'r+' }]) + }) + + const posixIt = process.platform === 'win32' ? it.skip : it + posixIt('opens directories read-only before fsync', () => { + const directory = mkdtempSync(join(tmpdir(), 'orca-directory-fsync-')) + createdPaths.push(directory) + + bestEffortFsyncDirectorySync(directory) + + expect(openedPaths).toEqual([{ path: directory, flags: 'r' }]) + }) +}) diff --git a/src/shared/secure-file.ts b/src/shared/secure-file.ts index e3c38c70e09..7b2f646c540 100644 --- a/src/shared/secure-file.ts +++ b/src/shared/secure-file.ts @@ -133,8 +133,8 @@ export function writeSecureFile( } } -export function fsyncFileSync(path: string): void { - const descriptor = openSync(path, 'r') +function fsyncPathSync(path: string, flags: 'r' | 'r+'): void { + const descriptor = openSync(path, flags) try { fsyncSync(descriptor) } finally { @@ -142,12 +142,17 @@ export function fsyncFileSync(path: string): void { } } +export function fsyncFileSync(path: string): void { + // FlushFileBuffers requires a write-capable handle on Windows. + fsyncPathSync(path, 'r+') +} + export function bestEffortFsyncDirectorySync(directory: string): void { if (process.platform === 'win32') { return } try { - fsyncFileSync(directory) + fsyncPathSync(directory, 'r') } catch (error) { if ( error instanceof Error &&