# Java FIXP finalization metadata ownership

Java FIXP finalization metadata bypassed the store descriptor registry.
Reading a metadata alias of a live store released its process-owned file
lock, even though recovery refused the malformed metadata. Appending through
the alias wrote a finalization record into the live payload journal, with
the same unregistered-descriptor close hazard. The correction guards metadata
descriptor lifetimes;
ordinary application saves and replay are unchanged.

## Confirmed failures

At source pin `59748c1b44c2d545a497737c8257ed1d0dc0f331`, the new
nine-case fixture reports four passes, five failures and zero skips on
Java 17. Two failures are hard-link and symbolic-link metadata reads: a
separate JVM sees the victim's byte-zero lock held before the attempted
open and free afterwards. Both metadata append cases grow the victim's
payload file from **49 to 77 bytes**, adding one 28-byte `LHFF` record.
The fifth failure accepts a metadata read while the file is already
registered exclusively by another owner.

The victim is a live, synced pure-Java `FileStore`. Snapshots use its
existing channel with positional reads, never a second local descriptor
whose close could itself release the kernel lock. The candidate's metadata
path stays stable during each operation. The read preflight refuses the
candidate before creating its payload file.

The baseline log, Surefire XML, source pin and original fixture patch are
retained under `evidence/java-finalized-ownership-20261013/retained/` and
`/home/yann/archive/libhft/java-finalized-ownership-20261013/`.

## JVM compatibility

The same compiled Java 8-compatible production classes and twelve-case
fixture pass on Java **8, 11, 17, 21 and 25**: sixty executed cases, zero
failures, zero ignored tests and zero failed assumptions. The standalone
listener counts assumptions explicitly, since a default JUnit `OK` alone
would hide them. Each external lock contender uses that execution's own JVM.
Class, dependency and executed-driver hashes verify unchanged across all
five executions. These are store ownership checks, not five full-suite runs.
The runtime logs and hashes are retained in `retained/jvms.tar.gz`.

## Publication verification

Rebasing onto `179bd193cd0222760d2467be5efad02828aacc85` preserves
all **190 relevant Java store/FIXP source and test files and native storage
headers** selected from the qualification manifest, byte for byte. This
includes the corrected production files, regression fixture, common store
implementations and external contender. Every selected hash verifies after
the rebase. The integrated and upstream pins and selected manifest are
retained in `retained/publication.tar.gz`.

This is source-equivalence verification of the qualified store code after
integration. The full-suite counts above belong to the recorded qualification
pin; they are not a claim that every unrelated new main commit was retested.

## Reproduction

Wrap every driver in `host-cpu-slot`, using your own session worktree and
out-of-tree paths. `run-checks.sh focused` builds and runs the pure-Java
fixture; `run-checks.sh qualification` rebuilds both native libraries and
runs the full development/open suites and boundary/public checks.
Set `LIBHFT_REVIEW_REPO`, `LIBHFT_VERIFY_EVIDENCE`, `LIBHFT_VERIFY_BUILD_ROOT`
and `LIBHFT_VERIFY_TMPDIR` explicitly. `run-jvms.sh` uses the focused build's
classes and the installed five JVMs.

The missing-key baseline uses `run-null-key-baseline.sh`, the retained
pre-fallback source snapshot (`LIBHFT_BASELINE_INPUT_ROOT`) and the original
baseline build (`LIBHFT_BASELINE_BUILD_ROOT`). It expects exactly two
failures. The original nine-case baseline requires the original fixture
patch at its recorded production pin and expects five failures. Baseline
failures are evidence of the defects, never successful qualification.

## Descriptor ownership correction

`StoreFileHandles.exclusiveChannel` admits a metadata descriptor through
the existing canonical-path and file-key registry. A known alias is refused
before a second descriptor is opened. New metadata handles keep their
reservation until after the descriptor closes, on success and failure.
The existing store APIs and their shared-handle reference counting remain
unchanged.

Canonical paths alone miss hard links when a provider exposes no file key.
OpenJDK's [Windows attributes in Java 8](https://raw.githubusercontent.com/openjdk/jdk8u/master/jdk/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java)
and [Java 17](https://raw.githubusercontent.com/openjdk/jdk17u/master/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java)
return null there. The fallback now uses `Files.isSameFile` through the
registered pathname; providers without file keys require that pathname to
remain stable while the handle is held. The ordinary Linux file-key lookup
is unchanged. OpenJDK's [Unix identity lookup](https://raw.githubusercontent.com/openjdk/jdk17u/master/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java)
reads attributes rather than opening another payload descriptor.

Before this fallback, a twelve-case standalone fixture at production pin
`4cd3fe9b9` reports **ten passes and two expected failures**, with no ignored
tests or failed assumptions. Both failures clear only the victim handle's
cached file key to exercise the fallback while leaving its descriptor and
OS lock intact: a hard-linked metadata read frees the lock, and a hard-linked
append changes 49 payload bytes to 77. An independent-file control still
records and recovers metadata. Frozen pre-fallback sources, the executed
driver's hashes, reused class hashes and raw output are retained under
`retained/null-key-baseline.tar.gz`. This is a Linux reproduction of the
missing-key path, not a Windows runtime qualification.

Metadata reads remain read-only and never create an absent file. Appends
retain `CREATE`, `WRITE` and `APPEND`, followed by the existing record and
directory sync. A refused append poisons the journal and refuses retries,
as any other uncertain finalization write does. The guard adds no lock,
file operation or allocation to ordinary application save or replay.

The registry protects descriptors registered in this JVM. Arbitrary
application descriptor closes and pathname replacement during admission
remain outside its documented ownership contract. The C++ and Rust Linux
payload stores use descriptor-owned record locks plus `flock`; the managed
process-owned lock release proved here must not be inferred for them.

## Focused and full qualification

At source pin `7d74f3364639ad039c2c80fae2be7b44f932b9bd`, all twelve
focused cases pass with zero skips. These are pure-Java store tests; this
focused run explicitly disables the native build and native requirement.
The broader runs rebuild both the development and open-edition JNI libraries
through their normal Maven producer, with the native requirement enabled.
Both native-library guards pass rather than accepting a missing library as
a skip.

The full qualification runs under an `isol-no-shared` lease with Java 17
and GCC 14. The final Surefire reports contain **7,168 development cases
and 6,696 open-edition cases**, all passed, with zero errors or remaining
skips. The three disabled-counter cases skip in each ordinary fork and pass
in their dedicated disabled-counter fork. Counts are unique reported cases,
not an inflated sum of repeated special-fork executions.

Both full runs also pass the two live market-data/order allocation cases.
They are functional allocation checks on loopback, not NIC latency results.
The open boundary check compiles **222 public sources without 67 private
sources**. The three changed public Java files scan with zero findings.
Source and executed-driver hashes verify unchanged after qualification.

Raw logs, Surefire XML, toolchain details, source pins, patches and input
hashes are retained in `retained/fixed-focused.tar.gz` and
`retained/qualification.tar.gz`. Their original retained archive is
`/home/yann/archive/libhft/java-finalized-ownership-20261013/`.
