import unixPathResolve from 'unix-path-resolve' import b4a from 'b4a' import { raceWithAbortAndTimeout } from './bare-os-abort.js' import { bareOsIpcLogicalToActualFifoName } from './bare-os-ipc-namespace.js' import { listBareInitdJournalUnits } from './bare-initd-journal.js' import { bareOsVfsAclDeniesDriveOp } from './bare-os-vfs-acl-enforce.js' import { bareOsVfsPathCapabilityDeniesDriveRead } from './bare-os-path-capability.js' import { extractBareOs, identityNames, isPersonalRoute, isVirtualMountPoint, mergeBareOsOnWrite, mergeEntryMetadata, modeAllows, newBareOsForSymlink, parseUidGid, statFromBareOs, synthesizeStat, S_IFDIR, S_IFLNK, S_IFREG } from './vfs-posix-meta.js' import { bareOsKernelMetricInc } from './bare-os-kernel-metrics.js' /** * Policy-oriented path class for VFS routing (system vs personal vs pseudo vs mount vs volatile). * Used by operator tooling and future policy engines; routing itself stays in `createVfs`. * @param {string} p * @returns {'system' | 'personal' | 'pseudo' | 'mount' | 'volatile' | 'snapshot' | 'unknown'} */ export function classifyBareOsVfsPathClass(p) { const path = String(p || '').replace(/\\/g, '/') if (!path.startsWith('/')) return 'unknown' if (path === '/snapshots' || path.startsWith('/snapshots/')) return 'snapshot' if (path.startsWith('/proc/') || path === '/proc') return 'pseudo' if (path.startsWith('/sys/') || path === '/sys') return 'pseudo' if (path.startsWith('/dev/') || path === '/dev') return 'pseudo' if (path.startsWith('/run/') || path === '/run') return 'volatile' if ( path.startsWith('/mnt/') || path.startsWith('/media/') || path.startsWith('/mount/') ) return 'mount' if (path.startsWith('/home/')) return 'personal' if (path === '/root' || path.startsWith('/root/')) return 'personal' if (path.startsWith('/tmp/') || path === '/tmp') return 'volatile' if ( path.startsWith('/boot/') || path.startsWith('/bin/') || path.startsWith('/lib/') || path.startsWith('/etc/') || path === '/etc' || path.startsWith('/usr/') || path.startsWith('/var/') ) return 'system' return 'system' } /** * Declarative policy evaluation over {@link classifyBareOsVfsPathClass} (allow/deny/audit rule stack). * Later rules override earlier allow/deny; **`audit`** matches are recorded but do not change verdict. * @param {string} p * @param {{ id?: string, effect: 'allow' | 'deny' | 'audit', pathClass?: string, prefix?: string }[]} [rules] * @returns {{ path: string, pathClass: string, matched: { id?: string, effect: 'allow' | 'deny' | 'audit' }[], verdict: 'neutral' | 'allow' | 'deny', auditHits: number, metrics: { rulesEvaluated: number, matchCount: number } }} */ export function evaluateBareOsVfsPathPolicy(p, rules = []) { const path = String(p || '').replace(/\\/g, '/') const pathClass = classifyBareOsVfsPathClass(path) /** @type {{ id?: string, effect: 'allow' | 'deny' | 'audit' }[]} */ const matched = [] let verdict = 'neutral' let auditHits = 0 let rulesEvaluated = 0 for (const r of rules) { rulesEvaluated++ if ( !r || (r.effect !== 'allow' && r.effect !== 'deny' && r.effect !== 'audit') ) continue if (r.pathClass && r.pathClass !== pathClass) continue if (r.prefix && !path.startsWith(String(r.prefix))) continue matched.push({ id: r.id, effect: r.effect }) if (r.effect === 'audit') { auditHits++ continue } verdict = r.effect } return { path, pathClass, matched, verdict, auditHits, metrics: { rulesEvaluated, matchCount: matched.length } } } /** * Parse optional **`BARE_OS_VFS_POLICY_RULES_JSON`** env: `{ "rules": [ ... ] }`. * @param {Record | null | undefined} env */ export function parseBareOsVfsPolicyRulesFromEnv(env) { const raw = env && env.BARE_OS_VFS_POLICY_RULES_JSON if (raw == null || raw === '') return [] try { const j = JSON.parse(String(raw)) if (j && typeof j === 'object' && Array.isArray(j.rules)) return j.rules } catch { /* ignore */ } return [] } /** * Batch put helper: uses Hyperdrive `batch()` when present, else sequential `put`. * @param {{ put?: Function, batch?: () => { put: Function, flush?: () => Promise } }} drive * @param {{ path: string, buf: Uint8Array | ArrayBuffer, opts?: Record }[]} puts */ export async function bareOsVfsBatchPut(drive, puts) { if (!drive || typeof drive.put !== 'function') { throw new Error('bareOsVfsBatchPut: drive.put required') } if (!Array.isArray(puts) || !puts.length) return if (typeof drive.batch === 'function') { const b = drive.batch() for (const row of puts) { const p = String(row.path || '').trim() if (!p) continue await b.put(p, row.buf, row.opts) } if (typeof b.flush === 'function') await b.flush() return } for (const row of puts) { const p = String(row.path || '').trim() if (!p) continue await drive.put(p, row.buf, row.opts) } } /** * Collect diff entries when the drive exposes `diff` (signature varies by Hyperdrive version). * @param {{ diff?: (...args: unknown[]) => AsyncIterable | Iterable }} drive * @param {unknown} a * @param {unknown} b * @param {{ maxEntries?: number, diffOpts?: Record }} [opts] */ export async function bareOsHyperdriveDiffCollect(drive, a, b, opts = {}) { if (!drive || typeof drive.diff !== 'function') { return { ok: false, error: 'diff-unavailable' } } const max = opts.maxEntries ?? 5000 /** @type {unknown[]} */ const entries = [] try { const raw = drive.diff(a, b, opts.diffOpts) if (raw && Symbol.asyncIterator in raw) { for await (const ent of /** @type {AsyncIterable} */ (raw)) { entries.push(ent) if (entries.length >= max) break } } else if (raw && Symbol.iterator in raw) { for (const ent of /** @type {Iterable} */ (raw)) { entries.push(ent) if (entries.length >= max) break } } else { return { ok: false, error: 'diff-not-iterable' } } return { ok: true, entries, truncated: entries.length >= max } } catch (e) { return { ok: false, error: (e && e.message) || String(e) } } } /** Empty-directory marker (must match {@link ./git-fs-adapter.js}). */ const DIR_MARKER = '.bareos_empty' /** Capability word 6 `/proc` pseudo files → {@link ./bare-os-proc-replication-operator-surface.js} ids. */ export const BARE_OS_PROC_FILE_TO_ID_REPLICATION_OPERATOR_SURFACE = Object.freeze({ bare_os_udx_extended: 'udx_extended', bare_os_dht_status: 'dht_status', bare_os_replication_backpressure: 'replication_backpressure', bare_os_ipc_backpressure: 'ipc_backpressure', bare_os_delegate_red: 'delegate_red', bare_os_build_attestation_pointer: 'build_attestation_pointer', bare_os_pear_ipc_health: 'pear_ipc_health', bare_os_hypercore_lengths: 'hypercore_lengths', bare_os_slo_hints: 'slo_hints', bare_os_locale: 'locale', bare_os_worker_budget: 'worker_budget', bare_os_sandbox_profile: 'sandbox_profile', bare_os_dns_map_active: 'dns_map_active', bare_os_git_delegate_stats: 'git_delegate_stats', bare_os_replication_operator_panel: 'replication_operator_panel' }) /** Capability word 7 `/proc` pseudo files → {@link ./bare-os-proc-pear-corestore-hrpc.js} ids. */ export const BARE_OS_PROC_FILE_TO_ID_PEAR_CORESTORE_HRPC = Object.freeze({ bare_os_async_hooks_lag: 'async_hooks_lag', bare_os_autopass_session_sketch: 'autopass_session_sketch', bare_os_bare_diagnostics_channel: 'diagnostics_channel', bare_os_bare_net_interfaces: 'bare_net_interfaces', bare_os_bare_thread_pool: 'thread_pool', bare_os_blind_relay_router: 'blind_relay_router', bare_os_compact_encoding_profile: 'compact_encoding_profile', bare_os_corestore_gc_hint: 'corestore_gc_hint', bare_os_git_lfs_pointer_stats: 'git_lfs_pointer_stats', bare_os_hrpc_bridge_health: 'hrpc_bridge_health', bare_os_hyperdb_readonly_index: 'hyperdb_readonly_index', bare_os_pear_build_fingerprint: 'pear_build_fingerprint', bare_os_pear_runtime_channel: 'pear_runtime_channel', bare_os_protomux_channels: 'protomux_channels', bare_os_security_context: 'security_context', bare_os_updater_download_state: 'updater_download_state' }) /** Capability word 8 `/proc` pseudo files → {@link ./bare-os-proc-bare-runtime-proto-mux.js} ids. */ export const BARE_OS_PROC_FILE_TO_ID_BARE_RUNTIME_PROTO_MUX = Object.freeze({ bare_os_bare_kit_bridge: 'bare_kit_bridge', bare_os_brittle_snapshot_ci: 'brittle_snapshot_ci', bare_os_cellery_sidecar_hint: 'cellery_sidecar_hint', bare_os_form_data_delegate_limits: 'form_data_delegate_limits', bare_os_gip_transport_sketch: 'gip_transport_sketch', bare_os_http_dht_proxy_route: 'http_dht_proxy_route', bare_os_hyper_multisig_trust_pointer: 'hyper_multisig_trust_pointer', bare_os_hypercore_signing_status: 'hypercore_signing_status', bare_os_hypermininet_topology: 'hypermininet_topology', bare_os_libmqjs_queue_depth: 'libmqjs_queue_depth', bare_os_oidc_publishing_pointer: 'oidc_publishing_pointer', bare_os_pear_sidecar_bundle_index: 'pear_sidecar_bundle_index', bare_os_protomux_rpc_pool_health: 'protomux_rpc_pool_health', bare_os_react_native_bare_kit: 'react_native_bare_kit', bare_os_rocksdb_pointer: 'rocksdb_pointer', bare_os_safe_sodium_buffer_policy: 'safe_sodium_buffer_policy', bare_os_sandbox_worker_queue: 'sandbox_worker_queue', bare_os_structured_clone_profile: 'structured_clone_profile' }) /** Capability word 9 `/proc` pseudo files → {@link ./bare-os-proc-bare-module-crypto-staging.js} ids. */ export const BARE_OS_PROC_FILE_TO_ID_BARE_MODULE_CRYPTO_STAGING = Object.freeze({ bare_os_pear_stage_pointer: 'pear_stage_pointer', bare_os_pear_updater_state: 'pear_updater_state', bare_os_pear_appling_manifest: 'pear_appling_manifest', bare_os_drive_resolve_cache: 'drive_resolve_cache', bare_os_bare_module_resolution: 'bare_module_resolution', bare_os_bare_crypto_policy: 'bare_crypto_policy', bare_os_bare_ipc_bridge: 'bare_ipc_bridge', bare_os_bare_vm_sandbox_sketch: 'bare_vm_sandbox_sketch', bare_os_bare_daemon_hooks: 'bare_daemon_hooks', bare_os_bare_storage_quota: 'bare_storage_quota', bare_os_bare_worker_pool: 'bare_worker_pool', bare_os_pear_wakeups_schedule: 'pear_wakeups_schedule', bare_os_pear_drop_events: 'pear_drop_events', bare_os_pear_radio_state: 'pear_radio_state', bare_os_hypercore_repair_hint: 'hypercore_repair_hint', bare_os_hyperdrive_sparse_index: 'hyperdrive_sparse_index', bare_os_protomux_channel_alias_v2: 'protomux_channel_alias_v2', bare_os_structured_clone_budget_v2: 'structured_clone_budget_v2', bare_os_form_data_delegate_limits_v2: 'form_data_delegate_limits_v2' }) /** Capability word 10 `/proc` pseudo files → {@link ./bare-os-proc-pear-inspect-logger-tls.js} ids. */ export const BARE_OS_PROC_FILE_TO_ID_PEAR_INSPECT_LOGGER_TLS = Object.freeze({ bare_os_activity_queue_depth: 'activity_queue_depth', bare_os_autobase_writer_hint: 'autobase_writer_hint', bare_os_bare_boot_phase_map: 'bare_boot_phase_map', bare_os_bare_inspect_policy: 'bare_inspect_policy', bare_os_bare_logger_policy: 'bare_logger_policy', bare_os_bare_performance_counters: 'bare_performance_counters', bare_os_bare_rpc_registry_sketch: 'bare_rpc_registry_sketch', bare_os_bare_signals_mask: 'bare_signals_mask', bare_os_bare_stream_backpressure: 'bare_stream_backpressure', bare_os_bare_timers_budget: 'bare_timers_budget', bare_os_bare_tls_session_hint: 'bare_tls_session_hint', bare_os_bare_ws_gateway_sketch: 'bare_ws_gateway_sketch', bare_os_blind_pairing_sketch: 'blind_pairing_sketch', bare_os_broadcast_encryption_hint: 'broadcast_encryption_hint', bare_os_pear_api_allowlist_sketch: 'pear_api_allowlist_sketch', bare_os_pear_doctor_state: 'pear_doctor_state', bare_os_pear_rti_pointer: 'pear_rti_pointer', bare_os_pear_user_dirs_map: 'pear_user_dirs_map', bare_os_pear_workshop_flags: 'pear_workshop_flags' }) /** Capability word 11 `/proc` pseudo files → {@link ./bare-os-proc-hypercore-pack-hrpc-lifecycle.js} ids. */ export const BARE_OS_PROC_FILE_TO_ID_HYPERCORE_PACK_HRPC_LIFECYCLE = Object.freeze({ bare_os_autopass_rotation_sketch: 'autopass_rotation_sketch', bare_os_bare_addon_policy: 'bare_addon_policy', bare_os_bare_pack_cache: 'bare_pack_cache', bare_os_bare_signals_profile: 'bare_signals_profile', bare_os_bare_timers_histogram: 'bare_timers_histogram', bare_os_bundle_preload_hint: 'bundle_preload_hint', bare_os_drive_version_graph: 'drive_version_graph', bare_os_git_lfs_budget: 'git_lfs_budget', bare_os_hrpc_allowlist_sketch: 'hrpc_allowlist_sketch', bare_os_hypercore_replicate_budget: 'hypercore_replicate_budget', bare_os_indexer_catchup: 'indexer_catchup', bare_os_multisig_quorum_pointer: 'multisig_quorum_pointer', bare_os_net_qos_class: 'net_qos_class', bare_os_pear_runtime_matrix: 'pear_runtime_matrix', bare_os_protomux_backpressure: 'protomux_backpressure', bare_os_relay_geo_hint: 'relay_geo_hint', bare_os_sidecar_resource_cap: 'sidecar_resource_cap', bare_os_storage_tier_hint: 'storage_tier_hint', bare_os_wave11_operator_slo_v2: 'wave11_operator_slo_v2', bare_os_wave11_peer_qos_sketch: 'wave11_peer_qos_sketch', bare_os_kernel_program: 'kernel_program', bare_os_giant_phase_program: 'giant_phase_program' }) /** * Unified path view: system Hyperdrive for OS paths, personal Hyperdrive under $HOME, * optional HDMS mounts under /mnt/