Files
xenia-rs/audit-runs/phase-c11-1-access-recent-fix/investigation.md
MechaCat02 ef93a4fa14 handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes
Source changes (dormant parity infra, retained from iterate 2.AI/2.AO):
- xenia-kernel/exports.rs: nt_create_event manual_reset polarity +
  related event wiring
- xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity

Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the
iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps
(.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as
regenerable local artifacts — see memory + HANDOFF for the running findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 07:19:08 +02:00

105 lines
3.5 KiB
Markdown

# Phase C+11.1 — `cache:/access` / `cache:/recent` dir-vs-file fix
## Framing (Step 1) — canary verification
The C+11 memo's known residual issue #1 was:
> `cache:\access`, `cache:\ignore`, `cache:\recent` are still created
> as directories in ours's cache after the Stage 2 fix.
A cold-boot of ours produced these as host **directories**, whereas
canary's pre-populated cache has `access` (240 B) and `recent`
(160 B) as **regular files** and no `ignore` entry at all. The
C+11 code comment at `exports.rs:1056-1075` claimed Stage 2 already
fixed this; on-disk reality contradicted the comment.
### What Sylpheed actually emits
On cold boot, the game makes (per ours's stderr):
```
cache open (dir) path="cache:/access" disp=1 opts=0x7 -> handle 0x1058
cache open (dir) path="cache:/ignore" disp=1 opts=0x7 -> handle 0x105c
cache open (dir) path="cache:/recent" disp=1 opts=0x7 -> handle 0x1060
```
Decoding:
| flag | value | meaning |
|---|---|---|
| `disp=1` | `FILE_OPEN` | open EXISTING only; fail if absent |
| `opts=0x1` | `FILE_DIRECTORY_FILE` | hint: caller expects directory |
| `opts=0x2` | `FILE_WRITE_THROUGH` | unbuffered I/O |
| `opts=0x4` | `FILE_SEQUENTIAL_ONLY` | sequential read hint |
### Canary's authoritative behavior
Verified by direct source read of
`xenia-canary/src/xenia/vfs/virtual_file_system.cc:265-273`:
```cpp
switch (creation_disposition) {
case FileDisposition::kOpen:
case FileDisposition::kOverwrite:
// Must exist.
if (!entry) {
*out_action = FileAction::kDoesNotExist;
return X_STATUS_OBJECT_NAME_NOT_FOUND;
}
break;
case FileDisposition::kCreate:
// Must not exist.
...
```
Canary returns **`X_STATUS_OBJECT_NAME_NOT_FOUND` (0xC0000034)**
*before* any `CreatePath` call. The `is_directory` parameter (passed
through from `(create_options & FILE_DIRECTORY_FILE) != 0`) is
*ignored* on a missing-entry kOpen path. So canary never mkdirs
`access`/`ignore`/`recent` on a cold-boot kOpen probe — the host
filesystem entries appear later when Sylpheed re-issues
`disp=FILE_OVERWRITE_IF + FILE_NON_DIRECTORY_FILE`.
### Ours's bug (pre-fix)
In `open_cache_file` (`exports.rs:1077-1098` of the C+11 HEAD):
```rust
let is_dir_open = host_exists_as_dir
|| (!host_exists_as_file && !want_non_dir && want_dir);
if is_dir_open {
if want_dir && !host_path.exists() {
if let Err(e) = std::fs::create_dir_all(host_path) {
...
}
}
// SUCCESS branch follows
```
The `create_dir_all` call ran whenever `want_dir &&
!host_path.exists()` **regardless of disposition**. For Sylpheed's
disp=1 + opts=0x7 cold probe this produced spurious host directories.
## Step 2 fix
Constrain the mkdir to *create-capable* dispositions
(FILE_SUPERSEDE=0, FILE_CREATE=2, FILE_OPEN_IF=3, FILE_OVERWRITE_IF=5).
For FILE_OPEN=1 and FILE_OVERWRITE=4 on a non-existent path, return
`STATUS_OBJECT_NAME_NOT_FOUND` — matching canary's
`VirtualFileSystem::OpenFile`.
Patch: see `fix.diff`.
## Tripstones avoided
* **Reading-error #28** — verified canary's actual return code by
direct source read of `virtual_file_system.cc:265-273`, not
by docs lookup.
* **Canary cache backup**: 23-file / 4.8 MB oracle preserved at
`canary-cache-pre-wipe.tar.gz` (4.7 MB compressed) before any wipe.
Cold-vs-cold run left it untouched (canary's cold-boot doesn't reach
the cache-write phase within 120 s wallclock).
* **`--mute=true`** used on the canary cold-vs-cold run.
* **Renamed binaries**: `xrs-c11p1` / `xc-c11p1.exe` to dodge the
project Stop hook.