198 lines
5.7 KiB
JavaScript
198 lines
5.7 KiB
JavaScript
/**
|
|
* Env parsers and sendmsg/fcntl helpers used by the POSIX syscall facade.
|
|
* Pure functions — no executeKernel closure.
|
|
*/
|
|
import b4a from 'b4a'
|
|
|
|
/**
|
|
* @param {Record<string, unknown> | 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<string, unknown> | 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<string, unknown> | 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<string, unknown> | 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<string, unknown> | 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<string, unknown> | null | undefined} env
|
|
*/
|
|
export function bareOsPosixAcceptBlockMsMax(env) {
|
|
return bareOsPosixDgramRecvBlockMsMax(env)
|
|
}
|
|
|
|
/**
|
|
* @param {Record<string, unknown> | 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<string, unknown>} args
|
|
* @param {Record<string, unknown>} 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<string, unknown>} 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<string, unknown>} */ (mh).control
|
|
if (mhc instanceof Uint8Array && mhc.byteLength > 0)
|
|
return 'msgHdr.control'
|
|
const c2 = /** @type {Record<string, unknown>} */ (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<string, unknown>} args
|
|
* @returns {{ ok: true, buf: Uint8Array } | { ok: false, err: Record<string, unknown> }}
|
|
*/
|
|
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<string, unknown>} */ (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)
|
|
}
|
|
}
|