/** * Env parsers and sendmsg/fcntl helpers used by the POSIX syscall facade. * Pure functions — no executeKernel closure. */ import b4a from 'b4a' /** * @param {Record | null | undefined} env */ export function wantPosixSocketFdBridge(env) { const v = String(env?.BARE_OS_POSIX_SOCKET_FD_BRIDGE ?? '') .trim() .toLowerCase() return v === '1' || v === 'true' } /** * @param {Record | null | undefined} env */ export function wantPosixFcntlBlockingWait(env) { const v = String(env?.BARE_OS_POSIX_FCNTL_BLOCKING_WAIT ?? '') .trim() .toLowerCase() return v === '1' || v === 'true' || v === 'yes' } /** * @param {Record | null | undefined} env */ export function bareOsPosixDgramRecvQueueMax(env) { const raw = String(env?.BARE_OS_POSIX_DGRAM_RECVQ_MAX ?? '').trim() const n = Math.floor(Number(raw)) if (Number.isFinite(n) && n > 0) return Math.min(65536, n) return 256 } /** * @param {Record | null | undefined} env */ export function bareOsPosixDgramRecvBlockMsMax(env) { const raw = String(env?.BARE_OS_POSIX_DGRAM_RECV_BLOCK_MS_MAX ?? '').trim() const n = Math.floor(Number(raw)) if (Number.isFinite(n) && n > 0) return Math.min(300000, n) return 30000 } /** * @param {Record | null | undefined} env */ export function bareOsPosixAcceptQueueMax(env) { const n = Math.floor( Number(String(env?.BARE_OS_POSIX_ACCEPT_QUEUE_MAX ?? '').trim()) || 0 ) const v = n > 0 ? n : 64 return Math.min(1024, Math.max(1, v)) } /** * @param {Record | null | undefined} env */ export function bareOsPosixAcceptBlockMsMax(env) { return bareOsPosixDgramRecvBlockMsMax(env) } /** * @param {Record | null | undefined} env */ export function bareOsFcntlLockWaitMsMax(env) { const raw = String(env?.BARE_OS_FCNTL_LOCK_WAIT_MS_MAX ?? '').trim() const n = Math.floor(Number(raw)) if (Number.isFinite(n) && n > 0) return Math.min(300000, n) return 30000 } /** * @param {Record} args * @param {Record} c */ export function bareOsResolveCooperativeLockPath(args, c) { const p = String(args.path || '').trim() if (p.startsWith('/')) return p const fd = Number(args.fd) if (!Number.isFinite(fd)) return '' const k = String(fd >>> 0) const fds = c.bareOsLogicalFds const t = fds && typeof fds === 'object' && fds[k] != null ? String(fds[k]) : '' if (t.startsWith('/')) return t return '' } /** * Non-empty ancillary / control payloads are rejected with ENOTSUP (schema 3 contract). * @param {Record} args * @returns {string | null} reject reason key for diagnostics, or null if absent/empty */ export function bareOsSendmsgAncillaryRejectReason(args) { if (!args || typeof args !== 'object') return null if (Array.isArray(args.cmsgs) && args.cmsgs.length > 0) return 'cmsgs' const ctl = args.control if (ctl instanceof Uint8Array && ctl.byteLength > 0) return 'control' const clen = Number(args.controllen) if (Number.isFinite(clen) && clen > 0) return 'controllen' const mh = args.msgHdr if (mh && typeof mh === 'object') { const mhc = /** @type {Record} */ (mh).control if (mhc instanceof Uint8Array && mhc.byteLength > 0) return 'msgHdr.control' const c2 = /** @type {Record} */ (mh).cmsgs if (Array.isArray(c2) && c2.length > 0) return 'msgHdr.cmsgs' } return null } /** * Flatten sendmsg iovec list (bounded) for the SOCK_DGRAM bridge. * @param {Record} args * @returns {{ ok: true, buf: Uint8Array } | { ok: false, err: Record }} */ export function bareOsFlattenSendmsgIovs(args) { const maxIovs = 16 const maxPer = 65536 const maxTotal = 1024 * 1024 const iovs = Array.isArray(args.iovs) ? args.iovs : [] let total = 0 /** @type {Uint8Array[]} */ const parts = [] for (const iov of iovs.slice(0, maxIovs)) { if (!iov || typeof iov !== 'object') continue const io = /** @type {Record} */ (iov) let chunk = io.buf ?? io.buffer ?? io.data if (chunk == null) continue if (typeof chunk === 'string') chunk = b4a.from(chunk, 'utf8') if (!(chunk instanceof Uint8Array)) { return { ok: false, err: { ok: false, op: 'sendmsg', code: 'EINVAL', errnoHint: 'EINVAL', posixAlignment: 'ENOTSUP', note: 'sendmsg: each iov buf must be Uint8Array or string' } } } const slice = chunk.byteLength > maxPer ? chunk.slice(0, maxPer) : chunk if (total + slice.byteLength > maxTotal) { return { ok: false, err: { ok: false, op: 'sendmsg', code: 'EMSGSIZE', errnoHint: 'EMSGSIZE', posixAlignment: 'partial', note: 'sendmsg: total iov length exceeds cap' } } } parts.push(slice) total += slice.byteLength } if (parts.length === 0 && args.buf != null) { let chunk = args.buf if (typeof chunk === 'string') chunk = b4a.from(chunk, 'utf8') if (!(chunk instanceof Uint8Array)) { return { ok: false, err: { ok: false, op: 'sendmsg', code: 'EINVAL', errnoHint: 'EINVAL' } } } const buf = chunk.byteLength > maxTotal ? chunk.slice(0, maxTotal) : chunk return { ok: true, buf } } if (parts.length === 0) { return { ok: false, err: { ok: false, op: 'sendmsg', code: 'EINVAL', errnoHint: 'EINVAL', note: 'sendmsg: args.iovs or args.buf required' } } } return { ok: true, buf: parts.length === 1 ? parts[0] : b4a.concat(parts) } }