Skip to content

fix(libkernel): leave a file's times untouched when Windows utimes refuses them - #8594

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
mantavisual:fix/libkernel-utimes-rejected-keeps-atime
Oct 20, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
mantavisual:fix/libkernel-utimes-rejected-keeps-atime

Conversation

@mantavisual

@mantavisual mantavisual commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What

On Windows, a utimes, lutimes, sceKernelUtimes or futimes call that is refused because the host cannot represent a time still changes the file: it sets the file's access time to the current time. guest_file_timestamps fails on it wherever NTFS last-access updates are enabled, at the first check after the rejected calls:

access time preserves microseconds (guest errno 22)

NativeUtimes and NativeFutimes open a FILE_WRITE_ATTRIBUTES handle and then SetTimes encodes the times. For a time WindowsFileTime::Encode rejects (here a modification time of INT64_MIN, INT64_MAX, -11644473601 or -11644473600 seconds), SetTimes returns EINVAL without calling SetFileTime, and the handle is closed. On this machine, with last-access updates enabled, opening and closing such a handle in the test process sets the access time to the current time. Whether NTFS itself or a file system filter (such as the antivirus) performs that update was not isolated; a standalone program making the same calls did not reproduce it. A successful call hides it because its SetFileTime writes the requested time last.

SetFileTime documents the remedy: "To prevent file operations using the given handle from modifying the last access time, call SetFileTime immediately after opening the file handle and pass a FILETIME structure that has both the dwLowDateTime and dwHighDateTime members set to 0xFFFFFFFF", and the same for the last write time. SetTimes now does that for both times before returning EINVAL. Nothing else uses the handle in between. The handle is still opened before the times are encoded, so the error order (ENOENT and the other path errors before EINVAL) is unchanged, and the accepted path is untouched.

Tested

Windows 11 Pro 23H2 (10.0.22631), Intel Core i5-13600K, NTFS volume with fsutil behavior query disablelastaccess reporting 2 (System Managed, Last Access Time Updates ENABLED), WinLibs GCC 15.2.0 r7, Release, BUILD_TESTING=ON.

  • main 77e31219c: guest_file_timestamps fails 5 of 5 runs with the message above. With the test instrumented (locally, not part of this PR), the access time is still the requested 1000000000 after restore valid times and reads the current time right after the first rejected utimes. In the same process, a bare CreateFileW(FILE_WRITE_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS) and CloseHandle on the fixture also sets the access time to the current time, and with SetFileTime(handle, nullptr, {0xFFFFFFFF, 0xFFFFFFFF}, {0xFFFFFFFF, 0xFFFFFFFF}) before the close it does not.
  • With this change: the instrumented test reads 1000000000 after each of the four rejected utimes and four rejected futimes calls, and the unmodified guest_file_timestamps passes 10 of 10 (ctest --repeat until-fail:10).
  • Full build and libs target on main ae12a951f with this change (plus test(agc): align the array window test's texels at run time #8310's change, uncommitted, so that the default Windows build completes): ctest --parallel 8 --timeout 120 passes 1757 of 1763. The six failures (windows_fixed_base, optional_plt, empty_tls, windows_address_space, windows_bss, windows_icon) also fail without this change: the executables these relinker tests create are blocked by the antivirus on this machine, as BUILD.md describes for four of them.
  • python3 tools/check_conventions.py --base origin/main passes. GitHub's Windows runners pass guest_file_timestamps on main; their last-access setting is not known to me, and this change does not alter the accepted path they take.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

…fuses them

On Windows, utimes, lutimes, sceKernelUtimes and futimes open a
FILE_WRITE_ATTRIBUTES handle and only then encode the times. A time the
host cannot represent returned EINVAL without calling SetFileTime, and
with NTFS last-access updates enabled closing that handle set the file's
access time to the current time, so a rejected call still changed the
file and guest_file_timestamps failed on "access time preserves
microseconds". A rejected time now marks the handle with 0xFFFFFFFF,
which SetFileTime documents as preventing operations on the handle from
updating the times, before the handle is closed. Error order is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 5021 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 6977 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 7121 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 7201 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 7530 core/libs/prx/libkernel/File/src/Stdio.cpp

@oneandonlydean
oneandonlydean merged commit a9cfea2 into boykopovar:main Oct 20, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants