blob: c52e9cae792cdf249980ac9ebcb0887c0b564ec6 [file]
import { createHash, randomBytes } from 'node:crypto';
import type { BigIntStats } from 'node:fs';
import { chmod, lstat, mkdir, open, realpath, stat, type FileHandle } from 'node:fs/promises';
import { userInfo } from 'node:os';
import { isAbsolute, join, normalize, parse, resolve } from 'node:path';
import { tryLock, unlock, waitForLock } from 'fs-native-extensions';
import { withArtifactWriterBootstrapLock } from './artifact-writer-bootstrap-lock.js';
import { publishMarkerFile, readBoundedMarkerFile } from './marker-file.js';
export const STORAGE_ROOT_MARKER_FILE = '.maka-storage-root.json';
export const STORAGE_ROOT_MARKER_SCHEMA_VERSION = 1 as const;
const MAX_STORAGE_ROOT_MARKER_BYTES = 1_024;
const ARTIFACT_WRITER_BOOTSTRAP_DIRECTORY = 'artifact-writer-bootstrap';
export type StorageRootKind = 'interactive' | 'headless';
export type StorageRootAccess = 'read' | 'write';
const capabilityBrand: unique symbol = Symbol('StorageRootCapability');
const leaseBrand: unique symbol = Symbol('StorageRootLease');
const repairBrand: unique symbol = Symbol('StorageRootIdentityRepairCandidate');
const artifactWriterBootstrapAuthorityBrand: unique symbol = Symbol(
'ArtifactWriterBootstrapAuthority',
);
const artifactWriterLockAuthorityBrand: unique symbol = Symbol('ArtifactWriterLockAuthority');
export interface StorageRootCapability<K extends StorageRootKind = StorageRootKind> {
readonly kind: K;
readonly canonicalPath: string;
readonly rootId: string;
readonly [capabilityBrand]: true;
}
export type DiscoveredStorageRootCapability =
| StorageRootCapability<'interactive'>
| StorageRootCapability<'headless'>;
export interface StorageRootLease<
K extends StorageRootKind = StorageRootKind,
A extends StorageRootAccess = StorageRootAccess,
> {
readonly kind: K;
readonly access: A;
readonly canonicalPath: string;
readonly rootId: string;
readonly [leaseBrand]: true;
}
export interface ArtifactWriterLockAuthority {
readonly bootstrapLockPath: string;
readonly controlDirectory: string;
readonly assertCurrentRoot: () => Promise<void>;
readonly [artifactWriterLockAuthorityBrand]: true;
}
export interface ArtifactWriterBootstrapAuthority {
readonly lockPath: string;
readonly canonicalPath: string;
readonly assertCurrentRoot: () => Promise<void>;
readonly [artifactWriterBootstrapAuthorityBrand]: true;
}
export interface ResolveStorageRootInput<K extends StorageRootKind> {
path: string;
kind: K;
}
export interface DiscoverStorageRootInput {
path: string;
}
export interface ResolveExistingStorageRootInput<K extends StorageRootKind>
extends ResolveStorageRootInput<K> {
expectedRootId: string;
}
export type AdoptStorageRootOnImportInput<K extends StorageRootKind> =
ResolveExistingStorageRootInput<K>;
export interface StorageRootIdentityRepairCandidate<K extends StorageRootKind = StorageRootKind> {
readonly kind: K;
readonly canonicalPath: string;
readonly rootId: string;
readonly [repairBrand]: true;
}
export interface InteractiveRootOwner {
readonly capability: StorageRootCapability<'interactive'>;
readonly lease: StorageRootLease<'interactive', 'write'>;
readonly controlDirectory: string;
readonly lockPath: string;
readonly closed: boolean;
close(): Promise<void>;
}
export interface InteractiveRootReader {
readonly capability: StorageRootCapability<'interactive'>;
readonly lease: StorageRootLease<'interactive', 'read'>;
readonly controlDirectory: string;
readonly lockPath: string;
readonly closed: boolean;
close(): Promise<void>;
}
interface RootIdentity {
dev: bigint;
ino: bigint;
}
interface CapabilityRecord<K extends StorageRootKind = StorageRootKind> {
kind: K;
canonicalPath: string;
rootId: string;
identity: RootIdentity;
}
interface LeaseRecord<
K extends StorageRootKind = StorageRootKind,
A extends StorageRootAccess = StorageRootAccess,
> extends CapabilityRecord<K> {
access: A;
isActive: () => boolean;
beginOperation: () => () => void;
}
interface RootMarker {
schemaVersion: typeof STORAGE_ROOT_MARKER_SCHEMA_VERSION;
kind: StorageRootKind;
rootId: string;
rootIdentity: {
dev: string;
ino: string;
};
}
interface StorageRootIdentityRepairRecord<K extends StorageRootKind = StorageRootKind>
extends CapabilityRecord<K> {
marker: RootMarker;
}
const capabilities = new WeakMap<object, CapabilityRecord>();
const leases = new WeakMap<object, LeaseRecord>();
const interactiveRootLocks = new WeakMap<object, { access: StorageRootAccess }>();
const storageRootIdentityRepairs = new WeakMap<object, StorageRootIdentityRepairRecord>();
export type StorageRootAuthorityErrorCode =
| 'invalid_root'
| 'invalid_root_kind'
| 'root_not_found'
| 'root_unmarked'
| 'invalid_marker'
| 'root_kind_mismatch'
| 'root_identity_collision'
| 'root_identity_changed'
| 'invalid_repair'
| 'invalid_capability'
| 'invalid_lease'
| 'invalid_owner'
| 'invalid_lock_artifact'
| 'insecure_control_directory'
| 'root_io_failed'
| 'control_io_failed'
| 'lock_failed';
export class StorageRootAuthorityError extends Error {
constructor(
readonly code: StorageRootAuthorityErrorCode,
message: string,
options?: ErrorOptions,
) {
super(message, options);
this.name = 'StorageRootAuthorityError';
}
}
function assertStorageRootKind(kind: unknown): asserts kind is StorageRootKind {
if (kind !== 'interactive' && kind !== 'headless') {
throw new StorageRootAuthorityError(
'invalid_root_kind',
`Unsupported storage root kind: ${String(kind)}`,
);
}
}
export async function resolveStorageRoot<K extends StorageRootKind>(
input: ResolveStorageRootInput<K>,
): Promise<StorageRootCapability<K>> {
assertStorageRootKind(input.kind);
return withAuthorityFailure('root_io_failed', 'Unable to resolve the storage root', () =>
resolveStorageRootUnchecked(input),
);
}
export async function discoverMarkedStorageRoot(
input: DiscoverStorageRootInput,
): Promise<DiscoveredStorageRootCapability> {
return withAuthorityFailure('root_io_failed', 'Unable to discover the storage root', async () => {
const { canonicalPath, rootStat } = await resolveExistingRootPath(input.path);
const identity = { dev: rootStat.dev, ino: rootStat.ino };
const marker = await confirmRootSnapshot({
root: canonicalPath,
identity,
readMarker: () => readRootMarker(canonicalPath),
markerMismatchCode: 'root_identity_collision',
markerMismatchMessage: `Storage root marker belongs to a different directory: ${canonicalPath}`,
});
return marker.kind === 'interactive'
? createCapability('interactive', canonicalPath, marker.rootId, identity)
: createCapability('headless', canonicalPath, marker.rootId, identity);
});
}
async function resolveStorageRootUnchecked<K extends StorageRootKind>(
input: ResolveStorageRootInput<K>,
): Promise<StorageRootCapability<K>> {
const requestedPath = resolve(input.path);
await ensureRootDirectory(requestedPath);
const canonicalPath = canonicalizePath(await realpath(requestedPath));
const rootStat = await stat(canonicalPath, { bigint: true });
if (!rootStat.isDirectory()) {
throw new StorageRootAuthorityError(
'invalid_root',
`Storage root is not a directory: ${canonicalPath}`,
);
}
const identity = { dev: rootStat.dev, ino: rootStat.ino };
const marker = await confirmRootSnapshot({
root: canonicalPath,
identity,
readMarker: () => ensureRootMarker(canonicalPath, input.kind, identity),
markerMismatchCode: 'root_identity_collision',
markerMismatchMessage: `Storage root marker belongs to a different directory: ${canonicalPath}`,
});
return createCapability(input.kind, canonicalPath, marker.rootId, identity);
}
export async function resolveExistingStorageRoot<K extends StorageRootKind>(
input: ResolveExistingStorageRootInput<K>,
): Promise<StorageRootCapability<K>> {
assertStorageRootKind(input.kind);
return withAuthorityFailure(
'root_io_failed',
'Unable to resolve the existing storage root',
async () => {
const { canonicalPath, rootStat } = await resolveExistingRootPath(input.path);
const identity = { dev: rootStat.dev, ino: rootStat.ino };
const marker = await confirmRootSnapshot({
root: canonicalPath,
identity,
readMarker: () => readAndValidateRootMarker(canonicalPath, input.kind),
expectedRootId: input.expectedRootId,
markerMismatchCode: 'root_identity_changed',
markerMismatchMessage: `Storage root identity does not match the expected root: ${canonicalPath}`,
});
return createCapability(input.kind, canonicalPath, marker.rootId, identity);
},
);
}
/**
* Explicit import boundary for a storage root copied through an archive.
* The durable rootId stays authoritative while the host-local dev/ino binding
* is atomically adopted for the extracted directory.
*/
export async function adoptStorageRootOnImport<K extends StorageRootKind>(
input: AdoptStorageRootOnImportInput<K>,
): Promise<StorageRootCapability<K>> {
assertStorageRootKind(input.kind);
return withAuthorityFailure(
'root_io_failed',
'Unable to adopt the imported storage root',
async () => {
const { canonicalPath, rootStat } = await resolveExistingRootPath(input.path);
const identity = { dev: rootStat.dev, ino: rootStat.ino };
let marker = await readAndValidateRootMarker(canonicalPath, input.kind);
if (marker.rootId !== input.expectedRootId) {
throw new StorageRootAuthorityError(
'root_identity_collision',
`Imported storage root does not match the expected root: ${canonicalPath}`,
);
}
await assertRootPathIdentity(
canonicalPath,
identity,
`Storage root identity changed while adopting an import: ${canonicalPath}`,
);
if (!markerMatchesIdentity(marker, identity)) {
marker = await replaceRootMarkerIdentity(canonicalPath, identity, marker);
}
await confirmRootSnapshot({
root: canonicalPath,
identity,
readMarker: () => readAndValidateRootMarker(canonicalPath, input.kind),
expectedRootId: input.expectedRootId,
markerMismatchCode: 'root_identity_changed',
markerMismatchMessage: `Imported storage root identity changed: ${canonicalPath}`,
});
return createCapability(input.kind, canonicalPath, marker.rootId, identity);
},
);
}
export async function prepareStorageRootIdentityRepair<K extends StorageRootKind>(
input: ResolveStorageRootInput<K>,
): Promise<StorageRootIdentityRepairCandidate<K> | undefined> {
assertStorageRootKind(input.kind);
return withAuthorityFailure(
'root_io_failed',
'Unable to prepare the storage root identity repair',
async () => {
const { canonicalPath, rootStat } = await resolveExistingRootPath(input.path);
const identity = { dev: rootStat.dev, ino: rootStat.ino };
const identityChangedMessage = `Storage root identity changed while preparing its repair: ${canonicalPath}`;
await assertRootPathIdentity(canonicalPath, identity, identityChangedMessage);
let marker: RootMarker;
try {
marker = await readAndValidateRootMarker(canonicalPath, input.kind);
} catch (error) {
await assertRootPathIdentity(canonicalPath, identity, identityChangedMessage);
throw error;
}
await assertRootPathIdentity(canonicalPath, identity, identityChangedMessage);
if (markerMatchesIdentity(marker, identity)) return undefined;
const record: StorageRootIdentityRepairRecord<K> = {
kind: input.kind,
canonicalPath,
rootId: marker.rootId,
identity,
marker,
};
const candidate = Object.freeze({
kind: record.kind,
canonicalPath: record.canonicalPath,
rootId: record.rootId,
}) as StorageRootIdentityRepairCandidate<K>;
storageRootIdentityRepairs.set(candidate, record);
return candidate;
},
);
}
/**
* Explicit recovery boundary for a root whose host-local filesystem identity
* is stale. Callers must obtain user intent for this exact candidate first.
*/
export async function repairStorageRootIdentity<K extends StorageRootKind>(
candidate: StorageRootIdentityRepairCandidate<K>,
): Promise<StorageRootCapability<K>> {
const record = storageRootIdentityRepairs.get(candidate) as
| StorageRootIdentityRepairRecord<K>
| undefined;
if (!record) {
throw new StorageRootAuthorityError(
'invalid_repair',
'Expected a prepared storage root identity repair',
);
}
storageRootIdentityRepairs.delete(candidate);
return withAuthorityFailure(
'root_io_failed',
'Unable to repair the storage root identity',
async () => {
const identityChangedMessage = `Storage root identity changed while repairing its marker: ${record.canonicalPath}`;
await assertRootPathIdentity(record.canonicalPath, record.identity, identityChangedMessage);
const marker = await readAndValidateRootMarker(record.canonicalPath, record.kind);
await assertRootPathIdentity(record.canonicalPath, record.identity, identityChangedMessage);
if (!rootMarkersEqual(marker, record.marker)) {
throw new StorageRootAuthorityError(
'root_identity_changed',
`Storage root marker changed while awaiting repair: ${record.canonicalPath}`,
);
}
const repaired = await replaceRootMarkerIdentity(
record.canonicalPath,
record.identity,
record.marker,
);
await confirmRootSnapshot({
root: record.canonicalPath,
identity: record.identity,
readMarker: () => readAndValidateRootMarker(record.canonicalPath, record.kind),
expectedRootId: record.rootId,
markerMismatchCode: 'root_identity_changed',
markerMismatchMessage: `Repaired storage root identity changed: ${record.canonicalPath}`,
});
return createCapability(record.kind, record.canonicalPath, repaired.rootId, record.identity);
},
);
}
async function resolveExistingRootPath(path: string): Promise<{
canonicalPath: string;
rootStat: BigIntStats;
}> {
let canonicalPath: string;
try {
canonicalPath = canonicalizePath(await realpath(resolve(path)));
} catch (error) {
if (isMissingPathError(error)) {
throw new StorageRootAuthorityError(
'root_not_found',
`Storage root does not exist: ${resolve(path)}`,
);
}
throw error;
}
let rootStat: BigIntStats;
try {
rootStat = await stat(canonicalPath, { bigint: true });
} catch (error) {
if (isMissingPathError(error)) {
throw new StorageRootAuthorityError(
'root_not_found',
`Storage root does not exist: ${resolve(path)}`,
);
}
throw error;
}
if (!rootStat.isDirectory()) {
throw new StorageRootAuthorityError(
'invalid_root',
`Storage root is not a directory: ${canonicalPath}`,
);
}
return { canonicalPath, rootStat };
}
function createCapability<K extends StorageRootKind>(
kind: K,
canonicalPath: string,
rootId: string,
identity: RootIdentity,
): StorageRootCapability<K> {
const record: CapabilityRecord<K> = {
kind,
canonicalPath,
rootId,
identity,
};
const capability = Object.freeze({
kind: record.kind,
canonicalPath: record.canonicalPath,
rootId: record.rootId,
}) as StorageRootCapability<K>;
capabilities.set(capability, record);
return capability;
}
async function ensureRootDirectory(path: string): Promise<void> {
try {
await mkdir(path, { recursive: true, mode: 0o700 });
} catch (error) {
const existing = await statRootIfPresent(path);
if (existing && !existing.isDirectory()) {
throw new StorageRootAuthorityError(
'invalid_root',
`Storage root is not a directory: ${path}`,
);
}
throw error;
}
}
export function resolveRootControlNamespace(): string {
try {
const accountHome = userInfo().homedir;
if (!isAbsolute(accountHome)) {
throw new Error('OS account home must be an absolute path');
}
if (process.platform === 'darwin') {
return join(accountHome, 'Library', 'Caches', 'Maka', 'runtime-hosts');
}
if (process.platform === 'win32') {
return join(accountHome, 'AppData', 'Local', 'Maka', 'runtime-hosts');
}
return join(accountHome, '.cache', 'maka', 'runtime-hosts');
} catch (error) {
throw normalizeAuthorityFailure(
error,
'control_io_failed',
'Unable to resolve the Runtime Host control namespace',
);
}
}
export async function tryAcquireInteractiveRootOwner(
capability: StorageRootCapability<'interactive'>,
): Promise<InteractiveRootOwner | undefined> {
return withAuthorityFailure(
'lock_failed',
'Unable to acquire the interactive storage root owner lock',
() => acquireInteractiveRootLock(capability, 'write'),
);
}
export async function prepareStorageRootControlDirectory(
capability: StorageRootCapability,
): Promise<{ controlRoot: string; controlDirectory: string }> {
return withAuthorityFailure(
'control_io_failed',
'Unable to prepare the Runtime Host control directory',
async () => {
const record = requireCapability(capability, capability.kind);
return prepareStorageRootControlDirectoryForRecord(record);
},
);
}
export async function resolveExistingStorageRootControlDirectory(
capability: StorageRootCapability,
): Promise<{ controlRoot: string; controlDirectory: string }> {
return withAuthorityFailure(
'control_io_failed',
'Unable to validate the existing Runtime Host control directory',
async () => {
const record = requireCapability(capability, capability.kind);
await assertRootIdentity(record);
const controlRoot = resolve(resolveRootControlNamespace());
const controlDirectory = join(controlRoot, record.rootId);
await assertPrivateDirectory(controlRoot);
await assertPrivateDirectory(controlDirectory);
await assertRootIdentity(record);
return { controlRoot, controlDirectory };
},
);
}
export async function prepareArtifactWriterBootstrapAuthority(
path: string,
): Promise<ArtifactWriterBootstrapAuthority> {
return withAuthorityFailure(
'control_io_failed',
'Unable to prepare the Artifact writer bootstrap authority',
async () => {
const { canonicalPath, rootStat } = await resolveExistingRootPath(path);
const identity = { dev: rootStat.dev, ino: rootStat.ino };
const identityChangedMessage = `Storage root identity changed while preparing its Artifact writer bootstrap lock: ${canonicalPath}`;
await assertRootPathIdentity(canonicalPath, identity, identityChangedMessage);
const controlRoot = await preparePrivateControlRoot();
const lockPath = await prepareArtifactWriterBootstrapLockPathForIdentity(
controlRoot,
identity,
);
await assertRootPathIdentity(canonicalPath, identity, identityChangedMessage);
return Object.freeze({
lockPath,
canonicalPath,
assertCurrentRoot: () =>
assertRootPathIdentity(canonicalPath, identity, identityChangedMessage),
[artifactWriterBootstrapAuthorityBrand]: true as const,
});
},
);
}
export async function prepareArtifactWriterLockAuthorityForLease<K extends StorageRootKind>(
lease: StorageRootLease<K, 'write'>,
expectedKind: K,
): Promise<ArtifactWriterLockAuthority> {
return withAuthorityFailure(
'control_io_failed',
'Unable to prepare the Artifact writer lock control path',
async () => {
const record = requireLease(lease, expectedKind, 'write');
const authority = await prepareArtifactWriterLockAuthorityForRecord(record);
requireLease(lease, expectedKind, 'write');
return authority;
},
);
}
export async function prepareArtifactWriterLockAuthorityForMarkedRoot(
path: string,
): Promise<ArtifactWriterLockAuthority | undefined> {
let capability: DiscoveredStorageRootCapability;
try {
capability = await discoverMarkedStorageRoot({ path });
} catch (error) {
if (error instanceof StorageRootAuthorityError && error.code === 'root_unmarked') {
return undefined;
}
throw error;
}
const record = requireCapability(capability, capability.kind);
return withAuthorityFailure(
'control_io_failed',
'Unable to prepare the Artifact writer lock control path',
() => prepareArtifactWriterLockAuthorityForRecord(record),
);
}
export async function tryAcquireInteractiveRootReader(
capability: StorageRootCapability<'interactive'>,
): Promise<InteractiveRootReader | undefined> {
return withAuthorityFailure(
'lock_failed',
'Unable to acquire the interactive storage root reader lock',
() => acquireInteractiveRootLock(capability, 'read'),
);
}
export function createHeadlessRootLease<A extends StorageRootAccess>(
capability: StorageRootCapability<'headless'>,
access: A,
): StorageRootLease<'headless', A> {
const record = requireCapability(capability, 'headless');
return createLease(record, access, () => true);
}
export async function assertStorageRootLease<
K extends StorageRootKind,
A extends StorageRootAccess,
>(lease: StorageRootLease<K, A>, expectedKind: K, expectedAccess: A): Promise<void> {
const record = requireLease(lease, expectedKind, expectedAccess);
await assertRootIdentity(record);
requireLease(lease, expectedKind, expectedAccess);
}
export function createStorageRootLeaseIdentityGuard<
K extends StorageRootKind,
A extends StorageRootAccess,
>(lease: StorageRootLease<K, A>, expectedKind: K, expectedAccess: A): () => Promise<void> {
const record = requireLease(lease, expectedKind, expectedAccess);
return () => assertRootIdentity(record);
}
export async function runWithStorageRootLease<
K extends StorageRootKind,
A extends StorageRootAccess,
T,
>(
lease: StorageRootLease<K, A>,
expectedKind: K,
expectedAccess: A,
operation: (canonicalPath: string) => Promise<T>,
): Promise<T> {
const record = requireLease(lease, expectedKind, expectedAccess);
const finishOperation = record.beginOperation();
try {
await assertRootIdentity(record);
return await operation(record.canonicalPath);
} finally {
finishOperation();
}
}
export async function assertStorageRootCapability<K extends StorageRootKind>(
capability: StorageRootCapability<K>,
expectedKind: K,
): Promise<void> {
const record = requireCapability(capability, expectedKind);
await assertRootIdentity(record);
}
export async function assertInteractiveRootOwner(owner: InteractiveRootOwner): Promise<void> {
const authenticOwner = authenticateInteractiveRootOwner(owner);
const capabilityRecord = requireCapability(authenticOwner.capability, 'interactive');
requireLease(authenticOwner.lease, 'interactive', 'write');
await assertRootIdentity(capabilityRecord);
requireLease(authenticOwner.lease, 'interactive', 'write');
}
export function authenticateInteractiveRootOwner(
owner: InteractiveRootOwner,
): InteractiveRootOwner {
if (interactiveRootLocks.get(owner)?.access !== 'write') {
throw new StorageRootAuthorityError(
'invalid_owner',
'Expected an authentic interactive storage root owner',
);
}
return owner;
}
function acquireInteractiveRootLock(
capability: StorageRootCapability<'interactive'>,
access: 'write',
): Promise<InteractiveRootOwner | undefined>;
function acquireInteractiveRootLock(
capability: StorageRootCapability<'interactive'>,
access: 'read',
): Promise<InteractiveRootReader | undefined>;
async function acquireInteractiveRootLock(
capability: StorageRootCapability<'interactive'>,
access: StorageRootAccess,
): Promise<InteractiveRootOwner | InteractiveRootReader | undefined> {
const capabilityRecord = requireCapability(capability, 'interactive');
const { controlDirectory } = await prepareStorageRootControlDirectory(capability);
const lockPath = join(controlDirectory, 'owner.lock');
const existingLock = await lstatPathIfPresent(lockPath);
if (existingLock && !existingLock.isFile()) {
throw invalidLockArtifact(lockPath);
}
const handle = await open(lockPath, 'a+', 0o600);
try {
await assertStableLockArtifact(handle, lockPath);
await handle.chmod(0o600);
} catch (error) {
await handle.close();
throw error;
}
let granted = false;
try {
granted = tryLock(handle.fd, { shared: access === 'read' });
} catch (error) {
await handle.close();
throw error;
}
if (!granted) {
await handle.close();
return undefined;
}
try {
await assertStableLockArtifact(handle, lockPath);
await assertRootIdentity(capabilityRecord);
} catch (error) {
releaseLock(handle);
await handle.close();
throw error;
}
let active = true;
let activeOperations = 0;
const operationDrainWaiters = new Set<() => void>();
let closePromise: Promise<void> | undefined;
const beginOperation = () => {
if (!active) throw invalidLease(capabilityRecord.kind, access);
activeOperations += 1;
let finished = false;
return () => {
if (finished) return;
finished = true;
activeOperations -= 1;
if (activeOperations !== 0) return;
for (const resolve of operationDrainWaiters) resolve();
operationDrainWaiters.clear();
};
};
const waitForOperations = () =>
activeOperations === 0
? Promise.resolve()
: new Promise<void>((resolve) => operationDrainWaiters.add(resolve));
const close = () => {
if (closePromise) return closePromise;
active = false;
closePromise = withAuthorityFailure(
'lock_failed',
'Unable to close the interactive storage root lock',
async () => {
await waitForOperations();
releaseLock(handle);
await handle.close();
},
);
return closePromise;
};
return createInteractiveRootLock(
capability,
capabilityRecord,
access,
controlDirectory,
lockPath,
() => active,
beginOperation,
close,
);
}
function createInteractiveRootLock(
capability: StorageRootCapability<'interactive'>,
capabilityRecord: CapabilityRecord<'interactive'>,
access: StorageRootAccess,
controlDirectory: string,
lockPath: string,
isActive: () => boolean,
beginOperation: () => () => void,
close: () => Promise<void>,
): InteractiveRootOwner | InteractiveRootReader {
const lock = Object.freeze({
capability,
lease: createLease(capabilityRecord, access, isActive, beginOperation),
controlDirectory,
lockPath,
get closed() {
return !isActive();
},
close,
}) as InteractiveRootOwner | InteractiveRootReader;
interactiveRootLocks.set(lock, { access });
return lock;
}
function createLease<K extends StorageRootKind, A extends StorageRootAccess>(
capability: CapabilityRecord<K>,
access: A,
isActive: () => boolean,
beginOperation: () => () => void = () => {
if (!isActive()) throw invalidLease(capability.kind, access);
return () => {};
},
): StorageRootLease<K, A> {
const lease = Object.freeze({
kind: capability.kind,
access,
canonicalPath: capability.canonicalPath,
rootId: capability.rootId,
}) as StorageRootLease<K, A>;
leases.set(lease, { ...capability, access, isActive, beginOperation });
return lease;
}
function requireCapability<K extends StorageRootKind>(
capability: StorageRootCapability<K>,
expectedKind: K,
): CapabilityRecord<K> {
const record = capabilities.get(capability);
if (!record || record.kind !== expectedKind) {
throw new StorageRootAuthorityError(
'invalid_capability',
`Expected a ${expectedKind} storage root capability`,
);
}
return record as CapabilityRecord<K>;
}
function requireLease<K extends StorageRootKind, A extends StorageRootAccess>(
lease: StorageRootLease<K, A>,
expectedKind: K,
expectedAccess: A,
): LeaseRecord<K, A> {
const record = leases.get(lease);
if (
!record ||
record.kind !== expectedKind ||
record.access !== expectedAccess ||
!record.isActive()
) {
throw invalidLease(expectedKind, expectedAccess);
}
return record as LeaseRecord<K, A>;
}
function invalidLease(kind: StorageRootKind, access: StorageRootAccess): StorageRootAuthorityError {
return new StorageRootAuthorityError(
'invalid_lease',
`Expected an active ${kind} ${access} storage root lease`,
);
}
async function prepareStorageRootControlDirectoryForRecord(
record: CapabilityRecord,
): Promise<{ controlRoot: string; controlDirectory: string }> {
await assertRootIdentity(record);
const controlRoot = await preparePrivateControlRoot();
const controlDirectory = join(controlRoot, record.rootId);
await ensurePrivateDirectory(controlDirectory);
await assertRootIdentity(record);
return { controlRoot, controlDirectory };
}
async function prepareArtifactWriterLockAuthorityForRecord(
record: CapabilityRecord,
): Promise<ArtifactWriterLockAuthority> {
const { controlRoot, controlDirectory } =
await prepareStorageRootControlDirectoryForRecord(record);
const bootstrapLockPath = await prepareArtifactWriterBootstrapLockPathForIdentity(
controlRoot,
record.identity,
);
await assertRootIdentity(record);
return createArtifactWriterLockAuthority(record, bootstrapLockPath, controlDirectory);
}
function createArtifactWriterLockAuthority(
record: CapabilityRecord,
bootstrapLockPath: string,
controlDirectory: string,
): ArtifactWriterLockAuthority {
return Object.freeze({
bootstrapLockPath,
controlDirectory,
assertCurrentRoot: () => assertRootIdentity(record),
[artifactWriterLockAuthorityBrand]: true as const,
});
}
async function preparePrivateControlRoot(): Promise<string> {
const controlRoot = resolve(resolveRootControlNamespace());
await ensurePrivateDirectory(controlRoot);
return controlRoot;
}
async function prepareArtifactWriterBootstrapLockPathForIdentity(
controlRoot: string,
identity: RootIdentity,
): Promise<string> {
const directory = join(controlRoot, ARTIFACT_WRITER_BOOTSTRAP_DIRECTORY);
await ensurePrivateDirectory(directory);
const identityHash = createHash('sha256')
.update(`${identity.dev.toString()}:${identity.ino.toString()}`)
.digest('hex');
return join(directory, `${identityHash}.lock`);
}
async function assertRootIdentity(record: CapabilityRecord): Promise<void> {
await withAuthorityFailure(
'root_io_failed',
`Unable to validate storage root identity: ${record.canonicalPath}`,
async () => {
await confirmRootSnapshot({
root: record.canonicalPath,
identity: record.identity,
readMarker: () => readAndValidateRootMarker(record.canonicalPath, record.kind),
expectedRootId: record.rootId,
markerMismatchCode: 'root_identity_changed',
markerMismatchMessage: `Storage root marker identity changed: ${record.canonicalPath}`,
});
},
);
}
interface ConfirmRootSnapshotInput {
root: string;
identity: RootIdentity;
readMarker(): Promise<RootMarker>;
expectedRootId?: string;
markerMismatchCode: 'root_identity_collision' | 'root_identity_changed';
markerMismatchMessage: string;
}
async function confirmRootSnapshot(input: ConfirmRootSnapshotInput): Promise<RootMarker> {
const identityChangedMessage = `Storage root identity changed while validating its marker: ${input.root}`;
await assertRootPathIdentity(input.root, input.identity, identityChangedMessage);
let marker: RootMarker;
try {
marker = await input.readMarker();
} catch (error) {
await assertRootPathIdentity(input.root, input.identity, identityChangedMessage);
throw error;
}
await assertRootPathIdentity(input.root, input.identity, identityChangedMessage);
if (
(input.expectedRootId !== undefined && marker.rootId !== input.expectedRootId) ||
!markerMatchesIdentity(marker, input.identity)
) {
throw new StorageRootAuthorityError(input.markerMismatchCode, input.markerMismatchMessage);
}
return marker;
}
async function ensureRootMarker(
root: string,
kind: StorageRootKind,
identity: RootIdentity,
): Promise<RootMarker> {
const markerPath = join(root, STORAGE_ROOT_MARKER_FILE);
try {
await lstat(markerPath);
return await readAndValidateRootMarker(root, kind);
} catch (error) {
if (!isNodeError(error, 'ENOENT')) throw error;
}
const marker: RootMarker = {
schemaVersion: STORAGE_ROOT_MARKER_SCHEMA_VERSION,
kind,
rootId: randomBytes(32).toString('hex'),
rootIdentity: {
dev: identity.dev.toString(),
ino: identity.ino.toString(),
},
};
await publishMarkerFile({
root,
markerFile: STORAGE_ROOT_MARKER_FILE,
contents: `${JSON.stringify(marker)}\n`,
maxBytes: MAX_STORAGE_ROOT_MARKER_BYTES,
publication: 'create',
beforePublish: () =>
assertRootPathIdentity(
root,
identity,
`Storage root identity changed before publishing its marker: ${root}`,
),
invalidFile: () =>
new StorageRootAuthorityError(
'invalid_marker',
`Storage root marker candidate exceeds the size limit: ${markerPath}`,
),
});
return readAndValidateRootMarker(root, kind);
}
async function replaceRootMarkerIdentity(
root: string,
identity: RootIdentity,
sourceMarker: RootMarker,
): Promise<RootMarker> {
return withExclusiveRootMarker(root, identity, sourceMarker.kind, async (current) => {
assertRootMarkerUnchanged(root, current, sourceMarker);
const marker: RootMarker = {
...current,
rootIdentity: {
dev: identity.dev.toString(),
ino: identity.ino.toString(),
},
};
const markerPath = join(root, STORAGE_ROOT_MARKER_FILE);
await publishMarkerFile({
root,
markerFile: STORAGE_ROOT_MARKER_FILE,
contents: `${JSON.stringify(marker)}\n`,
maxBytes: MAX_STORAGE_ROOT_MARKER_BYTES,
publication: 'replace',
beforePublish: async () => {
await assertRootPathIdentity(
root,
identity,
`Storage root identity changed before updating its marker: ${root}`,
);
assertRootMarkerUnchanged(
root,
await readAndValidateRootMarker(root, sourceMarker.kind),
sourceMarker,
);
},
invalidFile: () =>
new StorageRootAuthorityError(
'invalid_marker',
`Storage root marker candidate exceeds the size limit: ${markerPath}`,
),
});
await assertRootPathIdentity(
root,
identity,
`Storage root identity changed after updating its marker: ${root}`,
);
const adopted = await readAndValidateRootMarker(root, sourceMarker.kind);
if (adopted.rootId !== sourceMarker.rootId || !markerMatchesIdentity(adopted, identity)) {
throw new StorageRootAuthorityError(
'root_identity_changed',
`Storage root marker changed while updating its identity: ${root}`,
);
}
return adopted;
});
}
async function withExclusiveRootMarker<T>(
root: string,
identity: RootIdentity,
expectedKind: StorageRootKind,
operation: (marker: RootMarker) => Promise<T>,
): Promise<T> {
const controlRoot = await preparePrivateControlRoot();
const lockPath = await prepareArtifactWriterBootstrapLockPathForIdentity(controlRoot, identity);
return withArtifactWriterBootstrapLock(lockPath, async () => {
await assertRootPathIdentity(
root,
identity,
`Storage root identity changed while acquiring its marker publication lock: ${root}`,
);
const marker = await readAndValidateRootMarker(root, expectedKind);
return operation(marker);
});
}
function assertRootMarkerUnchanged(root: string, current: RootMarker, expected: RootMarker): void {
if (!rootMarkersEqual(current, expected)) {
throw new StorageRootAuthorityError(
'root_identity_collision',
`Storage root marker changed while updating its identity: ${root}`,
);
}
}
function invalidRootMarker(markerPath: string, cause?: unknown): StorageRootAuthorityError {
return new StorageRootAuthorityError(
'invalid_marker',
`Invalid storage root marker at ${markerPath}${cause instanceof Error ? `: ${cause.message}` : ''}`,
cause === undefined ? undefined : { cause },
);
}
async function assertRootPathIdentity(
root: string,
identity: RootIdentity,
message: string,
): Promise<void> {
const rootStat = await statRootIfPresent(root);
if (!rootStat?.isDirectory() || rootStat.dev !== identity.dev || rootStat.ino !== identity.ino) {
throw new StorageRootAuthorityError('root_identity_changed', message);
}
}
async function readAndValidateRootMarker(
root: string,
expectedKind: StorageRootKind,
): Promise<RootMarker> {
const marker = await readRootMarker(root);
if (marker.kind !== expectedKind) {
throw new StorageRootAuthorityError(
'root_kind_mismatch',
`Storage root ${root} is ${marker.kind}, not ${expectedKind}`,
);
}
return marker;
}
async function readRootMarker(root: string): Promise<RootMarker> {
const markerPath = join(root, STORAGE_ROOT_MARKER_FILE);
let contents: string;
try {
contents = await readBoundedMarkerFile({
path: markerPath,
maxBytes: MAX_STORAGE_ROOT_MARKER_BYTES,
invalidFile: () =>
new StorageRootAuthorityError(
'invalid_marker',
`Storage root marker must be one bounded regular file: ${markerPath}`,
),
});
} catch (error) {
if (error instanceof StorageRootAuthorityError) throw error;
if (isNodeError(error, 'ENOENT')) {
throw new StorageRootAuthorityError('root_unmarked', `Storage root is not marked: ${root}`);
}
if (isInvalidMarkerPathError(error)) throw invalidRootMarker(markerPath, error);
throw error;
}
return parseRootMarker(contents, markerPath);
}
function parseRootMarker(contents: string, markerPath: string): RootMarker {
let marker: unknown;
try {
marker = JSON.parse(contents);
} catch (error) {
throw invalidRootMarker(markerPath, error);
}
if (!isRootMarker(marker)) {
throw invalidRootMarker(markerPath);
}
return marker;
}
function isRootMarker(value: unknown): value is RootMarker {
if (!value || typeof value !== 'object') return false;
const marker = value as Record<string, unknown>;
return (
marker.schemaVersion === STORAGE_ROOT_MARKER_SCHEMA_VERSION &&
(marker.kind === 'interactive' || marker.kind === 'headless') &&
typeof marker.rootId === 'string' &&
/^[a-f0-9]{64}$/.test(marker.rootId) &&
isMarkerRootIdentity(marker.rootIdentity)
);
}
function isMarkerRootIdentity(value: unknown): value is RootMarker['rootIdentity'] {
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
const identity = value as Record<string, unknown>;
return (
typeof identity.dev === 'string' &&
/^\d+$/.test(identity.dev) &&
typeof identity.ino === 'string' &&
/^\d+$/.test(identity.ino)
);
}
/**
* Whether the marker describes the directory that was just stat'd.
*
* Both fields, and no classification of how a mismatch came about. A moved
* `dev` with a matching `ino` reads like a remount — the kernel hands out a
* device number per mount, so an unmoved directory reports a new one after its
* volume is mounted again — but a workspace restored onto another volume
* presents the same pair, because inode numbers are unique only within one
* mounted filesystem. Naming that case a remount would let a second, unrelated
* directory inherit the original's rootId without anyone confirming it, and
* nothing in the marker can tell the two apart. It stays a question for the
* person; `adoptStorageRootOnImport` is the sanctioned way in for a copy,
* where the caller states the rootId it expects.
*/
function markerMatchesIdentity(marker: RootMarker, identity: RootIdentity): boolean {
return (
marker.rootIdentity.dev === identity.dev.toString() &&
marker.rootIdentity.ino === identity.ino.toString()
);
}
function rootMarkersEqual(left: RootMarker, right: RootMarker): boolean {
return (
left.schemaVersion === right.schemaVersion &&
left.kind === right.kind &&
left.rootId === right.rootId &&
left.rootIdentity.dev === right.rootIdentity.dev &&
left.rootIdentity.ino === right.rootIdentity.ino
);
}
async function ensurePrivateDirectory(path: string): Promise<void> {
await mkdir(path, { recursive: true, mode: 0o700 });
let directoryStat = await lstat(path);
if (!directoryStat.isDirectory()) {
throw new StorageRootAuthorityError(
'insecure_control_directory',
`Runtime Host control path is not a directory: ${path}`,
);
}
if (process.platform === 'win32') return;
if (typeof process.getuid === 'function' && directoryStat.uid !== process.getuid()) {
throw new StorageRootAuthorityError(
'insecure_control_directory',
`Runtime Host control path is not owned by the current user: ${path}`,
);
}
await chmod(path, 0o700);
directoryStat = await lstat(path);
if (!directoryStat.isDirectory() || (directoryStat.mode & 0o077) !== 0) {
throw new StorageRootAuthorityError(
'insecure_control_directory',
`Runtime Host control path is not private: ${path}`,
);
}
}
async function assertPrivateDirectory(path: string): Promise<void> {
const directoryStat = await lstat(path);
if (!directoryStat.isDirectory()) {
throw new StorageRootAuthorityError(
'insecure_control_directory',
`Runtime Host control path is not a directory: ${path}`,
);
}
if (process.platform === 'win32') return;
if (typeof process.getuid === 'function' && directoryStat.uid !== process.getuid()) {
throw new StorageRootAuthorityError(
'insecure_control_directory',
`Runtime Host control path is not owned by the current user: ${path}`,
);
}
if ((directoryStat.mode & 0o077) !== 0) {
throw new StorageRootAuthorityError(
'insecure_control_directory',
`Runtime Host control path is not private: ${path}`,
);
}
}
async function assertStableLockArtifact(handle: FileHandle, path: string): Promise<void> {
let stable = false;
try {
const [handleStat, pathStat] = await Promise.all([
handle.stat({ bigint: true }),
lstat(path, { bigint: true }),
]);
stable =
handleStat.isFile() &&
pathStat.isFile() &&
handleStat.dev === pathStat.dev &&
handleStat.ino === pathStat.ino;
} catch (error) {
if (!isMissingPathError(error)) throw error;
}
if (!stable) {
throw invalidLockArtifact(path);
}
}
function invalidLockArtifact(path: string): StorageRootAuthorityError {
return new StorageRootAuthorityError(
'invalid_lock_artifact',
`Storage root lock path is not one stable regular file: ${path}`,
);
}
function releaseLock(handle: FileHandle): void {
try {
unlock(handle.fd);
} catch {
// Closing the OS handle is the authoritative release path.
}
}
function canonicalizePath(path: string): string {
const normalized = normalize(path);
const root = parse(normalized).root;
return normalized === root ? normalized : normalized.replace(/[\\/]+$/, '');
}
function isNodeError(error: unknown, code: string): boolean {
return (
error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === code
);
}
function isMissingPathError(error: unknown): boolean {
return isNodeError(error, 'ENOENT') || isNodeError(error, 'ENOTDIR');
}
function isInvalidMarkerPathError(error: unknown): boolean {
return isMissingPathError(error) || isNodeError(error, 'ELOOP') || isNodeError(error, 'ENXIO');
}
async function statRootIfPresent(path: string): Promise<BigIntStats | undefined> {
try {
return await stat(path, { bigint: true });
} catch (error) {
if (isMissingPathError(error)) return undefined;
throw error;
}
}
async function lstatPathIfPresent(path: string): Promise<BigIntStats | undefined> {
try {
return await lstat(path, { bigint: true });
} catch (error) {
if (isMissingPathError(error)) return undefined;
throw error;
}
}
async function withAuthorityFailure<T>(
code: Extract<
StorageRootAuthorityErrorCode,
'root_io_failed' | 'control_io_failed' | 'lock_failed'
>,
message: string,
operation: () => Promise<T>,
): Promise<T> {
try {
return await operation();
} catch (error) {
throw normalizeAuthorityFailure(error, code, message);
}
}
function normalizeAuthorityFailure(
error: unknown,
code: Extract<
StorageRootAuthorityErrorCode,
'root_io_failed' | 'control_io_failed' | 'lock_failed'
>,
message: string,
): StorageRootAuthorityError {
if (error instanceof StorageRootAuthorityError) return error;
return new StorageRootAuthorityError(code, message, { cause: error });
}