fix: harden native workspace root and docker gates

This commit is contained in:
2026-08-11 14:11:42 +02:00
parent ee87a59e1f
commit a5916f6177
16 changed files with 401 additions and 96 deletions
+10 -7
View File
@@ -108,18 +108,20 @@ export class BorrowedWorkspaceSessionReadersExclusiveLockLease {
}
export class WorkspaceWriterLockCapability {
private live = true; private readerExclusive = false;
private constructor(readonly workspaceId: CanonicalWorkspaceId, readonly rootIdentity: WorkspaceLockRootIdentityV1, private readonly root: VerifiedWorkspaceLockRootLease, private readonly fs: WorkspaceFsAtV1, private readonly writer: OwnedWorkspaceFsAtRegularFile) {}
static make(id: CanonicalWorkspaceId, identity: WorkspaceLockRootIdentityV1, root: VerifiedWorkspaceLockRootLease, fs: WorkspaceFsAtV1, writer: OwnedWorkspaceFsAtRegularFile) { return new WorkspaceWriterLockCapability(id, identity, root, fs, writer); }
private constructor(readonly workspaceId: CanonicalWorkspaceId, readonly rootIdentity: WorkspaceLockRootIdentityV1, private readonly root: VerifiedWorkspaceLockRootLease, private readonly writer: import("./workspace-lock-root-lease.js").WorkspaceRootLock) {}
static create(id: CanonicalWorkspaceId, identity: WorkspaceLockRootIdentityV1, root: VerifiedWorkspaceLockRootLease, writer: import("./workspace-lock-root-lease.js").WorkspaceRootLock): WorkspaceWriterLockCapability { return new WorkspaceWriterLockCapability(id, identity, root, writer); }
private check(): void { if (!this.live) throw fail(); }
async runUnderSessionReadersExclusive<T>(action: (lease: BorrowedWorkspaceSessionReadersExclusiveLockLease) => Promise<T>): Promise<T> {
this.check(); if (this.readerExclusive) throw fail(); this.readerExclusive = true;
let lock: OwnedWorkspaceFsAtRegularFile | undefined; let borrowed: BorrowedWorkspaceSessionReadersExclusiveLockLease | undefined; let result!: T;
try { result = await this.root._withRoot(async (dir, fs) => { lock = fs.openOrCreateLockAt(dir, "session-readers.lock", 0o600); fs.flockOwnedLock(lock, "exclusive", "nonblocking"); borrowed = BorrowedWorkspaceSessionReadersExclusiveLockLease.make(this.workspaceId, this.rootIdentity); return action(borrowed); }); return result; }
finally { borrowed?.invalidate(); let failed = false; try { lock?.close(); } catch { failed = true; } if (failed) { this.live = false; } this.readerExclusive = false; if (failed) throw fail(); }
let lock: import("./workspace-lock-root-lease.js").WorkspaceRootLock | undefined; let borrowed: BorrowedWorkspaceSessionReadersExclusiveLockLease | undefined;
try { lock = await this.root.acquireSessionReadersExclusive(); borrowed = BorrowedWorkspaceSessionReadersExclusiveLockLease.make(this.workspaceId, this.rootIdentity); return await action(borrowed); }
catch { throw fail(); }
finally { borrowed?.invalidate(); try { lock?.close(); } catch { this.live = false; } this.readerExclusive = false; }
}
async spawnChild(_request: WorkspaceLockedChildRequest): Promise<WorkspaceLockedChildResult> { this.check(); throw fail("child runner is not configured"); }
async close(): Promise<void> { if (!this.live) return; if (this.readerExclusive) throw fail(); this.live = false; let error: unknown; try { this.writer.close(); } catch (e) { error = e; } try { await this.root.close(); } catch (e) { error ??= e; } if (error) throw fail(); }
}
function makeWriterCapability(id: CanonicalWorkspaceId, identity: WorkspaceLockRootIdentityV1, root: VerifiedWorkspaceLockRootLease, writer: import("./workspace-lock-root-lease.js").WorkspaceRootLock): WorkspaceWriterLockCapability { return WorkspaceWriterLockCapability.create(id, identity, root, writer); }
export interface OrderedWorkspaceCapability { readonly workspaceId: CanonicalWorkspaceId; readonly rootLease: BorrowedVerifiedWorkspaceLockRootLease; readonly writerCapability: WorkspaceWriterLockCapability; }
export class OrderedWorkspaceWriterCapabilitySet {
private live = true; private constructor(private readonly caps: Map<CanonicalWorkspaceId, WorkspaceWriterLockCapability>) {}
@@ -132,9 +134,10 @@ export class OrderedWorkspaceWriterCapabilitySet {
export async function runUnderOrderedWorkspaceWriterLocks<T>(rootLeases: readonly VerifiedWorkspaceLockRootLease[], action: (capabilities: OrderedWorkspaceWriterCapabilitySet) => Promise<T>): Promise<T> {
const sorted = [...rootLeases].sort((a, b) => a.identity.workspaceId.localeCompare(b.identity.workspaceId)); if (new Set(sorted.map(x => x.identity.workspaceId)).size !== sorted.length) throw fail();
const caps: WorkspaceWriterLockCapability[] = [];
try { for (const source of sorted) { const root = source.transfer(); const fs = new WorkspaceFsAtV1(); let cap: WorkspaceWriterLockCapability | undefined; await root._withRoot(async (dir) => { const writer = fs.openOrCreateLockAt(dir, "writer.lock", 0o600); try { fs.flockOwnedLock(writer, "exclusive", "nonblocking"); cap = WorkspaceWriterLockCapability.make(root.identity.workspaceId, root.identity, root, fs, writer); } catch (e) { writer.close(); throw e; } }); if (!cap) throw fail(); caps.push(cap); }
const transferred: VerifiedWorkspaceLockRootLease[] = [];
try { for (const source of sorted) { const root = source.transfer(); transferred.push(root); let writer: import("./workspace-lock-root-lease.js").WorkspaceRootLock | undefined; try { writer = await root.acquireWriterLock(); caps.push(makeWriterCapability(root.identity.workspaceId, root.identity, root, writer)); } catch (error) { try { writer?.close(); } catch {} try { await root.close(); } catch {} throw error; } }
const set = OrderedWorkspaceWriterCapabilitySet.make(new Map(caps.map(c => [c.workspaceId, c]))); try { return await action(set); } finally { set.invalidate(); for (const cap of [...caps].reverse()) await cap.close(); }
} catch (error) { for (const cap of [...caps].reverse()) await cap.close().catch(() => undefined); throw error; }
} catch (error) { for (const cap of [...caps].reverse()) await cap.close().catch(() => undefined); for (const root of transferred.slice(caps.length).reverse()) await root.close().catch(() => undefined); throw error; }
}
export function runUnderWorkspaceWriterLock<T>(rootLease: VerifiedWorkspaceLockRootLease, action: (capability: WorkspaceWriterLockCapability) => Promise<T>) { return runUnderOrderedWorkspaceWriterLocks([rootLease], set => set.forWorkspace(rootLease.identity.workspaceId, x => action(x.writerCapability))); }
export async function probeWorkspaceWriterLock(rootLease: VerifiedWorkspaceLockRootLease): Promise<"available" | "held"> { try { await runUnderWorkspaceWriterLock(rootLease, async () => undefined); return "available"; } catch { return "held"; } }