fix: close workspace ownership and API escapes
This commit is contained in:
@@ -4,7 +4,7 @@ import { spawn } from "node:child_process";
|
||||
import type { WorkspaceFsAtBindingV1, NativeWorkspaceFsAtHandleV1, NativeWorkspaceFsAtStatV1, NativeWorkspaceFsAtComponentV1 } from "../native/workspace-fs-at-binding.js";
|
||||
const require = createRequire(import.meta.url);
|
||||
const binding = require("../../native/workspace-fs-at/build/Release/workspace_fs_at.node") as WorkspaceFsAtBindingV1 & {
|
||||
withFd(handle: NativeWorkspaceFsAtHandleV1, action: (fd: number) => void): void;
|
||||
withFd<T>(handle: NativeWorkspaceFsAtHandleV1, action: (fd: number) => T): T;
|
||||
duplicateForChildStdio(writer: NativeWorkspaceFsAtHandleV1, root: NativeWorkspaceFsAtHandleV1, writerFd: number, rootFd: number): void;
|
||||
};
|
||||
export interface WorkspaceFsAtStatV1 extends NativeWorkspaceFsAtStatV1 {}
|
||||
@@ -36,21 +36,22 @@ abstract class Owned {
|
||||
}
|
||||
}
|
||||
export class OwnedWorkspaceFsAtDirectory extends Owned {
|
||||
constructor(raw: NativeWorkspaceFsAtHandleV1, stat: WorkspaceFsAtStatV1, token: symbol) { super(raw, stat, token); }
|
||||
private constructor(raw: NativeWorkspaceFsAtHandleV1, stat: WorkspaceFsAtStatV1, token: symbol) { super(raw, stat, token); }
|
||||
static [INTERNAL](raw: NativeWorkspaceFsAtHandleV1, stat: WorkspaceFsAtStatV1): OwnedWorkspaceFsAtDirectory { return new OwnedWorkspaceFsAtDirectory(raw, stat, INTERNAL); }
|
||||
}
|
||||
export class OwnedWorkspaceFsAtRegularFile extends Owned {
|
||||
constructor(raw: NativeWorkspaceFsAtHandleV1, stat: WorkspaceFsAtStatV1, token: symbol) { super(raw, stat, token); }
|
||||
|
||||
private constructor(raw: NativeWorkspaceFsAtHandleV1, stat: WorkspaceFsAtStatV1, token: symbol) { super(raw, stat, token); }
|
||||
static [INTERNAL](raw: NativeWorkspaceFsAtHandleV1, stat: WorkspaceFsAtStatV1): OwnedWorkspaceFsAtRegularFile { return new OwnedWorkspaceFsAtRegularFile(raw, stat, INTERNAL); }
|
||||
}
|
||||
function wrapDirectory(result: {handle: NativeWorkspaceFsAtHandleV1; openedStat: WorkspaceFsAtStatV1}): OwnedWorkspaceFsAtDirectory {
|
||||
try { if ((result.openedStat.mode & 0o170000) !== 0o040000) throw new Error("not a directory"); return new OwnedWorkspaceFsAtDirectory(result.handle, result.openedStat, INTERNAL); }
|
||||
try { if ((result.openedStat.mode & 0o170000) !== 0o040000) throw new Error("not a directory"); return OwnedWorkspaceFsAtDirectory[INTERNAL](result.handle, result.openedStat); }
|
||||
catch (error) { try { binding.close(result.handle); } catch { /* preserve conversion error */ } throw error; }
|
||||
}
|
||||
function wrapLock(result: {handle: NativeWorkspaceFsAtHandleV1; openedStat: WorkspaceFsAtStatV1}): OwnedWorkspaceFsAtRegularFile {
|
||||
try {
|
||||
const st = result.openedStat;
|
||||
if ((st.mode & 0o170000) !== 0o100000 || (st.mode & 0o777) !== 0o600 || st.uid !== (process.getuid?.() ?? st.uid) || st.nlink !== 1n) throw new Error("invalid lock identity");
|
||||
return new OwnedWorkspaceFsAtRegularFile(result.handle, st, INTERNAL);
|
||||
return OwnedWorkspaceFsAtRegularFile[INTERNAL](result.handle, st);
|
||||
} catch (error) { try { binding.close(result.handle); } catch { /* preserve conversion error */ } throw error; }
|
||||
}
|
||||
function rawDirectory(value: OwnedWorkspaceFsAtDirectory): NativeWorkspaceFsAtHandleV1 { if (!rawHandles.has(value)) throw new Error("workspace descriptor is closed"); return rawHandles.get(value)!; }
|
||||
@@ -59,6 +60,12 @@ function withLockFd<T>(value: OwnedWorkspaceFsAtRegularFile, action: (fd: number
|
||||
const count = borrowing.get(value) ?? 0; borrowing.set(value, count + 1);
|
||||
try { return binding.withFd(rawHandles.get(value)!, action as (fd: number) => void) as T; } finally { borrowing.set(value, count); }
|
||||
}
|
||||
function anchoredDirectoryPath(value: OwnedWorkspaceFsAtDirectory): string {
|
||||
if (!rawHandles.has(value)) throw new Error("workspace descriptor is closed");
|
||||
const count = borrowing.get(value) ?? 0; borrowing.set(value, count + 1);
|
||||
try { return binding.withFd(rawHandles.get(value)!, fd => `${process.platform === "darwin" ? "/dev/fd" : "/proc/self/fd"}/${fd}`); }
|
||||
finally { borrowing.set(value, count); }
|
||||
}
|
||||
export class WorkspaceFsAtV1 {
|
||||
openRoot(): OwnedWorkspaceFsAtDirectory { return wrapDirectory(binding.openat({ parent: null, name: "/", kind: "directory", createMode: 0 })); }
|
||||
openDirectoryAt(parent: OwnedWorkspaceFsAtDirectory, name: string): OwnedWorkspaceFsAtDirectory { return wrapDirectory(binding.openat({ parent: rawDirectory(parent), name: component(name), kind: "directory", createMode: 0 })); }
|
||||
@@ -66,16 +73,19 @@ export class WorkspaceFsAtV1 {
|
||||
mkdirAt(parent: OwnedWorkspaceFsAtDirectory, name: string, mode: 0o700): void { binding.mkdirat(rawDirectory(parent), component(name), mode); }
|
||||
statAtNoFollow(parent: OwnedWorkspaceFsAtDirectory, name: string): WorkspaceFsAtStatV1 { return binding.fstatat(rawDirectory(parent), component(name)); }
|
||||
fsyncDirectory(directory: OwnedWorkspaceFsAtDirectory): void { binding.fsyncDirectory(rawDirectory(directory)); }
|
||||
/** Returns a proc-fd path tied to the retained directory open description. */
|
||||
anchoredDirectoryPath(directory: OwnedWorkspaceFsAtDirectory): string { return anchoredDirectoryPath(directory); }
|
||||
flockOwnedLock(owned: OwnedWorkspaceFsAtRegularFile, kind: WorkspaceFlockKindV1, wait: WorkspaceFlockWaitV1): void {
|
||||
const fsExt: { flockSync(fd: number, operation: string): void } = require("fs-ext");
|
||||
const operation = kind === "shared" ? (wait === "blocking" ? "sh" : "shnb") : (wait === "blocking" ? "ex" : "exnb");
|
||||
withLockFd(owned, fd => fsExt.flockSync(fd, operation));
|
||||
}
|
||||
fcntlOwnedLock(owned: OwnedWorkspaceFsAtRegularFile, operation: "probe-exclusive-nonblocking" | "hold-exclusive" | "unlock"): "held" | "available" { if (!rawHandles.has(owned)) throw new Error("workspace descriptor is closed"); return binding.fcntl(rawHandles.get(owned)!, operation); }
|
||||
}
|
||||
|
||||
|
||||
/** Internal child boundary. It deliberately returns a ChildProcess result, never an FD. */
|
||||
export function spawnChildWithWorkspaceCapabilities(
|
||||
export function spawnChildFromOwnedCapability(
|
||||
writer: OwnedWorkspaceFsAtRegularFile,
|
||||
root: OwnedWorkspaceFsAtDirectory,
|
||||
executable: string,
|
||||
|
||||
Reference in New Issue
Block a user