Skip to content

fix(libc): open fopen streams with delete sharing on Windows - #4090

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
EagleBearerYS:fix/libc-fopen-delete-sharing-windows
Oct 12, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
EagleBearerYS:fix/libc-fopen-delete-sharing-windows

Conversation

@EagleBearerYS

@EagleBearerYS EagleBearerYS commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

What

On Windows, fopen opened its stream with _wfopen, which shares read and write access but not delete access. While the guest held a stream open, rename of its file failed with EIO and remove with EBUSY. POSIX rename and remove work on a file that is open, as on Linux and FreeBSD, and since #3677 the descriptors sceKernelOpen returns share delete access, so a file opened with open could be moved or removed but the same file opened with fopen could not.

fopen now creates the handle the way #3677's NativeOpen does: CreateFileW sharing read, write and delete, an inheritable handle, and the access and creation disposition of the mode (r opens an existing file, w creates or truncates, a opens or creates, x creates a new file, + adds the other direction). The handle is wrapped with _open_osfhandle, with _O_APPEND for a modes, and _wfdopen with the same mode minus x. When CreateFileW fails, errno comes from #3677's error mapping, which moves from libkernel's Open.cpp to libc as WindowsOpenError_nid_no_patch (declared in General.hpp) so that both use the same table; Open.cpp keeps its behaviour. There is no _wfopen fallback.

freopen still reopens through _wfreopen, which does not share delete access; TechnicalDebt records it.

Tested

Windows 11 (10.0.26300), WinLibs MinGW-w64 GCC 15.2.0 r7 (UCRT), Release, -DBUILD_TESTING=ON, on main at 325e0a8 with #4021 applied locally, since main's Windows build stops in Socket.cpp without it:

  • file_streams now opens a stream with w+b, renames and removes its file while the stream is open, and reads the data back through the stream; then checks that ab appends across two streams and that wbx on an existing file fails with EEXIST. With main's FileAndHeap.cpp, General.cpp, General.hpp and Open.cpp the test fails: rename returns -1 with errno 5 (EIO) and remove -1 with errno 16 (EBUSY). With the change it passes, 20 runs in a row, as does guest_filesystem with fix(libkernel): allow delete sharing for open Windows files #3677's delete-sharing checks for sceKernelOpen.
  • All 128 tests registered in core/libs/prx/libc/CMakeLists.txt and core/libs/prx/libkernel/CMakeLists.txt that build on Windows pass.
  • tools/check_conventions.py --base origin/main passes.
  • Not tested: Linux (the change is Windows-only), macOS, any title.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes

fopen opened its stream with _wfopen, which shares read and write
access but not delete access. While the guest held a stream open,
rename of its file failed with EIO and remove with EBUSY. POSIX rename
and remove work on open files, as on Linux and FreeBSD, and since
boykopovar#3677 the descriptors sceKernelOpen returns share delete access.

fopen now creates the handle with CreateFileW sharing read, write and
delete, with the access and creation disposition of the mode, and
wraps it with _open_osfhandle and _wfdopen, keeping append mode. A
failed CreateFileW sets errno through the error mapping boykopovar#3677 added to
libkernel's NativeOpen, which moves to libc as
WindowsOpenError_nid_no_patch so that both use it. freopen still
reopens through _wfreopen; TechnicalDebt records it.
@github-actions

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
#4380 core/libs/tests/FileStreams.cpp L21, L93

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants