Fix FSReadDir when redirecting the /vol/content directory to another path

This commit is contained in:
Maschell 2021-09-30 14:25:17 +02:00
parent 91b7686e2e
commit 112c14498a
6 changed files with 246 additions and 32 deletions

View File

@ -11,8 +11,8 @@
}
#define ASYNC_RESULT_HANDLER [client, block, asyncData](FSStatus res) -> FSStatus { \
DEBUG_FUNCTION_LINE_VERBOSE("Result was %d", res); \
return send_result_async(client, block, asyncData, res);\
DEBUG_FUNCTION_LINE_VERBOSE("Result was %d", res); \
return send_result_async(client, block, asyncData, res); \
}
DECL_FUNCTION(FSStatus, FSOpenDir, FSClient *client, FSCmdBlock *block, char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask) {
@ -33,17 +33,24 @@ DECL_FUNCTION(FSStatus, FSOpenDir, FSClient *client, FSCmdBlock *block, char *pa
DECL_FUNCTION(FSStatus, FSOpenDirAsync, FSClient *client, FSCmdBlock *block, char *path, FSDirectoryHandle *handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE("%s", path);
FSStatus result = FSOpenDirWrapper(path, handle, errorMask,
[client, block, handle, errorMask, asyncData]
(char *_path) -> FSStatus {
return real_FSOpenDirAsync(client, block, _path, handle, errorMask, asyncData);
},
ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
FSErrorFlag realErrorMask = errorMask;
// Even real_FSOpenDir is still calling our FSOpenDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask.
if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) {
FSStatus result = FSOpenDirWrapper(path, handle, errorMask,
[client, block, handle, errorMask, asyncData]
(char *_path) -> FSStatus {
return real_FSOpenDirAsync(client, block, _path, handle, errorMask, asyncData);
},
ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
}
} else {
realErrorMask = FS_ERROR_FLAG_ALL;
}
return real_FSOpenDirAsync(client, block, path, handle, errorMask, asyncData);
return real_FSOpenDirAsync(client, block, path, handle, realErrorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSReadDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask) {
@ -58,12 +65,18 @@ DECL_FUNCTION(FSStatus, FSReadDir, FSClient *client, FSCmdBlock *block, FSDirect
DECL_FUNCTION(FSStatus, FSReadDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE();
FSStatus result = FSReadDirWrapper(handle, entry, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
FSErrorFlag realErrorMask = errorMask;
// Even real_FSReadDir is still calling our FSReadDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask.
if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) {
FSStatus result = FSReadDirWrapper(handle, entry, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
}
} else {
realErrorMask = FS_ERROR_FLAG_ALL;
}
return real_FSReadDirAsync(client, block, handle, entry, errorMask, asyncData);
return real_FSReadDirAsync(client, block, handle, entry, realErrorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSCloseDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) {
@ -78,12 +91,18 @@ DECL_FUNCTION(FSStatus, FSCloseDir, FSClient *client, FSCmdBlock *block, FSDirec
DECL_FUNCTION(FSStatus, FSCloseDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE();
FSStatus result = FSCloseDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
FSErrorFlag realErrorMask = errorMask;
// Even real_FSCloseDir is still calling our FSCloseDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask.
if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) {
FSStatus result = FSCloseDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
}
} else {
realErrorMask = FS_ERROR_FLAG_ALL;
}
return real_FSCloseDirAsync(client, block, handle, errorMask, asyncData);
return real_FSCloseDirAsync(client, block, handle, realErrorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSRewindDir, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask) {
@ -98,12 +117,18 @@ DECL_FUNCTION(FSStatus, FSRewindDir, FSClient *client, FSCmdBlock *block, FSDire
DECL_FUNCTION(FSStatus, FSRewindDirAsync, FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask, FSAsyncData *asyncData) {
DEBUG_FUNCTION_LINE_VERBOSE();
FSStatus result = FSRewindDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
FSErrorFlag realErrorMask = errorMask;
// Even real_FSRewindDir is still calling our FSRewindDirAsync hook. To bypass our code we use "FORCE_REAL_FUNC_WITH_FULL_ERRORS" as an errorMask.
if ((errorMask & ERROR_FLAG_MASK) != FORCE_REAL_FUNC_MAGIC) {
FSStatus result = FSRewindDirWrapper(handle, errorMask, ASYNC_RESULT_HANDLER);
if (result != FS_STATUS_USE_REAL_OS) {
return result;
}
} else {
realErrorMask = FS_ERROR_FLAG_ALL;
}
return real_FSRewindDirAsync(client, block, handle, errorMask, asyncData);
return real_FSRewindDirAsync(client, block, handle, realErrorMask, asyncData);
}
DECL_FUNCTION(FSStatus, FSMakeDir, FSClient *client, FSCmdBlock *block, char *path, FSErrorFlag errorMask) {

View File

@ -134,6 +134,8 @@ void freeDirHandle(uint32_t handle) {
dir_handle_mutex.unlock();
}
extern "C" FSStatus (*real_FSOpenDir)(FSClient *, FSCmdBlock *, char *, FSDirectoryHandle *, FSErrorFlag);
FSStatus FSOpenDirWrapper(char *path,
FSDirectoryHandle *handle,
FSErrorFlag errorMask,
@ -177,10 +179,30 @@ FSStatus FSOpenDirWrapper(char *path,
dir_handles[handle_index].path[0] = '\0';
strncat(dir_handles[handle_index].path, pathForCheck, sizeof(dir_handles[handle_index].path) - 1);
DCFlushRange(&dir_handles[handle_index], sizeof(dirMagic_t));
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
auto dir_info = &dir_handles[handle_index];
dir_info->readResult = nullptr;
dir_info->readResultCapacity = 0;
dir_info->readResultNumberOfEntries = 0;
dir_info->realDirHandle = 0;
if (gFSClient && gFSCmd) {
FSDirectoryHandle realHandle = 0;
if (real_FSOpenDir(gFSClient, gFSCmd, path, &realHandle, (FSErrorFlag) FORCE_REAL_FUNC_WITH_FULL_ERRORS) == FS_STATUS_OK) {
dir_info->realDirHandle = realHandle;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Failed to open real dir %s", path);
}
} else {
DEBUG_FUNCTION_LINE("Global FSClient or FSCmdBlock were null");
}
DCFlushRange(dir_info, sizeof(dirMagic_t));
}
OSUnlockMutex(dir_handles[handle_index].mutex);
} else {
DEBUG_FUNCTION_LINE("Dir not found %s", pathForCheck);
if (gReplacementInfo.contentReplacementInfo.fallbackOnError) {
return FS_STATUS_USE_REAL_OS;
}
@ -198,6 +220,8 @@ FSStatus FSOpenDirWrapper(char *path,
return FS_STATUS_USE_REAL_OS;
}
extern "C" FSStatus (*real_FSReadDir)(FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSDirectoryEntry *entry, FSErrorFlag errorMask);
FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
FSDirectoryEntry *entry,
FSErrorFlag errorMask,
@ -218,6 +242,19 @@ FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
OSLockMutex(dir_handles[handle_index].mutex);
DIR *dir = dir_handles[handle_index].dir;
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
auto dir_info = &dir_handles[handle_index];
// Init list if needed
if (dir_info->readResultCapacity == 0) {
dir_info->readResult = (FSDirectoryEntry *) malloc(sizeof(FSDirectoryEntry));
if (dir_info->readResult != nullptr) {
dir_info->readResultCapacity = 1;
}
}
DCFlushRange(dir_info, sizeof(dirMagic_t));
}
struct dirent *entry_ = readdir(dir);
FSStatus result = FS_STATUS_END;
if (entry_) {
@ -244,13 +281,73 @@ FSStatus FSReadDirWrapper(FSDirectoryHandle handle,
}
}
}
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
auto dir_info = &dir_handles[handle_index];
if (dir_info->readResultNumberOfEntries >= dir_info->readResultCapacity) {
auto newCapacity = dir_info->readResultCapacity * 2;
dir_info->readResult = (FSDirectoryEntry *) realloc(dir_info->readResult, newCapacity * sizeof(FSDirectoryEntry));
dir_info->readResultCapacity = newCapacity;
if (dir_info->readResult == nullptr) {
OSFatal("Failed to alloc memory for dir entry list");
}
}
memcpy(&dir_info->readResult[dir_info->readResultNumberOfEntries], entry, sizeof(FSDirectoryEntry));
dir_info->readResultNumberOfEntries++;
DCFlushRange(dir_info->readResult, sizeof(FSDirectoryEntry) * dir_info->readResultNumberOfEntries);
DCFlushRange(dir_info, sizeof(dirMagic_t));
}
result = FS_STATUS_OK;
} else if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
auto dir_info = &dir_handles[handle_index];
// Read the real directory.
if (dir_info->realDirHandle != 0) {
if (gFSClient && gFSCmd) {
FSDirectoryEntry realDirEntry;
FSStatus readDirResult = FS_STATUS_OK;
result = FS_STATUS_END;
while (readDirResult == FS_STATUS_OK) {
readDirResult = real_FSReadDir(gFSClient, gFSCmd, dir_info->realDirHandle, &realDirEntry, (FSErrorFlag) FORCE_REAL_FUNC_WITH_FULL_ERRORS);
if (readDirResult == FS_STATUS_OK) {
bool found = false;
for (int i = 0; i < dir_info->readResultNumberOfEntries; i++) {
auto curResult = &dir_info->readResult[i];
// Check if this is a new result
if (strncmp(curResult->name, realDirEntry.name, sizeof(realDirEntry.name) - 1) == 0) {
found = true;
break;
}
}
// If it's new we can use it :)
if (!found) {
memcpy(entry, &realDirEntry, sizeof(FSDirectoryEntry));
result = FS_STATUS_OK;
break;
}
} else if (readDirResult == FS_STATUS_END) {
result = FS_STATUS_END;
break;
} else {
DEBUG_FUNCTION_LINE("real_FSReadDir returned an unexpected error: %08X", readDirResult);
result = FS_STATUS_END;
break;
}
}
} else {
DEBUG_FUNCTION_LINE("Global FSClient or FSCmdBlock were null");
}
}
}
OSUnlockMutex(dir_handles[handle_index].mutex);
return result_handler(result);
}
extern "C" FSStatus (*real_FSCloseDir)(FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask);
FSStatus FSCloseDirWrapper(FSDirectoryHandle handle,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler) {
@ -274,11 +371,38 @@ FSStatus FSCloseDirWrapper(FSDirectoryHandle handle,
result = FS_STATUS_MEDIA_ERROR;
}
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
auto dir_info = &dir_handles[handle_index];
if (dir_info->realDirHandle != 0) {
if (gFSClient && gFSCmd) {
auto realResult = real_FSCloseDir(gFSClient, gFSCmd, dir_info->realDirHandle, (FSErrorFlag) FORCE_REAL_FUNC_WITH_FULL_ERRORS);
if (realResult == FS_STATUS_OK) {
dir_info->realDirHandle = 0;
} else {
DEBUG_FUNCTION_LINE("Failed to closed dir %d", realResult);
}
} else {
DEBUG_FUNCTION_LINE("Global FSClient or FSCmdBlock were null");
}
}
if (dir_info->readResult != nullptr) {
free(dir_info->readResult);
dir_info->readResult = nullptr;
dir_info->readResultCapacity = 0;
dir_info->readResultNumberOfEntries = 0;
}
DCFlushRange(dir_info, sizeof(dirMagic_t));
}
OSUnlockMutex(dir_handles[handle_index].mutex);
freeDirHandle(handle_index);
return result_handler(result);
}
extern "C" FSStatus (*real_FSRewindDir)(FSClient *client, FSCmdBlock *block, FSDirectoryHandle handle, FSErrorFlag errorMask);
FSStatus FSRewindDirWrapper(FSDirectoryHandle handle,
FSErrorFlag errorMask,
const std::function<FSStatus(FSStatus)> &result_handler) {
@ -297,6 +421,29 @@ FSStatus FSRewindDirWrapper(FSDirectoryHandle handle,
DIR *dir = dir_handles[real_handle].dir;
rewinddir(dir);
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
auto dir_info = &dir_handles[handle_index];
if (dir_info->readResult != nullptr) {
dir_info->readResultNumberOfEntries = 0;
memset(dir_info->readResult, 0, sizeof(FSDirectoryEntry) * dir_info->readResultCapacity);
}
if (dir_info->realDirHandle != 0) {
if (gFSClient && gFSCmd) {
if (real_FSRewindDir(gFSClient, gFSCmd, dir_info->realDirHandle, (FSErrorFlag) FORCE_REAL_FUNC_WITH_FULL_ERRORS) == FS_STATUS_OK) {
dir_info->realDirHandle = 0;
} else {
DEBUG_FUNCTION_LINE("Failed to rewind dir");
}
} else {
DEBUG_FUNCTION_LINE("Global FSClient or FSCmdBlock were null");
}
}
DCFlushRange(dir_info, sizeof(dirMagic_t));
}
OSUnlockMutex(dir_handles[handle_index].mutex);
return result_handler(FS_STATUS_OK);
}

View File

@ -7,11 +7,18 @@
#include <coreinit/mutex.h>
typedef struct dirMagic {
uint32_t handle;
DIR *dir;
bool in_use;
char path[256];
OSMutex *mutex;
uint32_t handle{};
DIR *dir{};
bool in_use{};
char path[256]{};
OSMutex *mutex{};
FSDirectoryEntry * readResult = nullptr;
int readResultCapacity = 0;
int readResultNumberOfEntries = 0;
FSDirectoryHandle realDirHandle = 0;
} dirMagic_t;
typedef struct fileMagic {
@ -21,6 +28,12 @@ typedef struct fileMagic {
OSMutex *mutex;
} fileMagic_t;
#define ERROR_FLAG_MASK (0xFFFF0000)
#define FORCE_REAL_FUNC_MAGIC (0x42420000)
#define FORCE_REAL_FUNC_WITH_FULL_ERRORS (FORCE_REAL_FUNC_MAGIC | 0x0000FFFF)
#define HANDLE_INDICATOR_MASK 0xFFFFFF00
#define HANDLE_INDICATOR_MASK 0xFFFFFF00
#define HANDLE_MASK (0x000000FF)
#define DIR_HANDLE_MAGIC 0x30000000

View File

@ -1,3 +1,7 @@
#include <coreinit/filesystem.h>
#include "globals.h"
RPXLoader_ReplacementInformation gReplacementInfo __attribute__((section(".data")));
RPXLoader_ReplacementInformation gReplacementInfo __attribute__((section(".data")));
FSClient * gFSClient __attribute__((section(".data"))) = nullptr;
FSCmdBlock * gFSCmd __attribute__((section(".data"))) = nullptr;

View File

@ -1,5 +1,6 @@
#include <wums.h>
#include <coreinit/mutex.h>
#include <coreinit/filesystem.h>
typedef struct MetaInformation_t {
char shortname[64];
@ -48,4 +49,6 @@ typedef struct RPXLoader_ReplacementInformation_t {
} RPXLoader_ReplacementInformation;
extern RPXLoader_ReplacementInformation gReplacementInfo;
extern RPXLoader_ReplacementInformation gReplacementInfo;
extern FSClient * gFSClient;
extern FSCmdBlock * gFSCmd;

View File

@ -46,6 +46,11 @@ WUMS_APPLICATION_ENDS() {
}
}
gReplacementInfo.rpxReplacementInfo.isRPXReplaced = false;
if (gFSClient) {
FSDelClient(gFSClient, FS_ERROR_FLAG_ALL);
free(gFSClient);
}
free(gFSCmd);
}
WUMS_APPLICATION_STARTS() {
@ -59,6 +64,23 @@ WUMS_APPLICATION_STARTS() {
}
WHBLogUdpInit();
if (gReplacementInfo.contentReplacementInfo.mode == CONTENTREDIRECT_FROM_PATH) {
auto fsClient = (FSClient *) memalign(0x20, sizeof(FSClient));
auto fsCmd = (FSCmdBlock *) memalign(0x20, sizeof(FSCmdBlock));
if (fsClient == nullptr || fsCmd == nullptr) {
DEBUG_FUNCTION_LINE("Failed to alloc memory for fsclient or fsCmd");
free(fsClient);
free(fsCmd);
} else {
auto rc = FSAddClient(fsClient, FS_ERROR_FLAG_ALL);
if (rc < 0) {
DEBUG_FUNCTION_LINE("Failed to add FSClient");
} else {
FSInitCmdBlock(fsCmd);
gFSClient = fsClient;
gFSCmd = fsCmd;
}
}
return;
}