Repository navigation
Windows libc fopen reports ENOENT for existing files under long host paths (PPSA99902) #7182
Description
Failure
Windows fopen can return ENOENT for an existing guest file when its resolved host path crosses MAX_PATH. Found while validating Sonic Unleashed Recompiled, PPSA99902 (#3036; discussion #4443).
A source-only fork observer at 043f7e7541 records repeated failures for /app0/ressources/dlc/Empire City & Adabat Adventure Pack/Languages/English/WorldMap.arl. The resolved host path has 266 characters and the file exists (288 bytes). The native open helper is unchanged from main 84b0d2f24863d1e88105ada295cc346f720c1a6a; this game run uses the broader development integration and is not an unchanged-main title result.
An independent native Windows control against that exact file returns:
ordinary path: 266 characters, opened=0, Win32 error=3
extended path: 270 characters, opened=1, Win32 error=0, file_size=288
The same file, access flags and process are used; only the native path spelling changes. No asset or configuration is modified.
Isolated reproduction on main84
Test-only commit 94ea13c621 adds a Windows fixture and registration to unchanged main 84b0d2f248. It creates a file through an extended host path, maps a short guest prefix to its deep host directory, then calls guest fopen/fread.
guest_fopen_long_host_path fails in 3/3 fresh processes (0.01–0.03 s each):
fopen mode rb failed: guest errno=2, resolved characters=328
fopen rejected a valid mapped long host path
Windows 11 Enterprise 26200, WinLibs GCC 15.2.0 r7, Release. This is CPU/filesystem behavior; no GPU inference is needed.
Cause and proposed correction
OpenNative in core/libs/prx/libc/src/FileAndHeap.cpp passes the resolved path directly to CreateFileW. The process's path-length opt-in therefore affects guest file access. Microsoft's CreateFileW documentation and file namespace documentation document the extended Unicode path form. #4618 enabled long paths for relinker.exe itself and explicitly did not change converted executables; it does not cover this runtime call.
Focused fork proposal: normalize the absolute host spelling and use its extended drive/UNC form only when it crosses MAX_PATH; preserve existing namespace prefixes, access/share/disposition flags and errors. The corrected fixture passes 3/3 and covers read, creation, exclusive-existing refusal, append/update, exact bytes and missing-file ENOENT. Full Windows relinker/system-library builds and 10 neighboring file/stream/lock groups, each repeated 3 times, pass. Upstream PR #7187 now records head cf84fc44a8f969bb39cc079c0e9b8c7abfc27207, rebased onto main66702f1c after #7068 merged. Full local Windows/Linux relinker/library builds pass, with 36/18 repeated focused passes and passing conventions/registration. The review's split-drive fixture defect is reproduced 3/3 before correction and passes 3/3 after; review reply and exact scope. A new maintainer full CI run is requested; the previous head's Windows CI failed that fixture.
#7068 concerns errno translation, #6287 append access, and #6541 random-device streams; none implements this path conversion. Searches found no existing issue/PR for the same runtime failure. This proposal is limited to fopen's native open path; it does not claim all Windows file APIs are long-path independent.
Matched title validation
The baseline observer043f and observer plus the production fix5675 run for 120.792 / 120.541 s on verified NVIDIA RTX5080, driver32.0.16.1062. Only libc.prx differs; the executable, guest module, other126 libraries and dependencies match. Both runtime roots have equal length, fresh original assets/user data, unchanged original configurations/protected hashes. Khronos1.4.363 is loaded, zero observed validation errors/warnings.
Among intact bounded samples, baseline has69 false ENOENT records for the existing266-character .arl; fixed has none. Corrupted/interleaved lines are excluded (1 baseline,6 fixed detected by the parser; fully interleaved markers can also be unparseable). Fixed samples instead include an absent WorldMap.ar; the directory contains WorldMap.ar.00. Those are different paths, and no new correctness claim is inferred from the absent unsuffixed probe. Libc read totals rise from30,095,141 to32,599,419 bytes. This is behavioral evidence, not a throughput measurement.
Both runs reach their120s harness limit (exit1), with32 captures and final initial NOW LOADING. The remaining loading cause is unknown; no title-unblock/FPS claim. These are component observations within the retained integration, not unchanged-main whole-title results.
Title scope
Both diagnostic runs still end at initial NOW LOADING after their 120-second harness limits. The failed open is confirmed; it is not yet established as the cause of that frontier. No menu, gameplay, FPS or title-unblock claim.
AI-assisted: yes, Codex; author responsible for the report and code.
Subsequent review and current title follow-up
#7187 current review follow-up, headcf84 on main667, preserves namespace prefixes before nonthrowing absolute-path resolution. The new existing-namespace case fails3/3 before and passes after; full Windows/Linux builds and36/18 focused passes are repeated, with3 split-drive passes. Automatic checks that ran are green; a fresh full maintainer CI run remains requested. The earlier matched title component evidence above retains its5675 pin.
The independent metadata gap #7195 is implemented by MarcZX100 in #7196, with ordinary/extended real-file controls and repeated Windows/Linux validation. Current integrationc784 incorporates both fixes on main667 and now reaches the known GPU stops from long physical roots: NVIDIA28.656s resident8x DCC refusal; AMD70.750s Vulkan device loss-4. Both last captures remain NOW LOADING, no timeout or original-input changes. This is composed-integration progress; #3036/#5336 retain the exact pins and limits. No menu/gameplay/FPS or sole-cause claim. Earlier short-directory567 controls reached equivalent stops at28.293/70.664s.
Activity