vms-bc7: RMS reaches files via channel + $QIO to the ACP; FAB._linux_fd removed (ATOMIC-FLIP-GROUP — red-by-design) - #649
Closed
baron-3dl wants to merge 2 commits into
Closed
vms-bc7: RMS reaches files via channel + $QIO to the ACP; FAB._linux_fd removed (ATOMIC-FLIP-GROUP — red-by-design)#649baron-3dl wants to merge 2 commits into
baron-3dl wants to merge 2 commits into
Conversation
…er alloc + dir insert + dealloc, epic vms-208)
The sixth and final rung of the executive Files-11 ODS-2 ACP-QIO surface: the
ACP now creates, deletes and modifies files against a real /dev/vms.
Codec port (Rule 8 -- ports the proven format logic from the userspace writer,
does not invent a new layout; parallel pure-function set in ods2_edit.c):
- ods2_fh2_build(): the pure write-side twin of ods2_writer.c's
write_fh2_header_ext() -- allocate/init a complete FH2 into a caller block,
owner/prot as parameters (INV-6 creator UIC).
- ods2_dir_insert_blocks() / ods2_dir_remove_blocks(): the pure twins of
ods2_wvolume_dir_insert() / merge_dir_record() -- flatten, splice/merge a
versioned {name,version,fid} record (or drop one), greedy-repack into blocks.
- ods2_ifbm_block_fid_used/alloc/free(): the index-file (INDEXF.SYS) bitmap
bit ops (SET == IN USE, opposite sense from the storage bitmap).
The userspace ods2_writer.c path is untouched.
ioctl-mapping decision: the ACP band 0x68-0x6F is full and 0x70 is mailboxes, so
IO$_CREATE/DELETE/MODIFY route through ONE new func-dispatched ioctl,
VMS_IOCTL_ACP_FILEOP, whose `func` field carries the $QIO function code. This is
more VMS-faithful than one-ioctl-per-function ($QIO is a single service selected
by function code) and extends #641's 0x6F umbrella. FILEOP reuses nr 0x6F with
its own larger, ATR-carrying struct: _IOWR folds sizeof into the request number
so FILEOP (252 B) and ACPCONTROL (200 B) are distinct 32-bit commands. No ABI
break to the frozen ACPCONTROL struct; a _Static_assert guards their distinctness.
Handler (vms_ioctl_acp_fileop, gated OVMX_ODS2_KERNEL; codec-free build refuses
SS$_DEVNOTMOUNT): CREATE allocates a real FID from the index bitmap, inits the
FH2 from the ATR list, optionally extends, enters a new highest version in the
directory, optionally accesses; DELETE removes the directory entry and (M_DELETE)
deallocates header + blocks; MODIFY extends / truncates (freeing blocks) / writes
attributes. Protection-gated (INV-6); fail-honest (SS$_NOSUCHFILE, SS$_BADPARAM,
SS$_DEVICEFULL, SS$_DUPLNAM).
Proof (real /dev/vms, QEMU kernel-executive harness): test_syssvc_acp_create.c,
27/27 -- CREATE assigns a real FID + ;1 entry, readable back by name; write +
persist across DEACCESS/re-ACCESS (INV-6); second create -> ;2 distinct FID;
DELETE removes + deallocs (ACCESS -> SS$_NOSUCHFILE); MODIFY extend/truncate/attr
each persist; fail-honest edges. Whole harness 89 suites / 1606 assertions, 0 fail.
Cascade: new executive symbol vms_kif_acp_fileop appended to libvmssys_shr.vec;
genuine negctl anchor acp-create-header-slot-offbyone (INDEXF header-slot
off-by-one) in facility_defects.sh (coverage PASS, FLOOR-NO-BUMP: 112 >= 104);
vms.ko builds out-of-tree AND codec-free (bootable overlay); kernel-core stays
Alpha/VAX-portable (fixed-width types, byte-wise LE accessors).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fd removed (ATOMIC-FLIP-GROUP — red-by-design)
RMS no longer does positioned POSIX I/O on a per-process fd. FAB._linux_fd is
retired; RMS reaches file data through the Files-11 ODS-2 ACP (epic vms-208):
$OPEN -> $ASSIGN SYS$DISK + IO$_ACCESS (name->FID, VBN->LBN window)
$CREATE -> IO$_CREATE(+IO$M_ACCESS) ($ERASE -> IO$_DELETE)
$CLOSE -> IO$_DEACCESS + $DASSGN ($EXTEND -> IO$_MODIFY)
$GET/$PUT record I/O -> IO$_READVBLK / IO$_WRITEVBLK at {VBN, byte-offset}
resolve_filename resolves via the ACP, NOT vmsfs_to_linux_path.
The block-I/O SUBSTRATE swap: a new rms_io.c re-homes the POSIX-fd cursor
vocabulary (lseek/read/write/read_exact/write_exact/ftruncate/fsync) the
seq/rel/idx record engines depend on onto {VBN,offset,length} READVBLK/WRITEVBLK
on the channel window. The record logic (RFM framing, cursor arithmetic, key
compares) is UNCHANGED -- only the fd+pread beneath it becomes channel+$QIO.
Two backends behind one interface: __linux__ = the ACP (product runtime);
otherwise = POSIX (the netbsd-vax standalone cross, until VAX's own ACP
re-target vms-d5d). No silent POSIX fallback on Linux -- an absent /dev/vms is
the real RMS/SS$ error (INV-6).
Scope: SEQUENTIAL (VAR/STMLF/FIX) proven byte-exact end-to-end. RELATIVE rides
the same substrate (cell pre-alloc via IO$_MODIFY). INDEXED is fail-honest
DEFERRED on the ACP (RMS$_ORG): its data fork rides the substrate, but the
ODS-2 prologue/bucket index has no ACP home yet -- a separate rung. Record
attributes (RFM/RAT/MRS) are supplied on the FAB; FAT persistence via an
extended IO$_CREATE ATR is deferred (the sidecar is retired on Linux).
PROVEN on a real /dev/vms (tests/qemu/test_syssvc_rms_acp.c, QEMU
kernel-executive harness): RMS-over-ACP 38 passed, 0 failed -- $CREATE+$PUT lands
records via WRITEVBLK, $CLOSE + re-$OPEN + $GET reads them back byte/record-exact
via READVBLK for VAR, STMLF and FIX; $EXTEND grows allocation; $ERASE deletes
(subsequent $OPEN is RMS$_FNF).
ATOMIC-FLIP-GROUP, red-by-design, DO NOT MERGE STANDALONE: existing RMS/DCL/MMK
suites that hit SYS$DISK now fail-honest (no ACP-mounted SYS$DISK at boot yet --
that mount co-lands with the flip). Expected-red: vmsrms_unit,
vmsrms_idx_close_flush, parts_rms_indexed_functional, toolchain-mmk-parse,
toolchain-mmk-component-plan (host ctest); test_syssvc_rms_scratch_create,
test_syssvc_mmk_build, test_syssvc_mmk_drive (QEMU).
Stacks on #644 (work/vms-5303-acp-create). Rebase onto main after #644 merges.
Cascade: vms_kif.h OVMX-UNWIRED annotations for acp_access/deaccess/readvb/
writevb/fileop deleted (RMS is now their product caller -- census gate green);
rms_core/rms_record OVMX service-register annotations updated to PARTIAL
(register gate green); mk_vmsrms_shr.sh native-link enumeration adds rms_io +
libvmssys include (acp symbols already in libvmssys_shr.vec; strtok_r already in
DECC$SHR); genuine negctl anchor rms-put-wrong-vbn added (coverage PASS,
FLOOR-NO-BUMP).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Aug 17, 2026
baron-3dl
added a commit
that referenced
this pull request
Aug 21, 2026
…-host userland (epic vms-d0c) (#689) * vms-5303: ACP answers IO$_CREATE / IO$_DELETE / IO$_MODIFY (file header alloc + dir insert + dealloc, epic vms-208) The sixth and final rung of the executive Files-11 ODS-2 ACP-QIO surface: the ACP now creates, deletes and modifies files against a real /dev/vms. Codec port (Rule 8 -- ports the proven format logic from the userspace writer, does not invent a new layout; parallel pure-function set in ods2_edit.c): - ods2_fh2_build(): the pure write-side twin of ods2_writer.c's write_fh2_header_ext() -- allocate/init a complete FH2 into a caller block, owner/prot as parameters (INV-6 creator UIC). - ods2_dir_insert_blocks() / ods2_dir_remove_blocks(): the pure twins of ods2_wvolume_dir_insert() / merge_dir_record() -- flatten, splice/merge a versioned {name,version,fid} record (or drop one), greedy-repack into blocks. - ods2_ifbm_block_fid_used/alloc/free(): the index-file (INDEXF.SYS) bitmap bit ops (SET == IN USE, opposite sense from the storage bitmap). The userspace ods2_writer.c path is untouched. ioctl-mapping decision: the ACP band 0x68-0x6F is full and 0x70 is mailboxes, so IO$_CREATE/DELETE/MODIFY route through ONE new func-dispatched ioctl, VMS_IOCTL_ACP_FILEOP, whose `func` field carries the $QIO function code. This is more VMS-faithful than one-ioctl-per-function ($QIO is a single service selected by function code) and extends #641's 0x6F umbrella. FILEOP reuses nr 0x6F with its own larger, ATR-carrying struct: _IOWR folds sizeof into the request number so FILEOP (252 B) and ACPCONTROL (200 B) are distinct 32-bit commands. No ABI break to the frozen ACPCONTROL struct; a _Static_assert guards their distinctness. Handler (vms_ioctl_acp_fileop, gated OVMX_ODS2_KERNEL; codec-free build refuses SS$_DEVNOTMOUNT): CREATE allocates a real FID from the index bitmap, inits the FH2 from the ATR list, optionally extends, enters a new highest version in the directory, optionally accesses; DELETE removes the directory entry and (M_DELETE) deallocates header + blocks; MODIFY extends / truncates (freeing blocks) / writes attributes. Protection-gated (INV-6); fail-honest (SS$_NOSUCHFILE, SS$_BADPARAM, SS$_DEVICEFULL, SS$_DUPLNAM). Proof (real /dev/vms, QEMU kernel-executive harness): test_syssvc_acp_create.c, 27/27 -- CREATE assigns a real FID + ;1 entry, readable back by name; write + persist across DEACCESS/re-ACCESS (INV-6); second create -> ;2 distinct FID; DELETE removes + deallocs (ACCESS -> SS$_NOSUCHFILE); MODIFY extend/truncate/attr each persist; fail-honest edges. Whole harness 89 suites / 1606 assertions, 0 fail. Cascade: new executive symbol vms_kif_acp_fileop appended to libvmssys_shr.vec; genuine negctl anchor acp-create-header-slot-offbyone (INDEXF header-slot off-by-one) in facility_defects.sh (coverage PASS, FLOOR-NO-BUMP: 112 >= 104); vms.ko builds out-of-tree AND codec-free (bootable overlay); kernel-core stays Alpha/VAX-portable (fixed-width types, byte-wise LE accessors). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-3e8e: IMGACT activates images via IO$_ACCESS+READVBLK (ACP file access, ATOMIC-FLIP-GROUP — red-by-design) The freestanding image activator now reads every image file over the executive Files-11 (ODS-2) ACP -- $ASSIGN a file-class channel to the mounted volume, IO$_ACCESS the file by walking its directory chain, IO$_READVBLK its header + PT_LOAD segments -- instead of open()/pread()/mmap() on a /vms POSIX path (the passthrough the Files-11 ACP pivot retires, docs/design-files11-acp-executive.md Sec 4.6). Read-then-place first cut; demand-page-through-the-window is the end state (noted as follow-up). - src/imgact/imgact_acp.{c,h}: freestanding ACP reader. Issues REGISTER (adopt- or-create PCB) / ACP_ASSIGN / ACP_ACCESS / ACP_READVBLK / ACP_DEACCESS / DASSGN as raw ioctls on /dev/vms via three host primitives (syscall6 in IMGACT, libc in the test). It calls NO libvmssys vms_kif_* symbol, so no libvmssys_shr.vec / SYS_VEC / native-link enumeration change is needed. - src/imgact/imgact.c: load_object, load_ovmx_producer, ovmx_find_section, apply_vms_rel and activate_symbol_vector read through an imgsrc handle backed by the ACP. NO silent POSIX fallback (INV-6): no /dev/vms or file-not-on-the- ACP-volume -> honest %IMGACT-F-IMGNOTFND, never a /vms read. SYS_ioctl added per arch. - tests/qemu: test_syssvc_imgact_acp.c drives the exact freestanding reader against a real /dev/vms over a generated ODS-2 fixture (mkimage_ods2_imgact.c + imgact_acp_fixture_elf.h) on a new 4th disk DKA300: (vdd). test_kmod_disk updated for the 4th disk (negctl moves to DKA400:). Genuine negctl anchor imgact-acp-valid-bytes-offbyone in facility_defects.sh (FLOOR-NO-BUMP). Proven on real /dev/vms (QEMU kernel-executive harness): test_syssvc_imgact_acp 13 passed, 0 failed -- header + program-header table + every PT_LOAD segment + whole image byte-exact vs the on-disk image, fail-honest SS$_NOSUCHFILE / SS$_NOSUCHDEV. test_kmod_disk 19 passed, 0 failed. ATOMIC-FLIP-GROUP member, red-by-design until the flip ACP-mounts SYS$DISK. DOES NOT MERGE STANDALONE. See the PR body for the expected-red inventory. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-bc7: RMS reaches files via channel + $QIO to the ACP; FAB._linux_fd removed (ATOMIC-FLIP-GROUP — red-by-design) RMS no longer does positioned POSIX I/O on a per-process fd. FAB._linux_fd is retired; RMS reaches file data through the Files-11 ODS-2 ACP (epic vms-208): $OPEN -> $ASSIGN SYS$DISK + IO$_ACCESS (name->FID, VBN->LBN window) $CREATE -> IO$_CREATE(+IO$M_ACCESS) ($ERASE -> IO$_DELETE) $CLOSE -> IO$_DEACCESS + $DASSGN ($EXTEND -> IO$_MODIFY) $GET/$PUT record I/O -> IO$_READVBLK / IO$_WRITEVBLK at {VBN, byte-offset} resolve_filename resolves via the ACP, NOT vmsfs_to_linux_path. The block-I/O SUBSTRATE swap: a new rms_io.c re-homes the POSIX-fd cursor vocabulary (lseek/read/write/read_exact/write_exact/ftruncate/fsync) the seq/rel/idx record engines depend on onto {VBN,offset,length} READVBLK/WRITEVBLK on the channel window. The record logic (RFM framing, cursor arithmetic, key compares) is UNCHANGED -- only the fd+pread beneath it becomes channel+$QIO. Two backends behind one interface: __linux__ = the ACP (product runtime); otherwise = POSIX (the netbsd-vax standalone cross, until VAX's own ACP re-target vms-d5d). No silent POSIX fallback on Linux -- an absent /dev/vms is the real RMS/SS$ error (INV-6). Scope: SEQUENTIAL (VAR/STMLF/FIX) proven byte-exact end-to-end. RELATIVE rides the same substrate (cell pre-alloc via IO$_MODIFY). INDEXED is fail-honest DEFERRED on the ACP (RMS$_ORG): its data fork rides the substrate, but the ODS-2 prologue/bucket index has no ACP home yet -- a separate rung. Record attributes (RFM/RAT/MRS) are supplied on the FAB; FAT persistence via an extended IO$_CREATE ATR is deferred (the sidecar is retired on Linux). PROVEN on a real /dev/vms (tests/qemu/test_syssvc_rms_acp.c, QEMU kernel-executive harness): RMS-over-ACP 38 passed, 0 failed -- $CREATE+$PUT lands records via WRITEVBLK, $CLOSE + re-$OPEN + $GET reads them back byte/record-exact via READVBLK for VAR, STMLF and FIX; $EXTEND grows allocation; $ERASE deletes (subsequent $OPEN is RMS$_FNF). ATOMIC-FLIP-GROUP, red-by-design, DO NOT MERGE STANDALONE: existing RMS/DCL/MMK suites that hit SYS$DISK now fail-honest (no ACP-mounted SYS$DISK at boot yet -- that mount co-lands with the flip). Expected-red: vmsrms_unit, vmsrms_idx_close_flush, parts_rms_indexed_functional, toolchain-mmk-parse, toolchain-mmk-component-plan (host ctest); test_syssvc_rms_scratch_create, test_syssvc_mmk_build, test_syssvc_mmk_drive (QEMU). Stacks on #644 (work/vms-5303-acp-create). Rebase onto main after #644 merges. Cascade: vms_kif.h OVMX-UNWIRED annotations for acp_access/deaccess/readvb/ writevb/fileop deleted (RMS is now their product caller -- census gate green); rms_core/rms_record OVMX service-register annotations updated to PARTIAL (register gate green); mk_vmsrms_shr.sh native-link enumeration adds rms_io + libvmssys include (acp symbols already in libvmssys_shr.vec; strtok_r already in DECC$SHR); genuine negctl anchor rms-put-wrong-vbn added (coverage PASS, FLOOR-NO-BUMP). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-481: DCL file commands + F$ lexicals reach files via RMS/$QIO-ACP (ATOMIC-FLIP-GROUP — red-by-design) DCL DIRECTORY / SET DEFAULT / MOUNT / TYPE / COPY / CREATE and the F$SEARCH / F$FILE_ATTRIBUTES / F$PARSE lexicals no longer reach files through vmsfs_to_linux_path() + POSIX opendir/stat/fopen on the /vms passthrough. They reach files the VMS way -- RMS ($OPEN/$GET/$CREATE/$PUT/$SEARCH) and an OVMX RMS attribute accessor -- which on the product runtime route to the Files-11 ODS-2 ACP over /dev/vms (epic vms-208), and on the netbsd-vax cross keep RMS's own POSIX backend until vms-d5d. RMS substrate (src/vmsrms): - rms_search.c: sys$search rerouted to the ACP wildcard directory context (IO$_ACPCONTROL) -- genuine ODS-2 order, real FIDs. Adds rms_search_fid() (DIRECTORY /FULL reads the real File ID) and rms_search_end(). This WIRES the previously-UNWIRED vms_kif_acp_acpcontrol to a product caller. - rms_core.c: adds rms_file_attr() -- the DIRECTORY /FULL + F$FILE_ATTRIBUTES source of truth: real FID + size + protection + dates + record format from the ODS-2 header via IO$_ACCESS's ATR list, not stat(). Shares rms_acp_resolve_did via rms_internal.h. DCL (src/vmsdcl): a new dcl_rms.h helper layer (homed in the existing dcl_filespec.c TU -- no new native-link TU, NOBJ stays 25) provides read/write/ dir/attr helpers over RMS. cmd_type/cmd_create/cmd_copy/cmd_directory, cmd_set_default, cmd_mount, and lex_search/lex_file_attributes/lex_parse route through them. DIRECTORY /FULL now emits the genuine ODS-2 File ID. cmd_mount mounts through the ACP ($MOUNT), WIRING the previously-UNWIRED vms_kif_acp_mount. vmsdcl now links vmsrms (Debug); mk_dcl.sh already --uses LIBVMSRMS$SHR. Fail-honest (Rule 9 / INV-6): no ACP-mounted SYS$DISK => the real RMS/SS$ error, never a silent POSIX fallback. PROVEN on a real /dev/vms (tests/qemu/test_syssvc_dcl_acp.c, QEMU kernel- executive harness, 20 passed / 0 failed): F$SEARCH/DIRECTORY returns A.TXT;3/;2/;1, B.TXT;1 in genuine ODS-2 order with real File IDs 14/13/12/16; rms_file_attr returns the same real FID + version + on-disk attributes; SET DEFAULT verifies a directory via the ACP; CREATE/TYPE/COPY round-trip byte-exact through the ACP; fail-honest edges. Negctl anchor dcl-acp-search-fid-fabricated (FLOOR-NO-BUMP). ATOMIC-FLIP-GROUP, red-by-design, DO NOT MERGE STANDALONE, stacks on #649 -> #644: existing DCL SYS$DISK tests now fail-honest with no boot-mounted ACP SYS$DISK (dcl-integration: %DCL-E-DIRECT / %RMS-E-FNF, no crashes). Co-lands with the flip that ACP-mounts SYS$DISK at boot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-274: LOGINOUT authenticates from SYSUAF via the ACP; boot writers use RMS $PUT/$CREATE (ATOMIC-FLIP-GROUP — red-by-design) The SYSUAF / RIGHTSLIST / $GETUAI authentication reads and the LASTLOGIN per-boot writer reach their file the VMS way now: RMS $OPEN/$GET and $CREATE/$PUT over the Files-11 ODS-2 ACP (rms_impl_open, #649), NOT fopen on the /vms passthrough. A new rms_textfile helper (src/libvms/rtl/rms_textfile.c) carries the sequential read + append/create-write vocabulary; sysuaf_scan, rightslist_scan, find_uaf_record and ovmx_accounting_* route through it. Library layering: these consumers live in LIBVMS, which sits BELOW RMS (LIBVMSRMS links LIBVMS). The RMS services are referenced WEAKLY so LIBVMS$SHR builds/loads with no hard dependency on LIBVMSRMS -- an image that also links vmsrms (LOGINOUT, VMSSSHD, DCL, the QEMU tests) binds the real services; one that does not sees NULL and fails honestly. No fanout across every vms consumer. Fail-honest (Rule 9 / INV-6): no ACP volume / no /dev/vms / no such file -> the reader returns NULL and the writer returns -1, never a POSIX fallback. #if defined(__linux__) guards the reroute; the netbsd-vax cross keeps POSIX. Proven on real /dev/vms (tests/qemu/test_syssvc_loginout_acp.c, 16/16 PASS): SYSUAF created + read back + authenticated off the ODS-2 volume via the ACP, DISMOUNTED read + absent file fail-honest, OPERATOR.LOG append + LASTLOGIN write land as genuine ODS-2 records read back byte-exact. Genuine negctl anchor loginout-acp-auth-from-ods2 (selftest + coverage PASS). DO NOT MERGE STANDALONE. Stacks on #649 (RMS-over-$QIO) -> #644 (CREATE/DELETE). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: PID-1 $MOUNTs SYS$DISK via the Files-11 ODS-2 ACP (step 2, atomic flip) bare_metal_init (flagless Linux path) now $MOUNTs the boot unit DKA0: through the executive ACP (vms_kif_acp_mount) instead of the vmsfs.ko VFS mount of a bespoke-VMFS volume at /vms. New boot seam ops ovmx_boot_acp_mount_system_disk + ovmx_boot_system_disk_unit; NetBSD backend gets non-behavioral stubs (VAX runtime re-target vms-d5d is driven separately). Requires executive_attach() first, which the flagless path already does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: boot master emits genuine ODS-2 by default (step 3, atomic flip) distro/Dockerfile.bootable: all three mastered boot disks (distrib, negctl, install-media) now built with 'vmsfs_master --ods2' -- a genuine ODS-2 (DECFILE11B) volume the Files-11 ACP $MOUNTs, not the bespoke OVMX VMFS. The distrib ground-source gate reads it back with the tool's genuine ODS-2 reader (--ods2 list, the same ods2_bdev codec the ACP uses) and greps the login chain present; byte-exact read-back is proven by the QEMU ACP tests (extract is VMFS-only). Validated master+list format locally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: wire the flip-group's new native-link symbol edges - rtl/rms_textfile added to LIBVMS$SHR TU manifest (mk_libvms_shr.sh): vms-274 added the SYSUAF/RIGHTSLIST RMS-over-ACP reader but not to the native-link source list, so sysuaf.c's rms_textfile_open was unresolved. - LIBVMSRMS$SHR now --use's LIBVMSSYS$SHR directly (mk_vmsrms_shr.sh + build_link_native.sh + 6 imgact-test harnesses): vms-bc7 made vmsrms IMPORT vms_kif_acp_* but LINK.EXE does not resolve a --use'd shareable's imports transitively, so the vmsrms link was red-by-design. All harnesses already build LIBVMSSYS$SHR (LIBVMS$SHR needs it) and derive its vector from libvmssys_shr.vec (which exports the 9 ACP symbols). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: fix auto-merge duplicate of vms_ioctl_acp_fileop in vmsfs_acp.c The bc7 merge auto-combined (no conflict reported) two copies of vms_ioctl_acp_fileop -- main's vms-233 DLM-locked version AND bc7's older pre-DLM version -- into one file (redefinition error, kernel-module build only; the Debug ctest does not compile drivers/ovmx so it slipped through). bc7's vmsfs_acp.c is a strict SUBSET of main's (git diff main..bc7 = -93/+0), so main's version is authoritative -- restored it (single definition, the acp-fileop-no-dlm-lock negctl anchor vms_lock_acp_vol_ex intact). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: ODS-2 writer emits format-2/3 FM2 pointers for files >128KB The map-encoder was format-1-only (<=256 blocks / 128KB per pointer, and one pointer per file), so ods2_wvolume_create_file_raw() rejected any larger file with ODS2_ERR_ARGS -- a genuine ODS-2 system disk could not hold real binaries (DCL.EXE is 840KB). This surfaced only when step 3 flipped the boot master to --ods2: 'create AUTHORIZE.EXE failed: bad arguments', aborting the master. encode_map_extent now picks the smallest FM2 format that covers the run -- format 1 (<=256 blk), 2 (<=16384 blk / 8MB), or 3 -- so a large CONTIGUOUS file is ONE pointer, exactly as real VMS records a contiguous file and within the runtime ACP window budget (ACP_WINDOW_MAX=24). write_fh2_header_ext advances the map by each pointer's actual width; the INDEXF callers keep format-1. The reader already decodes formats 2/3. Verified: DCL.EXE (840KB) round-trips BYTE-EXACT through the codec; new test_ods2_write BIGFILE.BIN case proves a 300-block file is one format-2 extent, byte-exact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: size the genuine ODS-2 boot volumes at 128MB (step 3 sizing) The full static system tree + the redundant SYS$UPDATE:OVMX-OS.KIT copy sit right at the old 64MB edge; the ODS-2 master overflowed it ('create OVMX-OS.KIT failed: no space'). ODS-2 gives every file >=1 block with no cross-file packing, so it needs a little more room than the retired VMFS master. Bumped all three mastered boot disks (distrib, negctl, install-media) to 128MB. Blank DKA0: disks stay 64M (no system tree). Confirmed not an alloc bug: a 3.2M tree masters into an 8MB ODS-2 volume exactly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: ODS-2 write-cache flushes when full (files may exceed WCACHE_CAP) Second half of the large-file fix. The bdev-mode write-cache (WCACHE_CAP=4096 blocks / ~2MB) buffers dirty blocks and did NOT evict -- so create_file_raw for any file whose data run exceeds ~2MB overflowed it (wcache_seed_zero_range + the data-copy loop both fill it) and returned ODS2_ERR_NOSPACE. That is why the boot master aborted on SYS$UPDATE:OVMX-OS.KIT (3.3MB) even on a 128MB volume: the volume had room, the 2MB cache did not. wcache_block() now flushes the whole working set to the device and retries when full, instead of failing. Safe because this writer's block accesses are write-forward within one op (seed/copy loops + header/dir builders use each returned pointer immediately, never holding one across the next wblk()); a flushed block re-read later returns exactly what was written (the zero_fill==0 miss path re-reads via ods2_blk_read). Bounds memory to WCACHE_CAP regardless of file size -- important as this cache is shared with the kernel ACP. Verified: a 20MB file (format-3 map, 40x the cache) round-trips BYTE-EXACT; test_ods2_master.sh now masters a 5MB binary; all 13 ods2 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: ship the ODS-2 codec in the in-tree bootable vms.ko (ACP $MOUNT) The boot reached PID 1's ACP $MOUNT of SYS$DISK and it fail-honestly refused: %OVMX-F-SYSINIT, system disk DKA0: (/dev/vda) would not $MOUNT via the Files-11 ACP Root cause: acp_validate_ods2() is gated on OVMX_ODS2_KERNEL, which only the out-of-tree QEMU-test vms.ko defined -- the in-tree BOOTABLE vms.ko was built without the codec, so vms_ioctl_acp_mount took the #else and returned SS$_DEVNOTMOUNT for EVERY volume (the codec-less refuse branch). The master's 128MB ODS-2 disk is genuine -- it passes the exact validation chain (home_parse strict=1, BITMAP.SYS FH2, SCB, struclev) in userspace; the kernel just couldn't run that chain. The flip is the first product path to call the bootable ACP $MOUNT, so vms.ko must now carry the codec -- mirrors vmsfs.ko's vms-4a8 solution: vms-y adds ods2_reader.o + ods2_edit.o (the pure parse/validate + edit surface; NOT the writer/bdev/block objects -- vmsfs_acp.c does its own exec_blockdev I/O), ccflags adds -DOVMX_ODS2_KERNEL, and sources.conf stages src/vmsfs/ods2/*.c + the flatten-safe vmsfs/ods2.h (the '->' convention). src/kernel/Makefile stays the co-authoritative object list. Comments updated in both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: ACP-read bootstrap bridge — clear the require_installed_system wall The atomic flip made SYS$DISK a genuine Files-11 (ODS-2) volume owned by the executive ACP and retired the /vms POSIX passthrough, but the boot halted at require_installed_system()'s POSIX stat("/vms/.../DCL.EXE") on the now-empty /vms tree. This lands the ACP-read bootstrap bridge the flip needs. The Linux kernel still activates a VMS image the Unix way: execve() maps a MAIN image's PT_LOAD and opens its PT_INTERP (IMGACT.EXE) BY POSIX PATH before any OVMX code runs. The boot chain genuinely fork()+execve()s a small first-hop set -- PROVISION.EXE, DCL.EXE, JOB_CONTROL.EXE, LOGINOUT.EXE, plus the PT_INTERP IMGACT.EXE. With /vms gone those files have no POSIX home. Bridge: - require_installed_system() probes DCL.EXE THROUGH THE ACP ($ASSIGN + IO$_ACCESS over /dev/vms), not a POSIX stat -- fail-honest, never faked. - PID 1 stage_boot_images() reads the first-hop set off the genuine ODS-2 volume THROUGH THE ACP (ovmx_boot_acp_read.c reuses the proven imgact_acp.c IO$_ACCESS + IO$_READVBLK walk, libc-backed) into OVMX_BOOT_STAGE_DIR (/run/ovmx-boot, a tmpfs), and every execve target that names a SYS$SYSTEM image is rewritten there (ovmx_boot_stage_exec_path, self-guarding on the staged copy's presence). The BYTES come from the ACP; tmpfs is only the Linux-exec handoff (INV-6: no /vms read, no faked presence, no initramfs stage). Sites wired: ovmx_init (PROVISION), ovmx_provision (DCL), sys$creprc (JOB_CONTROL/SPAWN), ovmx_job_control (LOGINOUT), vms_login (post-auth DCL). Linux-substrate only: the NetBSD-vax boot path (ovmx_boot_netbsd.c) is flipped separately by vms-d5d, so the bridge sources compile in only on the Linux backend and the call sites are OVMX_BOOT_LINUX-guarded / self-guard on the staged file -- NetBSD keeps its current boot behaviour untouched. DEFERRED (noted in link.c): IMGACT_INTERP (spot #3) stays /vms/... for now -- the boot walls at the data-read layer (PROVISION's SYSUAF read over the retired /vms) BEFORE any PT_INTERP is resolved, and ~30 native activation tests bake the interp string, so the interp flip must land with migrating those tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: interp flip + IMGACT staged-path map — DCL.EXE now activates via the ACP Completes the exec-bridge so a native image (DCL.EXE/LOGINOUT.EXE) actually activates at boot off the genuine ODS-2 volume: - Rewriter case-fix (ovmx_layout.h): the /vms passthrough resolves a filename component in LOWERCASE (VMS specs are case-insensitive) while the ODS-2 volume and the staged copies carry the UPPERCASE name. Make ovmx_boot_stage_exec_path() detect ".EXE"/"SYSEXE" case-insensitively and emit an UPPERCASE basename so the rewritten exec target matches the staged file. (Also fixes a macro double-evaluation bug in the uppercasing.) - Interp flip (spot #3, link.c + src/vmslink/CMakeLists.txt): IMGACT_INTERP is now overridable (#ifndef); the CMake `vmslink` target that LINK.EXE- builds the BOOTABLE DCL.EXE/LOGINOUT.EXE bakes PT_INTERP = "/run/ovmx-boot/IMGACT.EXE" (the staged loader), while the default stays the /vms path so the ~30 standalone native activation tests -- which build their own LINK.EXE from source and stage IMGACT.EXE under /vms -- are untouched. - IMGACT staged-path map (imgact.c): the kernel hands IMGACT the tmpfs path of a staged first-hop image; IMGACT maps it back to its SYS$SYSTEM volume location (/run/ovmx-boot/NAME -> /vms/SYS0/SYSCOMMON/SYSEXE/NAME) before the ACP open, so IMGACT still reads the GENUINE image bytes THROUGH THE ACP and never the tmpfs copy (INV-6). Non-staged paths pass through unchanged. Boot now advances four walls past require_installed_system: executive attach -> SYS$DISK ACP $MOUNT -> PROVISION establishes SYSTEM [1,4] identity -> DCL.EXE ACTIVATES VIA THE ACP -> DCL runs STARTUP.COM. New wall is the data-read half of the flip: RMS's ACP path (rms_acp_spec_from_fab, rms_core.c) does not resolve logical names, so SYS$STARTUP:VMS$PHASES.DAT / VMS$VMS.DAT return %RMS-E-FNF (it $ASSIGNs "SYS$STARTUP:" as a device). Diagnosed for the follow-on data-read-flip rung. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: RMS ACP-open resolves directory/concealed logicals via compose-candidates RMS opens files only through the Files-11 ODS-2 ACP on Linux (INV-6, no POSIX fallback), but rms_acp_spec_from_fab treated a logical like SYS$STARTUP: as a device to $ASSIGN, so ODS-2-only files (SYS$STARTUP:VMS$PHASES.DAT, SYS$SYSTEM:OVMXVMSSYS.PAR, ...) returned %RMS-E-FNF and STARTUP.COM stalled. Compose the effective filespec through vmsfs_compose_ods2_candidates() -- the same rooted/concealed search-list fan-out (SYS$SYSTEM: -> [SYS0.SYSEXE] + [SYS0.SYSCOMMON.SYSEXE]) the ACP directory walk already consumes in test_syssvc_dirlogical_acp -- into fully-composed PHYSDEV:[DIR]NAME.TYP candidates, and try each via the ACP in search order; first that opens wins, all-miss returns the honest RMS error. Wired into rms_impl_open (multi-candidate loop), rms_impl_create (create in the primary member), rms_impl_erase (delete in the first member that resolves) and rms_file_attr. Device-less specs fall back to the single naive parse with the DKA0: default -- pre-logical behaviour preserved. Debug ctest: same 9 pre-existing red-by-design-without-/dev/vms failures as the branch tip (vmsrms_unit et al fail identically with my change stashed); no regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: DCL OPEN/READ/WRITE/CLOSE ride RMS (ACP), not fopen-on-passthrough The atomic-flip boot wall was NOT in RMS: STARTUP.COM's `OPEN/READ PHASE_FILE SYS$STARTUP:VMS$PHASES.DAT` is DCL's OPEN builtin (dcl_cmd_io.c cmd_open), which did fopen() on a vmsfs_to_linux_path passthrough. With SYS$DISK now a genuine ODS-2 volume served only by the ACP, that host path does not exist, so every OPEN of an ODS-2-only file returned %RMS-E-FNF and STARTUP.COM spun on %DCL-E-IVLOGNAM. Re-plumb the DCL file channels onto RMS: a channel opened on a real file now holds a dcl_rms_reader / dcl_rms_writer (the existing sys$open/$get/$create/$put helpers TYPE/COPY already use) instead of a stdio FILE*, so it rides the Files-11 ODS-2 ACP and resolves SYS$STARTUP:/SYS$SYSTEM: logicals the VMS way. The SYS$OUTPUT:/SYS$ERROR:/SYS$INPUT: standard-stream channels keep their FILE* path (they are process streams, not RMS files). cmd_open/close/read/write and the exit-time channel cleanup all handle the {fp, reader, writer} union; fail-honest with the real RMS status, no POSIX fallback (INV-6). Depends on the preceding commit (RMS ACP-open resolves directory/concealed logicals), which is what lets SYS$STARTUP:VMS$PHASES.DAT resolve through RMS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: master text files as RFM=STMLF, not FIXED — un-hang the phase driver The atomic-flip boot hung in STARTUP.COM's phase driver: OPEN/READ SYS$STARTUP:VMS$PHASES.DAT succeeded but the READ loop never advanced past the first phase, so the END phase (which starts JOB_CONTROL -> LOGINOUT -> Username:) never ran and the console idled forever. Root cause is NOT the `$` in the name and NOT a lock/scan loop (the ODS-2 codec resolves [SYS0.SYSCOMMON.SYS$STARTUP]VMS$PHASES.DAT and reads its 71 bytes correctly host-side). It is the RECORD FORMAT: vmsfs_master --ods2 wrote EVERY regular file verbatim via ods2_wvolume_create_file_raw(), which stamps the FH2 as RFM=FIXED/512 (FH2_KIND_DATA_FIX). A line-oriented RMS/DCL reader on a FIXED/512 file returns the WHOLE 71-byte file as one 512-byte padded record, then EOF -- so the phase loop saw one bogus "phase name" and quit. Real VMS text files are stream/record files, not one giant fixed record. Fix: add a STMLF (stream-LF) verbatim writer path and route text files to it. - ods2_writer.c: new FH2_KIND_DATA_STMLF stamps RFM=STMLF (fat_rtype=5), implied-CR, rsize/maxrec=0, with the SAME verbatim block layout and efblk/ffbyte valid-byte length as _raw. create_file_raw + the new create_file_stmlf share one static verbatim body; STMLF keeps the bytes byte-identical to the host file (no VAR re-framing) AND frames one record per LF, so $GET returns one line per call. - ods2.h: ODS2_RTYPE_STMLF (5) + ods2_wvolume_create_file_stmlf() decl. - vmsfs_master.c: route text files to create_file_stmlf; binary images (.EXE/.OLB/.OBJ/... — read as blocks by IMGACT, never as records) stay on create_file_raw (RFM=FIXED), unchanged. - test_ods2_path.c: assert a create_file_stmlf file is stamped RFM=STMLF (not FIXED) and its bytes round-trip VERBATIM. Proven: rebuilt bootable image now runs the phase driver through all nine phases (reaches LPMAIN "executing the site-specific startup commands" and the END phase) instead of hanging at INITIAL. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: DCL @-procedure execution rides the ACP, not fopen-on-passthrough With the phase driver un-hung, STARTUP.COM reached the END phase and ran `@SYS$STARTUP:JOB_CONTROL_STARTUP.COM`, which failed %DCL-E-OPENIN: dcl_execute_script() still fopen()'d the vmsfs_to_linux_path("/vms/...") passthrough, which cannot see a procedure that lives only on the mounted ODS-2 SYS$DISK. DCL's other file verbs already ride RMS-over-ACP (vms-481/vms-5f0); @-execution did not. dcl_proc_open_acp() opens the procedure through RMS/the Files-11 ACP and stages its text in a transient stdio stream the existing fseek/fgets script engine drives unchanged (a STMLF/VAR text file's records ARE its lines, so joining them with '\n' reconstructs the procedure). Tries `spec` then the `.COM` default type. It returns NULL -- falling back to the passthrough fopen chain -- when the ACP has no such device/file, so the plain host ctest environment (no /dev/vms) behaves exactly as before (no new failures). Proven: the rebuilt boot now OPENS and RUNS JOB_CONTROL_STARTUP.COM (the %DCL-E-OPENIN is gone). Next wall is RUN/image-activation of SYS$SYSTEM:JOB_CONTROL.EXE (%DCL-E-IVIMAGE) -- the same passthrough->ACP conversion, for image lookup rather than record I/O. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: DCL RUN/$CREPRC image activation resolves via the ACP, not /vms The atomic flip retired the /vms passthrough, but dcl_resolve_activatable still probed image presence with access()/opendir() on /vms -- so JOB_CONTROL_STARTUP.COM's `RUN /DETACHED SYS$SYSTEM:JOB_CONTROL.EXE` failed %DCL-E-IVIMAGE and the END-phase console login never started. Resolve the image THROUGH the executive Files-11 (ODS-2) ACP instead: when /dev/vms is present, dcl_resolve_activatable() probes presence via dcl_rms_attr()/rms_file_attr() -- the same compose-ODS2-candidates + IO$_ACCESS search-list path RMS $OPEN and DIRECTORY/FULL already use (node member then SYSCOMMON member) -- and returns the boot-staged copy of a first-hop SYS$SYSTEM image (the POSIX home the Linux kernel execve's; IMGACT still reads the genuine bytes off the volume via the ACP) or the on-volume path. RMS$_ACC (no ACP-mounted SYS$DISK / no /dev/vms) defers to the legacy /vms resolver so the plain host ctest is byte-identical; RMS$_FNF with the ACP present is an honest miss with NO /vms fallback (INV-6). $CREPRC's existing ovmx_boot_stage_exec_path rewrite (sys_process.c) carries the detached child the rest of the way. Signature gains (ctx, vms_spec); both call sites (RUN, foreign-command dispatch) already had the VMS spec in hand. Guarded #if __linux__ so the netbsd-vax cross (vms-d5d) keeps its resolver. Boot proof (qemu-system-x86_64, genuine 128MB ODS-2 ovmx-distrib.img over virtio, /dev/vms executive): boot now runs STARTUP.COM's END phase, RUN /DETACHED JOB_CONTROL.EXE succeeds (%RUN-S-PROC_ID 10000003), JOB_CONTROL activates + execve's LOGINOUT via the ACP, and the console reaches `Username:` (and `Password:`) off the genuine ODS-2 ACP volume. Debug ctest: same 9 red-by-design-without-/dev/vms failures as the branch tip, no regression (this path returns to the identical legacy resolver when no /dev/vms is present). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: LINK/IMGACT weak-by-name imports — LOGINOUT reads SYSUAF through the ACP LOGINOUT authenticates by reading SYS$SYSTEM:SYSUAF.DAT, and sysuaf_lookup() reads it through RMS ($OPEN/$CONNECT/$GET) over the Files-11 ODS-2 ACP. Those sys$* entry points live in LIBVMSRMS$SHR but are CALLED from rms_textfile.c inside LIBVMS$SHR via #pragma weak. LIBVMS$SHR sits BELOW RMS (LIBVMSRMS$SHR --use's LIBVMS$SHR), so it cannot --use LIBVMSRMS$SHR to import them by (producer,index) without a build cycle. LINK.EXE was resolving the weak-undef references to 0 in place (ELF weak-undef semantics); at activation rms_services_present() read FALSE and rms_textfile_open() returned NULL BEFORE any ACP call — "User authorization failure", the ACP never reached. Pre-flip a /vms fopen fallback masked this; the flip retires /vms, so login regressed. Fix — a weak-by-name cross-image import the fixed (producer,index) .vms$imp path cannot express, matching how VMS resolves inter-shareable references at activation: - LINK.EXE (link.c): a #pragma-weak reference that no input object defines and no --use'd producer exports is no longer baked to 0 in place — it becomes a WEAK import (PLT stub + import-GOT cell, same as a strong import) recorded in a new .vms$wimp section carrying the symbol NAME + patch cell. A --use'd producer that DOES export it still wins as a strong .vms$imp import (the strong scan precedes the weak one). Linker-defined weak-undef section symbols (__init_array_start/_DYNAMIC) also land in .vms$wimp and stay 0 — harmless. - IMGACT (imgact.c): after the whole producer closure is loaded, resolve_weak_ imports() binds every producer's .vms$wimp by NAME against the loaded set (found -> patch the import-GOT cell; absent -> leave 0, the honest weak-undef result rms_services_present() reads as "RMS not present"). This closes the layering cycle: LIBVMS$SHR's sys$open/$get/$connect/$close bind to LIBVMSRMS$SHR, loaded because LOGINOUT --use's it. - ovmx_image.h: .vms$wimp section + magic + header/entry format (OVMX-original, labelled). - mk_loginout.sh: the --use LIBVMSRMS$SHR edge is LOAD-BEARING (puts RMS in the loaded set for by-name resolution), not "graph parity" — comment corrected. - run_weak_import_activation.sh: new regression. LINK level (host-runnable): a weak reference with no exporter -> .vms$wimp (not baked-0, not a link error); with an exporter --use'd -> strong .vms$imp. Activation level (needs a real /dev/vms ACP, i.e. QEMU): positive binds by name (exit 3), negative falls back to 0 (exit 0). Absent /dev/vms, activation is proven by the boot-to-DCL login. Verified: LINK.EXE emits .vms$wimp{sys$open,$close,$connect,$get,$put,$create, $disconnect} in LIBVMS$SHR (7 weak imports); the bootable native graph rebuilds clean; the LINK-level regression passes on host. No /vms fallback restored (INV-6); no stub, no hardcoded credential. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: CI — weak-import (.vms$wimp) LINK-level gate Runs run_weak_import_activation.sh in a plain x86_64 alpine container. The LINK-level assertions (a #pragma-weak reference with no exporter -> .vms$wimp, not baked-0; with an exporter --use'd -> strong .vms$imp) are the gate; the by-name activation half needs a real /dev/vms ACP (INV-6: no POSIX image-read fallback) and is proven by the boot-to-DCL login (uat-session). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: IMGACT legacy POSIX defer when /dev/vms absent (A1) imgsrc_open() rode the executive Files-11 ACP with no fallback, so every self-host / link / activation gate that builds IMGACT.EXE in a plain container (no /dev/vms) died with %IMGACT-F-IMGNOTFND. Mirror the RMS rung's RMS$_ACC defer: when imgact_acp_open() renders the executive-absent case as SS$_NOSUCHDEV, fall back to a POSIX open()+pread() on the pre-flip /vms path. When /dev/vms IS present the ACP open succeeds or fails for a real reason and the defer is never reached, so the runtime boot path stays ACP-only with no POSIX image-read fallback (CLAUDE.md Rule 9 / INV-6). Verified: native x86_64 activation (run_test recipe) — shareable present with no /dev/vms now activates (IMGACT-TEST: PASS, exit 0; pre-fix: IMGNOTFND); removed shareable still fails honestly with %IMGACT-F-IMGNOTFND. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: bound F$FILE_ATTRIBUTES PRO accumulator (A3, CodeQL) The protection-string formatter accumulated with a raw pi += (size_t)snprintf(pb + pi, sizeof(pb) - pi, ...) five times. snprintf() returns the length it WOULD have written, so pi could be driven >= sizeof(pb); the next unsigned sizeof(pb)-pi then underflows to a huge size_t and hands snprintf an out-of-bounds pointer and length -- the buffer-overflow CodeQL flagged (5 high-severity alerts). Replace with a bounded PRO_APPEND() accumulator: every append is guarded by "pi < sizeof(pb)" (so the subtraction is provably positive) and clamps pi to at most sizeof(pb)-1 on truncation. Output is unchanged for all real inputs (the protection string is <30 bytes; pb[80] never truncates) -- this removes the theoretical underflow only. Builds clean under -Wall -Wextra. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: move ACP-mount behind the boot seam -- ovmx_init.c substrate-neutral (A5) The atomic flip added a `#if defined(__linux__)` fork directly in ovmx_init.c (ACP $MOUNT on Linux vs vmsfs.ko load+mount on NetBSD). The VAX gate ("vax toolchain builds ovmx-images aggregate") rejects that: the boot sequence must stay ONE source; the substrate split lives ONLY in ovmx_boot_linux.c / ovmx_boot_netbsd.c (INV-DRIFT, vms-f2e). Relocate the whole system-disk mount behind a new backend hook ovmx_boot_mount_system_disk_native(): * Linux backend -> ovmx_boot_acp_mount_system_disk() (the Files-11 ACP flip; no vmsfs.ko VFS mount). * NetBSD backend -> load vmsfs.ko (best-effort) then mount as vmsfs at SYSDISK_MOUNT -- its existing pre-flip sequence, relocated verbatim (same ops, order, errno contract). NetBSD boot semantics (vms-d5d) untouched. ovmx_init.c now calls the one hook and halts honestly on failure with a substrate-neutral message -- no #ifdef. Verified: `docker run ovmx-cross-vax build-ovmx-images-vax-cmake.sh` PASSES all proofs -- "OK: ovmx_init.c has no __NetBSD__/__linux__ boot-logic fork", "all 9 ovmx_boot.h ops defined by the NetBSD backend", ovmx_init built under the vax--netbsdelf toolchain, and the full ovmx-images aggregate links. Linux ctest build of ovmx_init (STARTUP.EXE) also links clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: RMS legacy-POSIX defer when /dev/vms absent (A2a) The Files-11 ACP flip left every RMS entry point ACP-only, so the host Debug ctest and the plain-container self-host/link gates (no /dev/vms) went red: $OPEN/$CREATE/$ERASE returned RMS$_ACC, $SEARCH returned RMS$_DNF, and rms_file_attr failed -- the same class IMGACT's imgsrc_open() hit (f2817d31). Mirror that defer across RMS. A cheap $ASSIGN probe (rms_acp_absent) renders the executive-absent case as SS$_NOSUCHDEV; on that, and only that, RMS falls back to its legacy POSIX bodies (rms_posix_open/create/erase/search/file_attr). With /dev/vms present the probe passes and RMS stays ACP-only, failing honest with no POSIX fallback (Rule 9 / INV-6). - rms_io.c: compile BOTH backends; rms_io_* dispatches POSIX vs ACP at runtime on the handle's fd (ACP handles carry fd == -1). rms_io_posix_wrap/unwrap/fd now available on __linux__ too. - rms_search.c: dispatcher routes $SEARCH to the ACP or POSIX backend; a continuation call stays on the backend that opened the context (is_posix tag). - rms_validate_path_boundary: confine to SYSDISK's ACTUAL mount (vmsfs device table), not a hardcoded /vms, so a remapped DKA0: (a test's mkdtemp root) is honoured exactly as the runtime's /vms -- still one registered mount, not a weakening. - rms_posix_file_attr: a "[p]C.DIR" spec resolves to the Linux directory that backs it, so SET DEFAULT's dir probe works on the passthrough. Tests: - test_libvms_{sysuaf_write,accounting}_veracity now link vmsrms (--no-as-needed forces the weak-only DT_NEEDED) so rms_textfile's RMS reads bind; accounting checks the flat LASTLOGIN_<user>.dat record version-agnostic. - vmslink IMGACT_INTERP passed as a bare token + stringified in link.c, so the quoted -D no longer emits \" backslashes into compile_commands.json (was failing the kif_caller_census gate; a vms-5f0 regression from 38befbe0). Host Debug ctest: 9 red -> 1 (only dcl-integration, tracked separately). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: POSIX $SEARCH composes the resultant from the searched DEV:[DIR] (A2a) The atomic-flip DCL COPY/DIRECTORY rewrite (dcl_cmd_file.c) resolves filespecs through sys$search. On the executive-absent defer that routes to the POSIX backend, whose resultant spec was round-tripped through vmsfs_to_vms_spec -- which maps /vms/X/Y to the malformed "X:[000000]Y" (dir promoted to device). COPY/DIRECTORY then re-opened that bogus spec and got %RMS-E-FNF. Compose the resultant as DEV:[DIR]NAME from the ORIGINAL expanded spec's device/directory prefix (as the ACP backend does from ctx->devnam/dirpath) plus the matched entry name upper-cased, and store that VMS spec on _resolved_path (the record engines re-parse it, exactly as the ACP path leaves it). Also fixes the two `vmsfs_*_path(...) < 0` success checks -- those return VMS status codes (odd == success), never < 0 (the project's standing vmsfs gotcha). dcl-integration: from every file subtest failing to 133/148 passing. The residual 15 are DIRECTORY/ODS-2 listing-fidelity cases (subdirectories as NAME.DIR;1, version synthesis) the passthrough search does not yet emulate -- tracked separately, not a defer regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: export pread/pwrite from DECC$SHR for the RMS legacy-POSIX defer (A2a fix) A2a's RMS legacy-POSIX defer (0c1055fa) added a positioned-I/O POSIX backend to rms_io.c: rms_io_read_posix/rms_io_write_posix pread()/pwrite() an ODS-2 block at f->cursor without moving the file position (the byte-offset form of the ACP backend's IO$_READVBLK-by-VBN). Those two libc entry points were never exported by DECC$SHR's symbol vector, so the STRICT VMS-native LINK.EXE link of LIBVMSRMS$SHR failed: %LINK-F-ERROR, unresolved external symbol 'pread' (no --use'd shareable exports it as a universal ... must be appended to DECC$SHR's symbol vector) and every downstream native-link/self-host/IMGACT gate (LINK/DCL/LOGINOUT/TCC/ LIBRARIAN/BUILD.COM/self-host fixpoint) red'd behind it. (readdir, the search backend's other new libc call, was already exported.) Fix: append pread=PROCEDURE,pwrite=PROCEDURE to the DECC$SHR vector in mk_decc_shr.sh (append-only -> prior consumers' indices unchanged, GSMATCH LEQUAL-compatible). open/close/read/write/lseek were already there; pread/pwrite are their positioned-I/O companions that real OpenVMS DECC$SHR exports and musl's libc.a defines, so DECC$SHR is the correct producer -- the faithful fix, not --allow-undefined. Verified (alpine musl, linux/amd64): link_native_graph builds clean, 9 EM_X86_64 artifacts, zero DT_NEEDED; OVMX_IMGACT x86_64 build+activate proof passes (IMGACT_INTERP intact). No compiled code touched -> host Debug ctest unaffected (uses ld, not LINK.EXE). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: sysvol carries the REAL SYSUAF/RIGHTSLIST; login tests read them via the ACP (R1) Make the login/rights kernel suites read the ACTUAL shipped records off a genuine ODS-2 volume through the RMS-over-ACP rooted-logical open, and root-cause the remaining boot-login blocker. FIXTURE (verified GREEN via test_syssvc_dirlogical_acp): - mkimage_ods2_sysvol.c now masters the REAL shipped SYSUAF.DAT / RIGHTSLIST.DAT (verbatim from distro/rootfs) into [SYS0.SYSCOMMON.SYSEXE], as RFM=STMLF (ods2_wvolume_create_file_stmlf) so RMS $GET frames one LF record per line -- create_file_raw's RFM=FIXED made $GET see one 512-byte record then EOF, the exact "LOGINOUT's SYSUAF scan" failure that primitive's own doc warns about. - Dockerfile passes the two distro paths to the generator. - test_syssvc_dirlogical_acp verifies VBN 1 begins with the real shipped header (was a synthetic 0x5A^ pattern) -- byte-exact against what boots. TESTS (authentic; fail-honest until the product blocker below is fixed): - test_syssvc_sysuaf_uic_base / test_syssvc_rightslist now $MOUNT the ODS-2 sysvol on DKA300:, seed the concealed-rooted system logicals (SYS$SYSDEVICE= DKA300:), and read the REAL records through sysuaf_lookup / rightslist_name_to_ value -> rms_impl_open -> vmsfs_compose_ods2_candidates -> ACP $GET. Each keeps a DISMOUNT-then-fail-honest provenance check so a /vms read cannot masquerade as an ACP read (INV-6). ROOT CAUSE of the boot-login failure (ESCALATED, product fix reserved): rms_acp_absent() (src/vmsrms/rms_core.c) and rms_impl_search()'s probe (rms_search.c) decide ACP-vs-legacy-POSIX by $ASSIGNing a HARDCODED DKA0: and reading SS$_NOSUCHDEV as "executive absent". But vms_ioctl_acp_assign returns SS$_NOSUCHDEV for BOTH "no /dev/vms" AND "that unit is simply not mounted" (vmsfs_acp.c). So whenever /dev/vms is present but SYS$DISK is $MOUNTed on a unit other than DKA0:, the probe wrongly reports "absent" and RMS silently defers to the /vms POSIX passthrough -- the exact /vms-on-a-/dev/vms-present read INV-6 forbids. The RMS-over-ACP open path is thus NEVER REACHED (proven: an instrumented rms_acp_open_file printed nothing; only the POSIX body ran). On the real boot DKA0: is the mounted system disk so this is masked, but any volume mounted elsewhere -- incl. these fixtures -- is diverted to /vms. The obvious fixes each hit a reserved wall: - probing /dev/vms directly (vms_kif_open() < 0) is REJECTED by the standing runtime_target_gate ("do not branch on whether the executive opened"). - making vms_ioctl_acp_assign return SS$_DEVNOTMOUNT for an existing-but- unmounted unit (so the acp_assign probe becomes correct and the gate stays green) is a kernel-core semantic change that also needs the INV-6 assertions in test_syssvc_acp_channel / test_syssvc_acp_mount updated (they pin unmounted -> SS$_NOSUCHDEV) and a device-existence check to keep a truly-absent unit honest. Kernel-core => 3-way VAX/Alpha gate; could not be verified in QEMU this session (shared-host disk at ~1.8G, docker build cache un-reclaimable under the standing constraint). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: disambiguate ACP $ASSIGN unmounted-vs-absent — close the INV-6 POSIX-masquerade hole (vms-03b) vms_ioctl_acp_assign returned SS$_NOSUCHDEV for BOTH "no /dev/vms" and "unit exists but no volume mounted." RMS's executive-presence probe (rms_acp_absent / rms_impl_search) and IMGACT's imgsrc_open defer to the legacy /vms POSIX passthrough on SS$_NOSUCHDEV, so with /dev/vms present but SYS$DISK mounted on any unit other than the probe's hardcoded DKA0:, the probe wrongly concluded "executive absent" and RMS silently read the POSIX passthrough — the exact INV-6 masquerade the atomic flip exists to kill. Masked on real boot only because DKA0: happens to be the system disk. Fix: the executive's $ASSIGN handler now returns SS$_DEVNOTMOUNT (device present, no volume) for the not-a-mounted-volume path, DISTINCT from the SS$_NOSUCHDEV that ONLY the userspace KIF (acp_bind_ok) emits when /dev/vms is absent. Real VMS $ASSIGN never conflates them: an existing device assigns regardless of mount state; SS$_NOSUCHDEV is reserved for a non-existent device. The presence probe now reflects /dev/vms PRESENCE, not DKA0:'s mount/existence, so an unmounted or non-DKA0: unit takes the ACP path and fails honestly with no POSIX fallback. The deferral sites already gate on == SS$_NOSUCHDEV only, so no runtime logic change was needed — the single status disambiguation fixes RMS and IMGACT together; comments hardened to make the invariant legible. Test assertions corrected to the authentic status (unmounted $ASSIGN -> SS$_DEVNOTMOUNT, not NOSUCHDEV) in test_syssvc_acp_channel / acp_mount / imgact_acp; negctl acp-assign-unmounted-fabricates-channel re-anchored to the new line (still flips fail-honest -> SS$_NORMAL, still caught). Expected to flip test_syssvc_sysuaf_uic_base / rightslist GREEN: they mount on DKA300: and assert the read fails after DISMOUNT — the provenance the masquerade defeated. Residual: the hardcoded DKA0: probe unit is now harmless (DEVNOTMOUNT != NOSUCHDEV); the OPEN-path DKA0: default remains a vms-47d device-native-naming follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: bind the RMS-over-ACP service seam in the syssvc suites — sys$open was NULL, not the codec (vms-058) The RMS-over-ACP record read of SYSUAF/RIGHTSLIST was failing in the login suites, but NOT for the reasons the two candidate root causes assumed. WHAT IT IS NOT (both disproven with ground truth): - NOT a record-format / binary-indexed-ISAM problem. OVMX's shipped SYSUAF.DAT / RIGHTSLIST.DAT are the product's OWN pipe/colon-delimited ASCII TEXT files (distro/rootfs/.../SYSEXE), not VMS binary indexed files. RFM=STMLF is the correct framing. - NOT the octal-vs-decimal UIC parse. sysuaf.c / rightslist.c already parse UIC fields in octal (SYSUAF_UIC_RADIX / strtoul base 8). The ODS-2 codec, the fixture, the map and the multi-block read are all byte-correct: a host build of the genuine writer + block-backed reader (ods2_bdev_read_file) resolves [SYS0.SYSCOMMON.SYSEXE]SYSUAF.DAT and reads all 2966/3322 bytes BYTE-EXACT (efblk=6/ffbyte=406 single 6-block extent). WHAT IT IS: the reader never opened the file. src/libvms/rtl/rms_textfile.c references sys$open/$get/... with `#pragma weak` (the LIBVMS-below-RMS layering seam). A WEAK UNDEFINED reference does NOT force the linker to pull the member from the vmsrms archive (static) NOR record LIBVMSRMS$SHR as DT_NEEDED (shared, --as-needed). So a suite that only reaches RMS through that weak seam got sys$open == NULL, rms_services_present() returned FALSE, and rms_textfile_open() bailed to NULL BEFORE any ACP call — every sysuaf_lookup / rightslist read failed as "record not found", masquerading as a missing file. Proven directly: `nm test_syssvc_sysuaf_uic_base` had NO sys$open at all, while test_syssvc_loginout_acp / test_syssvc_rms_acp — which STRONGLY call sys$create / sys$open — carried `T sys$open` and read fine. FIX (general, one place): tests/qemu/rms_acp_bind.c makes a strong reference to the seven RMS entry points and is linked into every syssvc suite via the shared qemu_syssvc_add_test() recipe, so the linker resolves them from the vmsrms the recipe already links. Verified in the Debug tree: the test binaries now carry LIBVMSRMS$SHR as a runtime dependency (ldd) and import sys$open (nm). General: every current and future syssvc suite that reads a file via the RMS-over-ACP layer inherits the binding — no per-suite change. Touches tests/ only (no src/, no kernel-core/codec → 3-way cross gate N/A; mutation-sandbox negctls copy only src/+top CMakeLists, unaffected). Fixes the in-process pure-read suites (sysuaf_uic_base, rightslist, setuai). The activated production images that read SYSUAF via the ACP after IMGACT (LOGINOUT.EXE — the boot login gate; the spawned DCL that answers F$IDENTIFIER; MMK.EXE) hit the SAME weak seam through a DIFFERENT mechanism (symbol-vector weak-import binding of a --use'd LIBVMSRMS$SHR at activation) and/or the pre-existing cross-process ACP mount-visibility gap (test_kmod_vmsfs_mountvis, red on the prior commit too) — tracked follow-on, see the vms-058 report. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: unbloat the kernel-test initramfs + bind LOGINOUT's weak RMS seam at activation (vms-058) Two faces of the same RMS-over-ACP weak seam, clearing the two dominant CI failures on the atomic flip. FIX 1 — kernel-test initramfs 36M -> 31M (clears the Kernel Executive boot overflow). c63d700c linked tests/qemu/rms_acp_bind.c into EVERY test_syssvc_* binary via the shared qemu_syssvc_add_test() recipe. A strong reference to the seven RMS entry points EXTRACTS the whole vmsrms archive + ODS-2 codec into each static musl binary, so all 31 suites gained ~200KB, bloating the shared initramfs 31M->36M and overflowing the QEMU test-VM boot ("Initramfs unpacking failed: write error" -- every shard died before shard selection). Only THREE suites actually need the anchor: those that read an identity file IN-PROCESS through the libvms reader AND carry no strong sys$ RMS call of their own (sysuaf_uic_base, rightslist, setuai). The ACP suites (loginout_acp, rms_acp, dcl_acp, scratch_writable, acp_*) already pull vmsrms via a direct sys$create. Made the anchor OPT-IN via target_sources() on exactly those three; removed it from the blanket recipe. Verified: initramfs back to 32.1MB (31M as run_tests.sh reports it), shard 0 BOOTS and unpacks clean, all its suites RUN (test_syssvc_rightslist 34/0 among them). FIX 2 — LOGINOUT.EXE authenticates against SYSUAF again (clears the boot/login e2e gates + the LOGINOUT VMS-native Link+Activate gate). LOGINOUT reads SYSUAF through RMS over the Files-11 ACP; rms_textfile.c (in LIBVMS$SHR, below RMS) `#pragma weak`-references sys$open/... and IMGACT binds those by name at activation (resolve_weak_imports) against the LOADED producer set. But IMGACT loads a producer only when the image names it in a STRONG .vms$imp entry (bind_imports). LOGINOUT --use's LIBVMSRMS$SHR yet makes no strong reference to it (all its RMS use is via the weak seam), so LIBVMSRMS$SHR was never loaded, resolve_weak_imports could not find sys$open, LIBVMS$SHR's weak cell stayed 0, rms_services_present() read FALSE, SYSUAF was never read, and every login failed "User authorization failure" -- the exact --as-needed/no-DT_NEEDED root cause the FIX 1 test anchor closes for the static suites, on the LINK.EXE image path. Added src/vmslink/loginout_rms_bind.c (a guarded, never-executed strong CALL to sys$open et al.) to mk_loginout.sh: LINK.EXE now records a strong .vms$imp import naming LIBVMSRMS$SHR, IMGACT loads it, and the weak seam binds. The facade ASCII-SHA256 SYSUAF is unchanged (its authentic rebuild is separate follow-on); this only makes the real read happen. Corrected the stale "--use is enough" claim in mk_loginout.sh's header. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: test_kmod_disk — correct the disk-topology fixture to 5 disks (vde→DKA400:) run_tests.sh now attaches FIVE virtio disks (vms-3e8e added vde → DKA400:, the ODS-2 image volume the IMGACT-over-ACP test mounts). test_kmod_disk's negative control still asserted "DKA400: → SS$_NOSUCHDEV (no fifth disk attached)", which is stale: DKA400: now resolves to vde. This turned a real fixture drift into a red suite. Fix (a genuine fixture correction, not a weakening): - stat /dev/vde and assert it is present (fifth virtio disk); - add the DKA400: → vde positive resolution block (parallel to the DKA0..DKA300 blocks: unit exists, backing == "vde", dev_t matches); - move the truly-absent negative control up one unit to DKA500:, which still proves an unenumerated unit reports SS$_NOSUCHDEV. The negctl keeps its teeth (a resolver that always succeeded would fail the DKA500: SS$_NOSUCHDEV check); only the boundary moved to match the rig. Verified in-guest against a real /dev/vms: test_kmod_disk 23/0 (was red). * vms-5f0: genuine Files-11 Prolog-3 indexed READ over the ACP (supersede .rms_idx fake) Root cause of the RMS$_ORG on a real indexed open: rms_impl_open's ACP branch hard-rejected FAB$C_IDX ("the index has no ACP home yet") before ever accessing the file, so every indexed $OPEN over /dev/vms failed. The only indexed path that worked was the executive-absent defer to the in-memory `.rms_idx` B-tree sidecar (a private, non-VMS structure). This lands the real read engine. A new substrate-agnostic module (src/vmsrms/rms_prolog3.{c,h}) parses a genuine on-disk Prolog-3 file -- fixed-prolog + area descriptors (VBN 3, 64 bytes) + per-key descriptors (root VBN, first-data VBN, key flags, seg-0 position/size) -- then walks the key-of-reference index buckets (14-byte header, records at 0x0E, 2-byte child pointers) down to the primary data bucket and returns the record whose embedded key matches ($GET/$FIND by key, incl. KGE/KGT). Every on-disk field is a fixed-width uint read through le16()/le32() accessors and there is no substrate #ifdef, so VAX ILP32 and Alpha/x86_64 LP64 share the file byte-for-byte. All block reads ride rms_io_read_exact (rms_io.h) -> IO$_READVBLK on the ACP channel window when /dev/vms is present (Rule 9 / INV-6). rms_impl_open now ACCESSes the indexed data fork over the ACP and binds the Prolog-3 prologue; rms_idx_get/find/cleanup dispatch to the engine when the FAB carries a bound Prolog-3 context (tagged P3_CTX_MAGIC to disambiguate from the legacy btree in _rms_state). The `.rms_idx` sidecar remains ONLY behind the executive-absent host defer; the /dev/vms-present runtime path uses the real engine. Fail-honest: a non-Prolog-3 / malformed / compression-bearing prologue returns RMS$_PLG rather than mis-decoding. Scope is the smallest genuine increment -- read by primary key over a single-level (Root Level 1) index, uncompressed keys/records; compression decode, multi-level descent, bucket split/overflow chains, SIDR (secondary-key) read, and the WRITE engine (vms-045) are labelled follow-on rungs. Oracle grounding (docs/oracle/vax73-alpha84-rms-prolog3.md, vms-8438): pinned geometry ([PIN]) honored -- Prolog Version 3, area descriptors @VBN 3 x64, key descriptor @VBN 1 chain, key-flag bit positions, 14-byte bucket header + records@0x0E, 2-byte index pointers, data-record control-flags/Record-ID/RRV lead. Byte offsets the oracle does not publish are OVMX design choices ([OVMX]-labelled in rms_prolog3.h) so the writer (vms-045) and reader agree. Test: tests/vmsrms/test_prolog3_read.c authors a real Prolog-3 image (prologue + single-level index + two data buckets) and reads records BY KEY across both buckets (index walk chooses the child), plus RNF miss, KGE, RTB, and fail-honest-on-compression -- 20 assertions, host-side (POSIX rms_io backend, no /dev/vms). The QEMU/ACP end-to-end is the paired positive pending the write rung. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: genuine Files-11 Prolog-3 indexed WRITE over the ACP (create/put/split/update) Completes the read/write loop the READ rung (dd89246f) opened: rms_prolog3.c now authors a real Prolog-3 indexed file and maintains it as records insert, over the same IO$_WRITEVBLK/READVBLK ACP substrate (Rule 9/INV-6) -- no .rms_idx sidecar, no /vms write, no flat file. WRITE engine (src/vmsrms/rms_prolog3.{c,h}), all fixed-width LE, no substrate #ifdef (VAX ILP32 + Alpha/x86_64 LP64 identical): - rms_p3_create: writes the prologue (fixed prolog + key descriptor + area descriptor), the root index bucket (Root Level 1) and the first data bucket. - rms_p3_put: sorted keyed insert with control-flags/Record-ID/RRV lead and index high-key maintenance. - genuine data-bucket SPLIT: allocates a new bucket, redistributes records by key, leaves an RRV stub in the original bucket per moved record (RFA stability, IRC$V_RRV), and inserts the new bucket's 2-byte child pointer + high-key into the parent index bucket. Index-full -> fail-honest RMS$_ORG (2-level growth is the labelled follow-on), never a mis-write. - rms_p3_update: in place when same-size, else compact-delete + reinsert. Reader updated to read the write high-water and skip RRV stubs. Emits EXACTLY the [PIN]/[OVMX] byte offsets the reader parses -- writer<->reader round-trip. Verify: - tests/vmsrms/test_prolog3_write.c (host round-trip, POSIX rms_io backend): authors an indexed file, $PUTs 40 records forcing 4 real bucket splits, reads them ALL back BY KEY via the read engine, asserts content + strict key order (KGT walk) + that a split happened; $UPDATE same-size + grow; duplicate -> RMS$_DUP; plus a seg0_siz<key_size (padded index key) file. PASSES. - tests/qemu/test_syssvc_rms_p3_acp.c (ACP e2e over real /dev/vms): IO$_CREATE + rms_p3_create/put/get over the window, split, read-back by key, and durability across DEACCESS+re-ACCESS. Compiles+links, honest-SKIPs 77 with no executive; CI-pending on the QEMU harness (local disk 97%, kernel/QEMU build deferred to CI). Anchored to rms-put-wrong-vbn negctl (facility_defects.sh). Oracle docs/oracle/vax73-alpha84-rms-prolog3.md (vms-8438): 14-byte bucket header, records@0x0E, area desc @VBN3 (64B), key desc 102B stride, 2-byte index pointers, control-flags/Record-ID/RRV record lead, key-flag bit positions, Prolog Version 3. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: genuine Files-11 Prolog-3 SECONDARY keys / SIDR over the ACP (vms-2ae) The last RMS Prolog-3 rung before real SYSUAF/RIGHTSLIST: a second key of reference with its OWN index tree whose level-0 leaves are SIDR (secondary index data record) buckets mapping a secondary-key VALUE to the primary record(s) carrying it -- real index descent + real RFA resolution to the primary record, NEVER a flat scan filtered by the secondary field, no sidecar. Engine (src/vmsrms/rms_prolog3.{c,h}): - SIDR on-disk record [OVMX] (oracle §5 leaves the per-pointer sub-layout unpinned): [u8 ctrl][u16 payload_len][key_size key][u16 nptr][nptr*{u32 vbn, u16 id}], in a level-0 bucket sharing the [PIN] 14-byte header. Duplicate secondary values grow the pointer array; NODUP rejects with RMS$_DUP. - rms_p3_add_secondary_key: defines a key of reference on the empty file (chains the descriptor in VBN1, allocates its root + first SIDR bucket). - rms_p3_put now maintains every secondary SIDR after the primary insert, keyed on the record's STABLE home RFA {home_vbn,home_recid}. - RFA stability across a primary split: the split now PRESERVES the moved record's home rrv-ptr/rrv-id (instead of self), and rms_p3_get_by_rfa follows the RRV stub chain home->current, so a SIDR pointer resolves after the primary record moves. rms_p3_delete recovers that home RFA to purge the SIDRs. - rms_p3_sidr_lookup / rms_p3_get_by_rfa; rms_p3_get_by_key(krf>=1) routes through them. Genuine SIDR-bucket split (p3_split_sidr_bucket); 2-level secondary index growth fails honest (RMS$_ORG), never a mis-write (INV-6). - Fixed-width le16/le32 only, no substrate #ifdef (VAX ILP32 + LP64 identical). Verify: - Host round-trip tests/vmsrms/test_prolog3_seckey.c (SYSUAF-shaped: 32-byte username primary + 4-byte UIC secondary, dups allowed): 40 records past BOTH a primary data-bucket split AND a SIDR-bucket split; reads BY UIC resolve to the byte-exact primary; a 3-member duplicate group resolves all three; RFA stable across the primary split; $DELETE purges the SIDR (dup array shrinks, unique-UIC SIDR removed). rms ctests 10/10 -> 11/11. - ACP e2e tests/qemu/test_syssvc_rms_p3_acp.c extended with a UIC-secondary file read BY SECONDARY KEY over a real /dev/vms (IO$_WRITEVBLK/READVBLK), dup array + $DELETE + DEACCESS/re-ACCESS durability. Reuses the existing rms-put-wrong-vbn negctl anchor (no new suite, no floor bump). Deferred (fail-honest, labelled): 2-level secondary index growth; adding a secondary key to a non-empty file (back-fill); KGE/KGT on a secondary key; data-type-aware key ordering (string memcmp, as the primary rung). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-5f0: real binary $UAFDEF SYSUAF over the Prolog-3 indexed engine (vms-f88) Drop the ASCII+SHA-256 / 368-byte SYSUAF facade the operator caught and build the genuine binary $UAFDEF record on the Files-11 Prolog-3 indexed engine (rms_prolog3.c: read/write/secondary-keys already complete). Record (src/libvms/include/sysuaf.h): sysuaf_rms_record_t is now the 644-byte $UAFDEF record from docs/oracle/vax73-alpha84-uafdef.md (644B on both VAX V7.3 and Alpha V8.4). Oracle-[PIN] offsets asserted at compile time: USERNAME@0x04, UIC@0x24, owner-id@0x2C, UAF$Q_PWD@0x154 (quadword), UAF$W_SALT@0x166, UAF$B_ENCRYPT@0x168 (0x03=UAI$C_PURDY_S), PWD_LENGTH@0x16A, UAF$Q_PWD2@0x16C. Fields whose byte offset the oracle does not publish are labelled [OVMX] (Rule 8). All on-disk fields are fixed-width LE byte arrays (p3_le/put_le, incl. new p3_le64/put_le64) with alignment 1 so the record is byte-identical on LP64 and VAX ILP32 -- no substrate #ifdef. Password i…
Contributor
Author
|
Closing as SUPERSEDED. The Files-11 ODS-2 executive ACP flip landed on main via the winning train (#633/#640/#641/#644/#654 -> #689 V0.5 -> #690/#696 V0.5-1), and the real VAX-side ACP flip landed as vms-329/PR #706. This PR is part of the parallel, CI-red work/vms-0.5-final integration branch (last touched Aug 20) that did not win the race; spot-checks found its runtime functionality already on main. Reopen if a specific piece here is found genuinely missing from main. -- conductor stale-queue cleanup (operator-authorized) |
baron-3dl
added a commit
that referenced
this pull request
Sep 2, 2026
…test_syssvc_loginout_acp (#1045) Measure-first (3rd disproof in this lane, after vms-03b/vms-058): the concealed- rooted logical composition the caveat says is missing is DONE and proven on origin/main. The iterative translation (compose_ods2_r, vmsfs_translate.c:774), concealed-rooted concatenation (merge_rooted_root :603, LNM$M_CONCEALED), and boot-time definitions (lnm_setup_defaults seeds concealed-rooted SYS$SYSROOT/ SYS$COMMON) are all wired into the RMS-over-ACP open path (rms_acp_specs_from_fab -> vmsfs_compose_ods2_candidates, rms_core.c:368) and proven end-to-end on a live executive: - test_syssvc_sysuaf_uic_base / _rightslist / _setuai do a real RMS $OPEN of SYS$SYSTEM:SYSUAF.DAT (sysuaf_lookup -> rms_open_named_handle) and read the real record off the ACP; - test_syssvc_dirlogical_acp walks the concealed-rooted chain to the file FID (teeth: negctl dirlogical-compose-drops-common-member); - tests/vmsrms/test_dirlogical_compose proves the compose via the real lnm_setup_defaults host-side. test_syssvc_loginout_acp.c still carried the stale #649 "does not yet compose" caveat -- the exact false premise that seeded the whole vms-058 trail -- plus a top-of-file ISOLATION note claiming the test redefines SYS$SYSTEM: -> [OVMXDIR] (it does not; assertion 4 proves SYS$SYSTEM: fails-honest, which that redefinition would contradict). Correct both to reality: the compose IS wired + proven elsewhere; THIS suite's real-VAX fixture carries only [OVMXDIR] (no [SYS0.SYSCOMMON.SYSEXE] system tree), so the product SYS$SYSTEM:SYSUAF.DAT composes to on-volume candidates absent on this volume and resolves fail-honest (RMS$_FNF) -- a fixture property, not a missing compose. Assertion (4)'s label updated to the accurate reason. The assertions are unchanged (the suite stays green); full boot-time login through the product's own SYS$SYSTEM: against a real system tree remains vms-ead (a full-boot proof, not a composition gap). Comment/label only; no functional change. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⚠ ATOMIC-FLIP-GROUP — red-by-design — DO NOT MERGE STANDALONE. Stacks on #644.
Stacks on #644 (
work/vms-5303-acp-create), which provides IO$_CREATE/DELETE/MODIFY. Rebase onto main after #644 merges (conductor handles reap-time rebase). Co-lands with the boot-flip rung that ACP-mounts SYS$DISK.What this does
RMS stops doing positioned POSIX I/O on a per-process fd.
FAB._linux_fdis removed; RMS reaches file data through the Files-11 ODS-2 ACP (epic vms-208).$OPEN$ASSIGNSYS$DISK +IO$_ACCESS(name→FID, builds VBN→LBN window)$CREATEIO$_CREATE(+IO$M_ACCESS)$CLOSEIO$_DEACCESS+$DASSGN$ERASEIO$_DELETE$EXTENDIO$_MODIFY$GET/$PUTrecord I/OIO$_READVBLK/IO$_WRITEVBLKat {VBN, byte-offset}resolve_filenameresolves via the ACP (device + directory walk + name→FID), notvmsfs_to_linux_path.The block-I/O substrate swap
New
src/vmsrms/rms_io.cre-homes the POSIX-fd cursor vocabulary (lseek/read/write/read_exact/write_exact/ftruncate/fsync) the seq/rel/idx engines depend on onto{VBN,offset,length}READVBLK/WRITEVBLK on the channel window. The record logic (RFM framing, cursor arithmetic, key compares) is unchanged — onlyfd+pread→channel+$QIObeneath it. Two backends behind one interface:__linux__= the ACP (product runtime); otherwise = POSIX (netbsd-vax standalone cross, until VAX's own re-target vms-d5d). No silent POSIX fallback on Linux — an absent/dev/vmsis the real RMS/SS$ error (INV-6). Grep of the record engines' SYS$DISK path shows zero POSIX file calls.Proof (real /dev/vms, QEMU kernel-executive harness)
tests/qemu/test_syssvc_rms_acp.con the mounted real-VAX ODS-2 volume:For VAR, STMLF and FIX:
$CREATE+$PUTlands records via WRITEVBLK →$CLOSE→ re-$OPEN+$GETreads them back byte/record-exact via READVBLK →$GETat EOF is RMS$_EOF →$EXTENDgrows allocation →$ERASEdeletes → subsequent$OPENis RMS$_FNF.Scope
.rms_metasidecar is retired on Linux).Expected-red (atomic-flip-group)
Suites that hit SYS$DISK now fail-honest (no ACP-mounted SYS$DISK at boot yet — that mount co-lands):
vmsrms_unit(RMS$_ACC),vmsrms_idx_close_flush/parts_rms_indexed_functional(RMS$_ORG, indexed deferred),toolchain-mmk-parse/toolchain-mmk-component-plan(MMK-over-RMS, fail-honest even status).test_syssvc_rms_scratch_create,test_syssvc_mmk_build,test_syssvc_mmk_drive.All fail-honest (real RMS/SS$ codes, no crash, no silent POSIX success).
Cascade
vms_kif.h: deleted theOVMX-UNWIREDlines foracp_access/deaccess/readvb/writevb/fileop— RMS is now their product caller (kif_caller_census green).rms_core.c/rms_record.c: OVMX service-register annotations updated to PARTIAL for the now-executive-reaching services (userspace_service_register green).mk_vmsrms_shr.sh: native-link enumeration addsrms_io+ libvmssys include. Thevms_kif_acp_*imports are already exported inlibvmssys_shr.vec(resolved at final exe link);strtok_ralready in DECC$SHR.tests/qemu/facility_defects.sh: genuine negctl anchorrms-put-wrong-vbn(a $PUT to the wrong VBN reddens the suite) — coverage PASS, FLOOR-NO-BUMP (113 ≥ floor 104).🤖 Generated with Claude Code