Skip to content

fix(libkernel): rewind Windows directory descriptors on lseek - #2680

Closed
husseinkad wants to merge 3 commits into
boykopovar:mainfrom
husseinkad:fix/kernel-windows-directory-rewind
Closed

husseinkad wants to merge 3 commits into
boykopovar:mainfrom
husseinkad:fix/kernel-windows-directory-rewind

Conversation

@husseinkad

@husseinkad husseinkad commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What

On Windows a guest directory descriptor sits on the NUL device, so sceKernelLseek on it did nothing: after lseek(fd, 0, SEEK_SET) a second getdents returned 0 entries instead of listing the directory again. FreeBSD getdirentries(2) documents that the position "may be set and retrieved by lseek(2)", and offset 0 is the standard rewind.

File::SeekDirectoryDescriptor now handles lseek on directory descriptors (hook in sceKernelLseek):

  • offset 0 with SEEK_SET rewinds; the next read lists the live directory again
  • SEEK_CUR returns the entry index (offset 0 is a pure query that does not touch the snapshot)
  • other SEEK_SET/SEEK_CUR offsets are entry indexes; a negative result throws like any other lseek failure (EINVAL), an offset overflow reports EOVERFLOW
  • SEEK_END throws via NotImplemented_nid_no_patch (FreeBSD defines it relative to the directory size, which is not modelled; it used to return 0 from the NUL descriptor)

Per-descriptor state has its own lock, so a slow listing does not block lseek on other descriptors, and lseek/close on non-directory descriptors return before taking any lock while no directory is open. The limits (O(entries) re-listing per rewind, getdirentries still reporting 0 through basep) are recorded in TechnicalDebt.

Tested

Windows 11 (10.0.26200), WinLibs GCC 15.2.0 r7, Release:

  • New guest_directory_rewind: rewind after a partial and a full read, a file added between listings, SEEK_CUR exactly 2 after . and .., a seek past the end, negative and overflowing seeks, SEEK_CUR 0 not disturbing a listing, rewind after getdents failed on a too-small buffer, and a reused descriptor that was a directory behaving as a file. It fails without the change.
  • ctest -R "guest_directory|guest_filesystem|guest_file_timestamps": 6 of 6 pass.
  • Outside the repo: a 3-thread, 3 s stress of getdents + directory lseek + regular-file lseek (about 12.6M operations) had 0 errors and no crash; separate probes with 0, 1, 5 and 10,000 entries relisted correctly.
  • tools/check_conventions.py --base origin/main passes; tools/pr_overlap.py --branch reports no overlap. Open PR fix(kernel): return guest errors from scalar file operations #2144 also rewrites sceKernelLseek in Open.cpp (error returns instead of throws); the directory hook there would need to follow whichever style lands first.
  • Linux not run (the NUL-descriptor limitation is Windows-only, and the test is registered for Windows).

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
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

lseek on a directory descriptor returned 0 without moving the listing, so getdents after lseek(fd, 0, SEEK_SET) reported nothing.
…ffset overflow

lseek on a non-directory descriptor no longer takes the directory table lock when no directory is open, and a listing is read under the descriptor's own lock. SEEK_CUR with offset 0 only reports the position, and an overflowing SEEK_CUR offset reports EOVERFLOW.
@github-actions github-actions Bot added the intake paused Closed while PR intake is limited to authors with at least 10 PRs merged into main label Oct 10, 2026
@github-actions

Copy link
Copy Markdown

Thank you for your interest in the project! We are sorry, but we cannot accept more PRs while we work through the existing backlog. This PR has been automatically closed and labeled "intake paused". This is not a judgment of the quality of your changes.

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

Labels

intake paused Closed while PR intake is limited to authors with at least 10 PRs merged into main

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant