Read the clock of a libkernel condition attribute, returning
SCE_KERNEL_ERROR_EINVAL for a null or destroyed attribute as
scePthreadCondattrSetclock does, after FreeBSD 11.4 libthr's
_pthread_condattr_getclock. A null clock pointer throws.
The EINVAL for a descriptor without a vnode now applies only to a pipe or socket. A character device is a vnode on FreeBSD, so getvnode succeeds and change_dir fails with ENOTDIR; File::IsCharacterDevice keeps that answer for the host character devices (a tty, NUL or /dev/null) and the guest random device. guest_kernel_sys pins it with /dev/random.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
FreeBSD 11.4's sys_fchdir resolves the descriptor with getvnode, which fails with EINVAL for a descriptor that has no vnode; only a vnode that is not a directory gives ENOTDIR from change_dir. fchdir returned ENOTDIR for a pipe or socket. A descriptor that is neither a vnode nor a random device now gets EINVAL; the debt entry records the order of the three errors.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
fstat must fill the same FileStat as _fstat for an open descriptor, return EFAULT for a null buffer and EBADF for a closed or invalid descriptor, as the fstatfs check beside it does for _fstatfs.
Resolves the conflict with #8522 in link_nid_postfix: keep its ENOTDIR
return for a destination below a regular file and the Windows
symbolic-link follow after it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
link_nid_postfix decided whether to follow the source with
std::filesystem::is_symlink, which MinGW-w64's libstdc++ never reports for
a Windows symbolic link, so on Windows link made a hard link to the link
itself. Follow a reparse-point source to its final path with
GetFinalPathNameByHandleW instead, and run the link-through-a-link case of
guest_kernel_sys on Windows too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
lchmod forwards to fchmodat with AT_FDCWD and AT_SYMLINK_NOFOLLOW, as
FreeBSD 11.4's sys_lchmod does. On Linux and Windows, which cannot change
the mode of a symbolic link itself, a symbolic link throws instead of
succeeding without a change.
FreeBSD lookup() refuses "." and ".." as the last component of a DELETE
lookup with EINVAL (sys/kern/vfs_lookup.c:750-754 and 823-826), before the
unlink and rmdir checks run. unlink and sceKernelUnlink answered EPERM,
rmdir and sceKernelRmdir answered ENOTEMPTY for "..", and unlinkat without
AT_REMOVEDIR answered EPERM. Reuse the RenameDot check added for rename, so a
missing or non-directory parent still reports ENOENT or ENOTDIR first.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The largest target below MAXPATHLEN must pass the length check, so the
comparison cannot slip from >= 1024 to >= 1023.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Both fixtures required FSCTL_SET_REPARSE_POINT to succeed; a user without the right would have
aborted guest_filesystem and guest_kernel_sys. They now print that the checks are skipped and carry
on, as the symbolic link checks do without Developer Mode. On this PC the control succeeds at
medium integrity, so the checks ran both elevated and unelevated.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
FreeBSD 11.4 kern_symlinkat copies the target with copyinstr(path1, ..., MAXPATHLEN)
(sys/kern/vfs_syscalls.c:1490), so a target of 1024 bytes or more fails with
ENAMETOOLONG before the link name is looked up. symlink_nid_postfix had no length
check on the target.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A path whose directory part is a regular file, such as file/child, failed
with ENOENT in readlink and readlinkat on Windows, where FreeBSD 11.4 fails
it with ENOTDIR (vfs_cache_lookup, sys/kern/vfs_cache.c). The not-found
error now checks whether the nearest existing ancestor is not a directory,
as was previously done for chmod, truncate, utimes, rename, remove, chdir,
opendir, and fopen.
Co-Authored-By: Antigravity <[email protected]>
FreeBSD 11.4 kern_readlinkat returns EINVAL for a count above IOSIZE_MAX
before namei looks at the path. readlink and readlinkat checked a NULL or
empty path first, so readlink(NULL, buf, SIZE_MAX) returned EFAULT and
readlink("", buf, SIZE_MAX) returned ENOENT where FreeBSD returns EINVAL.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
lstat and readlink threw NotImplemented for every Windows reparse point
other than a symbolic link or a drive junction. A tag without the name
surrogate bit (cloud files, deduplication) does not stand for another path,
so lstat now answers as stat does and readlink returns EINVAL, as FreeBSD
does for a file that is not a link and as CPython's os.lstat and os.readlink
do (bpo-37834). A name surrogate that is neither a symbolic link nor a
junction (a mounted volume, a WSL link) is reported as the reparse point
itself with the mode of its attributes, and readlink returns EINVAL.
The tests set reparse tags with FSCTL_SET_REPARSE_POINT, one without the
name surrogate bit and one with it. A compact /exe (WOF) file cannot serve
as the fixture: on Windows 11 24H2 the WOF filter hides the reparse point
from GetFileAttributesW and FileAttributeTagInfo alike. Windows refuses
every open of an unhandled tag without FILE_FLAG_OPEN_REPARSE_POINT, so the
test requires lstat to agree with stat rather than to succeed.
mashingaan's boykopovar/AnyPS5#6447 returned EINVAL for other reparse
points without the tag check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.1 <[email protected]>
FreeBSD 11.4 vfs_cache_lookup answers ENOTDIR when the directory it searches
is not a directory (sys/kern/vfs_cache.c:2104-2105), the lookup behind the
calls fixed in the earlier ENOTDIR changes. These four calls answered ENOENT
for a path such as file/child on Windows.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
FreeBSD 11.4 rejects access mode 3 in kern_openat and
O_CREAT|O_EXCL|O_DIRECTORY in vn_open_cred before namei reads the path
or resolves the directory descriptor. open, _open and openat checked a
NULL or empty path first and returned EFAULT or ENOENT for those flags.
InvalidOpenFlags moves to FileFlags.hpp and runs first in all three.
MinGW libstdc++ up to GCC 16 does not treat a reparse point as a link in
std::filesystem::remove_all, so a host tree that holds a symlink is walked
through the link and never returns. Add RemoveTree, which removes the tree
itself on Windows, and use it where save data, app content and the shader
disk cache removed host trees. The libstdc++ fix is in GCC 17 (88856ad855ae).
The MinGW libstdc++ reports a dangling symbolic link as missing and clears
the error, so std::filesystem::remove neither deletes it nor fails, and
remove_all loops forever over a tree that contains one. Remove reparse
points with DeleteFileW and RemoveDirectoryW and the fixture through the
shell; record the gap in the libs technical debt.
Replace the leading-slash absolute-target check with a root-name/root-directory
check so Windows targets like C:/ or \\server\share are rejected too, and
add guest tests covering rejected (.. , ../x, a/../b, a/../../x, /abs, plus
C:/ and UNC paths on Windows) and accepted (sub/dir, a..b, ...) targets.
A mounted folder uses the junction reparse tag and would otherwise be returned as a relative Volume GUID path. Only a drive path is reported, through the absolute-link text helpers.
lstat and readlink aborted on an absolute symbolic link because only a relative reparse point was accepted. Read the link text, and still refuse reparse points that are not symbolic links.
FreeBSD rejects an access mode of 3 in kern_openat and the combination of O_CREAT, O_EXCL and O_DIRECTORY in vn_open_cred before it looks the path up. sceKernelOpen, open and openat threw std::invalid_argument for access mode 3 and, on Windows, created the file for the second combination.
(cherry picked from commit 038a07098ef38e95fade52154847b579dcb0f9d0)
The case that removes search permission from a directory and expects
fchdir to fail with EACCES, leave the working directory unchanged and
succeed again once search is allowed ran only on Linux. It now runs on
every host but Windows, with the POSIX headers it needs.