# Rust QuickFIX recovery preserves the index on seek failure

The Rust QuickFIX C++ and QuickFIX/J stores now finish their append-position
checks before trimming an incomplete index suffix. Previously, a refused open
could already have changed the index. This record retains the driven failures,
the cold recovery correction and independent verification of the recently
landed inherited-lock fixes. It does not complete the all-flavour review.

## Reproduced failures

Baseline source is ba6c5d9a1, with only the new integration test added. Each
profile is exercised with a complete record, an incomplete record and an empty
index. A FIFO body reproduces an actual ESPIPE without production hooks. Its
reported length is zero, so recovery regards the final complete record as
dangling, truncates the index, then fails its body append seek. The C++ index
shrinks from 6 bytes to zero; the Java-format index shrinks from 16 to zero.
The one-byte incomplete indexes also disappear. Both empty controls pass.

An external LD_PRELOAD probe then injects EIO on regular files, separately at
the body append seek and the header append-position seek. The complete-record
cases have a valid payload and index prefix followed by one incomplete byte.
The baseline trims that byte before refusing the open: 7 to 6 bytes for C++,
17 to 16 for Java. Both one-byte incomplete indexes again become empty, while
the empty controls pass. Each of the three baseline executions therefore has
four failures and two passing controls, with the exact old executable retained.

The regular-file probe demonstrates premature repair, not deletion of a valid
regular-file record: its accepted prefix survives. The FIFO case is invalid
backing storage, not evidence of data loss on a healthy filesystem. No genuine
device failure was induced. The probe ignores read_to_end's optional SEEK_CUR
buffer-sizing query and verifies six injected append-position errors per run;
the initially misdirected sizing-query probe is retained separately.

## Correction and regression coverage

Recovery first positions the body for appending. If the header needs repair,
it seeks to the intended repaired length before truncating; otherwise it seeks
to the existing end. There is no fallible positioning operation after repair.
Accepted-record validation and payload reads still precede any truncation.
There are still two final positioning operations, and the send path, locks,
allocation behaviour, disk layout and ABI are unchanged.

The six checked-in Linux cases assert the refused open and exact errno, byte
preservation of the index and existing sequence/session checkpoints, then
replace only the FIFO body and reopen. They check the recovered payload and
counters, append a new record and reopen again. This also proves that refusal
releases the writer and owner fences. The optional regular-file mode requires
an external injector; without an error it fails instead of silently skipping.

## Verification and retained evidence

Rust 1.92.0, offline, on hp, with outputs under the session build root and every
foreground job protected by host-cpu-slot in the shared pool:

| Check | Development edition | Open edition |
| --- | --- | --- |
| FIFO recovery regressions, six test threads | 6 passed | 6 passed |
| Regular body append EIO, serial cases | 6 passed | 6 passed |
| Regular header append EIO, serial cases | 6 passed | 6 passed |
| Independent inherited-lock regressions, six test threads | 6 passed | 6 passed |
| Full default-feature crate suite | 1,021 passed | 890 passed |

There are no failed tests. Each full suite retains the one existing ignored
builders doctest. The development total is 1,004 library tests plus 17 other
tests; the open total is 878 library tests plus 12 integration tests. These
counts overlap the focused checks and must not be summed as unique coverage.
Both changed engine files pass the export scrub with zero findings.

After integration over 6bd2d5c99, both full suites pass again: 1,033 development
tests (1,012 library and 21 others) and 900 open-edition tests (884 library and
16 others), with the same ignored doctest. These totals include the other
sessions' landed HA checkpoint and session-counter tests. Both regular-file
fault selections pass with six concurrent test threads and exactly six injected
errors each. Without the injector, both regular-file negative controls fail all
six cases as required. Strict Clippy 1.93.1 with all targets and `-D warnings`
passes in both editions. Integrated logs and the exact two-file source manifest
are retained separately from the initial verification.

Evidence lives in
`~/archive/libhft/rust-quickfix-recovery-io-20261003/`: source pin, exact patches
and copies of the new test, compiler versions, scripts, injector source,
baseline binary hash, verified source hashes and complete logs. The retained
old executable and compiled injector are copied into that archive too, so
session build cleanup does not remove this evidence.

No latency improvement is claimed. This does not prove transactional recovery
across subsequent checkpoint-write failures, explain the older missing-record
observation, or verify NIC transports, optional venue features, Windows or the
entire monorepo conformance matrix. Concurrent sequence-width and other store
changes are outside this correction.
