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 <noreply@anthropic.com>
(cherry picked from commit 5f74ddba8b346e6c552cf02554d47382708206f3)
This commit is contained in:
Achraf Reyani 2026-10-10 04:15:23 +09:00 committed by MarcZX100
parent 57f96fd2b3
commit 3779ffa375
2 changed files with 9 additions and 2 deletions

View File

@ -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;

View File

@ -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<int>(0x80020011u));
Require(sceKernelMkdir("kernel_sync_probe/existing.bin/child", 0777) == static_cast<int>(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");