From 3779ffa3757ea2461734898d48cdeb397671de52 Mon Sep 17 00:00:00 2001 From: Achraf Reyani Date: Sat, 10 Oct 2026 04:15:23 +0900 Subject: [PATCH] fix(libkernel): accept a trailing slash in sceKernelMkdir sceKernelMkdir checked that the parent of the resolved path exists, but for "dir/" std::filesystem::path reports "dir" itself as the parent, so the call failed with ENOENT and created nothing, while FreeBSD's mkdir (and the POSIX mkdir export) accept it. Strip trailing separators first, and report ENOTDIR when the parent exists but is not a directory. Co-Authored-By: Claude Opus 5.5 (cherry picked from commit 5f74ddba8b346e6c552cf02554d47382708206f3) --- core/libs/prx/libkernel/File/src/Stdio.cpp | 7 +++++-- core/libs/tests/GuestKernelSync.cpp | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index 711b9f388..5db08e781 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -544,10 +544,13 @@ int APS5_VABI getdents_nid_postfix(int fd, char* buf, int nbytes) { int APS5_VABI sceKernelMkdir(const char* path, uint16_t mode) { (void)mode; if (path == nullptr) throw std::invalid_argument("sceKernelMkdir: path is null"); - const auto native = ResolvePath_nid_no_patch(path); + auto native = ResolvePath_nid_no_patch(path); + while (!native.has_filename() && native.has_relative_path()) native = native.parent_path(); std::error_code error; if (std::filesystem::exists(native, error)) return SceErrorFromErrno(GUEST_EEXIST); - if (!std::filesystem::exists(native.parent_path(), error)) return SceErrorFromErrno(GUEST_ENOENT); + const auto parent = std::filesystem::status(native.parent_path(), error); + if (!std::filesystem::exists(parent)) return SceErrorFromErrno(GUEST_ENOENT); + if (!std::filesystem::is_directory(parent)) return SceErrorFromErrno(GUEST_ENOTDIR); if (!std::filesystem::create_directory(native, error)) return SceErrorFromErrno(error.value() ? error.value() : GUEST_EIO); RecordWrittenPath_nid_no_patch(native); return 0; diff --git a/core/libs/tests/GuestKernelSync.cpp b/core/libs/tests/GuestKernelSync.cpp index 526218a99..9fc11185f 100644 --- a/core/libs/tests/GuestKernelSync.cpp +++ b/core/libs/tests/GuestKernelSync.cpp @@ -40,6 +40,10 @@ int main() { Require(sceKernelMkdir("kernel_sync_probe", 0777) == 0); Require(Recorded("kernel_sync_probe")); std::ofstream("kernel_sync_probe/existing.bin") << "existing"; + Require(sceKernelMkdir("kernel_sync_probe/nested/", 0777) == 0); + Require(std::filesystem::is_directory("kernel_sync_probe/nested") && Recorded("kernel_sync_probe/nested")); + Require(sceKernelMkdir("kernel_sync_probe/nested/", 0777) == static_cast(0x80020011u)); + Require(sceKernelMkdir("kernel_sync_probe/existing.bin/child", 0777) == static_cast(0x80020014u)); const int reader = sceKernelOpen("kernel_sync_probe/existing.bin", 0x0, 0); Require(reader >= 0 && sceKernelClose(reader) == 0); void* stream = fopen_nid_postfix("kernel_sync_probe/existing.bin", "r");