sceKernelRmdir decided whether the last component was a symbolic link with
std::filesystem::is_symlink, which follows the link with WinLibs libstdc++ on
Windows. A dangling or self-referencing file link then looked missing on
Windows 10 (ENOENT instead of ENOTDIR), and a directory link to an empty
directory looked like that directory, so rmdir removed the link and
returned 0. The last component is now classified with FillLinkStat, the
same lstat path lstat_nid_postfix uses, matching FreeBSD 11 kern_rmdirat,
which returns ENOTDIR for any non-directory vnode found without following
the last link.
Fixes#12079
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceKernelTruncate and truncate checked for a symbolic link loop only
after the native truncate failed, but on Windows std::filesystem::status
already reports a self-referencing link as missing, so the call returned
ENOENT before reaching that check. The loop check now runs before the
not-found result too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dup2 called EqueueDescriptorClosed(to) while holding the positioned-transfer lock of the descriptor, and the in-flight measurement it waits for needs that lock for its sceKernelLseek, so dup2 onto a watched descriptor deadlocked. The events are removed first, before the lock is taken.
NativeLseek seeked first and only checked for a pipe when the host seek
succeeded, so a negative offset reached the guest as EINVAL from
_lseeki64. FreeBSD 11.4's kern_lseek returns ESPIPE for any seek on a
descriptor without DFLAG_SEEKABLE, so the pipe check now comes first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
lchflags is chflags without following a symbolic link in the last path
component, as FreeBSD 11.4's sys_lchflags calls kern_chflagsat with
AT_SYMLINK_NOFOLLOW. chflags now goes through a shared helper that takes
the follow choice; a link in the last component is accepted as the
target when not following, and the flag checks are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
rmdir followed the junction and reported ENOTEMPTY, and unlink treated it as a real directory, so a junction to a non-empty folder could not be removed.
std::filesystem::status does not follow a Windows symbolic link under
MinGW, so the lookup chflags makes saw a link whose target is missing as
an existing file and returned 0. A reparse point in the last component
is now looked up through the target with the guest stat, as FreeBSD 11.4's
namei follows it: a missing target gives ENOENT, and a trailing slash
after a link to a file gives ENOTDIR.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Destroying a std::optional is noexcept in libstdc++, so a failing sync in
the guard's destructor ended the process with std::terminate instead of
raising the sync error. The Windows writev and pwritev path now uses a
plain guard, which does nothing for -1. The debt line names the synced
write calls and leaves out the AIO writes.
FreeBSD's F_SETFL may change O_FSYNC (FCNTLFLAGS), and vn_write then
writes with IO_SYNC. The flag now lives in the status table, and the
write entry points sync a regular file after the write while it is set.
Main's #9618 has lstat fill a local FileStat and copy it out to the guest.
The merge keeps that and this branch's module lookup in the same call, so an
original .prx name still resolves to its relinked .guest.prx.