# Phase C+13 fix.diff — targeted excerpt # (full tree has many uncommitted prior-phase changes; this file # documents only the C+13 delta against the pre-C+13 state.) ## crates/xenia-kernel/src/exports.rs ### 1. New helper `is_disc_prefix` (~30 LOC, inserted near `STATUS_OBJECT_NAME_COLLISION`, ~line 1282) ```rust /// Phase C+13 — does `raw_path` start with a prefix that aliases the /// (read-only) game disc? Used to scope the synth-empty fallback in /// `open_vfs_file`: missing disc files report `STATUS_OBJECT_NAME_NOT_FOUND` /// (matching canary's `NtCreateFile_entry` for game-data lookups), while /// missing writable-partition paths keep the legacy zero-byte synth. /// /// Mirrors the disc-mapped subset of `crate::path::DEVICE_PREFIXES`: /// - `game:\` — canary's symbolic-link alias for the disc /// (xenia-canary/src/xenia/kernel/kernel_state.cc registrations). /// - `d:\` / `D:\` — drive-letter alias for the disc. /// - `\Device\Cdrom0\` — NT device path for the disc. /// /// Compares case-insensitively to match canary's path resolver. fn is_disc_prefix(raw_path: &str) -> bool { let lowered = raw_path.trim_start().to_ascii_lowercase(); const DISC_PREFIXES: &[&str] = &[ "game:\\", "game:/", "d:\\", "d:/", "\\device\\cdrom0\\", "\\device\\cdrom0/", ]; DISC_PREFIXES.iter().any(|p| lowered.starts_with(p)) } ``` ### 2. `open_vfs_file` — capture raw path (after the `path` initialization, ~line 1314) ```rust // Phase C+13 — recover the raw (un-stripped) path so we can tell a // disc-aliased prefix (`game:\`, `d:\`, `\Device\Cdrom0\`) apart from a // writable-partition prefix (`\Device\Harddisk0\…`, `\??\`, raw "no // prefix" cases). The synth-empty fallback below covers both today but // canary's `NtCreateFile_entry` (xboxkrnl_io.cc:83-110) returns the // VFS lookup status verbatim, which is `STATUS_OBJECT_NAME_NOT_FOUND` // for any disc path that isn't in the ISO. Scoping the synth to // non-disc prefixes makes us match canary's behaviour for missing // game-data files (e.g. `game:\dat\files.tbl` at Phase C+13 idx 103862). let raw_path = crate::path::object_attributes_raw_name(mem, obj_attrs_ptr) .unwrap_or_default(); ``` ### 3. `open_vfs_file` — short-circuit disc paths in the `Err(e)` synth-empty branch (~line 1413) ```rust Err(e) => { // Phase C+13 — scope the synth-empty fallback to non-disc // prefixes only. Canary's `NtCreateFile_entry` returns the VFS // result verbatim (xboxkrnl_io.cc:83-110); for a missing disc // file like `game:\dat\files.tbl` that's // `STATUS_OBJECT_NAME_NOT_FOUND`. Sylpheed handles NOT_FOUND // cleanly (next event in canary's trace at idx 103862 is // `RtlNtStatusToDosError(0xc0000034) -> 2`, then the boot // validator continues), so the synth was masking the // correct branch. // // Synth-empty is still kept for writable system partitions // (`\Device\Harddisk0\…`, `\Device\Mass*`, `\??\`, raw paths) // because those aren't backed by the disc — Canary mounts // them on host directories // ([xenia_main.cc:612-651](xenia-canary/src/xenia/app/xenia_main.cc)); // ours skips the host mount for those and falls back to the // legacy stub to avoid regressing audit-006 / audit-018 // disc-validation probes. `cache:/` was already routed to // `open_cache_file` upstream of this branch (AUDIT-038). if is_disc_prefix(&raw_path) { if handle_out != 0 { mem.write_u32(handle_out, 0); } write_io_status_block( mem, io_status_block, STATUS_OBJECT_NAME_NOT_FOUND as u32, 0, ); tracing::info!( "Disc path missing: raw={:?} norm={:?} err={} -> NOT_FOUND", raw_path, path, e ); return STATUS_OBJECT_NAME_NOT_FOUND; } // … existing synth-empty branch unchanged … } ``` ### 4. Tests — 4 new tests inserted before `cache_resolve_strips_path_traversal` (~line 7838) - `is_disc_prefix_recognises_disc_aliases` — unit test for the prefix classifier; covers `game:\`, `D:\`, `\Device\Cdrom0\`, and several non-disc prefixes that MUST return false. - `nt_create_file_game_prefix_missing_returns_not_found` — primary fix test (idx 103862 in canary's cold trace). Asserts `STATUS_OBJECT_NAME_NOT_FOUND`, null handle, and IOSB.status records NOT_FOUND. - `nt_create_file_cdrom_prefix_missing_returns_not_found` — `\Device\Cdrom0\` alias variant. - `nt_create_file_non_disc_prefix_missing_still_synthesizes` — regression guard: a missing `\Device\Harddisk0\Partition1\sys.bin` still gets a zero-byte synth (preserves audit-006 / audit-018 behaviour). ## Summary Total: ~30 LOC engine code + ~95 LOC tests = ~125 LOC. Kernel tests 177 → 181. Stable digest shifts: `ad4f74ee324fdedb0bfdd4cc4c6468e9` → `e1dfcb1559f987b35012a7f2dc6d93f5`. Phase B `image_loaded_sha256` unchanged: `ea8d160e…`. Main cold-vs-cold matched prefix: **103862 → 104574 (+712)**.