FreeBSD 11.4's kern_settimeofday rejects a timeval with a microsecond
field outside 0 to 999999 or a negative second field with EINVAL before
changing anything, sets the clock from a valid timeval, and then stores
the timezone, which gettimeofday reports. settimeofday now checks the
timeval the same way and throws for a valid one, as clock_settime does,
because the host clock is not changed. A timezone given without a
timeval is stored and gettimeofday reports it instead of 0 and 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Windows, mkdirat with a directory descriptor and a relative path that goes through a symbolic link pointing to itself reported EIO. CreateDirectoryW fails with ERROR_CANT_RESOLVE_FILENAME for such a path, and the error mapping had no case for it, so it fell through to GuestEio. FreeBSD fails the lookup with ELOOP. The path form of mkdir and openat with the same descriptor already return ELOOP. ERROR_CANT_RESOLVE_FILENAME now gives ELOOP.
guest_kernel_sys gains a Windows check with a file-type and a directory-type self-loop: mkdirat through the loop returns ELOOP and mkdirat of the link itself still returns EEXIST. The check is skipped when the host denies symbolic link creation.
kern_fpathconf answers _PC_ASYNC_IO with _POSIX_ASYNCHRONOUS_IO (200112) after the descriptor lookup, and a pipe or socket (no vnode) with PIPE_BUF for _PC_PIPE_BUF and EINVAL for every other name. Drop the unused File.hpp include and the duplicate KernelErrors.hpp include.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
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.
All four exports threw. The ownership calls follow fchown: owner and
group 0 and the unchanged value are accepted and any other value
throws. The path is looked up first with stat, lstat or fstatat, as
FreeBSD 11's kern_fchownat does, so a lookup error is returned before
the owner is checked. fchownat rejects flags other than
AT_SYMLINK_NOFOLLOW with EINVAL. linkat passes AT_FDCWD and absolute
paths to link and rejects flags other than AT_SYMLINK_FOLLOW with
EINVAL; a relative path with a directory descriptor and a symbolic
link source without AT_SYMLINK_FOLLOW throw. On Windows a symbolic
link source is detected with the file attributes, since libstdc++
there reports it as a regular file.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
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.
_openat now opens files as openat does. It reads the mode argument only
when O_CREAT is set, as openat and _open do, and calls openat with the
same descriptor, path, flags and mode, so it returns the same
descriptors and errors.
The PS5 3.20 libkernel_sys export list (DNNDHH/PS5-3.20_Libs) has
_openat, NID bGVEgWXy6dg, as well as openat (A8Yk5AhNDck). In FreeBSD
11.4, _openat is the openat system call stub (lib/libc/sys/Makefile.inc,
amd64/SYS.h) and only libthr's openat adds a cancellation point
(thr_syscalls.c). openat here is not a cancellation point, so
forwarding to it leaves _openat without one, as in FreeBSD.
The openat debt entry now names _openat and no longer says that
O_NOFOLLOW is ignored: open, and so openat, has returned EMLINK for
O_NOFOLLOW on a symbolic link since 9421484572.
The kernel_sys test opens a file through a directory descriptor with
_openat and reads it back, creates one with O_CREAT, O_EXCL and mode
0600, checks outside Windows that it has mode 0600, reopens it for
writing, and checks EEXIST, EINVAL, EBADF and ENOTDIR.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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.