diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index 813e45767..a6e7bd4ff 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -408,6 +408,19 @@ bool DescriptorIsOpen_nid_no_patch(int descriptor) { } #endif +static std::filesystem::path WithoutTrailingSeparator(const std::filesystem::path& native) { + return native.has_filename() ? native : native.parent_path(); +} + +static bool HasNonDirectoryPrefix(const std::filesystem::path& native) { + std::error_code error; + for (auto prefix = WithoutTrailingSeparator(native).parent_path(); prefix.has_relative_path(); prefix = prefix.parent_path()) { + const auto status = std::filesystem::status(prefix, error); + if (std::filesystem::exists(status)) return !std::filesystem::is_directory(status); + } + return false; +} + extern "C" { int APS5_VABI chmod_nid_postfix(const char* path, int mode) { @@ -553,7 +566,9 @@ int APS5_VABI mkdir_nid_postfix(const char* path, uint16_t mode) { if (const int error = PathError(path)) return PosixFailure(error); auto native = ResolvePath_nid_no_patch(path); if (NativeMkdir(native, mode) != 0) { - return PosixResult(SceErrorFromErrno(errno)); + const int nativeError = errno; + const bool blockedByFile = (nativeError == ENOENT || nativeError == EINVAL) && HasNonDirectoryPrefix(native); + return PosixResult(SceErrorFromErrno(blockedByFile ? GUEST_ENOTDIR : nativeError)); } RecordWrittenPath_nid_no_patch(native); return 0; @@ -784,14 +799,10 @@ int APS5_VABI sceKernelMkdir(const char* path, uint16_t mode) { auto native = ResolvePath_nid_no_patch(path); while (!native.has_filename() && native.has_relative_path()) native = native.parent_path(); std::error_code error; - const auto status = std::filesystem::status(native, error); - if (error && error != std::errc::no_such_file_or_directory) return SceErrorFromErrno(error.value()); - if (std::filesystem::exists(status)) return SceErrorFromErrno(GUEST_EEXIST); - error.clear(); - const auto parent = std::filesystem::status(native.parent_path(), error); - if (error) return SceErrorFromErrno(error.value()); - if (!std::filesystem::exists(parent)) return SceErrorFromErrno(GUEST_ENOENT); - if (!std::filesystem::is_directory(parent)) return SceErrorFromErrno(GUEST_ENOTDIR); + if (std::filesystem::exists(native, error)) return SceErrorFromErrno(GUEST_EEXIST); + if (!std::filesystem::is_directory(WithoutTrailingSeparator(native).parent_path(), error)) { + return SceErrorFromErrno(HasNonDirectoryPrefix(native) ? GUEST_ENOTDIR : GUEST_ENOENT); + } if (!std::filesystem::create_directory(native, error)) return SceErrorFromErrno(error.value() ? error.value() : GUEST_EIO); RecordWrittenPath_nid_no_patch(native); return 0; @@ -992,7 +1003,10 @@ int APS5_VABI sceKernelRmdir(const char* path) { if (path == nullptr) throw std::invalid_argument("sceKernelRmdir: path is null"); const auto native = ResolvePath_nid_no_patch(path); std::error_code error; - if (!std::filesystem::is_directory(native, error)) return SceErrorFromErrno(std::filesystem::exists(native, error) ? GUEST_ENOTDIR : GUEST_ENOENT); + if (!std::filesystem::is_directory(native, error)) { + const bool blockedByFile = HasNonDirectoryPrefix(native) || std::filesystem::exists(WithoutTrailingSeparator(native), error); + return SceErrorFromErrno(blockedByFile ? GUEST_ENOTDIR : GUEST_ENOENT); + } if (!std::filesystem::is_empty(native, error)) return SceErrorFromErrno(GUEST_ENOTEMPTY); if (!RemoveEmptyDirectory(native)) return SceErrorFromErrno(GUEST_EIO); RecordWrittenPath_nid_no_patch(native); diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index 1a053eb18..4f2710df7 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -48,6 +48,7 @@ int APS5_VABI sceKernelStat(const char*, FileStat*); int APS5_VABI sceKernelFstat(int, FileStat*); int APS5_VABI sceKernelUnlink(const char*); int APS5_VABI sceKernelRmdir(const char*); +int APS5_VABI sceKernelMkdir(const char*, unsigned short); int* APS5_VABI __error_nid_postfix(); int APS5_VABI pipe_nid_postfix(int*); std::int64_t APS5_VABI read_nid_postfix(int, void*, std::size_t); @@ -447,6 +448,32 @@ int main() { Require(rmdir_nid_postfix(lockedDirectoryPosix.string().c_str()) == 0 && !std::filesystem::exists(lockedDirectoryPosix)); const auto lockedDirectoryKernel = lockedDirectory("locked-directory-kernel"); Require(sceKernelRmdir(lockedDirectoryKernel.string().c_str()) == 0 && !std::filesystem::exists(lockedDirectoryKernel)); + const auto underFile = presentName + "/child"; + const auto deepUnderFile = presentName + "/child/leaf"; + Require(mkdir_nid_postfix(underFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20); + Require(mkdir_nid_postfix(deepUnderFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20); + Require(sceKernelMkdir(underFile.c_str(), 0700) == static_cast(0x80020014u)); + Require(sceKernelMkdir(deepUnderFile.c_str(), 0700) == static_cast(0x80020014u)); + Require(rmdir_nid_postfix(underFile.c_str()) == -1 && *__error_nid_postfix() == 20); + Require(sceKernelRmdir(deepUnderFile.c_str()) == static_cast(0x80020014u)); + Require(rmdir_nid_postfix((presentName + "/").c_str()) == -1 && *__error_nid_postfix() == 20); + Require(rmdir_nid_postfix((missingName + "/").c_str()) == -1 && *__error_nid_postfix() == 2); + Require(sceKernelRmdir((presentName + "/").c_str()) == static_cast(0x80020014u)); + Require(sceKernelRmdir((missingName + "/").c_str()) == static_cast(0x80020002u)); + const auto missingParentLeaf = rootName + "/missing/leaf"; + Require(sceKernelMkdir(missingParentLeaf.c_str(), 0700) == static_cast(0x80020002u)); + const auto invalidNameUnderFile = presentName + "/a:b/x"; + Require(mkdir_nid_postfix(invalidNameUnderFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20); + Require(sceKernelMkdir(invalidNameUnderFile.c_str(), 0700) == static_cast(0x80020014u)); + const auto trailingPosix = root / "trailing_posix"; + const auto trailingKernel = root / "trailing_kernel"; + const auto trailingPosixName = trailingPosix.string() + "/"; + const auto trailingKernelName = trailingKernel.string() + "/"; + Require(mkdir_nid_postfix(trailingPosixName.c_str(), 0700) == 0 && std::filesystem::is_directory(trailingPosix)); + Require(sceKernelMkdir(trailingKernelName.c_str(), 0700) == 0 && std::filesystem::is_directory(trailingKernel)); + Require(rmdir_nid_postfix(trailingPosix.string().c_str()) == 0 && sceKernelRmdir(trailingKernel.string().c_str()) == 0); + Require(!std::filesystem::exists(trailingPosix) && !std::filesystem::exists(trailingKernel)); + Require(std::filesystem::is_regular_file(present)); Require(rmdir_nid_postfix("") == -1 && *__error_nid_postfix() == 2); Require(rmdir_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == 14); Require(unlink_nid_postfix(missingName.c_str()) == -1 && *__error_nid_postfix() == 2);