ContentRedirectionModule/src/FileUtils.cpp

339 lines
13 KiB
C++

#include "FileUtils.h"
#include "FSWrapper.h"
#include "IFSWrapper.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include <coreinit/cache.h>
#include <coreinit/filesystem_fsa.h>
#include <map>
#include <unistd.h>
std::mutex workingDirMutexFS;
std::map<uint32_t, std::string> workingDirsFS;
std::mutex workingDirMutexFSA;
std::map<uint32_t, std::string> workingDirsFSA;
std::mutex fsLayerMutex;
std::vector<std::unique_ptr<IFSWrapper>> fsLayers;
std::string getFullPathGeneric(uint32_t client, const char *path, std::mutex &mutex, std::map<uint32_t, std::string> &map) {
std::lock_guard<std::mutex> workingDirLock(mutex);
std::string res;
if (path[0] != '/' && path[0] != '\\') {
if (map.count(client) == 0) {
DEBUG_FUNCTION_LINE_WARN("No working dir found for client %08X, fallback to \"/\"", client);
workingDirsFS[client] = "/";
}
res = string_format("%s%s", map.at(client).c_str(), path);
} else {
res = path;
}
std::replace(res.begin(), res.end(), '\\', '/');
return res;
}
void setWorkingDirGeneric(uint32_t client, const char *path, std::mutex &mutex, std::map<uint32_t, std::string> &map) {
if (!path) {
DEBUG_FUNCTION_LINE_WARN("Path was NULL");
return;
}
std::lock_guard<std::mutex> workingDirLock(mutex);
std::string cwd(path);
if (cwd.empty() || cwd.back() != '/') {
cwd.push_back('/');
}
map[client] = cwd;
OSMemoryBarrier();
}
std::string getFullPathForFSClient(FSClient *pClient, const char *path) {
return getFullPathGeneric((uint32_t) pClient, path, workingDirMutexFS, workingDirsFS);
}
void setWorkingDirForFSClient(FSClient *client, const char *path) {
setWorkingDirGeneric((uint32_t) client, path, workingDirMutexFS, workingDirsFS);
}
std::string getFullPathForFSAClient(FSAClientHandle client, const char *path) {
return getFullPathGeneric((uint32_t) client, path, workingDirMutexFSA, workingDirsFSA);
}
void setWorkingDirForFSAClient(FSAClientHandle client, const char *path) {
setWorkingDirGeneric(client, path, workingDirMutexFSA, workingDirsFSA);
}
void clearFSLayer() {
{
std::lock_guard<std::mutex> workingDirLock(workingDirMutexFS);
workingDirsFS.clear();
}
{
std::lock_guard<std::mutex> workingDirLock(workingDirMutexFSA);
workingDirsFSA.clear();
}
{
std::lock_guard<std::mutex> layerLock(fsLayerMutex);
fsLayers.clear();
}
}
FSError doForLayerFSA(const std::function<FSError()> &real_function,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer)> &layer_callback,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer, FSError)> &result_handler) {
std::lock_guard<std::mutex> lock(fsLayerMutex);
if (!fsLayers.empty()) {
uint32_t startIndex = fsLayers.size();
if (startIndex > 0) {
for (uint32_t i = startIndex; i > 0; i--) {
auto &layer = fsLayers[i - 1];
if (!layer->isActive()) {
continue;
}
FSError layerResult = layer_callback(layer);
if (layerResult != FS_ERROR_FORCE_PARENT_LAYER) {
auto result = layerResult >= 0 ? layerResult : (FSError) ((layerResult & FS_ERROR_REAL_MASK) | FS_ERROR_EXTRA_MASK);
if (result < FS_ERROR_OK && result != FS_ERROR_END_OF_DIR && result != FS_ERROR_END_OF_FILE && result != FS_ERROR_CANCELLED) {
if (layer->fallbackOnError()) {
// Only fallback if FS_ERROR_FORCE_NO_FALLBACK flag is not set.
if (static_cast<FSError>(layerResult & FS_ERROR_EXTRA_MASK) != FS_ERROR_FORCE_NO_FALLBACK) {
continue;
}
}
}
return result_handler(layer, result);
}
}
}
}
return real_function();
}
// FUN_0204cc20
#define fsClientHandleFatalErrorAndBlock ((void (*)(FSClientBody *, uint32_t))(0x101C400 + 0x4cc20))
#define fsaDecodeFsaStatusToFsStatus ((FSStatus(*)(FSError))(0x101C400 + 0x4b148))
FSStatus doForLayer(FSClient *client,
FSErrorFlag errorMask,
const std::function<FSStatus(FSErrorFlag errorMask)> &real_function,
const std::function<FSError(std::unique_ptr<IFSWrapper> &layer)> &layer_callback,
const std::function<FSStatus(std::unique_ptr<IFSWrapper> &layer, FSStatus)> &result_handler) {
FSErrorFlag realErrorMask = errorMask;
std::lock_guard<std::mutex> lock(fsLayerMutex);
if (!fsLayers.empty()) {
uint32_t startIndex = fsLayers.size();
for (uint32_t i = fsLayers.size(); i > 0; i--) {
if ((uint32_t) fsLayers[i - 1]->getHandle() == errorMask) {
startIndex = i - 1;
realErrorMask = FS_ERROR_FLAG_ALL;
break;
}
}
if (startIndex > 0) {
for (uint32_t i = startIndex; i > 0; i--) {
auto &layer = fsLayers[i - 1];
if (!layer->isActive()) {
continue;
}
FSError layerResult = layer_callback(layer);
if (layerResult != FS_ERROR_FORCE_PARENT_LAYER) {
auto maskedResult = (FSError) ((layerResult & FS_ERROR_REAL_MASK) | FS_ERROR_EXTRA_MASK);
auto result = layerResult >= 0 ? static_cast<FSStatus>(layerResult) : fsaDecodeFsaStatusToFsStatus(maskedResult);
if (result < FS_STATUS_OK && result != FS_STATUS_END && result != FS_STATUS_CANCELLED) {
if (layer->fallbackOnError()) {
// Only fallback if FS_ERROR_FORCE_NO_FALLBACK flag is not set.
if (static_cast<FSError>(layerResult & FS_ERROR_EXTRA_MASK) != FS_ERROR_FORCE_NO_FALLBACK) {
continue;
}
}
}
if (result >= FS_STATUS_OK || result == FS_STATUS_END || result == FS_STATUS_CANCELLED) {
DEBUG_FUNCTION_LINE_VERBOSE("Returned %08X by %s", result, layer->getName().c_str());
return result_handler(layer, result);
}
FSErrorFlag errorFlags = FS_ERROR_FLAG_NONE;
bool forceError = false;
switch ((int32_t) result) {
case FS_STATUS_MAX:
errorFlags = FS_ERROR_FLAG_MAX;
break;
case FS_STATUS_ALREADY_OPEN:
errorFlags = FS_ERROR_FLAG_ALREADY_OPEN;
break;
case FS_STATUS_EXISTS:
errorFlags = FS_ERROR_FLAG_EXISTS;
break;
case FS_STATUS_NOT_FOUND:
errorFlags = FS_ERROR_FLAG_NOT_FOUND;
break;
case FS_STATUS_NOT_FILE:
errorFlags = FS_ERROR_FLAG_NOT_FILE;
break;
case FS_STATUS_NOT_DIR:
errorFlags = FS_ERROR_FLAG_NOT_DIR;
break;
case FS_STATUS_ACCESS_ERROR:
errorFlags = FS_ERROR_FLAG_ACCESS_ERROR;
break;
case FS_STATUS_PERMISSION_ERROR:
errorFlags = FS_ERROR_FLAG_PERMISSION_ERROR;
break;
case FS_STATUS_FILE_TOO_BIG:
errorFlags = FS_ERROR_FLAG_FILE_TOO_BIG;
break;
case FS_STATUS_STORAGE_FULL:
errorFlags = FS_ERROR_FLAG_STORAGE_FULL;
break;
case FS_STATUS_JOURNAL_FULL:
errorFlags = FS_ERROR_FLAG_JOURNAL_FULL;
break;
case FS_STATUS_UNSUPPORTED_CMD:
errorFlags = FS_ERROR_FLAG_UNSUPPORTED_CMD;
break;
case FS_STATUS_MEDIA_NOT_READY:
case FS_STATUS_MEDIA_ERROR:
case FS_STATUS_CORRUPTED:
case FS_STATUS_FATAL_ERROR:
forceError = true;
break;
case FS_STATUS_OK:
break;
}
if (forceError || (realErrorMask != FS_ERROR_FLAG_NONE && (errorFlags & realErrorMask) == 0)) {
DEBUG_FUNCTION_LINE_ERR("Transit to Fatal Error");
auto clientBody = fsClientGetBody(client);
fsClientHandleFatalErrorAndBlock(clientBody, clientBody->lastError);
return FS_STATUS_FATAL_ERROR;
}
DEBUG_FUNCTION_LINE_VERBOSE("%08X Returned %08X by %s ", errorMask, result, layer->getName().c_str());
return result_handler(layer, result);
}
}
}
}
auto mask = static_cast<FSErrorFlag>((realErrorMask & FS_ERROR_FLAG_REAL_MASK) | FS_ERROR_FLAG_FORCE_REAL);
return real_function(mask);
}
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock) {
if (!cmdBlock) {
return nullptr;
}
auto body = (FSCmdBlockBody *) (ROUNDUP((uint32_t) cmdBlock, 0x40));
return body;
}
FSClientBody *fsClientGetBody(FSClient *client) {
if (!client) {
return nullptr;
}
auto body = (FSClientBody *) (ROUNDUP((uint32_t) client, 0x40));
body->client = client;
return body;
}
FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus status) {
if (asyncData->callback != nullptr) {
if (asyncData->ioMsgQueue != nullptr) {
DEBUG_FUNCTION_LINE_ERR("callback and ioMsgQueue both set.");
return FS_STATUS_FATAL_ERROR;
}
// userCallbacks are called in the DefaultAppIOQueue.
asyncData->ioMsgQueue = OSGetDefaultAppIOQueue();
//DEBUG_FUNCTION_LINE("Force to OSGetDefaultAppIOQueue (%08X)", asyncData->ioMsgQueue);
}
if (asyncData->ioMsgQueue != nullptr) {
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
FSAsyncResult *result = &(fsCmdBlockGetBody(block)->asyncResult);
//DEBUG_FUNCTION_LINE("Send result %d to ioMsgQueue (%08X)", status, asyncData->ioMsgQueue);
result->asyncData.callback = asyncData->callback;
result->asyncData.param = asyncData->param;
result->asyncData.ioMsgQueue = asyncData->ioMsgQueue;
memset(&result->ioMsg, 0, sizeof(result->ioMsg));
result->ioMsg.data = result;
result->ioMsg.type = OS_FUNCTION_TYPE_FS_CMD_ASYNC;
result->client = client;
result->block = block;
result->status = status;
while (!OSSendMessage(asyncData->ioMsgQueue, (OSMessage *) &(result->ioMsg), OS_MESSAGE_FLAGS_NONE)) {
DEBUG_FUNCTION_LINE_ERR("Failed to send message");
}
}
return FS_STATUS_OK;
}
int64_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count) {
auto sizeToRead = size * count;
/*
// https://github.com/decaf-emu/decaf-emu/blob/131aeb14fccff8461a5fd9f2aa5c040ba3880ef5/src/libdecaf/src/cafe/libraries/coreinit/coreinit_fs_cmd.cpp#L2346
if (sizeToRead > 0x100000) {
sizeToRead = 0x100000;
}*/
void *newBuffer = buffer;
int32_t curResult;
int64_t totalSize = 0;
while (sizeToRead > 0) {
curResult = read(handle, newBuffer, sizeToRead);
if (curResult < 0) {
DEBUG_FUNCTION_LINE_ERR("Reading %08X bytes from handle %08X failed. result %08X errno: %d ", size * count, handle, curResult, errno);
return -1;
}
if (curResult == 0) {
break;
}
newBuffer = (void *) (((uint32_t) newBuffer) + curResult);
totalSize += curResult;
sizeToRead -= curResult;
}
return totalSize;
}
int64_t writeFromBuffer(int32_t handle, void *buffer, size_t size, size_t count) {
auto sizeToWrite = size * count;
void *ptr = buffer;
int32_t curResult;
int64_t totalSize = 0;
while (sizeToWrite > 0) {
curResult = write(handle, ptr, sizeToWrite);
if (curResult < 0) {
DEBUG_FUNCTION_LINE_ERR("Writing %08X bytes from handle %08X failed. result %08X errno: %d ", size * count, handle, curResult, errno);
return -1;
}
if (curResult == 0) {
break;
}
ptr = (void *) (((uint32_t) ptr) + curResult);
totalSize += curResult;
sizeToWrite -= curResult;
}
return totalSize;
}