WIP, untested

This commit is contained in:
Maschell 2023-10-06 16:51:09 +02:00
parent 98a137c91f
commit 0fbfd24198
8 changed files with 249 additions and 105 deletions

View File

@ -49,7 +49,7 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
endif endif
LIBS := -lwums -lwut -lfunctionpatcher LIBS := -lwums -lwut -lfunctionpatcher -lmocha
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level # list of directories containing libraries, this must be the top level

View File

@ -7,20 +7,29 @@
static FSError processFSAShimInThread(FSAShimBuffer *shimBuffer) { static FSError processFSAShimInThread(FSAShimBuffer *shimBuffer) {
FSError res; FSError res;
if (gThreadsRunning) {
auto upid = OSGetUPID();
if (!sLayerInfoForUPID.contains(upid)) {
DEBUG_FUNCTION_LINE_ERR("invalid UPID %d", upid);
OSFatal("Tried to start threads for invalid UPID.");
}
auto & layerInfo = sLayerInfoForUPID[upid];
if (layerInfo->threadsRunning) {
auto param = (FSShimWrapper *) malloc(sizeof(FSShimWrapper)); auto param = (FSShimWrapper *) malloc(sizeof(FSShimWrapper));
if (param == nullptr) { if (param == nullptr) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for FSShimWrapper"); DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for FSShimWrapper");
OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapper"); OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapper");
} }
param->upid = OSGetUPID();
param->api = FS_SHIM_API_FSA; param->api = FS_SHIM_API_FSA;
param->sync = FS_SHIM_TYPE_SYNC; param->sync = FS_SHIM_TYPE_SYNC;
param->shim = shimBuffer; param->shim = shimBuffer;
if (OSGetCurrentThread() == gThreadData[OSGetCoreId()].thread) { if (OSGetCurrentThread() == layerInfo->threadData[OSGetCoreId()].thread) {
res = processShimBufferForFSA(param); res = processShimBufferForFSA(param);
//No need to clean "param", it has been already free'd in processFSAShimBuffer. // No need to clean "param", it has been already free'd in processFSAShimBuffer.
} else { } else {
auto message = (FSShimWrapperMessage *) malloc(sizeof(FSShimWrapperMessage)); auto message = (FSShimWrapperMessage *) malloc(sizeof(FSShimWrapperMessage));
if (message == nullptr) { if (message == nullptr) {
@ -31,7 +40,7 @@ static FSError processFSAShimInThread(FSAShimBuffer *shimBuffer) {
constexpr int32_t messageSize = sizeof(message->messages) / sizeof(message->messages[0]); constexpr int32_t messageSize = sizeof(message->messages) / sizeof(message->messages[0]);
OSInitMessageQueue(&message->messageQueue, message->messages, messageSize); OSInitMessageQueue(&message->messageQueue, message->messages, messageSize);
if (!sendMessageToThread(message)) { if (!sendMessageToThread(layerInfo, message)) {
DEBUG_FUNCTION_LINE_ERR("Failed to send message to thread"); DEBUG_FUNCTION_LINE_ERR("Failed to send message to thread");
OSFatal("ContentRedirectionModule: Failed send message to thread"); OSFatal("ContentRedirectionModule: Failed send message to thread");
} }

View File

@ -2,8 +2,10 @@
#include "FileUtils.h" #include "FileUtils.h"
#include "utils/StringTools.h" #include "utils/StringTools.h"
#include "utils/logger.h" #include "utils/logger.h"
#include <content_redirection/redirection.h>
#include <coreinit/core.h> #include <coreinit/core.h>
#include <coreinit/thread.h> #include <coreinit/thread.h>
#include <mocha/mocha.h>
FSStatus processFSError(FSError fsError, FSClient *client, FSErrorFlag errorMask) { FSStatus processFSError(FSError fsError, FSClient *client, FSErrorFlag errorMask) {
auto result = fsError >= 0 ? static_cast<FSStatus>(fsError) : fsaDecodeFsaStatusToFsStatus(fsError); auto result = fsError >= 0 ? static_cast<FSStatus>(fsError) : fsaDecodeFsaStatusToFsStatus(fsError);
@ -89,7 +91,15 @@ void handleAsyncRequestsCallback(IOSError err, void *context) {
bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBlock *block, FSErrorFlag errorMask, FSAsyncData *asyncData) { bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBlock *block, FSErrorFlag errorMask, FSAsyncData *asyncData) {
bool res = false; bool res = false;
if (gThreadsRunning) {
auto upid = OSGetUPID();
if (!sLayerInfoForUPID.contains(upid)) {
DEBUG_FUNCTION_LINE_ERR("invalid UPID %d", upid);
OSFatal("Tried to start threads for invalid UPID.");
}
auto & layerInfo = sLayerInfoForUPID[upid];
if (layerInfo->threadsRunning) {
// we **don't** need to free this in this function. // we **don't** need to free this in this function.
auto param = (FSShimWrapper *) malloc(sizeof(FSShimWrapper)); auto param = (FSShimWrapper *) malloc(sizeof(FSShimWrapper));
if (param == nullptr) { if (param == nullptr) {
@ -97,6 +107,7 @@ bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBl
OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapper"); OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapper");
} }
param->upid = OSGetUPID();
param->api = FS_SHIM_API_FS; param->api = FS_SHIM_API_FS;
param->sync = FS_SHIM_TYPE_ASYNC; param->sync = FS_SHIM_TYPE_ASYNC;
param->shim = shimBuffer; param->shim = shimBuffer;
@ -111,7 +122,7 @@ bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBl
// Copy by value // Copy by value
param->asyncFS.errorMask = errorMask; param->asyncFS.errorMask = errorMask;
if (OSGetCurrentThread() == gThreadData[OSGetCoreId()].thread) { if (OSGetCurrentThread() == layerInfo->threadData[OSGetCoreId()].thread) {
processShimBufferForFS(param); processShimBufferForFS(param);
// because we're doing this in sync, free(param) has already been called at this point. // because we're doing this in sync, free(param) has already been called at this point.
res = true; res = true;
@ -122,7 +133,7 @@ bool processFSAShimInThread(FSAShimBuffer *shimBuffer, FSClient *client, FSCmdBl
OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapperMessage"); OSFatal("ContentRedirectionModule: Failed to allocate memory for FSShimWrapperMessage");
} }
message->param = param; message->param = param;
res = sendMessageToThread(message); res = sendMessageToThread(layerInfo, message);
// the other thread is call free for us, so we can return early! // the other thread is call free for us, so we can return early!
} }
} else { } else {
@ -217,7 +228,6 @@ DECL_FUNCTION(FSStatus, FSOpenFileExAsync, FSClient *client, FSCmdBlock *block,
auto *hackyBuffer = (uint32_t *) &shimBuffer->response; auto *hackyBuffer = (uint32_t *) &shimBuffer->response;
hackyBuffer[1] = (uint32_t) handle; hackyBuffer[1] = (uint32_t) handle;
if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) { if (processFSAShimInThread(shimBuffer, client, block, errorMask, asyncData)) {
return FS_STATUS_OK; return FS_STATUS_OK;
} }
} }
@ -555,28 +565,74 @@ DECL_FUNCTION(FSStatus, FSChangeDirAsync, FSClient *client, FSCmdBlock *block, c
return real_FSChangeDirAsync(client, block, path, errorMask, asyncData); return real_FSChangeDirAsync(client, block, path, errorMask, asyncData);
} }
function_replacement_data_t fs_file_function_replacements[] = { DECL_FUNCTION(void, __PPCExit, uint32_t u1) {
REPLACE_FUNCTION(FSOpenFileExAsync, LIBRARY_COREINIT, FSOpenFileExAsync), auto UPID = OSGetUPID();
REPLACE_FUNCTION(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync), if (UPID != 2 && UPID != 15 && sLayerInfoForUPID.contains(UPID)) {
REPLACE_FUNCTION(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync), DEBUG_FUNCTION_LINE_ERR("Clear layer for UPID %d", UPID);
REPLACE_FUNCTION(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync), clearFSLayer(sLayerInfoForUPID[UPID]);
REPLACE_FUNCTION_VIA_ADDRESS(FSReadFileGeneric, 0x3201C400 + 0x4ecc0, 0x101C400 + 0x4ecc0), stopFSIOThreads();
REPLACE_FUNCTION(FSSetPosFileAsync, LIBRARY_COREINIT, FSSetPosFileAsync), }
REPLACE_FUNCTION(FSGetPosFileAsync, LIBRARY_COREINIT, FSGetPosFileAsync), real___PPCExit(u1);
REPLACE_FUNCTION(FSIsEofAsync, LIBRARY_COREINIT, FSIsEofAsync), }
REPLACE_FUNCTION_VIA_ADDRESS(FSWriteFileGeneric, 0x3201C400 + 0x4eec0, 0x101C400 + 0x4eec0),
REPLACE_FUNCTION(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync),
REPLACE_FUNCTION(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync),
REPLACE_FUNCTION(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync),
REPLACE_FUNCTION(FSFlushFileAsync, LIBRARY_COREINIT, FSFlushFileAsync),
REPLACE_FUNCTION(FSChangeModeAsync, LIBRARY_COREINIT, FSChangeModeAsync),
REPLACE_FUNCTION(FSOpenDirAsync, LIBRARY_COREINIT, FSOpenDirAsync), MochaUtilsStatus MountWrapper(const char *mount, const char *dev, const char *mountTo) {
REPLACE_FUNCTION(FSReadDirAsync, LIBRARY_COREINIT, FSReadDirAsync), auto res = Mocha_MountFS(mount, dev, mountTo);
REPLACE_FUNCTION(FSCloseDirAsync, LIBRARY_COREINIT, FSCloseDirAsync), if (res == MOCHA_RESULT_ALREADY_EXISTS) {
REPLACE_FUNCTION(FSRewindDirAsync, LIBRARY_COREINIT, FSRewindDirAsync), res = Mocha_MountFS(mount, nullptr, mountTo);
REPLACE_FUNCTION(FSMakeDirAsync, LIBRARY_COREINIT, FSMakeDirAsync), }
REPLACE_FUNCTION(FSChangeDirAsync, LIBRARY_COREINIT, FSChangeDirAsync),
if (res == MOCHA_RESULT_SUCCESS) {
std::string mountPath = std::string(mount) + ":/";
DEBUG_FUNCTION_LINE_VERBOSE("Mounted %s", mountPath.c_str());
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to mount %s: %s [%d]", mount, Mocha_GetStatusStr(res), res);
}
return res;
}
extern ContentRedirectionApiErrorType CRAddFSLayerEx(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType, uint32_t upid);
DECL_FUNCTION(void, START_HOOK) {
real_START_HOOK();
auto UPID = OSGetUPID();
if (UPID != 2 && UPID != 15 && sLayerInfoForUPID.contains(UPID)) {
DEBUG_FUNCTION_LINE_ERR("Clear layer for UPID %d", UPID);
clearFSLayer(sLayerInfoForUPID[UPID]);
DEBUG_FUNCTION_LINE_ERR("Start threads");
startFSIOThreads();
}
if (UPID == 8) {
CRLayerHandle handle;
int test = (int) 2;
Mocha_MountFS("storage_mlc", nullptr, "/vol/storage_mlc01");
CRAddFSLayerEx(&handle, "browser_test", "storage_mlc:/usr/tmp", FS_LAYER_TYPE_CONTENT_MERGE, UPID);
}
}
function_replacement_data_t fs_file_function_replacements[] = {
REPLACE_FUNCTION_FOR_PROCESS(__PPCExit, LIBRARY_COREINIT, __PPCExit, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSOpenFileExAsync, LIBRARY_COREINIT, FSOpenFileExAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSCloseFileAsync, LIBRARY_COREINIT, FSCloseFileAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSGetStatAsync, LIBRARY_COREINIT, FSGetStatAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSGetStatFileAsync, LIBRARY_COREINIT, FSGetStatFileAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(FSReadFileGeneric, 0x3201C400 + 0x4ecc0, 0x101C400 + 0x4ecc0, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSSetPosFileAsync, LIBRARY_COREINIT, FSSetPosFileAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSGetPosFileAsync, LIBRARY_COREINIT, FSGetPosFileAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSIsEofAsync, LIBRARY_COREINIT, FSIsEofAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(FSWriteFileGeneric, 0x3201C400 + 0x4eec0, 0x101C400 + 0x4eec0, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(START_HOOK, 0x3201C400 + 0x3edc0, 0x101C400 + 0x3edc0, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSTruncateFileAsync, LIBRARY_COREINIT, FSTruncateFileAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSRemoveAsync, LIBRARY_COREINIT, FSRemoveAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSRenameAsync, LIBRARY_COREINIT, FSRenameAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSFlushFileAsync, LIBRARY_COREINIT, FSFlushFileAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSChangeModeAsync, LIBRARY_COREINIT, FSChangeModeAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSOpenDirAsync, LIBRARY_COREINIT, FSOpenDirAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSReadDirAsync, LIBRARY_COREINIT, FSReadDirAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSCloseDirAsync, LIBRARY_COREINIT, FSCloseDirAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSRewindDirAsync, LIBRARY_COREINIT, FSRewindDirAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSMakeDirAsync, LIBRARY_COREINIT, FSMakeDirAsync, FP_TARGET_PROCESS_ALL),
REPLACE_FUNCTION_FOR_PROCESS(FSChangeDirAsync, LIBRARY_COREINIT, FSChangeDirAsync, FP_TARGET_PROCESS_ALL),
}; };
uint32_t fs_file_function_replacements_size = sizeof(fs_file_function_replacements) / sizeof(function_replacement_data_t); uint32_t fs_file_function_replacements_size = sizeof(fs_file_function_replacements) / sizeof(function_replacement_data_t);

View File

@ -239,7 +239,7 @@ FSWrapperMergeDirsWithParent::~FSWrapperMergeDirsWithParent() {
if (clientHandle) { if (clientHandle) {
FSError res; FSError res;
if ((res = FSADelClient(clientHandle)) != FS_ERROR_OK) { if ((res = FSADelClient(clientHandle)) != FS_ERROR_OK) {
DEBUG_FUNCTION_LINE_ERR("[%s] FSADelClient failed: %s (%d)", FSAGetStatusStr(res), res); DEBUG_FUNCTION_LINE_ERR("FSADelClient failed: %s (%d)", FSAGetStatusStr(res), res);
} }
clientHandle = 0; clientHandle = 0;
} }

View File

@ -11,23 +11,17 @@
#include <map> #include <map>
#include <unistd.h> #include <unistd.h>
std::mutex workingDirMutex; std::string getFullPathGeneric(std::shared_ptr<LayerInfo> &layerInfo, FSAClientHandle client, const char *path) {
std::map<FSAClientHandle, std::string> workingDirs; std::lock_guard<std::mutex> workingDirLock(layerInfo->mutex);
std::mutex fsLayerMutex;
std::vector<std::unique_ptr<IFSWrapper>> fsLayers;
std::string getFullPathGeneric(FSAClientHandle client, const char *path, std::mutex &mutex, std::map<FSAClientHandle, std::string> &map) {
std::lock_guard<std::mutex> workingDirLock(mutex);
std::string res; std::string res;
if (path[0] != '/' && path[0] != '\\') { if (path[0] != '/' && path[0] != '\\') {
if (map.count(client) == 0) { if (layerInfo->workingDirs.count(client) == 0) {
DEBUG_FUNCTION_LINE_WARN("No working dir found for client %08X, fallback to \"/\"", client); DEBUG_FUNCTION_LINE_WARN("No working dir found for client %08X, fallback to \"/\"", client);
workingDirs[client] = "/"; layerInfo->workingDirs[client] = "/";
} }
res = string_format("%s%s", map.at(client).c_str(), path); res = string_format("%s%s", layerInfo->workingDirs.at(client).c_str(), path);
} else { } else {
res = path; res = path;
} }
@ -54,27 +48,33 @@ void setWorkingDirGeneric(FSAClientHandle client, const char *path, std::mutex &
} }
std::string getFullPath(FSAClientHandle pClient, const char *path) { std::string getFullPath(std::shared_ptr<LayerInfo> &layerInfo, FSAClientHandle pClient, const char *path) {
return getFullPathGeneric(pClient, path, workingDirMutex, workingDirs); return getFullPathGeneric(layerInfo, pClient, path);
} }
void setWorkingDir(FSAClientHandle client, const char *path) { void setWorkingDir(std::shared_ptr<LayerInfo> &layerInfo, FSAClientHandle client, const char *path) {
setWorkingDirGeneric(client, path, workingDirMutex, workingDirs); setWorkingDirGeneric(client, path, layerInfo->workingDirMutex, layerInfo->workingDirs);
} }
void clearFSLayer() { void clearFSLayer(std::shared_ptr<LayerInfo> &layerInfo) {
{ {
std::lock_guard<std::mutex> workingDirLock(workingDirMutex); std::lock_guard<std::mutex> workingDirLock(layerInfo->workingDirMutex);
workingDirs.clear(); layerInfo->workingDirs.clear();
} }
{ {
std::lock_guard<std::mutex> layerLock(fsLayerMutex); std::lock_guard<std::mutex> layerLock(layerInfo->mutex);
fsLayers.clear(); layerInfo->layers.clear();
} }
} }
bool sendMessageToThread(FSShimWrapperMessage *param) { void clearFSLayers() {
auto *curThread = &gThreadData[OSGetCoreId()]; for (auto &[upid, layerInfo] : sLayerInfoForUPID) {
clearFSLayer(layerInfo);
}
}
bool sendMessageToThread(std::shared_ptr<LayerInfo> &layerInfo, FSShimWrapperMessage *param) {
auto *curThread = &layerInfo->threadData[OSGetCoreId()];
if (curThread->setup) { if (curThread->setup) {
OSMessage send; OSMessage send;
send.message = param; send.message = param;
@ -92,12 +92,20 @@ bool sendMessageToThread(FSShimWrapperMessage *param) {
return false; return false;
} }
std::map<uint32_t, std::shared_ptr<LayerInfo>> sLayerInfoForUPID;
FSError doForLayer(FSShimWrapper *param) { FSError doForLayer(FSShimWrapper *param) {
std::lock_guard<std::mutex> lock(fsLayerMutex); if (!sLayerInfoForUPID.contains(param->upid)) {
if (!fsLayers.empty()) { DEBUG_FUNCTION_LINE_ERR("INVALID UPID IN SHIMWRAPPER: %d", param->upid);
uint32_t startIndex = fsLayers.size(); OSFatal("Invalid UPID");
for (uint32_t i = fsLayers.size(); i > 0; i--) { }
if ((uint32_t) fsLayers[i - 1]->getLayerId() == param->shim->clientHandle) { auto &layerInfo = sLayerInfoForUPID[param->upid];
std::lock_guard<std::mutex> lock(layerInfo->mutex);
if (!layerInfo->layers.empty()) {
uint32_t startIndex = layerInfo->layers.size();
for (uint32_t i = layerInfo->layers.size(); i > 0; i--) {
if ((uint32_t) layerInfo->layers[i - 1]->getLayerId() == param->shim->clientHandle) {
startIndex = i - 1; startIndex = i - 1;
break; break;
} }
@ -105,7 +113,7 @@ FSError doForLayer(FSShimWrapper *param) {
if (startIndex > 0) { if (startIndex > 0) {
for (uint32_t i = startIndex; i > 0; i--) { for (uint32_t i = startIndex; i > 0; i--) {
auto &layer = fsLayers[i - 1]; auto &layer = layerInfo->layers[i - 1];
if (!layer->isActive()) { if (!layer->isActive()) {
continue; continue;
} }
@ -116,7 +124,7 @@ FSError doForLayer(FSShimWrapper *param) {
switch (command) { switch (command) {
case FSA_COMMAND_OPEN_DIR: { case FSA_COMMAND_OPEN_DIR: {
auto *request = &param->shim->request.openDir; auto *request = &param->shim->request.openDir;
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] OpenDir: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str()); DEBUG_FUNCTION_LINE_VERBOSE("[%s] OpenDir: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str());
// Hacky solution: // Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response; auto *hackyBuffer = (uint32_t *) &param->shim->response;
@ -154,14 +162,14 @@ FSError doForLayer(FSShimWrapper *param) {
} }
case FSA_COMMAND_MAKE_DIR: { case FSA_COMMAND_MAKE_DIR: {
auto *request = &param->shim->request.makeDir; auto *request = &param->shim->request.makeDir;
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] MakeDir: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str()); DEBUG_FUNCTION_LINE_VERBOSE("[%s] MakeDir: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str());
layerResult = layer->FSMakeDirWrapper(fullPath.c_str()); layerResult = layer->FSMakeDirWrapper(fullPath.c_str());
break; break;
} }
case FSA_COMMAND_OPEN_FILE: { case FSA_COMMAND_OPEN_FILE: {
auto *request = &param->shim->request.openFile; auto *request = &param->shim->request.openFile;
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path);
// Hacky solution: // Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response; auto *hackyBuffer = (uint32_t *) &param->shim->response;
auto *handlePtr = (FSFileHandle *) hackyBuffer[1]; auto *handlePtr = (FSFileHandle *) hackyBuffer[1];
@ -185,7 +193,7 @@ FSError doForLayer(FSShimWrapper *param) {
case FSA_COMMAND_GET_INFO_BY_QUERY: { case FSA_COMMAND_GET_INFO_BY_QUERY: {
auto *request = &param->shim->request.getInfoByQuery; auto *request = &param->shim->request.getInfoByQuery;
if (request->type == FSA_QUERY_INFO_STAT) { if (request->type == FSA_QUERY_INFO_STAT) {
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] GetStat: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str()); DEBUG_FUNCTION_LINE_VERBOSE("[%s] GetStat: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str());
// Hacky solution: // Hacky solution:
auto *hackyBuffer = (uint32_t *) &param->shim->response; auto *hackyBuffer = (uint32_t *) &param->shim->response;
@ -258,15 +266,15 @@ FSError doForLayer(FSShimWrapper *param) {
} }
case FSA_COMMAND_REMOVE: { case FSA_COMMAND_REMOVE: {
auto *request = &param->shim->request.remove; auto *request = &param->shim->request.remove;
auto fullPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->path); auto fullPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Remove: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str()); DEBUG_FUNCTION_LINE_VERBOSE("[%s] Remove: %s (full path: %s)", layer->getName().c_str(), request->path, fullPath.c_str());
layerResult = layer->FSRemoveWrapper(fullPath.c_str()); layerResult = layer->FSRemoveWrapper(fullPath.c_str());
break; break;
} }
case FSA_COMMAND_RENAME: { case FSA_COMMAND_RENAME: {
auto *request = &param->shim->request.rename; auto *request = &param->shim->request.rename;
auto fullOldPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->oldPath); auto fullOldPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->oldPath);
auto fullNewPath = getFullPath((FSAClientHandle) param->shim->clientHandle, request->newPath); auto fullNewPath = getFullPath(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->newPath);
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Rename: %s -> %s (full path: %s -> %s)", layer->getName().c_str(), request->oldPath, request->newPath, fullOldPath.c_str(), fullNewPath.c_str()); DEBUG_FUNCTION_LINE_VERBOSE("[%s] Rename: %s -> %s (full path: %s -> %s)", layer->getName().c_str(), request->oldPath, request->newPath, fullOldPath.c_str(), fullNewPath.c_str());
layerResult = layer->FSRenameWrapper(fullOldPath.c_str(), fullNewPath.c_str()); layerResult = layer->FSRenameWrapper(fullOldPath.c_str(), fullNewPath.c_str());
break; break;
@ -280,7 +288,7 @@ FSError doForLayer(FSShimWrapper *param) {
case FSA_COMMAND_CHANGE_DIR: { case FSA_COMMAND_CHANGE_DIR: {
auto *request = &param->shim->request.changeDir; auto *request = &param->shim->request.changeDir;
DEBUG_FUNCTION_LINE_VERBOSE("[%s] ChangeDir: %s", layer->getName().c_str(), request->path); DEBUG_FUNCTION_LINE_VERBOSE("[%s] ChangeDir: %s", layer->getName().c_str(), request->path);
setWorkingDir((FSAClientHandle) param->shim->clientHandle, request->path); setWorkingDir(layerInfo, (FSAClientHandle) param->shim->clientHandle, request->path);
// We still want to call the original function. // We still want to call the original function.
layerResult = FS_ERROR_FORCE_PARENT_LAYER; layerResult = FS_ERROR_FORCE_PARENT_LAYER;
break; break;
@ -458,9 +466,6 @@ int64_t writeFromBuffer(int32_t handle, const void *buffer, size_t size, size_t
return totalSize; return totalSize;
} }
FSIOThreadData gThreadData[3];
bool gThreadsRunning = false;
static int32_t fsIOthreadCallback([[maybe_unused]] int argc, const char **argv) { static int32_t fsIOthreadCallback([[maybe_unused]] int argc, const char **argv) {
auto *magic = ((FSIOThreadData *) argv); auto *magic = ((FSIOThreadData *) argv);
@ -512,10 +517,24 @@ static int32_t fsIOthreadCallback([[maybe_unused]] int argc, const char **argv)
void startFSIOThreads() { void startFSIOThreads() {
int32_t threadAttributes[] = {OS_THREAD_ATTRIB_AFFINITY_CPU0, OS_THREAD_ATTRIB_AFFINITY_CPU1, OS_THREAD_ATTRIB_AFFINITY_CPU2}; int32_t threadAttributes[] = {OS_THREAD_ATTRIB_AFFINITY_CPU0, OS_THREAD_ATTRIB_AFFINITY_CPU1, OS_THREAD_ATTRIB_AFFINITY_CPU2};
auto stackSize = 16 * 1024; auto stackSize = 16 * 1024;
auto upid = OSGetUPID();
if (!sLayerInfoForUPID.contains(upid)) {
DEBUG_FUNCTION_LINE_ERR("Tried to start threads for invalid UPID %d", upid);
OSFatal("Tried to start threads for invalid UPID.");
}
auto &layerInfo = sLayerInfoForUPID[upid];
if (layerInfo->threadsRunning) {
return;
}
int coreId = 0; int coreId = 0;
for (int core : threadAttributes) { for (int core : threadAttributes) {
auto *threadData = &gThreadData[coreId]; if (upid != 2 && upid != 15 && core == OS_THREAD_ATTRIB_AFFINITY_CPU2) {
DEBUG_FUNCTION_LINE_ERR("Skip core 2 for non-game UPID %d", upid);
continue;
}
auto *threadData = &layerInfo->threadData[coreId];
memset(threadData, 0, sizeof(*threadData)); memset(threadData, 0, sizeof(*threadData));
threadData->setup = false; threadData->setup = false;
threadData->thread = (OSThread *) memalign(8, sizeof(OSThread)); threadData->thread = (OSThread *) memalign(8, sizeof(OSThread));
@ -549,16 +568,24 @@ void startFSIOThreads() {
coreId++; coreId++;
} }
gThreadsRunning = true; layerInfo->threadsRunning = true;
OSMemoryBarrier(); OSMemoryBarrier();
} }
void stopFSIOThreads() { void stopFSIOThreads() {
if (!gThreadsRunning) { auto upid = OSGetUPID();
if (!sLayerInfoForUPID.contains(upid)) {
DEBUG_FUNCTION_LINE_ERR("Tried to start threads for invalid UPID %d", upid);
OSFatal("Tried to start threads for invalid UPID.");
}
auto &layerInfo = sLayerInfoForUPID[upid];
if (!layerInfo->threadsRunning) {
return; return;
} }
for (auto &gThread : gThreadData) {
auto *thread = &gThread; for (auto &curThread : layerInfo->threadData) {
auto *thread = &curThread;
if (!thread->setup) { if (!thread->setup) {
continue; continue;
} }
@ -581,5 +608,5 @@ void stopFSIOThreads() {
} }
} }
gThreadsRunning = false; layerInfo->threadsRunning = false;
} }

View File

@ -6,6 +6,7 @@
#include <coreinit/filesystem.h> #include <coreinit/filesystem.h>
#include <coreinit/filesystem_fsa.h> #include <coreinit/filesystem_fsa.h>
#include <functional> #include <functional>
#include <map>
#include <mutex> #include <mutex>
#include <romfs_dev.h> #include <romfs_dev.h>
#include <string> #include <string>
@ -42,6 +43,7 @@ struct FSShimWrapper {
AsyncParamFS asyncFS; AsyncParamFS asyncFS;
FSShimSyncType sync; FSShimSyncType sync;
FSShimApiType api; FSShimApiType api;
uint32_t upid;
}; };
struct FSShimWrapperMessage { struct FSShimWrapperMessage {
@ -54,10 +56,16 @@ struct FSShimWrapperMessage {
#define FS_IO_QUEUE_COMMAND_PROCESS_FS_COMMAND 0x42424242 #define FS_IO_QUEUE_COMMAND_PROCESS_FS_COMMAND 0x42424242
#define FS_IO_QUEUE_SYNC_RESULT 0x43434343 #define FS_IO_QUEUE_SYNC_RESULT 0x43434343
extern bool gThreadsRunning; struct LayerInfo {
extern FSIOThreadData gThreadData[3]; std::mutex mutex{};
extern std::mutex fsLayerMutex; std::vector<std::unique_ptr<IFSWrapper>> layers{};
extern std::vector<std::unique_ptr<IFSWrapper>> fsLayers; std::mutex workingDirMutex{};
std::map<FSAClientHandle, std::string> workingDirs{};
FSIOThreadData threadData[3]{};
bool threadsRunning{};
};
extern std::map<uint32_t, std::shared_ptr<LayerInfo>> sLayerInfoForUPID;
#define fsaShimPrepareRequestReadFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, uint8_t * buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAReadFlag readFlags))(0x101C400 + 0x436cc)) #define fsaShimPrepareRequestReadFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, uint8_t * buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAReadFlag readFlags))(0x101C400 + 0x436cc))
#define fsaShimPrepareRequestWriteFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAWriteFlag writeFlags))(0x101C400 + 0x437f4)) #define fsaShimPrepareRequestWriteFile ((FSError(*)(FSAShimBuffer * shim, IOSHandle clientHandle, const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, FSAWriteFlag writeFlags))(0x101C400 + 0x437f4))
@ -87,9 +95,11 @@ extern std::vector<std::unique_ptr<IFSWrapper>> fsLayers;
extern "C" FSError __FSAShimDecodeIosErrorToFsaStatus(IOSHandle handle, IOSError err); extern "C" FSError __FSAShimDecodeIosErrorToFsaStatus(IOSHandle handle, IOSError err);
bool sendMessageToThread(FSShimWrapperMessage *param); bool sendMessageToThread(std::shared_ptr<LayerInfo> &layerInfo, FSShimWrapperMessage *param);
void clearFSLayer(); void clearFSLayer(std::shared_ptr<LayerInfo> &layerInfo);
void clearFSLayers();
FSError doForLayer(FSShimWrapper *param); FSError doForLayer(FSShimWrapper *param);

View File

@ -16,12 +16,12 @@ struct AOCTitle {
WUT_CHECK_SIZE(AOCTitle, 0x68); WUT_CHECK_SIZE(AOCTitle, 0x68);
bool getAOCPath(std::string &outStr) { bool getAOCPath(std::string &outStr) {
int32_t (*AOC_Initialize)() = nullptr; int32_t (*AOC_Initialize)() = nullptr;
int32_t (*AOC_Finalize)() = nullptr; int32_t (*AOC_Finalize)() = nullptr;
int32_t (*AOC_ListTitle)(uint32_t * titleCountOut, AOCTitle * titleList, uint32_t maxCount, void *workBuffer, uint32_t workBufferSize) = nullptr; int32_t (*AOC_ListTitle)(uint32_t *titleCountOut, AOCTitle *titleList, uint32_t maxCount, void *workBuffer, uint32_t workBufferSize) = nullptr;
int32_t (*AOC_OpenTitle)(char *pathOut, AOCTitle *aocTitleInfo, void *workBuffer, uint32_t workBufferSize) = nullptr; int32_t (*AOC_OpenTitle)(char *pathOut, AOCTitle *aocTitleInfo, void *workBuffer, uint32_t workBufferSize) = nullptr;
int32_t (*AOC_CalculateWorkBufferSize)(uint32_t count) = nullptr; int32_t (*AOC_CalculateWorkBufferSize)(uint32_t count) = nullptr;
int32_t (*AOC_CloseTitle)(AOCTitle * aocTitleInfo) = nullptr; int32_t (*AOC_CloseTitle)(AOCTitle *aocTitleInfo) = nullptr;
AOCTitle title{}; AOCTitle title{};
char aocPath[256]; char aocPath[256];
@ -88,7 +88,7 @@ end:
return result; return result;
} }
ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType) { ContentRedirectionApiErrorType CRAddFSLayerEx(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType, uint32_t upid) {
if (!handle || layerName == nullptr || replacementDir == nullptr) { if (!handle || layerName == nullptr || replacementDir == nullptr) {
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG"); DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_INVALID_ARG");
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG; return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
@ -121,28 +121,23 @@ ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *l
} }
if (ptr) { if (ptr) {
DEBUG_FUNCTION_LINE_VERBOSE("Added new layer (%s). Replacement dir: %s Type:%d", layerName, replacementDir, layerType); DEBUG_FUNCTION_LINE_VERBOSE("Added new layer (%s). Replacement dir: %s Type:%d", layerName, replacementDir, layerType);
std::lock_guard<std::mutex> lock(fsLayerMutex); auto &layerInfo = sLayerInfoForUPID[upid];
std::lock_guard<std::mutex> lock(layerInfo->mutex);
*handle = (CRLayerHandle) ptr->getHandle(); *handle = (CRLayerHandle) ptr->getHandle();
fsLayers.push_back(std::move(ptr)); layerInfo->layers.push_back(std::move(ptr));
return CONTENT_REDIRECTION_API_ERROR_NONE; return CONTENT_REDIRECTION_API_ERROR_NONE;
} }
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory"); DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory");
return CONTENT_REDIRECTION_API_ERROR_NO_MEMORY; return CONTENT_REDIRECTION_API_ERROR_NO_MEMORY;
} }
ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) { ContentRedirectionApiErrorType CRAddFSLayer(CRLayerHandle *handle, const char *layerName, const char *replacementDir, FSLayerType layerType) {
if (!remove_locked_first_if(fsLayerMutex, fsLayers, [handle](auto &cur) { return (CRLayerHandle) cur->getHandle() == handle; })) { return CRAddFSLayerEx(handle, layerName, replacementDir, layerType, 2);
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle);
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
}
return CONTENT_REDIRECTION_API_ERROR_NONE;
} }
ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) { ContentRedirectionApiErrorType CRRemoveFSLayer(CRLayerHandle handle) {
std::lock_guard<std::mutex> lock(fsLayerMutex); for (auto &[key, layerInfo] : sLayerInfoForUPID) {
for (auto &cur : fsLayers) { if (remove_locked_first_if(layerInfo->mutex, layerInfo->layers, [handle](auto &cur) { return (CRLayerHandle) cur->getHandle() == handle; })) {
if ((CRLayerHandle) cur->getHandle() == handle) {
cur->setActive(active);
return CONTENT_REDIRECTION_API_ERROR_NONE; return CONTENT_REDIRECTION_API_ERROR_NONE;
} }
} }
@ -151,11 +146,27 @@ ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) {
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND; return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
} }
ContentRedirectionApiErrorType CRSetActive(CRLayerHandle handle, bool active) {
for (auto &[key, layerInfo] : sLayerInfoForUPID) {
std::lock_guard<std::mutex> lock(layerInfo->mutex);
for (auto &cur : layerInfo->layers) {
if ((CRLayerHandle) cur->getHandle() == handle) {
cur->setActive(active);
return CONTENT_REDIRECTION_API_ERROR_NONE;
}
}
}
DEBUG_FUNCTION_LINE_WARN("CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND for handle %08X", handle);
return CONTENT_REDIRECTION_API_ERROR_LAYER_NOT_FOUND;
}
ContentRedirectionApiErrorType CRGetVersion(ContentRedirectionVersion *outVersion) { ContentRedirectionApiErrorType CRGetVersion(ContentRedirectionVersion *outVersion) {
if (outVersion == nullptr) { if (outVersion == nullptr) {
return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG; return CONTENT_REDIRECTION_API_ERROR_INVALID_ARG;
} }
*outVersion = 1; *outVersion = 2;
return CONTENT_REDIRECTION_API_ERROR_NONE; return CONTENT_REDIRECTION_API_ERROR_NONE;
} }
@ -169,6 +180,7 @@ int CRRemoveDevice(const char *name) {
WUMS_EXPORT_FUNCTION(CRGetVersion); WUMS_EXPORT_FUNCTION(CRGetVersion);
WUMS_EXPORT_FUNCTION(CRAddFSLayer); WUMS_EXPORT_FUNCTION(CRAddFSLayer);
WUMS_EXPORT_FUNCTION(CRAddFSLayerEx);
WUMS_EXPORT_FUNCTION(CRRemoveFSLayer); WUMS_EXPORT_FUNCTION(CRRemoveFSLayer);
WUMS_EXPORT_FUNCTION(CRSetActive); WUMS_EXPORT_FUNCTION(CRSetActive);
WUMS_EXPORT_FUNCTION(CRAddDevice); WUMS_EXPORT_FUNCTION(CRAddDevice);

View File

@ -3,7 +3,9 @@
#include "FileUtils.h" #include "FileUtils.h"
#include "utils/StringTools.h" #include "utils/StringTools.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "utils/utils.h"
#include "version.h" #include "version.h"
#include <mocha/mocha.h>
#include <wums.h> #include <wums.h>
WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection"); WUMS_MODULE_EXPORT_NAME("homebrew_content_redirection");
@ -13,7 +15,14 @@ WUMS_DEPENDS_ON(homebrew_functionpatcher);
#define VERSION "v0.2.5" #define VERSION "v0.2.5"
DECL_FUNCTION(void, OSCancelThread, OSThread *thread) { DECL_FUNCTION(void, OSCancelThread, OSThread *thread) {
if (thread == gThreadData[0].thread || thread == gThreadData[1].thread || thread == gThreadData[2].thread) { auto upid = OSGetUPID();
if (!sLayerInfoForUPID.contains(upid)) {
DEBUG_FUNCTION_LINE_ERR("invalid UPID %d", upid);
OSFatal("Invalid UPID.");
}
auto &layerInfo = sLayerInfoForUPID[upid];
if (thread == layerInfo->threadData[0].thread || thread == layerInfo->threadData[1].thread || thread == layerInfo->threadData[2].thread) {
DEBUG_FUNCTION_LINE_INFO("Prevent calling OSCancelThread for ContentRedirection IO Threads"); DEBUG_FUNCTION_LINE_INFO("Prevent calling OSCancelThread for ContentRedirection IO Threads");
return; return;
} }
@ -29,6 +38,11 @@ WUMS_INITIALIZE() {
OSFatal("homebrew_content_redirection: FunctionPatcher_InitLibrary failed"); OSFatal("homebrew_content_redirection: FunctionPatcher_InitLibrary failed");
} }
int mochaInitResult;
if ((mochaInitResult = Mocha_InitLibrary()) != MOCHA_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Mocha_InitLibrary() failed %d", mochaInitResult);
}
bool wasPatched; bool wasPatched;
for (uint32_t i = 0; i < fs_file_function_replacements_size; i++) { for (uint32_t i = 0; i < fs_file_function_replacements_size; i++) {
wasPatched = false; wasPatched = false;
@ -46,6 +60,19 @@ WUMS_INITIALIZE() {
if (FunctionPatcher_AddFunctionPatch(&OSCancelThreadReplacement, nullptr, &wasPatched) != FUNCTION_PATCHER_RESULT_SUCCESS || !wasPatched) { if (FunctionPatcher_AddFunctionPatch(&OSCancelThreadReplacement, nullptr, &wasPatched) != FUNCTION_PATCHER_RESULT_SUCCESS || !wasPatched) {
OSFatal("homebrew_content_redirection: Failed to patch OSCancelThreadReplacement"); OSFatal("homebrew_content_redirection: Failed to patch OSCancelThreadReplacement");
} }
// Give UPID 2 (Wii U Menu) and UPID 15 the same layer
auto layerInfoGameMenu = make_shared_nothrow<LayerInfo>();
sLayerInfoForUPID[2] = layerInfoGameMenu;
sLayerInfoForUPID[15] = layerInfoGameMenu;
// Fill in for all other UPIDs
for (int i = 0; i < 16; i++) {
if (i == 2 || i == 15) {
continue;
}
sLayerInfoForUPID[i] = make_shared_nothrow<LayerInfo>();
}
DEBUG_FUNCTION_LINE("Patch functions finished"); DEBUG_FUNCTION_LINE("Patch functions finished");
deinitLogging(); deinitLogging();
} }
@ -57,7 +84,10 @@ WUMS_APPLICATION_STARTS() {
} }
WUMS_APPLICATION_ENDS() { WUMS_APPLICATION_ENDS() {
clearFSLayer(); if (sLayerInfoForUPID.contains(2)) {
DEBUG_FUNCTION_LINE_ERR("Clear layer for UPID %d", 2);
clearFSLayer(sLayerInfoForUPID[2]);
}
stopFSIOThreads(); stopFSIOThreads();