vms-4ac: fix create-file + HELP defects (RMS-over-ACP candidate walk, RFM, read-ahead; provision homes over ACP) - #685
Closed
baron-3dl wants to merge 125 commits into
Closed
vms-4ac: fix create-file + HELP defects (RMS-over-ACP candidate walk, RFM, read-ahead; provision homes over ACP)#685baron-3dl wants to merge 125 commits into
baron-3dl wants to merge 125 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>
…ccess, 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>
…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>
… (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>
… 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-atomic-flip # Conflicts: # src/libvmssys/vms_kif.h # tests/qemu/facility_defects.sh
…vms-5f0-atomic-flip # Conflicts: # tests/qemu/facility_defects.sh
…work/vms-5f0-atomic-flip # Conflicts: # src/vmsdcl/CMakeLists.txt # tests/qemu/facility_defects.sh
…ork/vms-5f0-atomic-flip # Conflicts: # tests/qemu/Dockerfile # tests/qemu/facility_defects.sh # tests/qemu/run_tests.sh # tests/qemu/test_kmod_disk.c
…tomic 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>
…lip) 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>
- 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>
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>
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>
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>
…_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>
…UNT) 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>
…em 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>
… 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>
…se-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>
…hrough
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>
…e 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>
…rough
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>
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>
…ough 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>
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>
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>
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>
…-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>
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 (f2817d3). 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 38befbe). Host Debug ctest: 9 red -> 1 (only dcl-integration, tracked separately). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…port Two defects surfaced by the real cluster-params/scsnode e2e (READ over the ACP worked -- '31 parameters loaded from SYS$SYSTEM:OVMXVMSSYS.PAR' -- but WRITE CURRENT failed %SYSGEN-E-OPENOUT RMS$_DNF 0x1c04a): 1. rms_open_named_handle create path resolved only specs[0]'s directory, so a concealed-rooted logical (SYS$SYSTEM:) that expands to several ODS-2 candidates failed RMS$_DNF on create even though the READ loop -- which tries every candidate -- resolved it. Create now walks the same candidate set and mints the new version in the first directory that resolves. 2. #pragma weak weakened the DEFINITIONS in sysgen_acp.c too, so the symbols emitted as 'W'; LIBVMSRMS$SHR's native symbol vector is generated from 'nm type T' and dropped them, and dropped the shared lib for SYSGEN/SCSD (a weak reference does not mark a lib needed) -> undefined at link. OVMX_SYSGEN_ACP_STRONG now keeps the defining TU and the linking images (SYSGEN/SCSD/SYSMAN) strong; libvms's inline callers stay weak and fail-honest. Replaces the --undefined link hack. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…me header The readers now go through sysgen_load_current_db -> the ACP seam; the old sysgen_current_path() (vmsfs_to_linux_path + vmsfs_get_highest_version, i.e. the retired /vms passthrough) had no remaining callers. Removing it clears the last /vms residual from sysgen_params.h's runtime path (INV-6 grep-guard). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… 2 rungs)
With the foreign-command TCC/LIBRARIAN/LINK now resolved over the ACP (prior
commit), the compile -> archive -> LINK chain greens in-guest (16/18). The two
remaining assertions were a stale expectation newly exposed once the chain ran:
the produced OVMXRT.EXE's PT_INTERP.
The vmslink build bakes PT_INTERP=/run/ovmx-boot/IMGACT.EXE -- the OVMX image
activator's boot-staged (ACP-read) POSIX home, NOT the retired /vms passthrough
(src/vmslink/CMakeLists.txt IMGACT_INTERP_PATH). The test asserted the old
/vms/SYS0/SYSCOMMON/SYSEXE/IMGACT.EXE and never staged IMGACT.EXE at
/run/ovmx-boot, so the PT_INTERP check and the activation-to-216 both failed.
* tests/qemu/Dockerfile: master IMGACT.EXE onto the ODS-2 system volume
(DKA300:) alongside TCC/LIBRARIAN/LINK/DECC$SHR (rung ii), so the harness can
read it over the ACP.
* test_syssvc_mmk_build.c: generalize the ACP-stage helper to take a directory
tree; stage BOTH SYS$SHARE:DECC$SHR.EXE (SYSLIB) and IMGACT.EXE (SYSEXE) off
the volume over the ACP into /run/ovmx-boot -- the producer + the interp the
kernel opens, bytes sourced from the volume, never /vms. Assert the produced
image carries PT_INTERP=/run/ovmx-boot/IMGACT.EXE.
Local QEMU shard-2 run before this commit: 16 passed / 2 failed (the two above);
with IMGACT.EXE staged and the assertion corrected these close. Handing off to CI
(KE shard 2) for green-by-SHA per the no-local-QEMU directive.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s residual The atomic flip (epic vms-208) made SYS$DISK a genuine ODS-2 volume the executive Files-11 ACP owns and retired the /vms POSIX passthrough. The flagless boot already reads OVMXVMSSYS.PAR over the ACP, but the CONVERSATIONAL SYSBOOT> branch still (a) legacy-mounted vmsfs via ovmx_boot_mount_system_disk(...,"vmsfs",...) and (b) let sysboot.c read/write the parameter file with vmsfs_to_linux_path()+fopen() off /vms. So a SYSBOOT> WRITE never reached the real volume and a read saw nothing. Fix (Linux runtime, OVMX_BOOT_LINUX; NetBSD-vax keeps its path until vms-d5d): - ovmx_init.c: the conversational branch now attaches the executive SILENTLY before the SYSBOOT> prompt (executive_attach_silent; the ACP $MOUNT needs the executive, but §3.1 shows nothing precedes "SYSBOOT> "), ACP-mounts the system disk via ovmx_boot_mount_system_disk_native() — the same path the flagless boot uses — and defers the %OVMX-I-EXEC/banner/mount narration to after the prompt (executive_announce). A disk that will not ACP-mount is a fail-honest halt, exactly as on the flagless path. - sysboot.c: SET/USE CURRENT/WRITE now route through the SHARED sysgen_params.h machinery (sysgen_load_working / sysgen_commit_working over the ovmx_sysgen_acp_* seam) — the exact reader/writer every other consumer uses. The /vms fopen readers (load_from_file/highest_version_path) are compiled out on the Linux runtime. - ovmx_boot_sysgen_acp.c (new, Linux-only): the STRONG ovmx_sysgen_acp_* definition for PID 1. STARTUP.EXE is statically linked and links NO RMS, so rather than pull rms_core.o (the full FAB/RAB/idx engine) into the static image, this backs the seam with the imgact_acp.c ACP client ALREADY linked into PID 1 (the flip's image-staging bridge): IO$_ACCESS/IO$_READVBLK for read, a self-contained IO$_CREATE/IO$_WRITEVBLK for write, all over /dev/vms. Two seam definitions for two link contexts — no new link deps in the static PID-1 image, no duplicate symbols (ovmx_init does not link vmsrms). INV-6 / Rule 9: no /vms fallback anywhere on the conversational path; if the ACP is unreachable it fails honest (SS$_NOSUCHDEV / SS$_NOSUCHFILE). Runtime proof runs off-box (tests/qemu/test_sysboot_cluster_params_e2e.sh). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The header comment above #include "sysboot.h" still described the conversational path as resolving OVMXVMSSYS.PAR to a raw Linux directory; that is now only the NetBSD-vax path. Note the Linux ACP route. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…at open() BUILD.COM S3.2 (mk_vmsrms_shr) went RED linking LIBVMSRMS$SHR.EXE: %LINK-F-ERROR, unresolved external symbol 'fchmod' (no --use'd shareable exports it as a universal ... it must be appended to DECC$SHR's symbol vector) because rms_stage_over_acp (this branch's ACP image-staging helper) called fchmod() to set the staged copy's exec bit, and fchmod is not a universal in DECC$SHR's C RTL symbol vector -- so a VMS-native LINK --use of LIBVMSRMS$SHR cannot resolve it (vms-61f). The fchmod was redundant: rms_stage_over_acp already open()s the staged file O_CREAT with mode 0755, which under any sane umask leaves the owner-exec bit the kernel needs to execve() a staged tool and the read bit it needs for a staged PT_INTERP. The staging path is only reached when the boot bridge has NOT already staged the image, so the file is freshly created here with that mode. Drop the fchmod() call rather than widen DECC$SHR's exported surface -- keeps the change local to this branch and off the shared C RTL symbol vector / symvec_freeze_gate. nm rms_core.c.o: no 'chmod' undefined symbol remains. vmsrms builds 100%. KE shard 2 (test_syssvc_mmk_build) already GREEN on the prior SHA. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…riv mask The atomic flip (SYSUAF -> binary $UAFDEF record read over the ACP) exposed a lossy round-trip in the login persona path. LOGINOUT built the persona privilege mask by RE-PARSING the rendered privilege NAME string (rec->privileges) with parse_privilege_string() (vms/privs.h), whose table knows only 17 privilege names and has no MOUNT row. But the string is produced by sysuaf_format_privileges() from the full 37-name table (VMS_PRIV_NAME_LIST), which DOES emit MOUNT. Pre-flip the seed record's priv field was the literal "ALL" (special-cased to PRV$M_ALL); post-flip the binary all-bits uaf$q_priv renders as the expanded name list, so the re-parser silently dropped MOUNT (and ~19 other privileges). SYSTEM then logged in without MOUNT and `MOUNT DKA100:` returned %SYSTEM-F-NOPRIV. Fix: add sysuaf_record_privileges() (header-inline, no new export) that returns the authoritative uaf$q_priv quadword straight from the binary $UAFDEF record, and use it at both persona-establishing sites — tools/vms_login.c (LOGINOUT) and src/vmsssh/vmssshd.c. The persona now carries exactly the privileges SYSUAF genuinely grants; no privilege is faked or hardcoded and the $MOUNT privilege check is unchanged (INV-6). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t string re-parse test_libvms_sysuaf_priv_persona builds the seed SYSTEM record exactly as tools/mksysuaf.c does (all-bits uaf$q_priv), reads it back via sysuaf_raw_to_view (the sysuaf_lookup path), and asserts the persona mask sysuaf_record_privileges() carries PRV$M_MOUNT and equals the on-disk all-bits quadword, while the OLD parse_privilege_string(rec.privileges) path drops MOUNT — the exact divergence that produced %SYSTEM-F-NOPRIV on MOUNT DKA100:. Ground-source coverage for the vms-26a fix (Rule 7). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…utive envs
Two environments run the self-host drive, and it must resolve foreign commands +
--use producers in both:
- KE shard 2 (test_syssvc_mmk_build): real /dev/vms + Files-11 ACP present ->
resolve over the ACP and stage (GREEN, prior SHA).
- BUILD.COM S3.2 (src/imgact/test/run_build_com_native.sh): a HOST DCL driver,
DCL.EXE native on the arm64 runner, NO /dev/vms -> must use the LEGACY
resolver / legacy POSIX producer location.
The prior commit broke the no-executive case two ways; both fixed here:
1. dcl_resolve_activatable_acp (dcl_cmd_process.c): with /dev/vms absent,
rms_file_attr answers from a POSIX stat, so dcl_rms_attr returned RMS$_NORMAL
for SYS$SYSTEM:TCC.EXE -- the ACP resolver then tried to stage it OVER an
absent ACP, failed, and reported a false %DCL-E-IVIMAGE instead of deferring.
Guard the whole ACP path with rms_executive_absent(): no executive -> return
0 with acp_usable=0 so the legacy resolver (SYS$SYSTEM: -> /vms POSIX, the
sanctioned no-executive path) runs. Preserves the ACP-stage path when
/dev/vms IS present (KE shard 2 stays green).
2. resolve_producer_path (link.c): a --use SYS$SHARE:/SYS$LIBRARY:/SYS$SYSTEM:
producer now resolves (1) to the boot-staged (ACP-read) /run/ovmx-boot copy
when present -- the runtime path -- else (2) to the legacy POSIX SYS$SYSROOT
location (/vms/SYS0/SYSCOMMON/{SYSLIB,SYSEXE}), which is where the host ctest
stages the installed images. Previously it only looked at /run/ovmx-boot, so
native LINK died %LINK-F-ERROR cannot open producer under the host driver.
The /vms branch is NEVER reached on the runtime (the ACP-staged copy resolves
first), so the runtime path stays /vms-free.
VERIFIED LOCALLY (host DCL test, NOT QEMU): the exact CI step for BUILD.COM S3.2
(arm64 alpine, `sh run_build_com_native.sh`, BUILD_EXPECT=1) now:
%BUILD-I-COMPILE (TCC.EXE) -> HELLO.OBJ; %BUILD-I-LINK (LINK.EXE);
%LINK-S-CREATED HELLO.EXE; ran and printed 'hello'; %BUILD-S-OK; MILESTONE.
No FAIL, no %DCL-E-IVIMAGE, no %LINK-F. Grep-guard on BUILD.COM +
dcl_cmd_process.c CLEAN.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… chmod) PRODUCT INSTALL wrote every installed kit file with the ACP create-time per-file CLASS default (ods2_class_fileprot), ignoring the per-file VMS protection the kit carries in each entry's ke_protection. Close that gap: each installed file now takes its OWN protection from ke_protection, stamped into the installed file's ODS-2 fh2_fileprot header at $CREATE time over the executive Files-11 ACP -- the same header path SYSUAF/RIGHTSLIST/images use. No fchmod, no ugo*rwx flatten, no /vms protection path (Rule 9 / INV-6). - rms_core.c/rms_io.h: new rms_open_named_handle_kind_prot() carries a VMS SOGW fileprot into the ACP IO$_CREATE as attr.fileprot + VMS_ACP_ATTR_PROT; rms_open_named_handle_kind() is the fileprot==0 (class-default) wrapper, so existing callers are unchanged. - product.c: pd_write_file() takes a fileprot; do_install passes e->ke_protection per file. On the executive-absent host defer the value is ignored (no on-disk FH2 there -- host build/test tooling, not the runtime). - ovmx_kit_pack.c: stamp each entry's ke_protection with the GENUINE per-file VMS protection (ods2_class_fileprot, name-keyed) instead of one flat OVMX_KIT_PROT_DEFAULT, so an image lands World:RE while a SYSUAF.DAT-class file lands World-denied -- the installer can never leak a special-class file through a flat default. Proof: - tests/qemu/test_product_install_e2e.sh installs a second kit (staged at SYS$UPDATE:PROOF-PROT.KIT by Dockerfile.bootable) whose one ordinary-named file carries a DIVERGENT ke_protection 0xFF00 (S:RWED,O:RWED,G:,W:), then DIRECTORY/FULL reads the installed file's ODS-2 fh2_fileprot back over the ACP and asserts 0xFF00 -- NOT the ordinary create-time class default World:RE -- distinguishing "honoured kit metadata" from "took the class default". - tests/integration/test_ovmx_kit_pack_roundtrip.sh asserts the packer records genuine per-file protection (World:RE / World-denied / World:R), not a flat default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o work/vms-0.5-integration
…ork/vms-0.5-integration
Resolve conflict in src/vmsrms/rms_core.c: keep HEAD's restructured ACP file-create loop (vms-5f0 vms_kif_acp_assign retry across ODS-2 candidate directories) and graft vms-738's per-file protection stamp into it. The fileprot stamp (fop.attr_ctl |= VMS_ACP_ATTR_PROT; fop.attr.fileprot = fileprot) is placed immediately after fop.kind = kind in HEAD's relocated, loop-local CREATE fop setup, so each candidate's IO$_CREATE carries the caller's explicit VMS SOGW protection into the new file's ODS-2 fh2_fileprot over the executive ACP. fileprot==0 leaves attr_ctl clear -> ACP applies the per-file CLASS default (prior behaviour). Signature rms_open_named_handle_kind_prot(...,uint16_t fileprot,...) and its _kind wrapper (fileprot==0) merge cleanly; rms_io.h declaration auto-merged. product.c pd_write_file plumbs fileprot; installed kit files pass ovmx_kit_entry.ke_protection, product DB passes 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rep it The sysboot-cluster-params-e2e gate RED was a test-timing race, not a product defect. CI console proof (run 32407699859): SYSBOOT> reached, SET SCSNODE CLUX / SET SCSSYSTEMID 1027 applied, WRITE minted OVMXVMSSYS.PAR;2, and the booted guest genuinely adopted both (F$GETSYI(NODENAME)="CLUX" via a real sethostname, F$GETSYI(SCSSYSTEMID)=1027). 18/19 checks passed. The lone FAIL was the "%OVMX-I-SCSNODE, node name CLUX set from SYS$SYSTEM:OVMXVMSSYS.PAR" console line, which was polled with a one-shot `check` only `sleep 1` after CONTINUE. That line is emitted several seconds into the boot continuation -- behind the SILENT executive attach + DKA0: mount read_boot_parameters() runs -- so it was not yet in the log at grep time (it IS present later in the same transcript; the guest adopted CLUX). Fix: wait for the exact line (bounded 90s), matching the %SYSGEN-I-WRITTEN waitfor two lines above and the sibling test_boot_scsnode_hostname_e2e.sh's assertion, which greps the identical line only after Username: is reached. Assertion strength is unchanged -- the exact CLUX announce must still appear. No product code touched; no /vms residual; INV-6 fail-honest preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ork/vms-0.5-final
…e /vms residue) Two flip-authenticity defects behind the RED VMS User Acceptance Test, both a retired /vms POSIX passthrough that the atomic flip (epic vms-208) left behind. DEFECT 1 -- logged-in UICs could not create files (%RMS-E-CRE for SYSTEM in SYS$MANAGER:/SYS$SYSTEM:, GUEST in its own login dir). provision_ownership() and the per-account home step set ownership with lchown()/mkdir() on the retired /vms path -- ENOENT, silently swallowed -- so the genuine ODS-2 fh2_fileowner acp_check_access() reads was never set, and GUEST's home was never created. Rewritten to work over the executive ACP: - own_object_acp(): IO$_MODIFY by FID writes fh2_fileowner (the primitive the ACP already supports via attr_ctl VMS_ACP_ATTR_OWNER). - the system tree ([SYS0] and beneath) is re-owned to SYSTEM by walking it with IO$_ACPCONTROL wildcard search + per-file IO$_MODIFY. - each account's login directory is created-if-missing (IO$_CREATE, directory, owned by the account) and (re)owned over the ACP. - the writes run AFTER vms_kif_establish_system(), so they carry the SYSTEM identity (BYPASS) needed to create in [USERS] and stamp an arbitrary owner; the SYSUAF scan that still runs first only reads (continuity fail-stop) and collects the homes. The POSIX lchown/mkdir path is kept only for the netbsd-vax cross (#else), which still has /vms until vms-d5d. DEFECT 2 -- HELP SHOW answered %HELP-E-OPENIN opening SYS$HELP:HELPLIB.HLP, which IS mastered on the volume: both the DCL builtin and the standalone HELP.EXE fopen()'d the translated /vms path. The library is now read over the ACP (rms_textfile_*) with a POSIX /vms fallback for host tooling / the netbsd cross -- the same ACP-first, POSIX-for-host dual backend $SEARCH uses. The engine (dcl_help.c) stays free of RMS/vmsfs deps via two weak seams implemented in the new dcl_help_acp.c, so the hermetic engine unit test still links it alone. A binary .HLB is taken through the POSIX/HLB-reconstruct path (the record reader would mangle it); the product volume ships the .HLP source. HELP.EXE gains the RMS closure + a static weak-seam bind anchor. SHARED KERNEL-CORE NOTE (3-way convergence gate): this change writes NO new vmsfs_acp.c code -- the ACP set-owner primitive (IO$_MODIFY attr_ctl VMS_ACP_ATTR_OWNER writing fh2_fileowner) already exists on work/vms-0.5-final (b541e4f/vms-738 lineage) and is width-clean (fixed-width LE FH2 field writes via acp_put16, no long/pointer assumptions). Provision is now its first caller. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g probe The runtime DCL.EXE is built by the VMS-native LINK.EXE path (mk_dcl.sh, run by the link_native_graph cmake target the bootable image uses), NOT the plain cmake vmsdcl executable. dcl_help_acp.c (the HELP ACP-read weak seam) was only added to VMSDCL_SOURCES, so the runtime DCL.EXE's weak hooks stayed NULL and HELP still answered %HELP-E-OPENIN. Add dcl_help_acp to mk_dcl.sh's TU list and bump run_dcl_native.sh's NOBJ guard 25->26 (rms_textfile_* are already exported in libvms_shr.vec; vmsfs_to_linux_path via LIBVMSFS$SHR). probe_4ac.sh: throwaway diagnostic (removed before final) to capture the SYSTEM session's UIC/privs, the real owner+protection of the system tree, a create attempt, and provision's boot output -- to find why SYSTEM still gets %RMS-E-CRE in SYS$SYSTEM despite the volume owning it [1,4]. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…timeout Temporary: capture the exact RMS status behind SYSTEM's %RMS-E-CRE in SYS$MANAGER:/SYS$SYSTEM: (boot log already confirms identity [1,4] + clean ownership walk, so the CRE is a create-path failure, not ownership). Reverted before the PR is finalized. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The prior approach added dcl_help_acp.c as a NEW translation unit to the VMS-native DCL.EXE build (mk_dcl.sh / link_native_graph). Two rail boots hung right after STARTUP began (no login prompt) with that image, while the image built before the change booted to login -- the new native TU broke DCL.EXE. Rework: DCL reaches the HELP-library ACP read through the same two weak seams, but the DCL-side implementation now lives in dcl_cmd_misc.c (already a native DCL TU) and reads via DCL's OWN RMS reader (dcl_rms_read_*, the path TYPE/COPY already use over the executive ACP) -- no new native TU, no new shareable import, so the native DCL.EXE image shape is unchanged. dcl_help_acp.c is kept ONLY for the standalone HELP.EXE (cmake), which is not DCL and uses rms_textfile. Reverted mk_dcl.sh / run_dcl_native.sh / VMSDCL_SOURCES additions. (DEBUG still present in dcl_filespec.c to capture the sys$create status behind SYSTEM's %RMS-E-CRE -- reverted before the PR is finalized.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…LL candidates Ground truth from the rail (DBG capture): SYSTEM's %RMS-E-CRE in SYS$MANAGER:/ SYS$SYSTEM: was sys$create returning RMS$_DNF (114762, "directory not found") -- NOT an ownership denial. The boot log confirms the SYSTEM session is identity [1,4] and the system tree is owned [1,4]; the create simply never reached the directory. Root cause: rms_impl_create() (the sys$create backend) resolved the directory against specs[0] ONLY, while rms_impl_open() (sys$open) loops over ALL ODS-2 candidates. A concealed-rooted logical (SYS$SYSTEM: = SYS$SYSROOT:[SYSEXE] = [SYS0.SYSCOMMON.SYSEXE]) expands to several candidates in search-list order -- the node member [SYS0.SYSEXE] (which does not exist) THEN the SYSCOMMON member (which does). $OPEN walked to SYSCOMMON, so TYPE worked; $CREATE tried only specs[0] and failed RMS$_DNF. A physical directory ([USERS.GUEST]) has one candidate, so GUEST's own-home create was unaffected -- which is why only the system tree failed. This is the SAME multi-candidate walk vms-5f0 already gave rms_open_named_handle_kind. Fix: rms_impl_create resolves the DID against each candidate and creates in the FIRST that resolves -- the same directory the read path lands in. GUEST creating in SYS$SYSTEM: now correctly reaches the directory and is refused by the ACP protection check (RMS$_CRE), the intended access-control denial. (DEBUG in dcl_filespec.c retained one more run to confirm the DNF is gone; reverted before the PR is finalized.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fixed) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pty read-back) With the DNF walk fixed, SYSTEM's COPY into SYS$MANAGER:/SYS$SYSTEM: now succeeds -- but TYPE read the new files back EMPTY. Root cause: rms_impl_create stamped every non-FIXED file ODS2_FK_DATA (RFM=VAR, fat_rtype 2), including a FAB$C_STMLF source. A COPY of a stream-LF text file (LOGIN.COM ...) was written stream-LF but the on-disk FAT said VAR, so the reader (rms_file_attr -> fat_rtype -> sys$get) reframed the LF stream as variable-length records -- a bogus length word then EOF -- and returned nothing. Map the create kind to the RFM: FAB$C_STMLF -> ODS2_FK_DATA_STMLF, FAB$C_FIX -> ODS2_FK_DATA_FIX, else VAR; indexed files keep ODS2_FK_DATA (Prolog-3 on top). The created file's FAT now matches its bytes and TYPE reads the content back. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…HOW hang Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…p DEBUG HELP SHOW read SYS$HELP:HELPLIB.HLP over the ACP but was SLOW enough to blow the UAT's 10s command timeout on a loaded TCG node (rail trace: it progressed steadily -- recs 200..2400, len ->48 KB of 69 KB -- so it was throughput, not a hang). Root cause: rms_seq.c reads STMLF records BYTE-BY-BYTE, and rms_io_read_acp issued one IO$_READVBLK per call -- ~69000 single-byte ACP QIOs to read a 69 KB library. Fix: give the rms_file handle a one-block read-ahead cache. rms_io_read_acp now serves small (sub-block) reads from a cached 512-byte block, refilling with one QIO per block (~135 QIOs for HELPLIB instead of ~69000); a large read still transfers directly. The cache is keyed by absolute file offset (an lseek inside it still hits) and is invalidated by any write on the handle. Benefits every sequential text read over the ACP (TYPE of large files, HELP, ...), not just HELP. rms unit tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ing, not part of the fix) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Closing as subsumed by the V0.5/V0.5-1 releases (the ACP flip). Its payload already shipped on main via the #689/#696 squash-merges; verified by content-diff (two independent triage passes). Subsumed AND regressive — main rms_io.c has the multiblock read-ahead cache + ovmx_provision keyed on OVMX_HAVE_ACP (vms-329); this branch carries the older one-block cache + linux POSIX-passthrough. Merging would downgrade the cache and revert vms-329. Branch is retained (not deleted) — reopen if you find genuine residue I missed. |
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.
vms-4ac — VMS User Acceptance Test: fix the create-file + HELP defects (excise flip /vms residue)
Makes the RED
VMS User Acceptance TestGREEN: 59 passed / 0 failed on the real runtime (QEMU boot,node=k3s-worker, docker-wrapped e2e). Baseline was 51/8.The task framed DEFECT 1 as "provision_ownership no-op". Ground-truth diagnosis on the runtime showed the real cause was a cascade of RMS-over-ACP create bugs the retired /vms passthrough had masked, not ownership. Each layer is fixed with the executive Files-11 ACP as the only runtime path (Rule 9 / INV-6 — no /vms on the runtime path, fail-honest everywhere).
DEFECT 1 — logged-in UICs could not create files
src/vmsrms/rms_core.c,rms_impl_create).sys$createresolved the directory againstspecs[0]ONLY, whilesys$openloops ALL ODS-2 candidates. SYS$SYSTEM:/SYS$MANAGER: expand to[SYS0.SYSEXE](nonexistent node member) then the SYSCOMMON member — so TYPE opened fine but CREATE failed RMS$_DNF. A physical dir ([USERS.GUEST]) has one candidate, so only the system tree failed. Fix: walk all candidates and create in the first that resolves — the same fix vms-5f0 gaverms_open_named_handle_kind.rms_impl_create). Every non-FIXED file, including FAB$C_STMLF, was stampedODS2_FK_DATA(RFM=VAR). A stream-LF COPY was written stream-LF but the FAT said VAR, so the reader reframed it as VAR records → bogus length, then EOF → empty. Fix: map the create kind to the RFM (STMLF→ODS2_FK_DATA_STMLF, FIX→ODS2_FK_DATA_FIX, else VAR; indexed keepsODS2_FK_DATA).src/ovmx_provision/ovmx_provision.c). Homes are not mastered;prov_home_cbcreated them withmkdir()on the retired /vms path (ENOENT, swallowed). Fix:provision_home()creates-if-missing (IO$_CREATE, directory, owned by the account) and (re)owns each account's login directory over the ACP.provision_ownership()/own_object()set ownership withlchown()on /vms (no-op post-flip); rewritten to IO$_MODIFYfh2_fileownerover the ACP (walk [SYS0] via IO$_ACPCONTROL wildcard + per-file IO$_MODIFY), run as SYSTEM aftervms_kif_establish_system(). NOTE: the master already stamps [1,4], so this is belt-and-suspenders for SYSTEM; it is load-bearing only for per-account homes and any root-seeded files.DEFECT 2 — HELP SHOW → %HELP-E-OPENIN
cmd_helpand standalone HELP.EXE)fopen()'d the translated /vms path. Now reads SYS$HELP:HELPLIB.HLP over the ACP:dcl_help.creaches the read through two weak seams (engine stays dependency-free so its hermetic unit test still links it alone); the DCL-side seam lives indcl_cmd_misc.cand reuses DCL's owndcl_rms_read_*(no new native-DCL TU, no new shareable import); HELP.EXE carries its own seam (dcl_help_acp.c, rms_textfile) + an RMS force-bind anchor. A binary .HLB is taken through the POSIX/.HLB-reconstruct path; the volume ships the .HLP source.src/vmsrms/rms_io.c): STMLF records are read byte-by-byte, and eachrms_io_read_acpwas one IO$_READVBLK — ~69,000 single-byte QIOs for the 69 KB HELPLIB, slow enough to blow the UAT's 10 s command timeout on a loaded TCG node. Added a one-block read-ahead cache to the RMS handle (~135 QIOs now). Benefits every sequential text read over the ACP.⚠ Shared kernel-core / 3-way convergence gate
src/kernel-core/vmsfs_acp.ccode. The ACP set-owner primitive (IO$_MODIFYattr_ctlVMS_ACP_ATTR_OWNER writingfh2_fileowner) and directory IO$_CREATE already exist onwork/vms-0.5-final(b541e4f / vms-738 lineage); this branch is their first runtime caller. The primitives are width-clean (fixed-width LE FH2 field writes viaacp_put16, no long/pointer assumptions).Verification
VMS User Acceptance Teste2e on the dind-rail (docker-wrapped QEMU): Passed: 59, Failed: 0,node=k3s-worker. SYSTEM creates + TYPE-readback in SYS$MANAGER:/SYS$SYSTEM:, GUEST creates in its own login dir, GUEST correctly denied SYS$SYSTEM:, HELP SHOW renders.🤖 Generated with Claude Code