mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 09:03:22 -05:00
rename and sceKernelRename resolved "dir/." to "dir" and renamed the directory. FreeBSD 11.4's lookup fails a last component of "." for DELETE and RENAME with EINVAL, for the source and the destination alike. Check the guest string before it is resolved: a last component of "." (also under trailing slashes) fails with EINVAL when its parent exists, ENOENT when it does not and ENOTDIR when it is not a directory. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
81 lines
4.4 KiB
C++
81 lines
4.4 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
|
|
extern "C" {
|
|
int APS5_VABI mkdir_nid_postfix(const char*, int);
|
|
int APS5_VABI rmdir_nid_postfix(const char*);
|
|
int APS5_VABI rename_nid_postfix(const char*, const char*);
|
|
int APS5_VABI sceKernelRename(const char*, const char*);
|
|
int APS5_VABI stat_nid_postfix(const char*, void*);
|
|
int* APS5_VABI __error_nid_postfix();
|
|
}
|
|
|
|
namespace {
|
|
|
|
constexpr int ErrorNoEntry = 2;
|
|
constexpr int ErrorInvalid = 22;
|
|
constexpr char Source[] = "anyps5_rename_dot_source";
|
|
constexpr char Target[] = "anyps5_rename_dot_target";
|
|
constexpr char Moved[] = "anyps5_rename_dot_moved";
|
|
|
|
void Cleanup() {
|
|
rmdir_nid_postfix(Source);
|
|
rmdir_nid_postfix(Target);
|
|
rmdir_nid_postfix(Moved);
|
|
}
|
|
|
|
void Require(bool condition, const char* what) {
|
|
if (condition) return;
|
|
std::fprintf(stderr, "%s (errno %d)\n", what, *__error_nid_postfix());
|
|
Cleanup();
|
|
std::abort();
|
|
}
|
|
|
|
bool Fails(long result, int error) { return result == -1 && *__error_nid_postfix() == error; }
|
|
|
|
bool Exists(const char* path) {
|
|
unsigned char status[256];
|
|
return stat_nid_postfix(path, status) == 0;
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
Cleanup();
|
|
Require(mkdir_nid_postfix(Source, 0755) == 0 && mkdir_nid_postfix(Target, 0755) == 0, "mkdir failed");
|
|
const std::string dotted = std::string(Source) + "/.";
|
|
const std::string dottedSlash = std::string(Source) + "/./";
|
|
const std::string intoTarget = std::string(Target) + "/.";
|
|
Require(Fails(rename_nid_postfix(dotted.c_str(), Moved), ErrorInvalid), "rename from dir/. must fail with EINVAL (22)");
|
|
Require(Fails(rename_nid_postfix(dottedSlash.c_str(), Moved), ErrorInvalid), "rename from dir/./ must fail with EINVAL (22)");
|
|
Require(Exists(Source) && !Exists(Moved), "a failed rename from dir/. must leave the directory in place");
|
|
Require(Fails(rename_nid_postfix(Source, intoTarget.c_str()), ErrorInvalid), "rename to dir/. must fail with EINVAL (22)");
|
|
Require(Fails(rename_nid_postfix(".", Moved), ErrorInvalid), "rename of . must fail with EINVAL (22)");
|
|
Require(Fails(rename_nid_postfix(Source, "."), ErrorInvalid), "rename onto . must fail with EINVAL (22)");
|
|
Require(Fails(rename_nid_postfix("anyps5_rename_dot_missing/.", Moved), ErrorNoEntry), "rename from missing/. must fail with ENOENT (2)");
|
|
const std::string dotDot = std::string(Source) + "/..";
|
|
const std::string dotDotSlash = std::string(Source) + "/../";
|
|
Require(Fails(rename_nid_postfix(dotDot.c_str(), Moved), ErrorInvalid), "rename from dir/.. must fail with EINVAL (22)");
|
|
Require(Fails(rename_nid_postfix(dotDotSlash.c_str(), Moved), ErrorInvalid), "rename from dir/../ must fail with EINVAL (22)");
|
|
Require(Fails(rename_nid_postfix(Source, (std::string(Target) + "/..").c_str()), ErrorInvalid), "rename to dir/.. must fail with EINVAL (22)");
|
|
Require(Fails(rename_nid_postfix("..", Moved), ErrorInvalid), "rename of .. must fail with EINVAL (22)");
|
|
Require(Fails(rename_nid_postfix("anyps5_rename_dot_missing/..", Moved), ErrorNoEntry), "rename from missing/.. must fail with ENOENT (2)");
|
|
const std::string backslashDot = std::string(Source) + "\\.";
|
|
const std::string backslashDotDot = std::string(Source) + "\\..";
|
|
Require(Fails(rename_nid_postfix(backslashDot.c_str(), Moved), ErrorInvalid), "rename from dir\\. must fail with EINVAL (22), the guest resolver turns the backslash into a separator");
|
|
Require(Fails(rename_nid_postfix(backslashDotDot.c_str(), Moved), ErrorInvalid), "rename from dir\\.. must fail with EINVAL (22)");
|
|
Require(Exists(Source) && !Exists(Moved), "failed renames from dir\\. and dir\\.. must leave the directory in place");
|
|
Require(sceKernelRename(dotDot.c_str(), Moved) == static_cast<int>(0x80020016u), "sceKernelRename from dir/.. must return EINVAL");
|
|
Require(sceKernelRename(dotted.c_str(), Moved) == static_cast<int>(0x80020016u), "sceKernelRename from dir/. must return EINVAL");
|
|
Require(sceKernelRename(Source, intoTarget.c_str()) == static_cast<int>(0x80020016u), "sceKernelRename to dir/. must return EINVAL");
|
|
Require(Exists(Source) && Exists(Target) && !Exists(Moved), "failed renames must change nothing");
|
|
|
|
Require(rename_nid_postfix(Source, Moved) == 0 && Exists(Moved) && !Exists(Source), "an ordinary rename must still work");
|
|
Require(sceKernelRename(Moved, Source) == 0 && Exists(Source) && !Exists(Moved), "an ordinary sceKernelRename must still work");
|
|
Cleanup();
|
|
return 0;
|
|
}
|