mirror of
https://github.com/wiiu-env/ContentRedirectionModule.git
synced 2024-11-13 07:05:14 +01:00
715 lines
25 KiB
C++
715 lines
25 KiB
C++
|
#include "FSWrapper.h"
|
||
|
#include "FileUtils.h"
|
||
|
#include "utils/StringTools.h"
|
||
|
#include "utils/logger.h"
|
||
|
#include <algorithm>
|
||
|
#include <coreinit/cache.h>
|
||
|
#include <coreinit/debug.h>
|
||
|
#include <cstdio>
|
||
|
#include <filesystem>
|
||
|
#include <sys/dirent.h>
|
||
|
#include <sys/fcntl.h>
|
||
|
#include <sys/unistd.h>
|
||
|
|
||
|
FSStatus FSWrapper::FSOpenDirWrapper(const char *path, FSDirectoryHandle *handle) {
|
||
|
if (!IsPathToReplace(path)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
|
||
|
if (handle == nullptr) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] handle was nullptr", getName().c_str());
|
||
|
return FS_STATUS_FATAL_ERROR;
|
||
|
}
|
||
|
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
|
||
|
auto *dirHandle = getNewDirHandle();
|
||
|
if (dirHandle != nullptr) {
|
||
|
DIR *dir;
|
||
|
auto newPath = GetNewPath(path);
|
||
|
|
||
|
if ((dir = opendir(newPath.c_str()))) {
|
||
|
|
||
|
dirHandle->dir = dir;
|
||
|
dirHandle->handle = (((uint32_t) dirHandle) & 0x0FFFFFFF) | 0x30000000;
|
||
|
*handle = dirHandle->handle;
|
||
|
|
||
|
dirHandle->path[0] = '\0';
|
||
|
strncat(dirHandle->path, newPath.c_str(), sizeof(dirHandle->path) - 1);
|
||
|
{
|
||
|
std::lock_guard<std::mutex> lock(openDirsMutex);
|
||
|
openDirs.push_back(dirHandle);
|
||
|
OSMemoryBarrier();
|
||
|
}
|
||
|
} else {
|
||
|
delete dirHandle;
|
||
|
result = FS_STATUS_NOT_FOUND;
|
||
|
}
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc dir handle", getName().c_str());
|
||
|
result = FS_STATUS_MAX;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSReadDirWrapper(FSDirectoryHandle handle, FSDirectoryEntry *entry) {
|
||
|
if (!isValidDirHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
auto *dirHandle = getDirFromHandle(handle);
|
||
|
|
||
|
DIR *dir = dirHandle->dir;
|
||
|
|
||
|
FSStatus result = FS_STATUS_END;
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] readdir %08X (handle %08X)", getName().c_str(), dir, handle);
|
||
|
do {
|
||
|
struct dirent *entry_ = readdir(dir);
|
||
|
|
||
|
if (entry_) {
|
||
|
if (SkipDeletedFilesInReadDir() && std::string_view(entry_->d_name).starts_with(deletePrefix)) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("Skip file file name %s because of the prefix", entry_->d_name);
|
||
|
continue;
|
||
|
}
|
||
|
entry->name[0] = '\0';
|
||
|
strncat(entry->name, entry_->d_name, sizeof(entry->name) - 1);
|
||
|
entry->info.mode = (FSMode) FS_MODE_READ_OWNER;
|
||
|
if (entry_->d_type == DT_DIR) {
|
||
|
entry->info.flags = (FSStatFlags) ((uint32_t) FS_STAT_DIRECTORY);
|
||
|
entry->info.size = 0;
|
||
|
} else {
|
||
|
entry->info.flags = (FSStatFlags) 0;
|
||
|
if (strcmp(entry_->d_name, ".") == 0 || strcmp(entry_->d_name, "..") == 0) {
|
||
|
entry->info.size = 0;
|
||
|
} else {
|
||
|
struct stat sb {};
|
||
|
auto strLen = strlen(dirHandle->path) + 1 + strlen(entry_->d_name) + 1;
|
||
|
char path[strLen];
|
||
|
snprintf(path, sizeof(path), "%s/%s", dirHandle->path, entry_->d_name);
|
||
|
if (stat(path, &sb) >= 0) {
|
||
|
entry->info.size = sb.st_size;
|
||
|
entry->info.flags = (FSStatFlags) 0;
|
||
|
entry->info.owner = sb.st_uid;
|
||
|
entry->info.group = sb.st_gid;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
result = FS_STATUS_OK;
|
||
|
}
|
||
|
break;
|
||
|
} while (true);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSCloseDirWrapper(FSDirectoryHandle handle) {
|
||
|
if (!isValidDirHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
auto *dirHandle = getDirFromHandle(handle);
|
||
|
|
||
|
DIR *dir = dirHandle->dir;
|
||
|
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] closedir %08X (handle %08X)", getName().c_str(), dir, handle);
|
||
|
if (closedir(dir) != 0) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to close dir %08X (handle %08X)", getName().c_str(), dir, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
}
|
||
|
dirHandle->dir = nullptr;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSRewindDirWrapper(FSDirectoryHandle handle) {
|
||
|
if (!isValidDirHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
auto *dirHandle = getDirFromHandle(handle);
|
||
|
|
||
|
DIR *dir = dirHandle->dir;
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] rewindir %08X (handle %08X)", getName().c_str(), dir, handle);
|
||
|
rewinddir(dir);
|
||
|
|
||
|
return FS_STATUS_OK;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSMakeDirWrapper(const char *path) {
|
||
|
if (!IsPathToReplace(path)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
if (!pIsWriteable) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to create dir %s but layer is not writeable", getName().c_str(), path);
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
}
|
||
|
DEBUG_FUNCTION_LINE_ERR("NOT IMPLEMENTED MAKE DIR");
|
||
|
return FS_STATUS_FATAL_ERROR;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSOpenFileWrapper(const char *path, const char *mode, FSFileHandle *handle) {
|
||
|
if (!IsPathToReplace(path)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
|
||
|
if (path == nullptr) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] path was nullptr", getName().c_str());
|
||
|
return FS_STATUS_FATAL_ERROR;
|
||
|
}
|
||
|
|
||
|
if (mode == nullptr || handle == nullptr) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] mode or handle was nullptr", getName().c_str());
|
||
|
return FS_STATUS_FATAL_ERROR;
|
||
|
}
|
||
|
|
||
|
auto newPath = GetNewPath(path);
|
||
|
|
||
|
if (pCheckIfDeleted && CheckFileShouldBeIgnored(newPath)) {
|
||
|
return static_cast<FSStatus>((FS_STATUS_NOT_FOUND & 0x0000FFFF) | FS_STATUS_FORCE_NO_FALLBACK);
|
||
|
}
|
||
|
|
||
|
auto result = FS_STATUS_OK;
|
||
|
int _mode;
|
||
|
// Map flags to open modes
|
||
|
if (!IsFileModeAllowed(mode)) {
|
||
|
OSReport("## WARN ## [%s] Given mode is not allowed %s", getName().c_str(), mode);
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Given mode is not allowed %s", getName().c_str(), mode);
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
}
|
||
|
|
||
|
if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) {
|
||
|
_mode = 0x000;
|
||
|
} else if (strcmp(mode, "r+") == 0) {
|
||
|
_mode = 0x002;
|
||
|
} else if (strcmp(mode, "w") == 0) {
|
||
|
_mode = 0x601;
|
||
|
} else if (strcmp(mode, "w+") == 0) {
|
||
|
_mode = 0x602;
|
||
|
} else if (strcmp(mode, "a") == 0) {
|
||
|
_mode = 0x209;
|
||
|
} else if (strcmp(mode, "a+") == 0) {
|
||
|
_mode = 0x20A;
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] mode \"%s\" was allowed but is unsupported", getName().c_str(), mode);
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Open %s (as %s) mode %s,", getName().c_str(), path, newPath.c_str(), mode);
|
||
|
int32_t fd = open(newPath.c_str(), _mode);
|
||
|
if (fd >= 0) {
|
||
|
auto *fileHandle = getNewFileHandle();
|
||
|
if (fileHandle) {
|
||
|
std::lock_guard<std::mutex> lock(openFilesMutex);
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Opened %s (as %s) mode %s, fd %d (%08X)", getName().c_str(), path, newPath.c_str(), mode, fd, handle);
|
||
|
|
||
|
fileHandle->handle = (((uint32_t) fileHandle) & 0x0FFFFFFF) | 0x30000000;
|
||
|
*handle = fileHandle->handle;
|
||
|
fileHandle->fd = fd;
|
||
|
|
||
|
openFiles.push_back(fileHandle);
|
||
|
|
||
|
OSMemoryBarrier();
|
||
|
} else {
|
||
|
close(fd);
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to alloc new fileHandle", getName().c_str());
|
||
|
result = FS_STATUS_MAX;
|
||
|
}
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] File not found %s (%s)", getName().c_str(), path, newPath.c_str());
|
||
|
result = FS_STATUS_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSCloseFileWrapper(FSFileHandle handle) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Close %d (handle %08X)", getName().c_str(), real_fd, handle);
|
||
|
if (close(real_fd) != 0) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to close %d (handle %08X) ", getName().c_str(), real_fd, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
}
|
||
|
fileHandle->fd = -1;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool FSWrapper::CheckFileShouldBeIgnored(std::string &path) {
|
||
|
auto asPath = std::filesystem::path(path);
|
||
|
|
||
|
if (std::string(asPath.filename().c_str()).starts_with(deletePrefix)) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("Ignore %s, filename starts with %s", path.c_str(), deletePrefix.c_str());
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
auto newDelPath = asPath.replace_filename(deletePrefix + asPath.filename().c_str());
|
||
|
struct stat buf {};
|
||
|
if (stat(newDelPath.c_str(), &buf) == 0) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("Ignore %s, file %s exists", path.c_str(), newDelPath.c_str());
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSGetStatWrapper(const char *path, FSStat *stats) {
|
||
|
if (path == nullptr || stats == nullptr) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] path was or stats nullptr", getName().c_str());
|
||
|
return FS_STATUS_FATAL_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!IsPathToReplace(path)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
auto newPath = GetNewPath(path);
|
||
|
|
||
|
struct stat path_stat {};
|
||
|
|
||
|
if (pCheckIfDeleted && CheckFileShouldBeIgnored(newPath)) {
|
||
|
return static_cast<FSStatus>((FS_STATUS_NOT_FOUND & 0x0000FFFF) | FS_STATUS_FORCE_NO_FALLBACK);
|
||
|
}
|
||
|
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] stat of %s (%s)", getName().c_str(), path, newPath.c_str());
|
||
|
if (stat(newPath.c_str(), &path_stat) < 0) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Path %s (%s) not found ", getName().c_str(), path, newPath.c_str());
|
||
|
result = FS_STATUS_NOT_FOUND;
|
||
|
} else {
|
||
|
memset(&(stats->flags), 0, sizeof(stats->flags));
|
||
|
if (S_ISDIR(path_stat.st_mode)) {
|
||
|
stats->flags = (FSStatFlags) ((uint32_t) FS_STAT_DIRECTORY);
|
||
|
} else {
|
||
|
stats->size = path_stat.st_size;
|
||
|
stats->mode = (FSMode) FS_MODE_READ_OWNER;
|
||
|
stats->flags = (FSStatFlags) 0;
|
||
|
stats->owner = path_stat.st_uid;
|
||
|
stats->group = path_stat.st_gid;
|
||
|
}
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Stats file for %s (%s), size %016llX", getName().c_str(), path, newPath.c_str(), stats->size);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSGetStatFileWrapper(FSFileHandle handle, FSStat *stats) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
struct stat path_stat {};
|
||
|
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] fstat of fd %d (FSFileHandle %08X)", getName().c_str(), real_fd, handle);
|
||
|
if (fstat(real_fd, &path_stat) < 0) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] fstat of fd %d (FSFileHandle %08X) failed", getName().c_str(), real_fd, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
} else {
|
||
|
memset(&(stats->flags), 0, sizeof(stats->flags));
|
||
|
|
||
|
stats->size = path_stat.st_size;
|
||
|
stats->mode = (FSMode) FS_MODE_READ_OWNER;
|
||
|
stats->flags = (FSStatFlags) 0;
|
||
|
stats->owner = path_stat.st_uid;
|
||
|
stats->group = path_stat.st_gid;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSReadFileWrapper(void *buffer, uint32_t size, uint32_t count, FSFileHandle handle, [[maybe_unused]] uint32_t unk1) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
|
||
|
if (size * count == 0) {
|
||
|
return FS_STATUS_OK;
|
||
|
}
|
||
|
|
||
|
if (buffer == nullptr) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] buffer is null but size * count is not 0 (It's: %d)", getName().c_str(), size * count);
|
||
|
return FS_STATUS_FATAL_ERROR;
|
||
|
}
|
||
|
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Read %u bytes of fd %08X (FSFileHandle %08X) to buffer %08X", getName().c_str(), size * count, real_fd, handle, buffer);
|
||
|
int32_t read = readIntoBuffer(real_fd, buffer, size, count);
|
||
|
|
||
|
FSStatus result;
|
||
|
if (read < 0) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] read %u bytes of fd %d (FSFileHandle %08X) failed", getName().c_str(), size * count, real_fd, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
} else {
|
||
|
result = static_cast<FSStatus>((uint32_t) (read & 0xFFFFFFFF) / size);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSReadFileWithPosWrapper(void *buffer, uint32_t size, uint32_t count, uint32_t pos, FSFileHandle handle, int32_t unk1) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Read from with position.", getName().c_str());
|
||
|
FSStatus result;
|
||
|
if ((result = this->FSSetPosFileWrapper(handle, pos)) != FS_STATUS_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
result = this->FSReadFileWrapper(buffer, size, count, handle, unk1);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSSetPosFileWrapper(FSFileHandle handle, uint32_t pos) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %d (FSFileHandle %08X) to get current position for truncation", getName().c_str(), real_fd, handle);
|
||
|
if (lseek(real_fd, (off_t) pos, SEEK_SET) != pos) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] lseek fd %d (FSFileHandle %08X) to position %u failed", getName().c_str(), real_fd, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] pos set to %u for fd %d (FSFileHandle %08X)", getName().c_str(), pos, real_fd, handle);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSGetPosFileWrapper(FSFileHandle handle, uint32_t *pos) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %08X (FSFileHandle %08X) to get current position for truncation", getName().c_str(), real_fd, handle);
|
||
|
off_t currentPos = lseek(real_fd, (off_t) 0, SEEK_CUR);
|
||
|
if (currentPos == -1) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to get current position (res: %lld) of fd (handle %08X) to check EoF", getName().c_str(), currentPos, real_fd, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
} else {
|
||
|
*pos = currentPos;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSIsEofWrapper(FSFileHandle handle) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
|
||
|
FSStatus result;
|
||
|
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %08X (FSFileHandle %08X) to get current position for truncation", getName().c_str(), real_fd, handle);
|
||
|
off_t currentPos = lseek(real_fd, (off_t) 0, SEEK_CUR);
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %08X (FSFileHandle %08X) to get end position for truncation", getName().c_str(), real_fd, handle);
|
||
|
off_t endPos = lseek(real_fd, (off_t) 0, SEEK_END);
|
||
|
|
||
|
if (currentPos == -1 || endPos == -1) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to get current position (res: %lld) or endPos (res: %lld) of fd (handle %08X) to check EoF", getName().c_str(), currentPos, endPos, real_fd, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
} else if (currentPos == endPos) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] FSIsEof END for %d\n", getName().c_str(), real_fd);
|
||
|
result = FS_STATUS_END;
|
||
|
} else {
|
||
|
lseek(real_fd, currentPos, SEEK_CUR);
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] FSIsEof OK for %d\n", getName().c_str(), real_fd);
|
||
|
result = FS_STATUS_OK;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSTruncateFileWrapper(FSFileHandle handle) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
|
||
|
if (!pIsWriteable) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to truncate fd %d (handle %08X) but layer is not writeable", getName().c_str(), getFileFromHandle(handle)->fd, handle);
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
}
|
||
|
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] lseek fd %08X (FSFileHandle %08X) to get current position for truncation", getName().c_str(), real_fd, handle);
|
||
|
off_t currentPos = lseek(real_fd, (off_t) 0, SEEK_CUR);
|
||
|
if (currentPos == -1) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Failed to get current position of fd (handle %08X) to truncate file", getName().c_str(), real_fd, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Truncate fd %08X (FSFileHandle %08X) to %lld bytes ", getName().c_str(), real_fd, handle, currentPos);
|
||
|
if (ftruncate(real_fd, currentPos) < 0) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] ftruncate failed for fd %08X (FSFileHandle %08X) errno %d", getName().c_str(), real_fd, handle, errno);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSWriteFileWrapper(uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, [[maybe_unused]] uint32_t unk1) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
if (!pIsWriteable) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to write to fd %d (handle %08X) but layer is not writeable", getName().c_str(), getFileFromHandle(handle)->fd, handle);
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
}
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
|
||
|
FSStatus result;
|
||
|
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Write %u bytes to fd %08X (FSFileHandle %08X) from buffer %08X", getName().c_str(), count * size, real_fd, handle, buffer);
|
||
|
auto writeRes = writeFromBuffer(real_fd, buffer, size, count);
|
||
|
if (writeRes < 0) {
|
||
|
auto err = errno;
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Write failed %u bytes to fd %08X (FSFileHandle %08X) from buffer %08X errno %d", getName().c_str(), count * size, real_fd, handle, buffer, err);
|
||
|
if (err == EFBIG) {
|
||
|
result = FS_STATUS_FILE_TOO_BIG;
|
||
|
} else if (err == EACCES) {
|
||
|
result = FS_STATUS_ACCESS_ERROR;
|
||
|
} else {
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
}
|
||
|
} else {
|
||
|
result = static_cast<FSStatus>((uint32_t) (writeRes & 0xFFFFFFFF) / size);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSRemoveWrapper(const char *path) {
|
||
|
if (!IsPathToReplace(path)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
if (!pIsWriteable) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to remove %s but layer is not writeable", getName().c_str(), path);
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
}
|
||
|
auto newPath = GetNewPath(path);
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Remove %s (%s)", getName().c_str(), path, newPath.c_str());
|
||
|
if (remove(newPath.c_str()) < 0) {
|
||
|
auto err = errno;
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] rename failed %s (%s) errno %d", getName().c_str(), path, newPath.c_str(), err);
|
||
|
if (err == ENOTDIR) {
|
||
|
return FS_STATUS_NOT_DIR;
|
||
|
} else if (err == EACCES) {
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
} else if (err == EISDIR) {
|
||
|
return FS_STATUS_NOT_FILE;
|
||
|
} else if (err == EPERM) {
|
||
|
return FS_STATUS_PERMISSION_ERROR;
|
||
|
}
|
||
|
return FS_STATUS_MEDIA_ERROR;
|
||
|
}
|
||
|
return FS_STATUS_OK;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSRenameWrapper(const char *oldPath, const char *newPath) {
|
||
|
if (!IsPathToReplace(oldPath) || !IsPathToReplace(newPath)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
if (!pIsWriteable) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to rename %s to %s but layer is not writeable", getName().c_str(), oldPath, newPath);
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
}
|
||
|
auto oldPathRedirect = GetNewPath(oldPath);
|
||
|
auto newPathRedirect = GetNewPath(newPath);
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Rename %s (%s) -> %s (%s)", getName().c_str(), oldPath, oldPathRedirect.c_str(), newPath, newPathRedirect.c_str());
|
||
|
if (rename(oldPathRedirect.c_str(), newPathRedirect.c_str()) < 0) {
|
||
|
auto err = errno;
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Rename failed %s (%s) -> %s (%s). errno %d", getName().c_str(), oldPath, oldPathRedirect.c_str(), newPath, newPathRedirect.c_str(), err);
|
||
|
if (err == ENOTDIR) {
|
||
|
return FS_STATUS_NOT_DIR;
|
||
|
} else if (err == EACCES) {
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
} else if (err == EISDIR) {
|
||
|
return FS_STATUS_NOT_FILE;
|
||
|
} else if (err == EPERM) {
|
||
|
return FS_STATUS_PERMISSION_ERROR;
|
||
|
}
|
||
|
return FS_STATUS_MEDIA_ERROR;
|
||
|
}
|
||
|
return FS_STATUS_OK;
|
||
|
}
|
||
|
|
||
|
FSStatus FSWrapper::FSFlushFileWrapper(FSFileHandle handle) {
|
||
|
if (!isValidFileHandle(handle)) {
|
||
|
return FS_STATUS_FORCE_PARENT_LAYER;
|
||
|
}
|
||
|
if (!pIsWriteable) {
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Tried to fsync fd %d (handle %08X)) but layer is not writeable", getName().c_str(), getFileFromHandle(handle)->fd, handle);
|
||
|
return FS_STATUS_ACCESS_ERROR;
|
||
|
}
|
||
|
|
||
|
auto *fileHandle = getFileFromHandle(handle);
|
||
|
int real_fd = fileHandle->fd;
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] fsync fd %08X (FSFileHandle %08X)", real_fd, handle);
|
||
|
FSStatus result = FS_STATUS_OK;
|
||
|
if (fsync(real_fd) < 0) {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] fsync failed for fd %08X (FSFileHandle %08X)", getName().c_str(), real_fd, handle);
|
||
|
result = FS_STATUS_MEDIA_ERROR;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool FSWrapper::IsFileModeAllowed(const char *mode) {
|
||
|
if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (pIsWriteable && (strcmp(mode, "r+") == 0 ||
|
||
|
strcmp(mode, "w") == 0 ||
|
||
|
strcmp(mode, "w+") == 0 ||
|
||
|
strcmp(mode, "a") == 0 ||
|
||
|
strcmp(mode, "a+") == 0)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool FSWrapper::IsPathToReplace(const std::string_view &path) {
|
||
|
return path.starts_with(pPathToReplace);
|
||
|
}
|
||
|
|
||
|
std::string FSWrapper::GetNewPath(const std::string_view &path) {
|
||
|
auto subStr = path.substr(this->pPathToReplace.length());
|
||
|
auto res = string_format("%s%.*s", this->pReplacePathWith.c_str(), int(subStr.length()), subStr.data());
|
||
|
|
||
|
std::replace(res.begin(), res.end(), '/', '/');
|
||
|
|
||
|
uint32_t length = res.size();
|
||
|
|
||
|
//! clear path of double slashes
|
||
|
for (uint32_t i = 1; i < length; ++i) {
|
||
|
if (res[i - 1] == '/' && res[i] == '/') {
|
||
|
res.erase(i, 1);
|
||
|
i--;
|
||
|
length--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DEBUG_FUNCTION_LINE_VERBOSE("[%s] Redirect %.*s -> %s", getName().c_str(), int(path.length()), path.data(), res.c_str());
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
bool FSWrapper::isValidFileHandle(FSFileHandle handle) {
|
||
|
std::lock_guard<std::mutex> lock(openFilesMutex);
|
||
|
return std::ranges::any_of(openFiles, [handle](FileInfo *val) { return val->handle == handle; });
|
||
|
}
|
||
|
|
||
|
bool FSWrapper::isValidDirHandle(FSDirectoryHandle handle) {
|
||
|
std::lock_guard<std::mutex> lock(openDirsMutex);
|
||
|
return std::ranges::any_of(openDirs, [handle](DirInfo *val) { return val->handle == handle; });
|
||
|
}
|
||
|
|
||
|
FileInfo *FSWrapper::getNewFileHandle() {
|
||
|
return new (std::nothrow) FileInfo;
|
||
|
}
|
||
|
|
||
|
DirInfo *FSWrapper::getNewDirHandle() {
|
||
|
return new (std::nothrow) DirInfo;
|
||
|
}
|
||
|
|
||
|
FileInfo *FSWrapper::getFileFromHandle(FSFileHandle handle) {
|
||
|
std::lock_guard<std::mutex> lock(openFilesMutex);
|
||
|
for (auto &file : openFiles) {
|
||
|
if (file->handle == handle) {
|
||
|
return file;
|
||
|
}
|
||
|
}
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] FileInfo for handle %08X was not found. isValidFileHandle check missing?", getName().c_str(), handle);
|
||
|
OSFatal("Failed to find file handle");
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
DirInfo *FSWrapper::getDirFromHandle(FSDirectoryHandle handle) {
|
||
|
std::lock_guard<std::mutex> lock(openDirsMutex);
|
||
|
for (auto &dir : openDirs) {
|
||
|
if (dir->handle == handle) {
|
||
|
return dir;
|
||
|
}
|
||
|
}
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] DirInfo for handle %08X was not found. isValidDirHandle check missing?", getName().c_str(), handle);
|
||
|
OSFatal("Failed to find dir handle");
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
void FSWrapper::deleteDirHandle(FSDirectoryHandle handle) {
|
||
|
std::lock_guard<std::mutex> lock(openDirsMutex);
|
||
|
bool found = false;
|
||
|
int count = 0;
|
||
|
DirInfo *info;
|
||
|
for (auto &val : openDirs) {
|
||
|
if ((FSDirectoryHandle) val->handle == handle) {
|
||
|
found = true;
|
||
|
info = val;
|
||
|
break;
|
||
|
}
|
||
|
count++;
|
||
|
}
|
||
|
if (found) {
|
||
|
openDirs.erase(openDirs.begin() + count);
|
||
|
delete info;
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FSWrapper::deleteFileHandle(FSFileHandle handle) {
|
||
|
std::lock_guard<std::mutex> lock(openFilesMutex);
|
||
|
bool found = false;
|
||
|
auto count = 0;
|
||
|
FileInfo *info;
|
||
|
for (auto &val : openFiles) {
|
||
|
if ((FSFileHandle) val->handle == handle) {
|
||
|
found = true;
|
||
|
info = val;
|
||
|
break;
|
||
|
}
|
||
|
count++;
|
||
|
}
|
||
|
if (found) {
|
||
|
openFiles.erase(openFiles.begin() + count);
|
||
|
delete info;
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE_ERR("[%s] Delete failed because the handle %08X was not found", getName().c_str(), handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool FSWrapper::SkipDeletedFilesInReadDir() {
|
||
|
return true;
|
||
|
}
|