LeVCS/crates/levcs-store
Levi Neuwirth 30c464d97d Refuse redirected store metadata paths
The last three hazards of contract review 2026-07-28-D, closed together
because they are one invariant: no name the store *invents* beneath a root
may be reached through a link or resolve to an object of the wrong type. The
flags and descriptor checks are the safety property, so the mechanics go in
the `sys.rs` funnel where the next author looking for how this crate opens
files will find them.

Refusal semantics differ per name, because the names mean different things.
`write_fenced` adopts and empties an existing regular `.tmp` — that is
residue from an interrupted attempt at this exact write, and reusing the name
is how a retry works — which is why the open and the truncation had to be
separated: no flag combination truncates only regular files, so the type
check needs the descriptor first and `O_TRUNC` cannot be in the open.
`read_format` keeps three answers apart: absent stays `Io(NotFound)` because
startup states 1 and 3 depend on it, non-regular is `UnrecognizedLayout`, and
a corrupt regular marker keeps its decode error. `initialize_root` leaves the
caller's root and ancestors alone, adopts existing directories beneath it, and
classifies every planned entry before creating any missing one so a refusal
cannot half-extend the tree it refused.

Directory fences now go to descriptors already validated rather than
re-resolving the name, which would hand the fence to whatever the name
resolves to now instead of what was checked. The fence sequence is otherwise
identical on purpose: `engine.rs` asserts the count exactly.

Each protection was reverted independently and the witnesses observed:

  - `write_fenced` — a 4096-byte file outside the root truncated and
    rewritten through a live link, a file created outside the root through a
    dangling one, and a fifo at the name blocking the open for the full
    ten-second deadline: an unbounded startup hang from one `mkfifo`.
  - `read_format` — a foreign `FORMAT` read in full, its `shard_count` and
    `root_uuid` returned as this root's, so every file in the tree would then
    be validated against a marker the store never wrote. Same hang on the
    read side.
  - `initialize_root` — returned `Ok(FormatMarker)`, reporting a working
    store with its shard tree built outside the root. The preflight has its
    own witness: `shards/00/active` left behind by a refusal that named
    `shards/00/segments`.

The witnesses sit on the `segment` entry points. `StoreEngine::open`'s
classifier refuses a redirected root before any of this is reached, so a test
entering that way passes whether or not the protection exists — and
`RecoverySession::open`, `drive.rs` and `store-bench` all arrive without it.

Disclosed: the device-node residual now covers `FORMAT.tmp` too — one
`O_NONBLOCK` open before the `fstat` refuses it, still gated behind `mknod`
privilege inside a configured root. `rename_noreplace` needed no change;
`RENAME_NOREPLACE` fails `EEXIST` on an occupied target whether or not it is
a link.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKzM69CHmBuDcA3qN1jFdh
2026-07-29 21:47:33 -04:00
..
benches Freeze Wave A: Phase 1 storage spine 2026-07-26 19:47:03 -04:00
examples Freeze Wave A: Phase 1 storage spine 2026-07-26 19:47:03 -04:00
src Refuse redirected store metadata paths 2026-07-29 21:47:33 -04:00
tests Accept only the braced test-item shapes this crate uses 2026-07-29 20:30:41 -04:00
Cargo.toml Implement D0-B storage publication interfaces 2026-07-27 22:31:32 -04:00