Skip to content

fix(libkernel): append positioned writes on an O_APPEND descriptor on Windows - #8359

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
Ragbie:fix/windows-pwrite-append
Oct 23, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
Ragbie:fix/windows-pwrite-append

Conversation

@Ragbie

@Ragbie Ragbie commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

What

On Windows, sceKernelPwrite, pwrite, sceKernelPwritev, pwritev and the AIO write commands on a descriptor opened with O_APPEND write at the given offset, over the start of the file. #5831 made write and writev append on such a descriptor and left pwrite unchanged. On Linux these calls go to the host's pwrite and pwritev, which append.

FreeBSD 11.4 appends too: kern_pwritev calls dofilewrite with FOF_OFFSET, and vn_write sets IO_APPEND for an O_APPEND regular file whether or not an offset is given. ffs_write (and tmpfs) then writes at the end of the file. With FOF_OFFSET the file offset is not updated.

The change, Windows only:

  • NativePositionedTransfer writes on an O_APPEND descriptor (File::Appends) at offset 0xFFFFFFFFFFFFFFFF, Windows' write to the end of the file, which File::WriteAtEnd already uses for write. It still restores the file pointer afterwards, so the descriptor's offset does not move. Handles that are not disk files still fail with ESPIPE, directories with EBADF. sceKernelPwrite, pwrite, pwritev and the AIO write commands all go through this function.
  • TransferIovecs gathers a multi-buffer pwritev on an O_APPEND descriptor into one write, as it already does for writev: FreeBSD appends the whole vector in one VOP_WRITE, under the whole-file range lock vn_io_fault takes for O_APPEND.
  • The technical debt entry for sceKernelPread, sceKernelPwrite, sceKernelPreadv and sceKernelPwritev no longer says these transfer "at the given offset".

Tested

Windows 11, WinLibs GCC 15.2.0:

  • guest_append_writes gets a fifth concurrent writer, which appends with a two-buffer pwritev(fd, ..., 0) on its own descriptor, and then single-threaded sceKernelPwrite(fd, ..., 0) and pwritev(fd, ..., 32) that must append and leave lseek(fd, 0, SEEK_CUR) at 0. guest_aio gets an AIO write at offset 0 on an O_APPEND descriptor that must append.
  • With main's Stdio.cpp: append check failed at line 94 (file size after the concurrent writers: the pwritev writer overwrites other lines) and Aio check failed at line 413. With only the offset change: append check failed at line 102 in 5 of 5 runs (other writers land between the two pwritev buffers). With the change, both pass with --repeat until-fail:10.
  • Full build and ctest: 99% tests passed, 4 tests failed out of 1735. agc_driver_image_array_window_past_layer_limit does not build with MinGW on main (test(agc): align the array window test's texels at run time #8310); agc_driver_image_plane_address_on_line and agc_driver_pass_input_hazard fail on main on this machine too; agc_driver_sgpr_pattern_fill_keys failed once in the parallel run and passes 3 of 3 alone.

Linux (Ubuntu 24.04 container, GCC 13.3.0): both tests pass. The change compiles into the Linux build only through these two tests; full ctest: 99% tests passed, 17 tests failed out of 1700, all outside this change (16 agc_* tests on the container's lavapipe and kernel, which lacks UFFD_FEATURE_WP_ASYNC, and guest_system_load_exec_standard_error).

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

… Windows

On Windows, sceKernelPwrite, pwrite, sceKernelPwritev, pwritev and the AIO
write commands wrote at the given offset on a descriptor opened with
O_APPEND. FreeBSD's vn_write sets IO_APPEND for an O_APPEND regular file
whether or not an offset is given, the filesystem then writes at the end of
the file, and the file offset is left unchanged. Write at Windows'
end-of-file offset in that case, and gather a multi-buffer pwritev into one
write as writev already does.
@github-actions

github-actions Bot commented Oct 20, 2026 •

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
PR 9365 docs/dev/debt/libkernel.md L151-152
PR 9515 docs/dev/debt/libkernel.md L151-152
PR 9578 docs/dev/debt/libkernel.md L151-152

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
docs/dev/debt/libkernel.md
PR 6044 core/libs/tests/GuestAio.cpp
PR 6149 docs/dev/debt/libkernel.md
PR 6469 docs/dev/debt/libkernel.md
PR 6740 docs/dev/debt/libkernel.md
PR 6830 docs/dev/debt/libkernel.md
PR 6936 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 7049 docs/dev/debt/libkernel.md
PR 7088 docs/dev/debt/libkernel.md
PR 7105 docs/dev/debt/libkernel.md
PR 7121 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 7149 docs/dev/debt/libkernel.md
PR 7201 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 7509 docs/dev/debt/libkernel.md
PR 7565 docs/dev/debt/libkernel.md
PR 7653 docs/dev/debt/libkernel.md
PR 8010 docs/dev/debt/libkernel.md
PR 8520 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 8553 docs/dev/debt/libkernel.md
PR 8700 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9019 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9133 docs/dev/debt/libkernel.md
PR 9160 docs/dev/debt/libkernel.md
PR 9161 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9284 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9293 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9297 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9302 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9442 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9561 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9566 docs/dev/debt/libkernel.md
PR 9575 docs/dev/debt/libkernel.md
PR 9669 docs/dev/debt/libkernel.md
PR 9693 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9823 core/libs/prx/libkernel/File/src/Stdio.cpp

@cadenasynyster

Copy link
Copy Markdown
Contributor

Independent Windows run of this PR (head ad7e1ac).

Environment: Windows 11 10.0.26300, Ryzen 7 9800X3D, WinLibs GCC 15.2.0 r7 (MinGW), Release, BUILD_TESTING=ON.

Results:

  • PR head: guest_append_writes and guest_aio pass 10/10 (--repeat until-fail:10).
  • Baseline: I kept the PR's tests and replaced only core/libs/prx/libkernel/File/src/Stdio.cpp with the merge-base version (84be26d). guest_aio fails with Aio check failed at line 413 and guest_append_writes with append check failed at line 94, the same as the description. I restored the PR's file afterwards.

Not run: the offset-only variant from the description, Linux/macOS, the full repository CTest, and any title.

AI-assisted: yes

@oneandonlydean oneandonlydean self-assigned this Oct 22, 2026
@oneandonlydean
oneandonlydean merged commit b3e42bd into boykopovar:main Oct 23, 2026
3 checks passed
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.

3 participants