From 5a84f287c4c729a8ee69cf65584067764751e553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Sep 2026 13:58:00 -0600 Subject: [PATCH] sceIoRename: refuse wildcards, an existing destination, and don't wait on XDEV Three ways our rename differed from the PSP's: - A wildcard in either path was passed through to the host, so renaming "test*.txt" could quietly rename a real file. The PSP doesn't expand them here, it rejects them outright. - Renaming onto a file that already exists succeeded, because the host rename() replaces the destination. The PSP refuses, and renaming a file onto itself counts as that too. - Crossing devices returned the right error, but after the same wait as everything else. The hardware fails that one immediately. Fixes io/file/rename, moved to tests_good. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JvJR8oJNSCimCM9KXVLjfq --- Core/HLE/sceIo.cpp | 20 ++++++++++++++++++++ test.py | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index b1a83e87dc..293b388ca0 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -2096,12 +2096,32 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o return hleNoLog(SCE_KERNEL_ERROR_UNSUP); } +static bool IoPathHasWildcard(const char *path) { + return path && strpbrk(path, "*?") != nullptr; +} + static u32 sceIoRename(const char *from, const char *to) { // TODO: Timing isn't terribly accurate. + + // sceIoRename doesn't expand wildcards, it refuses them - in either path, and before it + // looks at whether anything is actually there. + if (IoPathHasWildcard(from) || IoPathHasWildcard(to)) { + return hleDelayResult(hleLogError(Log::sceIo, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "wildcard in path"), "file renamed", 1000); + } + if (!pspFileSystem.GetFileInfo(from).exists) return hleDelayResult(hleLogError(Log::sceIo, SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND), "file renamed", 1000); + // The PSP won't rename onto something that already exists, and renaming a file onto itself + // counts. Host rename() would happily replace the destination. + if (pspFileSystem.GetFileInfo(to).exists) + return hleDelayResult(hleLogError(Log::sceIo, SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS, "destination exists"), "file renamed", 1000); + int result = pspFileSystem.RenameFile(from, to); + if (result == (int)SCE_KERNEL_ERROR_XDEV) { + // Renaming across devices is refused up front, without the wait the other errors take. + return hleLogError(Log::sceIo, result, "cannot rename across devices"); + } if (result < 0) WARN_LOG(Log::sceIo, "Could not move %s to %s", from, to); return hleDelayResult(hleLogDebug(Log::sceIo, result), "file renamed", 1000); diff --git a/test.py b/test.py index d84850a2f0..949de887a5 100755 --- a/test.py +++ b/test.py @@ -211,6 +211,7 @@ tests_good = [ "intr/suspended", "intr/vblank/vblank", "io/cwd/cwd", + "io/file/rename", "io/open/badparent", "jpeg/create", "jpeg/delete", @@ -452,7 +453,6 @@ tests_next = [ "intr/waits", "io/directory/directory", "io/file/file", - "io/file/rename", "io/io/io", "io/iodrv/iodrv", "io/open/tty0",