Orginize
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* POSIX socket-FD bridge helpers (logical dup, shutdown, sendmsg prep).
|
||||
* Takes the guest ctx; no executeKernel closure.
|
||||
*/
|
||||
import {
|
||||
bareOsEnsureSocketSlotAliasKeys,
|
||||
bareOsParseSendmsgScmRights,
|
||||
bareOsScmRightsMaxFds,
|
||||
wantPosixSocketScmRights
|
||||
} from './bare-os-socket-scm-rights.js'
|
||||
import {
|
||||
bareOsFlattenSendmsgIovs,
|
||||
bareOsSendmsgAncillaryRejectReason
|
||||
} from './bare-os-posix-syscall-helpers.js'
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} c ctx
|
||||
* @param {number} srcFd
|
||||
* @returns {number | null}
|
||||
*/
|
||||
export function bareOsDupLogicalFdForScm(c, srcFd) {
|
||||
const k = String(Number(srcFd) >>> 0)
|
||||
const bridge = c.bareOsSocketBridgeByFd
|
||||
const slot = bridge && typeof bridge === 'object' ? bridge[k] : null
|
||||
if (slot && typeof slot === 'object') {
|
||||
if (
|
||||
slot.state === 'listening' ||
|
||||
slot.state === 'created' ||
|
||||
slot.state === 'bound'
|
||||
) {
|
||||
return null
|
||||
}
|
||||
bareOsEnsureSocketSlotAliasKeys(slot, k)
|
||||
const st = c.bareOsPosixFdSimState
|
||||
const newFd = st.nextFd++
|
||||
if (st.nextFd > 65530) return null
|
||||
const nk = String(newFd)
|
||||
if (
|
||||
!c.bareOsSocketBridgeByFd ||
|
||||
typeof c.bareOsSocketBridgeByFd !== 'object'
|
||||
)
|
||||
return null
|
||||
c.bareOsSocketBridgeByFd[nk] = slot
|
||||
slot.aliasKeys.add(nk)
|
||||
const targ = c.bareOsLogicalFds[k] || 'bare-os-socket-bridge:dup'
|
||||
c.bareOsRegisterLogicalFd(newFd, String(targ))
|
||||
const fl = c.bareOsLogicalFdFlags[k]
|
||||
if (typeof fl === 'number') c.bareOsLogicalFdFlags[nk] = fl
|
||||
return newFd
|
||||
}
|
||||
const target = c.bareOsLogicalFds[k]
|
||||
if (!target) return null
|
||||
const st = c.bareOsPosixFdSimState
|
||||
const newFd = st.nextFd++
|
||||
if (st.nextFd > 65530) return null
|
||||
const nk = String(newFd)
|
||||
c.bareOsRegisterLogicalFd(newFd, String(target))
|
||||
const fl = c.bareOsLogicalFdFlags[k]
|
||||
if (typeof fl === 'number') c.bareOsLogicalFdFlags[nk] = fl
|
||||
return newFd
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} c ctx
|
||||
* @param {number} fd
|
||||
*/
|
||||
export async function bareOsShutdownSocketBridgeFd(c, fd, howMaybe) {
|
||||
const k = String(Number(fd) >>> 0)
|
||||
const bridge = c.bareOsSocketBridgeByFd
|
||||
if (!bridge || typeof bridge !== 'object') {
|
||||
return {
|
||||
ok: false,
|
||||
op: 'shutdown',
|
||||
code: 'EINVAL',
|
||||
errnoHint: 'EINVAL',
|
||||
note: 'shutdown: not a bridge fd'
|
||||
}
|
||||
}
|
||||
const slot = bridge[k]
|
||||
if (!slot || typeof slot !== 'object') {
|
||||
return {
|
||||
ok: false,
|
||||
op: 'shutdown',
|
||||
code: 'EINVAL',
|
||||
errnoHint: 'EINVAL',
|
||||
note: 'shutdown: not a bridge fd'
|
||||
}
|
||||
}
|
||||
bareOsEnsureSocketSlotAliasKeys(slot, k)
|
||||
const howN = Number(howMaybe)
|
||||
const how = Number.isFinite(howN)
|
||||
? Math.min(2, Math.max(0, Math.floor(howN)))
|
||||
: 2
|
||||
const tcpHalf =
|
||||
slot.transport === 'tcp' &&
|
||||
slot.state === 'connected' &&
|
||||
slot.sock &&
|
||||
(how === 0 || how === 1)
|
||||
if (tcpHalf) {
|
||||
if (how === 0) slot.tcpShutRd = true
|
||||
if (how === 1) {
|
||||
slot.tcpShutWr = true
|
||||
try {
|
||||
slot.sock.end?.()
|
||||
} catch (_) {}
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
op: 'shutdown',
|
||||
bridge: true,
|
||||
posixAlignment: 'partial',
|
||||
how,
|
||||
note:
|
||||
how === 0
|
||||
? 'TCP bridge: SHUT_RD — recv drains then EOF; poll marks readable when queue drained after peer FIN.'
|
||||
: 'TCP bridge: SHUT_WR — further send returns EPIPE-shaped failure.'
|
||||
}
|
||||
}
|
||||
delete bridge[k]
|
||||
c.bareOsUnregisterLogicalFd(fd)
|
||||
slot.aliasKeys.delete(k)
|
||||
if (slot.aliasKeys.size > 0) {
|
||||
return {
|
||||
ok: true,
|
||||
op: 'shutdown',
|
||||
bridge: true,
|
||||
posixAlignment: 'partial',
|
||||
scmRightsRefDup: true
|
||||
}
|
||||
}
|
||||
if (slot.state === 'listening' && slot.server) {
|
||||
try {
|
||||
slot.server.close?.()
|
||||
} catch (_) {}
|
||||
slot.server = null
|
||||
if (Array.isArray(slot.acceptQueue)) {
|
||||
for (const w of slot.acceptQueue) {
|
||||
try {
|
||||
w.sock?.destroy?.()
|
||||
} catch (_) {}
|
||||
}
|
||||
slot.acceptQueue = []
|
||||
}
|
||||
slot.state = 'closed'
|
||||
return {
|
||||
ok: true,
|
||||
op: 'shutdown',
|
||||
bridge: true,
|
||||
posixAlignment: 'partial'
|
||||
}
|
||||
}
|
||||
if (
|
||||
(slot.state === 'connected' || slot.state === 'udp_bound') &&
|
||||
slot.sock
|
||||
) {
|
||||
try {
|
||||
slot.sock.destroy?.()
|
||||
} catch (_) {}
|
||||
try {
|
||||
await slot.sock.close?.()
|
||||
} catch (_) {}
|
||||
slot.state = 'closed'
|
||||
return {
|
||||
ok: true,
|
||||
op: 'shutdown',
|
||||
bridge: true,
|
||||
posixAlignment: 'partial'
|
||||
}
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
op: 'shutdown',
|
||||
code: 'ENOTSUP',
|
||||
errnoHint: 'ENOTSUP',
|
||||
posixAlignment: 'ENOTSUP',
|
||||
note: 'shutdown: bridge fd not connected, bound, or listening.'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} c ctx (`c.env` is the guest shell env)
|
||||
* @param {Record<string, unknown>} args
|
||||
*/
|
||||
export function prepareBridgeSendmsg(c, args) {
|
||||
const a =
|
||||
args && typeof args === 'object'
|
||||
? /** @type {Record<string, unknown>} */ (args)
|
||||
: {}
|
||||
const env =
|
||||
c && c.env && typeof c.env === 'object'
|
||||
? /** @type {Record<string, string | undefined>} */ (c.env)
|
||||
: {}
|
||||
const scmOn = wantPosixSocketScmRights(env)
|
||||
const parsed = bareOsParseSendmsgScmRights(
|
||||
a,
|
||||
scmOn,
|
||||
bareOsScmRightsMaxFds(env)
|
||||
)
|
||||
if (!parsed.ok) {
|
||||
return {
|
||||
ok: false,
|
||||
err: {
|
||||
ok: false,
|
||||
op: 'sendmsg',
|
||||
code: 'ENOTSUP',
|
||||
errnoHint: 'ENOTSUP',
|
||||
posixAlignment: 'ENOTSUP',
|
||||
ancillaryReject: parsed.reject,
|
||||
note: 'sendmsg: ancillary control rejected (see ancillaryReject).'
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!scmOn) {
|
||||
const leg = bareOsSendmsgAncillaryRejectReason(a)
|
||||
if (leg) {
|
||||
return {
|
||||
ok: false,
|
||||
err: {
|
||||
ok: false,
|
||||
op: 'sendmsg',
|
||||
code: 'ENOTSUP',
|
||||
errnoHint: 'ENOTSUP',
|
||||
posixAlignment: 'ENOTSUP',
|
||||
ancillaryReject: leg,
|
||||
note: 'sendmsg: ancillary control messages (cmsgs/control/controllen/msgHdr.*) are unsupported on the socket FD bridge without BARE_OS_POSIX_SOCKET_SCM_RIGHTS; use payload-only send or bare IPC.'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @type {{ from: number; to: number }[]} */
|
||||
const scmRightsLocalDup = []
|
||||
for (const src of parsed.fds) {
|
||||
const to = bareOsDupLogicalFdForScm(c, src)
|
||||
if (to == null) {
|
||||
return {
|
||||
ok: false,
|
||||
err: {
|
||||
ok: false,
|
||||
op: 'sendmsg',
|
||||
code: 'EBADF',
|
||||
errnoHint: 'EBADF',
|
||||
posixAlignment: 'ENOTSUP',
|
||||
note: 'sendmsg: SCM_RIGHTS logical dup failed (unknown fd, idle/listen/bound socket, or FD budget).'
|
||||
}
|
||||
}
|
||||
}
|
||||
scmRightsLocalDup.push({ from: Number(src) >>> 0, to })
|
||||
}
|
||||
const f = bareOsFlattenSendmsgIovs(a)
|
||||
if (!f.ok) return { ok: false, err: f.err }
|
||||
return {
|
||||
ok: true,
|
||||
payload: f.buf,
|
||||
scmRightsLocalDup
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user