Files
AnyPS5/docs/dev/debt/libkernel_sys.md
T
screammandClaude Fable 5.1 41b0ca597b feat(libkernel_sys): let chroot keep the guest root
Porpoise (PPSA99764) stops on its weak chroot import (#3632, #4497).
Run 5 on main c2565f014, and again on a copy of that runtime:

  FAIL: unresolved ELF import AKknh8Cx-V8 from libkernel_sys.prx

Its only call is in put_root_back (src/porpoise_jailbreak.cpp,
elripalda/Porpoise-Dolphin-Emulator-for-PS5 2ed1ab5a06995aa1): after a
jailbreak daemon has made the console's root its root and /app0 is
gone, it calls fchdir on the descriptor of "/" it opened at start, then
chroot("."), and checks the result. So it only ever puts back the root
it started in, and it does not use paths under any other root.

On top of the lookup errors of the previous commit, chroot now follows
FreeBSD 11.4 sys_chroot and pwd_chroot (8c639945, vfs_syscalls.c and
kern_descrip.c) for that case: a directory that is the guest root
("/", "/..", "." after fchdir to "/") succeeds without a change, since
fd_rdir would be set to the vnode it already is. The guest is taken
never to have changed its root, so with chroot_allow_open_directories 1
an open directory does not give EPERM. ENAMETOOLONG is added as in
pathconf, and a ".." through a file reports ENOTDIR instead of
escaping as a filesystem_error. Any other directory still throws: the
resolver's root and mount aliases are fixed, and no title is known that
needs a real new root.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:11:08 +01:00

7.3 KiB

libkernel_sys technical debt

Functional

  • fchdir (libkernel_sys) changes the guest working directory to the descriptor's directory: on Windows through the path recorded when the descriptor was opened, on Linux through the /proc/self/fd link target. It returns EBADF for a descriptor that is not open and ENOTDIR for one that is not a directory. FreeBSD change_dir's search-permission check (EACCES) is not modelled, and a directory renamed after it was opened is still reached through the recorded path. Console behaviour is unverified
  • openat (libkernel_sys) passes AT_FDCWD (-100) and absolute paths to open. Any other relative path is joined to the descriptor's host directory, found the same way as for fchdir, so guest path aliases and case-insensitive name matching do not apply to it, and a .. component is not kept inside the guest root. It returns EBADF for a descriptor that is not open and ENOTDIR for one that is not a directory. As with open, O_NOFOLLOW and O_NONBLOCK are ignored. PPSA99003 opens directory entries this way. Console behaviour is unverified
  • pathconf (libkernel_sys) looks the path up as FreeBSD 11's kern_pathconf does (links followed, ENAMETOOLONG for a path of 1024 bytes or more or a component over 255) and returns the constants of FreeBSD 11's UFS ufs_pathconf for files and directories alike: _PC_LINK_MAX 32767, _PC_NAME_MAX 255, _PC_PATH_MAX and _PC_SYMLINK_MAX 1024, _PC_PIPE_BUF 512, _PC_CHOWN_RESTRICTED and _PC_NO_TRUNC 1, _PC_FILESIZEBITS 64, _PC_REC_MAX_XFER_SIZE -1, _PC_PRIO_IO and _PC_SYNC_IO 0. Whether the console's filesystems report the same is not verified. It throws for the names whose UFS value depends on the mount (_PC_ALLOC_SIZE_MIN, _PC_REC_INCR_XFER_SIZE, _PC_REC_MIN_XFER_SIZE, _PC_MIN_HOLE_SIZE), the kernel options (_PC_ACL_EXTENDED, _PC_ACL_PATH_MAX, _PC_ACL_NFS4, _PC_MAC_PRESENT) or the page size (_PC_REC_XFER_ALIGN), and on which UFS and vop_stdpathconf disagree (_PC_MAX_CANON, _PC_MAX_INPUT, _PC_VDISABLE, _PC_ASYNC_IO); other names fail with EINVAL
  • mkdirat (libkernel_sys) passes AT_FDCWD (-100) and absolute paths to mkdir. Any other relative path is joined to the descriptor's host directory, found the same way as for fchdir, so guest path aliases and case-insensitive name matching do not apply to it, and a .. component is not kept inside the guest root. It returns EBADF for a descriptor that is not open and ENOTDIR for one that is not a directory. On Windows, mode is ignored. Console behaviour is unverified
  • readlink (libkernel_sys) on Windows reads a symbolic link's target from its reparse data and returns it with / separators, only when the target is relative, as symlink creates them. A symbolic link with an absolute target and a junction throw, as a host path has no guest form. Any other reparse point throws too, where FreeBSD reports EINVAL for a file that is not a link
  • readlinkat (libkernel_sys) passes AT_FDCWD (-100) and absolute paths to readlink. Any other relative path is joined to the descriptor's host directory, found the same way as for fchdir, so guest path aliases and case-insensitive name matching do not apply to it, and a .. component is not kept inside the guest root. It returns EBADF for a descriptor that is not open and ENOTDIR for one that is not a directory. Console behaviour is unverified
  • unlinkat (libkernel_sys) passes AT_FDCWD (-100) and absolute paths to rmdir (with AT_REMOVEDIR, 0x800) or unlink. Any other relative path is joined to the descriptor's host directory, found the same way as for fchdir, so guest path aliases and case-insensitive name matching do not apply to it, and a .. component is not kept inside the guest root. It returns EBADF for a descriptor that is not open and ENOTDIR for one that is not a directory. Removing a directory without AT_REMOVEDIR fails with EPERM, removing . or .. with AT_REMOVEDIR fails with EINVAL, and flags other than AT_REMOVEDIR fail with EINVAL. Console behaviour is unverified
  • renameat (libkernel_sys) passes AT_FDCWD (-100) and absolute paths to rename. Any other relative path is joined to its descriptor's host directory, found the same way as for fchdir, so guest path aliases and case-insensitive name matching do not apply to relative descriptors, and a .. component is not kept inside the guest root. It returns EBADF for a descriptor that is not open and ENOTDIR for one that is not a directory. Console behaviour is unverified
  • fork (libkernel_sys) throws: creating a child process is not supported. PPSA19111 imports it and fails to load without it. Its engine calls fork in Posix_Daemonize, only with -daemonize, and in Platform_ShellExecute before execvp("xdg-open"), which the main menu's Previews button reaches, so pressing Previews ends in this throw. setsid fails with EPERM, as FreeBSD's does for a process group leader, since the guest is taken to lead its own process group (see kill). umask keeps the guest's mask (initially 022, masked to 07777) and returns the previous one, Linux named shared-memory creation applies it, but ordinary files are still created with the host process's mask
  • chroot (libkernel_sys) follows FreeBSD 11.4's sys_chroot and pwd_chroot for a root caller: EFAULT for a null path, ENOENT for an empty or missing one, ENAMETOOLONG for a path of 1024 bytes or more or a component over 255, and ENOTDIR for a path that is not a directory or passes through a file. A directory that is the guest root (/, /.., or . after fchdir to a descriptor of /) succeeds and changes nothing, as the root stays the same directory; the guest is taken never to have changed its root, so open directory descriptors do not make it fail with EPERM (kern.chroot_allow_open_directories 1). Any other directory throws, naming the path: the guest path resolver has a fixed root and mount aliases (/app0, /savedata0) that do not move with it. On the console a title starts in a sandbox root, where pwd_chroot refuses with EPERM while a directory is open; whether that holds for titles, and whether a title holds PRIV_VFS_CHROOT, are unverified. Porpoise (PPSA99764) weak-imports it and calls it only as fchdir to its saved descriptor of / then chroot("."), to put its sandbox root back after a jailbreak daemon replaced it (put_root_back)