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>
105 lines
3.5 KiB
Markdown
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.
|