mirror of
https://github.com/wiiu-env/ContentRedirectionModule.git
synced 2024-09-27 10:38:43 +02:00
265 lines
13 KiB
C++
265 lines
13 KiB
C++
#include "FSWrapperMergeDirsWithParent.h"
|
|
#include "utils/logger.h"
|
|
#include "utils/utils.h"
|
|
#include <coreinit/cache.h>
|
|
#include <coreinit/debug.h>
|
|
#include <coreinit/filesystem.h>
|
|
#include <filesystem>
|
|
|
|
FSError FSWrapperMergeDirsWithParent::FSOpenDirWrapper(const char *path,
|
|
FSDirectoryHandle *handle) {
|
|
if (handle == nullptr) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] handle was NULL", getName().c_str());
|
|
return FS_ERROR_INVALID_PARAM;
|
|
}
|
|
|
|
auto res = FSWrapper::FSOpenDirWrapper(path, handle);
|
|
if (res == FS_ERROR_OK) {
|
|
if (!isValidDirHandle(*handle)) {
|
|
FSWrapper::FSCloseDirWrapper(*handle);
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), *handle);
|
|
return FS_ERROR_INVALID_DIRHANDLE;
|
|
}
|
|
auto dirHandle = getDirExFromHandle(*handle);
|
|
if (dirHandle != nullptr) {
|
|
dirHandle->readResultCapacity = 0;
|
|
dirHandle->readResultNumberOfEntries = 0;
|
|
dirHandle->realDirHandle = 0;
|
|
|
|
if (pFSClient && pCmdBlock) {
|
|
FSDirectoryHandle realHandle = 0;
|
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call real_FSOpenDir for %s with error_flag %08X", getName().c_str(), path, this->getHandle());
|
|
// Call FSOpen with "this" as errorFlag call FSOpen for "parent" layers only.
|
|
if (FSOpenDir(pFSClient, pCmdBlock, path, &realHandle, (FSErrorFlag) this->getHandle()) == FS_STATUS_OK) {
|
|
dirHandle->realDirHandle = realHandle;
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Failed to open real dir %s", getName().c_str(), path);
|
|
}
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
|
|
}
|
|
OSMemoryBarrier();
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool FSWrapperMergeDirsWithParent::SkipDeletedFilesInReadDir() {
|
|
return false;
|
|
}
|
|
|
|
FSError FSWrapperMergeDirsWithParent::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *entry) {
|
|
do {
|
|
auto res = FSWrapper::FSReadDirWrapper(handle, entry);
|
|
if (res == FS_ERROR_OK || res == FS_ERROR_END_OF_DIR) {
|
|
if (!isValidDirHandle(handle)) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle);
|
|
return FS_ERROR_INVALID_DIRHANDLE;
|
|
}
|
|
auto dirHandle = getDirExFromHandle(handle);
|
|
if (res == FS_ERROR_OK) {
|
|
if (dirHandle->readResultCapacity == 0) {
|
|
dirHandle->readResult = (FSDirectoryEntryEx *) malloc(sizeof(FSDirectoryEntryEx));
|
|
if (dirHandle->readResult == nullptr) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc memory for %08X (handle %08X)", getName().c_str(), dirHandle.get(), handle);
|
|
OSFatal("Failed to alloc memory for read result");
|
|
}
|
|
dirHandle->readResultCapacity = 1;
|
|
}
|
|
|
|
if (dirHandle->readResultNumberOfEntries >= dirHandle->readResultCapacity) {
|
|
auto newCapacity = dirHandle->readResultCapacity * 2;
|
|
dirHandle->readResult = (FSDirectoryEntryEx *) realloc(dirHandle->readResult, newCapacity * sizeof(FSDirectoryEntryEx));
|
|
dirHandle->readResultCapacity = newCapacity;
|
|
if (dirHandle->readResult == nullptr) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to realloc memory for %08X (handle %08X)", getName().c_str(), dirHandle.get(), handle);
|
|
OSFatal("Failed to alloc memory for read result");
|
|
}
|
|
}
|
|
|
|
memcpy(&dirHandle->readResult[dirHandle->readResultNumberOfEntries].realEntry, entry, sizeof(FSDirectoryEntry));
|
|
dirHandle->readResultNumberOfEntries++;
|
|
|
|
/**
|
|
* Read the next entry if this entry starts with deletePrefix. We keep the entry but mark it as deleted.
|
|
*/
|
|
if (std::string_view(entry->name).starts_with(deletePrefix)) {
|
|
dirHandle->readResult[dirHandle->readResultNumberOfEntries].isMarkedAsDeleted = true;
|
|
|
|
OSMemoryBarrier();
|
|
continue;
|
|
}
|
|
|
|
OSMemoryBarrier();
|
|
|
|
} else if (res == FS_ERROR_END_OF_DIR) {
|
|
// Read the real directory.
|
|
if (dirHandle->realDirHandle != 0) {
|
|
if (pFSClient && pCmdBlock) {
|
|
FSDirectoryEntry realDirEntry;
|
|
FSStatus readDirResult;
|
|
while (true) {
|
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call real_FSReadDir for %08X with error_flag %08X", getName().c_str(), dirHandle->realDirHandle, (uint32_t) this->getHandle());
|
|
readDirResult = FSReadDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, &realDirEntry, (FSErrorFlag) (uint32_t) this->getHandle());
|
|
if (readDirResult == FS_STATUS_OK) {
|
|
bool found = false;
|
|
auto nameDeleted = deletePrefix + realDirEntry.name;
|
|
for (int i = 0; i < dirHandle->readResultNumberOfEntries; i++) {
|
|
auto curResult = &dirHandle->readResult[i];
|
|
|
|
// Don't return file that are "deleted"
|
|
if (strcmp(curResult->realEntry.name, nameDeleted.c_str()) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
// Check if this is a new result
|
|
if (strcmp(curResult->realEntry.name, realDirEntry.name) == 0 && !curResult->isMarkedAsDeleted) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
// If it's new we can use it :)
|
|
if (!found) {
|
|
memcpy(entry, &realDirEntry, sizeof(FSDirectoryEntry));
|
|
res = FS_ERROR_OK;
|
|
break;
|
|
}
|
|
} else if (readDirResult == FS_STATUS_END) {
|
|
res = FS_ERROR_END_OF_DIR;
|
|
break;
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] real_FSReadDir returned an unexpected error: %08X", getName().c_str(), readDirResult);
|
|
res = FS_ERROR_END_OF_DIR;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
|
|
}
|
|
}
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Unexpected result %d", getName().c_str(), res);
|
|
}
|
|
}
|
|
return res;
|
|
} while (true);
|
|
}
|
|
|
|
FSError FSWrapperMergeDirsWithParent::FSCloseDirWrapper(FSDirectoryHandle handle) {
|
|
auto res = FSWrapper::FSCloseDirWrapper(handle);
|
|
|
|
if (res == FS_ERROR_OK) {
|
|
if (!isValidDirHandle(handle)) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle);
|
|
return FS_ERROR_INVALID_DIRHANDLE;
|
|
}
|
|
auto dirHandle = getDirExFromHandle(handle);
|
|
if (dirHandle->realDirHandle != 0) {
|
|
if (pFSClient && pCmdBlock) {
|
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call FSCloseDir for %08X with error_flag %08X (this)", getName().c_str(), dirHandle->realDirHandle, (uint32_t) this->getHandle());
|
|
auto realResult = FSCloseDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, (FSErrorFlag) (uint32_t) this->getHandle());
|
|
if (realResult == FS_STATUS_OK) {
|
|
dirHandle->realDirHandle = 0;
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to close realDirHandle %d: res %d", getName().c_str(), dirHandle->realDirHandle, realResult);
|
|
return realResult == FS_STATUS_CANCELLED ? FS_ERROR_CANCELLED : FS_ERROR_MEDIA_ERROR;
|
|
}
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
|
|
}
|
|
}
|
|
|
|
if (dirHandle->readResult != nullptr) {
|
|
free(dirHandle->readResult);
|
|
dirHandle->readResult = nullptr;
|
|
dirHandle->readResultCapacity = 0;
|
|
dirHandle->readResultNumberOfEntries = 0;
|
|
}
|
|
|
|
OSMemoryBarrier();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
FSError FSWrapperMergeDirsWithParent::FSRewindDirWrapper(FSDirectoryHandle handle) {
|
|
auto res = FSWrapper::FSRewindDirWrapper(handle);
|
|
if (res == FS_ERROR_OK) {
|
|
if (!isValidDirHandle(handle)) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] No valid dir handle %08X", getName().c_str(), handle);
|
|
return FS_ERROR_INVALID_DIRHANDLE;
|
|
}
|
|
auto dirHandle = getDirExFromHandle(handle);
|
|
if (dirHandle->readResult != nullptr) {
|
|
dirHandle->readResultNumberOfEntries = 0;
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
|
memset(dirHandle->readResult, 0, sizeof(FSDirectoryEntryEx) * dirHandle->readResultCapacity);
|
|
#pragma GCC diagnostic pop
|
|
}
|
|
|
|
if (dirHandle->realDirHandle != 0) {
|
|
if (pFSClient && pCmdBlock) {
|
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Call real_FSRewindDir for %08X with error_flag %08X (this->getHandle())", getName().c_str(), dirHandle->realDirHandle, (uint32_t) this->getHandle());
|
|
if (FSRewindDir(pFSClient, pCmdBlock, dirHandle->realDirHandle, (FSErrorFlag) (uint32_t) this->getHandle()) == FS_STATUS_OK) {
|
|
dirHandle->realDirHandle = 0;
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to rewind dir for realDirHandle %08X", getName().c_str(), dirHandle->realDirHandle);
|
|
}
|
|
} else {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Global FSClient or FSCmdBlock were null", getName().c_str());
|
|
}
|
|
}
|
|
OSMemoryBarrier();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
FSWrapperMergeDirsWithParent::FSWrapperMergeDirsWithParent(const std::string &name,
|
|
const std::string &pathToReplace,
|
|
const std::string &replaceWithPath,
|
|
bool fallbackOnError) : FSWrapper(name,
|
|
pathToReplace,
|
|
replaceWithPath,
|
|
fallbackOnError,
|
|
false) {
|
|
pFSClient = new (std::nothrow) FSClient;
|
|
pCmdBlock = new (std::nothrow) FSCmdBlock;
|
|
if (pFSClient == nullptr || pCmdBlock == nullptr) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc client or cmdBlock", name.c_str());
|
|
freeFSClient();
|
|
}
|
|
if (FSAddClient(pFSClient, FS_ERROR_FLAG_ALL) != FS_STATUS_OK) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to addClient");
|
|
freeFSClient();
|
|
}
|
|
FSInitCmdBlock(pCmdBlock);
|
|
}
|
|
|
|
FSWrapperMergeDirsWithParent::~FSWrapperMergeDirsWithParent() {
|
|
freeFSClient();
|
|
}
|
|
|
|
void FSWrapperMergeDirsWithParent::freeFSClient() {
|
|
if (pFSClient) {
|
|
FSDelClient(pFSClient, FS_ERROR_FLAG_ALL);
|
|
}
|
|
delete pFSClient;
|
|
delete pCmdBlock;
|
|
pFSClient = nullptr;
|
|
pCmdBlock = nullptr;
|
|
}
|
|
|
|
std::shared_ptr<DirInfoEx> FSWrapperMergeDirsWithParent::getDirExFromHandle(FSDirectoryHandle handle) {
|
|
auto dir = std::dynamic_pointer_cast<DirInfoEx>(getDirFromHandle(handle));
|
|
|
|
if (!dir) {
|
|
DEBUG_FUNCTION_LINE_ERR("[%s] dynamic_pointer_cast<DirInfoEx *>(%08X) failed", getName().c_str(), handle);
|
|
OSFatal("dynamic_pointer_cast<DirInfoEx *> failed");
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
std::shared_ptr<DirInfo> FSWrapperMergeDirsWithParent::getNewDirHandle() {
|
|
return make_shared_nothrow<DirInfoEx>();
|
|
}
|