90 lines
3.9 KiB
C++
90 lines
3.9 KiB
C++
#include "prx/libc/include/FilesystemError.hpp"
|
|
#include "prx/libc/include/General.hpp"
|
|
#include <cerrno>
|
|
#include <filesystem>
|
|
#include <new>
|
|
|
|
static int RenamePath_nid_no_patch(const std::filesystem::path& source, const std::filesystem::path& destination) {
|
|
constexpr int GuestEnoent = 2;
|
|
constexpr int GuestEio = 5;
|
|
constexpr int GuestEnotdir = 20;
|
|
constexpr int GuestEisdir = 21;
|
|
constexpr int GuestEinval = 22;
|
|
constexpr int GuestEnotempty = 66;
|
|
namespace fs = std::filesystem;
|
|
std::error_code error;
|
|
const auto sourceStatus = fs::symlink_status(source, error);
|
|
if (error && error != std::errc::no_such_file_or_directory) return FilesystemError(error);
|
|
if (sourceStatus.type() == fs::file_type::not_found) return GuestEnoent;
|
|
|
|
const auto parent = destination.parent_path().empty() ? fs::path(".") : destination.parent_path();
|
|
error.clear();
|
|
const auto parentStatus = fs::status(parent, error);
|
|
if (error) return FilesystemError(error);
|
|
if (!fs::exists(parentStatus)) return GuestEnoent;
|
|
if (!fs::is_directory(parentStatus)) return GuestEnotdir;
|
|
|
|
error.clear();
|
|
const auto destinationStatus = fs::symlink_status(destination, error);
|
|
if (error && error != std::errc::no_such_file_or_directory) return FilesystemError(error);
|
|
const bool destinationExists = destinationStatus.type() != fs::file_type::not_found;
|
|
const bool sourceIsDirectory = fs::is_directory(sourceStatus);
|
|
const bool destinationIsDirectory = destinationExists && fs::is_directory(destinationStatus);
|
|
bool sameFile = false;
|
|
if (destinationExists) {
|
|
error.clear();
|
|
sameFile = fs::equivalent(source, destination, error);
|
|
if (error == std::errc::no_such_file_or_directory) error.clear();
|
|
if (error) return FilesystemError(error);
|
|
if (!sameFile && sourceIsDirectory && !destinationIsDirectory) return GuestEnotdir;
|
|
if (!sameFile && !sourceIsDirectory && destinationIsDirectory) return GuestEisdir;
|
|
}
|
|
if (sourceIsDirectory && !sameFile) {
|
|
error.clear();
|
|
const auto canonicalSource = fs::weakly_canonical(source, error);
|
|
if (error) return FilesystemError(error);
|
|
const auto canonicalDestination = fs::weakly_canonical(destination, error);
|
|
if (error) return FilesystemError(error);
|
|
const auto relative = canonicalDestination.lexically_relative(canonicalSource);
|
|
if (!relative.empty() && *relative.begin() != "..") return GuestEinval;
|
|
}
|
|
if (destinationExists && !sameFile && destinationIsDirectory) {
|
|
error.clear();
|
|
if (!fs::is_empty(destination, error)) {
|
|
return error ? FilesystemError(error) : GuestEnotempty;
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
if (!sameFile && sourceIsDirectory && destinationIsDirectory) {
|
|
error.clear();
|
|
if (!fs::remove(destination, error)) return error ? FilesystemError(error) : GuestEio;
|
|
}
|
|
#endif
|
|
|
|
error.clear();
|
|
fs::rename(source, destination, error);
|
|
return error ? FilesystemError(error) : 0;
|
|
}
|
|
|
|
extern "C" int RenameGuestPath_nid_no_patch(const char* from, const char* to) {
|
|
if (!from || !to) { errno = 14; return -1; }
|
|
if (!*from || !*to) { errno = 2; return -1; }
|
|
try {
|
|
const auto source = ResolvePath_nid_no_patch(from);
|
|
const auto destination = ResolvePath_nid_no_patch(to);
|
|
const int error = RenamePath_nid_no_patch(source, destination);
|
|
if (error) { errno = error; return -1; }
|
|
RecordWrittenPath_nid_no_patch(source);
|
|
RecordWrittenPath_nid_no_patch(destination);
|
|
return 0;
|
|
} catch (const std::bad_alloc&) { errno = 12; return -1; }
|
|
catch (const std::filesystem::filesystem_error& error) {
|
|
errno = FilesystemError(error.code());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
extern "C" int APS5_VABI rename_nid_postfix(const char* from, const char* to) {
|
|
return RenameGuestPath_nid_no_patch(from, to);
|
|
}
|