diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6636ab0..b9284e7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -6,7 +6,7 @@ jobs: clang-format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: clang-format run: | docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source ./include @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest needs: clang-format steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build lib run: | docker build . -f Dockerfile.buildlocal -t builder diff --git a/.github/workflows/push_image.yml b/.github/workflows/push_image.yml index a69fa5e..26cf2f1 100644 --- a/.github/workflows/push_image.yml +++ b/.github/workflows/push_image.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Extract Docker metadata id: meta uses: docker/metadata-action@v4 diff --git a/source/devoptab/devoptab_fsa.cpp b/source/devoptab/devoptab_fsa.cpp index 3516491..c9bbe3c 100644 --- a/source/devoptab/devoptab_fsa.cpp +++ b/source/devoptab/devoptab_fsa.cpp @@ -1,6 +1,9 @@ #include "devoptab_fsa.h" -#include "logger.h" +#include "../logger.h" #include "mocha/mocha.h" + +#include +#include #include #include #include @@ -38,7 +41,7 @@ static const devoptab_t fsa_default_devoptab = { static bool fsa_initialised = false; static FSADeviceData fsa_mounts[0x10]; -static void fsaResetMount(FSADeviceData *mount, uint32_t id) { +static void fsaResetMount(FSADeviceData *mount, const uint32_t id) { *mount = {}; memcpy(&mount->device, &fsa_default_devoptab, sizeof(fsa_default_devoptab)); mount->device.name = mount->name; @@ -46,17 +49,20 @@ static void fsaResetMount(FSADeviceData *mount, uint32_t id) { mount->id = id; mount->setup = false; mount->mounted = false; + mount->isSDCard = false; mount->clientHandle = -1; mount->deviceSizeInSectors = 0; mount->deviceSectorSize = 0; - memset(mount->mount_path, 0, sizeof(mount->mount_path)); + mount->cwd[0] = '/'; + mount->cwd[1] = '\0'; + memset(mount->mountPath, 0, sizeof(mount->mountPath)); memset(mount->name, 0, sizeof(mount->name)); DCFlushRange(mount, sizeof(*mount)); } void fsaInit() { if (!fsa_initialised) { - uint32_t total = sizeof(fsa_mounts) / sizeof(fsa_mounts[0]); + constexpr uint32_t total = std::size(fsa_mounts); for (uint32_t i = 0; i < total; i++) { fsaResetMount(&fsa_mounts[i], i); } @@ -67,15 +73,10 @@ void fsaInit() { std::mutex fsaMutex; FSADeviceData *fsa_alloc() { - uint32_t i; - uint32_t total = sizeof(fsa_mounts) / sizeof(fsa_mounts[0]); - FSADeviceData *mount; - fsaInit(); - for (i = 0; i < total; i++) { - mount = &fsa_mounts[i]; - if (!mount->setup) { + for (auto &fsa_mount : fsa_mounts) { + if (FSADeviceData *mount = &fsa_mount; !mount->setup) { return mount; } } @@ -86,8 +87,8 @@ FSADeviceData *fsa_alloc() { static void fsa_free(FSADeviceData *mount) { FSError res; if (mount->mounted) { - if ((res = FSAUnmount(mount->clientHandle, mount->mount_path, FSA_UNMOUNT_FLAG_FORCE)) < 0) { - DEBUG_FUNCTION_LINE_WARN("FSAUnmount %s for %s failed: %s", mount->mount_path, mount->name, FSAGetStatusStr(res)); + if ((res = FSAUnmount(mount->clientHandle, mount->mountPath, FSA_UNMOUNT_FLAG_FORCE)) < 0) { + DEBUG_FUNCTION_LINE_WARN("FSAUnmount %s for %s failed: %s", mount->mountPath, mount->name, FSAGetStatusStr(res)); } } res = FSADelClient(mount->clientHandle); @@ -101,18 +102,17 @@ MochaUtilsStatus Mocha_UnmountFS(const char *virt_name) { if (!virt_name) { return MOCHA_RESULT_INVALID_ARGUMENT; } - std::lock_guard lock(fsaMutex); - uint32_t total = sizeof(fsa_mounts) / sizeof(fsa_mounts[0]); + std::lock_guard lock(fsaMutex); fsaInit(); - for (uint32_t i = 0; i < total; i++) { - FSADeviceData *mount = &fsa_mounts[i]; + for (auto &fsa_mount : fsa_mounts) { + FSADeviceData *mount = &fsa_mount; if (!mount->setup) { continue; } if (strcmp(mount->name, virt_name) == 0) { - std::string removeName = std::string(mount->name).append(":"); + const std::string removeName = std::string(mount->name).append(":"); RemoveDevice(removeName.c_str()); fsa_free(mount); return MOCHA_RESULT_SUCCESS; @@ -129,6 +129,9 @@ MochaUtilsStatus Mocha_MountFS(const char *virt_name, const char *dev_path, cons } MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, const char *mount_path, FSAMountFlags mountFlags, void *mountArgBuf, int mountArgBufLen) { + if (virt_name == nullptr || mount_path == nullptr) { + return MOCHA_RESULT_INVALID_ARGUMENT; + } if (!mochaInitDone) { if (Mocha_InitLibrary() != MOCHA_RESULT_SUCCESS) { DEBUG_FUNCTION_LINE_ERR("Mocha_InitLibrary failed"); @@ -137,7 +140,7 @@ MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, co } FSAInit(); - std::lock_guard lock(fsaMutex); + std::lock_guard lock(fsaMutex); FSADeviceData *mount = fsa_alloc(); if (mount == nullptr) { @@ -146,6 +149,23 @@ MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, co return MOCHA_RESULT_MAX_CLIENT; } + // make sure the paths are normalized + std::string normalizedMountPath(mount_path); + std::ranges::replace(normalizedMountPath, '\\', '/'); + while (!normalizedMountPath.empty() && (normalizedMountPath.ends_with("/"))) { + normalizedMountPath.pop_back(); + } + std::string normalizedDevPath(dev_path ? dev_path : ""); + std::ranges::replace(normalizedDevPath, '\\', '/'); + while (!normalizedDevPath.empty() && (normalizedDevPath.ends_with("/"))) { + normalizedDevPath.pop_back(); + } + + // Things like statvfs behave different on sd cards! + if (normalizedDevPath.starts_with("/dev/sdcard01") || normalizedMountPath.starts_with("/vol/external01")) { + mount->isSDCard = true; + } + mount->clientHandle = FSAAddClient(nullptr); if (mount->clientHandle < 0) { DEBUG_FUNCTION_LINE_ERR("FSAAddClient() failed: %s", FSAGetStatusStr(static_cast(mount->clientHandle))); @@ -162,12 +182,12 @@ MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, co mount->mounted = false; strncpy(mount->name, virt_name, sizeof(mount->name) - 1); - strncpy(mount->mount_path, mount_path, sizeof(mount->mount_path) - 1); + strncpy(mount->mountPath, normalizedMountPath.c_str(), sizeof(mount->mountPath) - 1); FSError res; - if (dev_path) { - res = FSAMount(mount->clientHandle, dev_path, mount_path, mountFlags, mountArgBuf, mountArgBufLen); + if (!normalizedDevPath.empty()) { + res = FSAMount(mount->clientHandle, normalizedDevPath.c_str(), normalizedMountPath.c_str(), mountFlags, mountArgBuf, mountArgBufLen); if (res < 0) { - DEBUG_FUNCTION_LINE_ERR("FSAMount(0x%08X, %s, %s, %08X, %p, %08X) failed: %s", mount->clientHandle, dev_path, mount_path, mountFlags, mountArgBuf, mountArgBufLen, FSAGetStatusStr(res)); + DEBUG_FUNCTION_LINE_ERR("FSAMount(0x%08X, %s, %s, %08X, %p, %08X) failed: %s", mount->clientHandle, normalizedDevPath.c_str(), normalizedMountPath.c_str(), mountFlags, mountArgBuf, mountArgBufLen, FSAGetStatusStr(res)); fsa_free(mount); if (res == FS_ERROR_ALREADY_EXISTS) { return MOCHA_RESULT_ALREADY_EXISTS; @@ -179,18 +199,20 @@ MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, co mount->mounted = false; } - if ((res = FSAChangeDir(mount->clientHandle, mount->mount_path)) < 0) { - DEBUG_FUNCTION_LINE_WARN("FSAChangeDir(0x%08X, %s) failed: %s", mount->clientHandle, mount->mount_path, FSAGetStatusStr(res)); + if ((res = FSAChangeDir(mount->clientHandle, mount->mountPath)) < 0) { + DEBUG_FUNCTION_LINE_WARN("FSAChangeDir(0x%08X, %s) failed: %s", mount->clientHandle, mount->mountPath, FSAGetStatusStr(res)); + } else { + strncpy(mount->cwd, normalizedMountPath.c_str(), sizeof(mount->mountPath) - 1); } FSADeviceInfo deviceInfo; - if ((res = FSAGetDeviceInfo(mount->clientHandle, mount_path, &deviceInfo)) >= 0) { + if ((res = FSAGetDeviceInfo(mount->clientHandle, normalizedMountPath.c_str(), &deviceInfo)) >= 0) { mount->deviceSizeInSectors = deviceInfo.deviceSizeInSectors; mount->deviceSectorSize = deviceInfo.deviceSectorSize; } else { mount->deviceSizeInSectors = 0xFFFFFFFF; mount->deviceSectorSize = 512; - DEBUG_FUNCTION_LINE_WARN("Failed to get DeviceInfo for %s: %s", mount_path, FSAGetStatusStr(res)); + DEBUG_FUNCTION_LINE_WARN("Failed to get DeviceInfo for %s: %s", normalizedMountPath.c_str(), FSAGetStatusStr(res)); } if (AddDevice(&mount->device) < 0) { diff --git a/source/devoptab/devoptab_fsa.h b/source/devoptab/devoptab_fsa.h index 2e3b7db..cd06916 100644 --- a/source/devoptab/devoptab_fsa.h +++ b/source/devoptab/devoptab_fsa.h @@ -13,23 +13,24 @@ #include typedef struct FSADeviceData { - devoptab_t device{}; - bool setup{}; - bool mounted{}; + devoptab_t device; + bool setup; + bool mounted; + bool isSDCard; uint32_t id{}; - char name[32]{}; - char mount_path[256]{}; - FSAClientHandle clientHandle{}; - uint64_t deviceSizeInSectors{}; - uint32_t deviceSectorSize{}; -} FSADeviceData; + char name[32]; + char mountPath[0x80]; + char cwd[FS_MAX_PATH + 1]; + FSAClientHandle clientHandle; + uint64_t deviceSizeInSectors; + uint32_t deviceSectorSize; +} __fsa_device_t; /** * Open file struct */ -typedef struct -{ - //! FS handle +typedef struct { + //! FSA file handle FSAFileHandle fd; //! Flags used in open(2) @@ -38,7 +39,7 @@ typedef struct //! Current file offset uint32_t offset; - //! Path stored for internal path tracking + //! Current file path char fullPath[FS_MAX_PATH + 1]; //! Guard file access @@ -52,17 +53,17 @@ typedef struct * Open directory struct */ typedef struct { - //! Should be set to FS_DIRITER_MAGIC + //! Should be set to FSA_DIRITER_MAGIC uint32_t magic; - //! FS handle + //! FS directory handle FSADirectoryHandle fd; //! Temporary storage for reading entries FSADirectoryEntry entry_data; - //! Current file path - char name[FS_MAX_PATH + 1]; + //! Current directory path + char fullPath[FS_MAX_PATH + 1]; //! Guard dir access MutexWrapper mutex; @@ -105,13 +106,20 @@ int __fsa_fchmod(struct _reent *r, void *fd, mode_t mode); int __fsa_rmdir(struct _reent *r, const char *name); int __fsa_utimes(struct _reent *r, const char *filename, const struct timeval times[2]); -// devoptab_fs_utils.c +// devoptab_fsa_utils.c char *__fsa_fixpath(struct _reent *r, const char *path); int __fsa_translate_error(FSError error); +mode_t __fsa_translate_stat_mode(FSStat *fsStat); +void __fsa_translate_stat(FSAClientHandle handle, FSStat *fsStat, ino_t ino, struct stat *posStat); +uint32_t __fsa_hashstring(const char *str); + +static inline FSMode +__fsa_translate_permission_mode(mode_t mode) { + // Convert normal Unix octal permission bits into CafeOS hexadecimal permission bits + return (FSMode) (((mode & S_IRWXU) << 2) | ((mode & S_IRWXG) << 1) | (mode & S_IRWXO)); +} + time_t __fsa_translate_time(FSTime timeValue); -FSMode __fsa_translate_permission_mode(mode_t mode); -mode_t __fsa_translate_stat_mode(FSAStat *fileStat); -void __fsa_translate_stat(FSAStat *fsStat, struct stat *posStat); #ifdef __cplusplus } diff --git a/source/devoptab/devoptab_fsa_chdir.cpp b/source/devoptab/devoptab_fsa_chdir.cpp index 8165bce..8408239 100644 --- a/source/devoptab/devoptab_fsa_chdir.cpp +++ b/source/devoptab/devoptab_fsa_chdir.cpp @@ -1,11 +1,9 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_chdir(struct _reent *r, const char *path) { - FSError status; - if (!path) { r->_errno = EINVAL; return -1; @@ -16,15 +14,27 @@ int __fsa_chdir(struct _reent *r, r->_errno = ENOMEM; return -1; } - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); - status = FSAChangeDir(deviceData->clientHandle, fixedPath); + const FSError status = FSAChangeDir(deviceData->clientHandle, fixedPath); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSAChangeDir(0x%08X, %s) failed: %s", deviceData->clientHandle, fixedPath, FSAGetStatusStr(status)); free(fixedPath); r->_errno = __fsa_translate_error(status); return -1; } + + // Remove trailing '/' + if (fixedPath[0] != '\0') { + if (fixedPath[strlen(fixedPath) - 1] == '/') { + fixedPath[strlen(fixedPath) - 1] = 0; + } + } + + if (snprintf(deviceData->cwd, sizeof(deviceData->cwd), "%s", fixedPath) >= static_cast(sizeof(deviceData->cwd))) { + DEBUG_FUNCTION_LINE_WARN("__wut_fsa_chdir: snprintf result was truncated"); + } + free(fixedPath); return 0; diff --git a/source/devoptab/devoptab_fsa_chmod.cpp b/source/devoptab/devoptab_fsa_chmod.cpp index 73ddb71..6f78916 100644 --- a/source/devoptab/devoptab_fsa_chmod.cpp +++ b/source/devoptab/devoptab_fsa_chmod.cpp @@ -1,13 +1,11 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include #include int __fsa_chmod(struct _reent *r, const char *path, mode_t mode) { - FSError status; - if (!path) { r->_errno = EINVAL; return -1; @@ -19,13 +17,14 @@ int __fsa_chmod(struct _reent *r, return -1; } - FSMode translatedMode = __fsa_translate_permission_mode(mode); + const FSMode translatedMode = __fsa_translate_permission_mode(mode); - auto *deviceData = (FSADeviceData *) r->deviceData; + const __fsa_device_t *deviceData = static_cast<__fsa_device_t *>(r->deviceData); - status = FSAChangeMode(deviceData->clientHandle, fixedPath, translatedMode); + const FSError status = FSAChangeMode(deviceData->clientHandle, fixedPath, translatedMode); if (status < 0) { - DEBUG_FUNCTION_LINE_ERR("FSAChangeMode(0x%08X, %s, 0x%X) failed: %s", deviceData->clientHandle, fixedPath, translatedMode, FSAGetStatusStr(status)); + DEBUG_FUNCTION_LINE_ERR("FSAChangeMode(0x%08X, %s, 0x%X) failed: %s", + deviceData->clientHandle, fixedPath, translatedMode, FSAGetStatusStr(status)); free(fixedPath); r->_errno = __fsa_translate_error(status); return -1; diff --git a/source/devoptab/devoptab_fsa_close.cpp b/source/devoptab/devoptab_fsa_close.cpp index cc3783a..e11876a 100644 --- a/source/devoptab/devoptab_fsa_close.cpp +++ b/source/devoptab/devoptab_fsa_close.cpp @@ -1,26 +1,24 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_close(struct _reent *r, void *fd) { - FSError status; - __fsa_file_t *file; - if (!fd) { r->_errno = EINVAL; return -1; } - file = (__fsa_file_t *) fd; + const auto file = static_cast<__fsa_file_t *>(fd); - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); - std::lock_guard lock(file->mutex); + std::scoped_lock lock(file->mutex); - status = FSACloseFile(deviceData->clientHandle, file->fd); + const FSError status = FSACloseFile(deviceData->clientHandle, file->fd); if (status < 0) { - DEBUG_FUNCTION_LINE_ERR("FSACloseFile(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, FSAGetStatusStr(status)); + DEBUG_FUNCTION_LINE_ERR("FSACloseFile(0x%08X, 0x%08X) (%s) failed: %s", + deviceData->clientHandle, file->fd, file->fullPath, FSAGetStatusStr(status)); r->_errno = __fsa_translate_error(status); return -1; } diff --git a/source/devoptab/devoptab_fsa_dirclose.cpp b/source/devoptab/devoptab_fsa_dirclose.cpp index 44ad080..c0fc5d5 100644 --- a/source/devoptab/devoptab_fsa_dirclose.cpp +++ b/source/devoptab/devoptab_fsa_dirclose.cpp @@ -1,25 +1,25 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_dirclose(struct _reent *r, DIR_ITER *dirState) { - FSError status; if (!dirState) { r->_errno = EINVAL; return -1; } - auto *dir = (__fsa_dir_t *) (dirState->dirStruct); - auto *deviceData = (FSADeviceData *) r->deviceData; + auto *dir = static_cast<__fsa_dir_t *>(dirState->dirStruct); - std::lock_guard lock(dir->mutex); + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); - status = FSACloseDir(deviceData->clientHandle, dir->fd); + std::scoped_lock lock(dir->mutex); + + const FSError status = FSACloseDir(deviceData->clientHandle, dir->fd); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSACloseDir(0x%08X, 0x%08X) (%s) failed: %s", - deviceData->clientHandle, dir->fd, dir->name, FSAGetStatusStr(status)); + deviceData->clientHandle, dir->fd, dir->fullPath, FSAGetStatusStr(status)); r->_errno = __fsa_translate_error(status); return -1; } diff --git a/source/devoptab/devoptab_fsa_dirnext.cpp b/source/devoptab/devoptab_fsa_dirnext.cpp index a087cb3..d756fcc 100644 --- a/source/devoptab/devoptab_fsa_dirnext.cpp +++ b/source/devoptab/devoptab_fsa_dirnext.cpp @@ -1,5 +1,5 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include #include @@ -7,33 +7,46 @@ int __fsa_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { - FSError status; if (!dirState || !filename || !filestat) { r->_errno = EINVAL; return -1; } - auto *deviceData = (FSADeviceData *) r->deviceData; - auto *dir = (__fsa_dir_t *) (dirState->dirStruct); + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); + const auto dir = static_cast<__fsa_dir_t *>(dirState->dirStruct); - std::lock_guard lock(dir->mutex); + std::scoped_lock lock(dir->mutex); memset(&dir->entry_data, 0, sizeof(dir->entry_data)); - status = FSAReadDir(deviceData->clientHandle, dir->fd, &dir->entry_data); + const auto status = FSAReadDir(deviceData->clientHandle, dir->fd, &dir->entry_data); if (status < 0) { if (status != FS_ERROR_END_OF_DIR) { DEBUG_FUNCTION_LINE_ERR("FSAReadDir(0x%08X, 0x%08X, %p) (%s) failed: %s", - deviceData->clientHandle, dir->fd, &dir->entry_data, dir->name, FSAGetStatusStr(status)); + deviceData->clientHandle, dir->fd, &dir->entry_data, dir->fullPath, FSAGetStatusStr(status)); } r->_errno = __fsa_translate_error(status); return -1; } - __fsa_translate_stat(&dir->entry_data.info, filestat); + ino_t ino; + size_t fullLen = strlen(dir->fullPath) + 1 + strlen(dir->entry_data.name) + 1; + char *fullStr = (char *) memalign(0x40, fullLen); + if (fullStr) { + if (snprintf(fullStr, fullLen, "%s/%s", dir->fullPath, dir->entry_data.name) >= (int) fullLen) { + DEBUG_FUNCTION_LINE_ERR("__fsa_dirnext: snprintf fullStr result was truncated"); + } + ino = __fsa_hashstring(fullStr); + free(fullStr); + } else { + ino = 0; + DEBUG_FUNCTION_LINE_ERR("__fsa_dirnext: Failed to allocate memory for fullStr. st_ino will be set to 0"); + } + __fsa_translate_stat(deviceData->clientHandle, &dir->entry_data.info, ino, filestat); if (snprintf(filename, NAME_MAX, "%s", dir->entry_data.name) >= NAME_MAX) { - DEBUG_FUNCTION_LINE_ERR("snprintf filename result was truncated\n"); + DEBUG_FUNCTION_LINE_ERR("__fsa_dirnext: snprintf filename result was truncated"); } + return 0; } diff --git a/source/devoptab/devoptab_fsa_diropen.cpp b/source/devoptab/devoptab_fsa_diropen.cpp index f3c74bf..6261e09 100644 --- a/source/devoptab/devoptab_fsa_diropen.cpp +++ b/source/devoptab/devoptab_fsa_diropen.cpp @@ -1,5 +1,5 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include DIR_ITER * @@ -7,7 +7,6 @@ __fsa_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) { FSADirectoryHandle fd; - FSError status; if (!dirState || !path) { r->_errno = EINVAL; @@ -18,18 +17,29 @@ __fsa_diropen(struct _reent *r, if (!fixedPath) { return nullptr; } - auto *dir = (__fsa_dir_t *) (dirState->dirStruct); - strncpy(dir->name, fixedPath, sizeof(dir->name) - 1); + const auto dir = static_cast<__fsa_dir_t *>(dirState->dirStruct); + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); + + // Remove trailing '/' + if (fixedPath[0] != '\0') { + if (fixedPath[strlen(fixedPath) - 1] == '/') { + fixedPath[strlen(fixedPath) - 1] = 0; + } + } + + if (snprintf(dir->fullPath, sizeof(dir->fullPath), "%s", fixedPath) >= (int) sizeof(dir->fullPath)) { + DEBUG_FUNCTION_LINE_ERR("__fsa_diropen: snprintf result was truncated"); + } + free(fixedPath); - dir->mutex.init(dir->name); - std::lock_guard lock(dir->mutex); + dir->mutex.init(dir->fullPath); + std::scoped_lock lock(dir->mutex); - auto *deviceData = (FSADeviceData *) r->deviceData; - status = FSAOpenDir(deviceData->clientHandle, dir->name, &fd); + const FSError status = FSAOpenDir(deviceData->clientHandle, dir->fullPath, &fd); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSAOpenDir(0x%08X, %s, %p) failed: %s", - deviceData->clientHandle, dir->name, &fd, FSAGetStatusStr(status)); + deviceData->clientHandle, dir->fullPath, &fd, FSAGetStatusStr(status)); r->_errno = __fsa_translate_error(status); return nullptr; } diff --git a/source/devoptab/devoptab_fsa_dirreset.cpp b/source/devoptab/devoptab_fsa_dirreset.cpp index 7fa348a..94538b8 100644 --- a/source/devoptab/devoptab_fsa_dirreset.cpp +++ b/source/devoptab/devoptab_fsa_dirreset.cpp @@ -1,25 +1,24 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_dirreset(struct _reent *r, DIR_ITER *dirState) { - FSError status; if (!dirState) { r->_errno = EINVAL; return -1; } - auto *dir = (__fsa_dir_t *) (dirState->dirStruct); - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto dir = static_cast<__fsa_dir_t *>(dirState->dirStruct); + const auto *deviceData = static_cast<__fsa_device_t *>(r->deviceData); - std::lock_guard lock(dir->mutex); + std::scoped_lock lock(dir->mutex); - status = FSARewindDir(deviceData->clientHandle, dir->fd); + const FSError status = FSARewindDir(deviceData->clientHandle, dir->fd); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSARewindDir(0x%08X, 0x%08X) (%s) failed: %s", - deviceData->clientHandle, dir->fd, dir->name, FSAGetStatusStr(status)); + deviceData->clientHandle, dir->fd, dir->fullPath, FSAGetStatusStr(status)); r->_errno = __fsa_translate_error(status); return -1; } diff --git a/source/devoptab/devoptab_fsa_fchmod.cpp b/source/devoptab/devoptab_fsa_fchmod.cpp index e0bd6ec..fa117f4 100644 --- a/source/devoptab/devoptab_fsa_fchmod.cpp +++ b/source/devoptab/devoptab_fsa_fchmod.cpp @@ -1,5 +1,5 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_fchmod(struct _reent *r, diff --git a/source/devoptab/devoptab_fsa_fstat.cpp b/source/devoptab/devoptab_fsa_fstat.cpp index f54d1e1..bd107da 100644 --- a/source/devoptab/devoptab_fsa_fstat.cpp +++ b/source/devoptab/devoptab_fsa_fstat.cpp @@ -1,11 +1,10 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_fstat(struct _reent *r, void *fd, struct stat *st) { - FSError status; FSAStat fsStat; if (!fd || !st) { @@ -13,20 +12,22 @@ int __fsa_fstat(struct _reent *r, return -1; } - auto *file = (__fsa_file_t *) fd; - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto file = static_cast<__fsa_file_t *>(fd); + const auto *deviceData = static_cast<__fsa_device_t *>(r->deviceData); - std::lock_guard lock(file->mutex); + std::scoped_lock lock(file->mutex); - status = FSAGetStatFile(deviceData->clientHandle, file->fd, &fsStat); + const FSError status = FSAGetStatFile(deviceData->clientHandle, file->fd, &fsStat); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSAGetStatFile(0x%08X, 0x%08X, %p) (%s) failed: %s", - deviceData->clientHandle, file->fd, &fsStat, file->fullPath, FSAGetStatusStr(status)); + deviceData->clientHandle, file->fd, &fsStat, + file->fullPath, FSAGetStatusStr(status)); r->_errno = __fsa_translate_error(status); return -1; } - __fsa_translate_stat(&fsStat, st); + const ino_t ino = __fsa_hashstring(file->fullPath); + __fsa_translate_stat(deviceData->clientHandle, &fsStat, ino, st); return 0; } diff --git a/source/devoptab/devoptab_fsa_fsync.cpp b/source/devoptab/devoptab_fsa_fsync.cpp index 3850391..d193754 100644 --- a/source/devoptab/devoptab_fsa_fsync.cpp +++ b/source/devoptab/devoptab_fsa_fsync.cpp @@ -1,22 +1,20 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_fsync(struct _reent *r, void *fd) { - FSError status; - if (!fd) { r->_errno = EINVAL; return -1; } - auto *file = (__fsa_file_t *) fd; - auto *deviceData = (FSADeviceData *) r->deviceData; + auto *file = static_cast<__fsa_file_t *>(fd); + const auto *deviceData = static_cast<__fsa_device_t *>(r->deviceData); - std::lock_guard lock(file->mutex); + std::scoped_lock lock(file->mutex); - status = FSAFlushFile(deviceData->clientHandle, file->fd); + const FSError status = FSAFlushFile(deviceData->clientHandle, file->fd); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSAFlushFile(0x%08X, 0x%08X) (%s) failed: %s", deviceData->clientHandle, file->fd, file->fullPath, FSAGetStatusStr(status)); diff --git a/source/devoptab/devoptab_fsa_mkdir.cpp b/source/devoptab/devoptab_fsa_mkdir.cpp index 467567d..78dd946 100644 --- a/source/devoptab/devoptab_fsa_mkdir.cpp +++ b/source/devoptab/devoptab_fsa_mkdir.cpp @@ -1,29 +1,26 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_mkdir(struct _reent *r, const char *path, int mode) { - FSError status; - char *fixedPath; - if (!path) { r->_errno = EINVAL; return -1; } - fixedPath = __fsa_fixpath(r, path); + char *fixedPath = __fsa_fixpath(r, path); if (!fixedPath) { r->_errno = ENOMEM; return -1; } - auto *deviceData = (FSADeviceData *) r->deviceData; + auto *deviceData = static_cast<__fsa_device_t *>(r->deviceData); - FSMode translatedMode = __fsa_translate_permission_mode(mode); + const FSMode translatedMode = __fsa_translate_permission_mode(mode); - status = FSAMakeDir(deviceData->clientHandle, fixedPath, translatedMode); + const FSError status = FSAMakeDir(deviceData->clientHandle, fixedPath, translatedMode); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSAMakeDir(0x%08X, %s, 0x%X) failed: %s", deviceData->clientHandle, fixedPath, translatedMode, FSAGetStatusStr(status)); diff --git a/source/devoptab/devoptab_fsa_open.cpp b/source/devoptab/devoptab_fsa_open.cpp index 8870e85..5396660 100644 --- a/source/devoptab/devoptab_fsa_open.cpp +++ b/source/devoptab/devoptab_fsa_open.cpp @@ -1,5 +1,5 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include // Extended "magic" value that allows opening files with FS_OPEN_FLAG_UNENCRYPTED in underlying FSOpenFileEx() call similar to O_DIRECTORY @@ -33,6 +33,12 @@ int __fsa_open(struct _reent *r, fsMode = "w"; } else if (((flags & O_ACCMODE) == O_RDWR) && ((flags & commonFlagMask) == (O_CREAT | O_TRUNC))) { fsMode = "w+"; + } else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == O_CREAT) && (flags & O_EXCL) == O_EXCL) { + // if O_EXCL is set, we don't need O_TRUNC + fsMode = "w"; + } else if (((flags & O_ACCMODE) == O_RDWR) && ((flags & commonFlagMask) == O_CREAT) && (flags & O_EXCL) == O_EXCL) { + // if O_EXCL is set, we don't need O_TRUNC + fsMode = "w+"; } else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_CREAT | O_APPEND))) { fsMode = "a"; } else if (((flags & O_ACCMODE) == O_RDWR) && ((flags & commonFlagMask) == (O_CREAT | O_APPEND))) { @@ -43,6 +49,10 @@ int __fsa_open(struct _reent *r, // It's not possible to open a file with write only mode which doesn't truncate the file // Technically we could read from the file, but our read implementation is blocking this. fsMode = "r+"; + } else if (((flags & O_ACCMODE) == O_RDWR) && ((flags & commonFlagMask) == (O_CREAT))) { + // Cafe OS doesn't have a matching mode for this, so we have to be creative and create the file. + createFileIfNotFound = true; + fsMode = "r+"; } else if (((flags & O_ACCMODE) == O_WRONLY) && ((flags & commonFlagMask) == (O_APPEND))) { // Cafe OS doesn't have a matching mode for this, so we have to check if the file exists. failIfFileNotFound = true; @@ -62,8 +72,13 @@ int __fsa_open(struct _reent *r, return -1; } - auto *file = (__fsa_file_t *) fileStruct; - strncpy(file->fullPath, fixedPath, sizeof(file->fullPath) - 1); + + auto *file = static_cast<__fsa_file_t *>(fileStruct); + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); + + if (snprintf(file->fullPath, sizeof(file->fullPath), "%s", fixedPath) >= (int) sizeof(file->fullPath)) { + DEBUG_FUNCTION_LINE_ERR("__fsa_open: snprintf result was truncated"); + } free(fixedPath); // Prepare flags @@ -73,9 +88,7 @@ int __fsa_open(struct _reent *r, // Init mutex and lock file->mutex.init(file->fullPath); - std::lock_guard lock(file->mutex); - - auto *deviceData = (FSADeviceData *) r->deviceData; + std::scoped_lock lock(file->mutex); if (createFileIfNotFound || failIfFileNotFound || (flags & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) { // Check if file exists @@ -113,9 +126,11 @@ int __fsa_open(struct _reent *r, status = FSAOpenFileEx(deviceData->clientHandle, file->fullPath, fsMode, translatedMode, openFlags, preAllocSize, &fd); if (status < 0) { - DEBUG_FUNCTION_LINE_ERR("FSAOpenFileEx(0x%08X, %s, %s, 0x%X, 0x%08X, 0x%08X, %p) failed: %s", - deviceData->clientHandle, file->fullPath, fsMode, translatedMode, openFlags, preAllocSize, &fd, - FSAGetStatusStr(status)); + if (status != FS_ERROR_NOT_FOUND) { + DEBUG_FUNCTION_LINE_ERR("FSAOpenFileEx(0x%08X, %s, %s, 0x%X, 0x%08X, 0x%08X, %p) failed: %s", + deviceData->clientHandle, file->fullPath, fsMode, translatedMode, openFlags, preAllocSize, &fd, + FSAGetStatusStr(status)); + } r->_errno = __fsa_translate_error(status); return -1; } diff --git a/source/devoptab/devoptab_fsa_read.cpp b/source/devoptab/devoptab_fsa_read.cpp index 29e3355..1ea4874 100644 --- a/source/devoptab/devoptab_fsa_read.cpp +++ b/source/devoptab/devoptab_fsa_read.cpp @@ -1,18 +1,17 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include #include ssize_t __fsa_read(struct _reent *r, void *fd, char *ptr, size_t len) { - FSError status; if (!fd || !ptr) { r->_errno = EINVAL; return -1; } // Check that the file was opened with read access - auto *file = (__fsa_file_t *) fd; + const auto file = static_cast<__fsa_file_t *>(fd); if ((file->flags & O_ACCMODE) == O_WRONLY) { r->_errno = EBADF; return -1; @@ -21,9 +20,9 @@ ssize_t __fsa_read(struct _reent *r, void *fd, char *ptr, size_t len) { // cache-aligned, cache-line-sized __attribute__((aligned(0x40))) uint8_t alignedBuffer[0x40]; - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); - std::lock_guard lock(file->mutex); + std::scoped_lock lock(file->mutex); size_t bytesRead = 0; while (bytesRead < len) { @@ -32,13 +31,13 @@ ssize_t __fsa_read(struct _reent *r, void *fd, char *ptr, size_t len) { uint8_t *tmp = (uint8_t *) ptr; size_t size = len - bytesRead; - if ((uintptr_t) ptr & 0x3F) { + if (size < 0x40) { + // read partial cache-line back-end + tmp = alignedBuffer; + } else if ((uintptr_t) ptr & 0x3F) { // read partial cache-line front-end tmp = alignedBuffer; size = MIN(size, 0x40 - ((uintptr_t) ptr & 0x3F)); - } else if (size < 0x40) { - // read partial cache-line back-end - tmp = alignedBuffer; } else { // read whole cache lines size &= ~0x3F; @@ -49,11 +48,12 @@ ssize_t __fsa_read(struct _reent *r, void *fd, char *ptr, size_t len) { size = 0x100000; } - status = FSAReadFile(deviceData->clientHandle, tmp, 1, size, file->fd, 0); + const FSError status = FSAReadFile(deviceData->clientHandle, tmp, 1, size, file->fd, 0); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSAReadFile(0x%08X, %p, 1, 0x%08X, 0x%08X, 0) (%s) failed: %s", deviceData->clientHandle, tmp, size, file->fd, file->fullPath, FSAGetStatusStr(status)); + if (bytesRead != 0) { return bytesRead; // error after partial read } diff --git a/source/devoptab/devoptab_fsa_rename.cpp b/source/devoptab/devoptab_fsa_rename.cpp index b551f93..d427918 100644 --- a/source/devoptab/devoptab_fsa_rename.cpp +++ b/source/devoptab/devoptab_fsa_rename.cpp @@ -1,34 +1,31 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_rename(struct _reent *r, const char *oldName, const char *newName) { - FSError status; - char *fixedOldPath, *fixedNewPath; - if (!oldName || !newName) { r->_errno = EINVAL; return -1; } - fixedOldPath = __fsa_fixpath(r, oldName); + char *fixedOldPath = __fsa_fixpath(r, oldName); if (!fixedOldPath) { r->_errno = ENOMEM; return -1; } - fixedNewPath = __fsa_fixpath(r, newName); + char *fixedNewPath = __fsa_fixpath(r, newName); if (!fixedNewPath) { free(fixedOldPath); r->_errno = ENOMEM; return -1; } - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); - status = FSARename(deviceData->clientHandle, fixedOldPath, fixedNewPath); + const FSError status = FSARename(deviceData->clientHandle, fixedOldPath, fixedNewPath); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSARename(0x%08X, %s, %s) failed: %s", deviceData->clientHandle, fixedOldPath, fixedNewPath, FSAGetStatusStr(status)); diff --git a/source/devoptab/devoptab_fsa_rmdir.cpp b/source/devoptab/devoptab_fsa_rmdir.cpp index 31aa59b..d99320c 100644 --- a/source/devoptab/devoptab_fsa_rmdir.cpp +++ b/source/devoptab/devoptab_fsa_rmdir.cpp @@ -1,11 +1,9 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_rmdir(struct _reent *r, const char *name) { - FSError status; - if (!name) { r->_errno = EINVAL; return -1; @@ -17,14 +15,14 @@ int __fsa_rmdir(struct _reent *r, return -1; } - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto *deviceData = static_cast<__fsa_device_t *>(r->deviceData); - status = FSARemove(deviceData->clientHandle, fixedPath); + const FSError status = FSARemove(deviceData->clientHandle, fixedPath); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSARemove(0x%08X, %s) failed: %s", deviceData->clientHandle, fixedPath, FSAGetStatusStr(status)); free(fixedPath); - r->_errno = status == FS_ERROR_ALREADY_EXISTS ? ENOTEMPTY : __fsa_translate_error(status); + r->_errno = __fsa_translate_error(status); return -1; } diff --git a/source/devoptab/devoptab_fsa_seek.cpp b/source/devoptab/devoptab_fsa_seek.cpp index 65a7a6b..7a52fb2 100644 --- a/source/devoptab/devoptab_fsa_seek.cpp +++ b/source/devoptab/devoptab_fsa_seek.cpp @@ -1,5 +1,5 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include off_t __fsa_seek(struct _reent *r, @@ -15,10 +15,11 @@ off_t __fsa_seek(struct _reent *r, return -1; } - auto *file = (__fsa_file_t *) fd; - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto file = static_cast<__fsa_file_t *>(fd); - std::lock_guard lock(file->mutex); + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); + + std::scoped_lock lock(file->mutex); // Find the offset to see from switch (whence) { diff --git a/source/devoptab/devoptab_fsa_stat.cpp b/source/devoptab/devoptab_fsa_stat.cpp index eda59f1..47d554c 100644 --- a/source/devoptab/devoptab_fsa_stat.cpp +++ b/source/devoptab/devoptab_fsa_stat.cpp @@ -1,11 +1,10 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_stat(struct _reent *r, const char *path, struct stat *st) { - FSError status; FSAStat fsStat; if (!path || !st) { @@ -19,18 +18,22 @@ int __fsa_stat(struct _reent *r, return -1; } - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); - status = FSAGetStat(deviceData->clientHandle, fixedPath, &fsStat); + const FSError status = FSAGetStat(deviceData->clientHandle, fixedPath, &fsStat); if (status < 0) { - DEBUG_FUNCTION_LINE_ERR("FSAGetStat(0x%08X, %s, %p) failed: %s", - deviceData->clientHandle, fixedPath, &fsStat, FSAGetStatusStr(status)); + if (status != FS_ERROR_NOT_FOUND) { + DEBUG_FUNCTION_LINE_ERR("FSAGetStat(0x%08X, %s, %p) failed: %s", + deviceData->clientHandle, fixedPath, &fsStat, FSAGetStatusStr(status)); + } free(fixedPath); r->_errno = __fsa_translate_error(status); return -1; } + const ino_t ino = __fsa_hashstring(fixedPath); free(fixedPath); - __fsa_translate_stat(&fsStat, st); + __fsa_translate_stat(deviceData->clientHandle, &fsStat, ino, st); + return 0; } \ No newline at end of file diff --git a/source/devoptab/devoptab_fsa_statvfs.cpp b/source/devoptab/devoptab_fsa_statvfs.cpp index 0906c05..055984f 100644 --- a/source/devoptab/devoptab_fsa_statvfs.cpp +++ b/source/devoptab/devoptab_fsa_statvfs.cpp @@ -1,13 +1,17 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" -#include int __fsa_statvfs(struct _reent *r, const char *path, struct statvfs *buf) { - FSError status; uint64_t freeSpace; + const auto *deviceData = static_cast<__fsa_device_t *>(r->deviceData); + if (deviceData->isSDCard) { + r->_errno = ENOSYS; + return -1; + } + memset(buf, 0, sizeof(struct statvfs)); char *fixedPath = __fsa_fixpath(r, path); @@ -16,9 +20,7 @@ int __fsa_statvfs(struct _reent *r, return -1; } - auto *deviceData = (FSADeviceData *) r->deviceData; - - status = FSAGetFreeSpaceSize(deviceData->clientHandle, fixedPath, &freeSpace); + const FSError status = FSAGetFreeSpaceSize(deviceData->clientHandle, fixedPath, &freeSpace); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSAGetFreeSpaceSize(0x%08X, %s, %p) failed: %s", deviceData->clientHandle, fixedPath, &freeSpace, FSAGetStatusStr(status)); @@ -48,4 +50,4 @@ int __fsa_statvfs(struct _reent *r, buf->f_namemax = 255; return 0; -} \ No newline at end of file +} diff --git a/source/devoptab/devoptab_fsa_truncate.cpp b/source/devoptab/devoptab_fsa_truncate.cpp index 0f1bba3..ebfb37e 100644 --- a/source/devoptab/devoptab_fsa_truncate.cpp +++ b/source/devoptab/devoptab_fsa_truncate.cpp @@ -1,11 +1,10 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_ftruncate(struct _reent *r, void *fd, off_t len) { - FSError status; // Make sure length is non-negative if (!fd || len < 0) { @@ -13,13 +12,13 @@ int __fsa_ftruncate(struct _reent *r, return -1; } - auto *file = (__fsa_file_t *) fd; - auto *deviceData = (FSADeviceData *) r->deviceData; + auto *file = static_cast<__fsa_file_t *>(fd); + const auto *deviceData = static_cast<__fsa_device_t *>(r->deviceData); - std::lock_guard lock(file->mutex); + std::scoped_lock lock(file->mutex); // Set the new file size - status = FSASetPosFile(deviceData->clientHandle, file->fd, len); + FSError status = FSASetPosFile(deviceData->clientHandle, file->fd, len); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSASetPosFile(0x%08X, 0x%08X, 0x%08llX) failed: %s", deviceData->clientHandle, file->fd, len, FSAGetStatusStr(status)); diff --git a/source/devoptab/devoptab_fsa_unlink.cpp b/source/devoptab/devoptab_fsa_unlink.cpp index e55ec68..8ac188c 100644 --- a/source/devoptab/devoptab_fsa_unlink.cpp +++ b/source/devoptab/devoptab_fsa_unlink.cpp @@ -1,25 +1,23 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include int __fsa_unlink(struct _reent *r, const char *name) { - FSError status; - char *fixedPath; if (!name) { r->_errno = EINVAL; return -1; } - fixedPath = __fsa_fixpath(r, name); + char *fixedPath = __fsa_fixpath(r, name); if (!fixedPath) { r->_errno = ENOMEM; return -1; } - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto *deviceData = static_cast<__fsa_device_t *>(r->deviceData); - status = FSARemove(deviceData->clientHandle, fixedPath); + const FSError status = FSARemove(deviceData->clientHandle, fixedPath); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSARemove(0x%08X, %s) failed: %s", deviceData->clientHandle, fixedPath, FSAGetStatusStr(status)); diff --git a/source/devoptab/devoptab_fsa_utils.cpp b/source/devoptab/devoptab_fsa_utils.cpp index 0ed5fd6..c816a32 100644 --- a/source/devoptab/devoptab_fsa_utils.cpp +++ b/source/devoptab/devoptab_fsa_utils.cpp @@ -1,14 +1,90 @@ +#include "../logger.h" #include "devoptab_fsa.h" +#include + +#define COMP_MAX 50 + +#define ispathsep(ch) ((ch) == '/' || (ch) == '\\') +#define iseos(ch) ((ch) == '\0') +#define ispathend(ch) (ispathsep(ch) || iseos(ch)) + +// https://gist.github.com/starwing/2761647 +static char * +__fsa_normpath(char *out, const char *in) { + char *pos[COMP_MAX], **top = pos, *head = out; + int isabs = ispathsep(*in); + + if (isabs) *out++ = '/'; + *top++ = out; + + while (!iseos(*in)) { + while (ispathsep(*in)) { + ++in; + } + + if (iseos(*in)) { + break; + } + + if (memcmp(in, ".", 1) == 0 && ispathend(in[1])) { + ++in; + continue; + } + + if (memcmp(in, "..", 2) == 0 && ispathend(in[2])) { + in += 2; + if (top != pos + 1) { + out = *--top; + } else if (isabs) { + out = top[-1]; + } else { + strcpy(out, "../"); + out += 3; + } + continue; + } + + if (top - pos >= COMP_MAX) { + return NULL; // path to complicate + } + + *top++ = out; + while (!ispathend(*in)) { + *out++ = *in++; + } + if (ispathsep(*in)) { + *out++ = '/'; + } + } + + *out = '\0'; + if (*head == '\0') { + strcpy(head, "./"); + } + return head; +} + +uint32_t +__fsa_hashstring(const char *str) { + uint32_t h; + uint8_t *p; + + h = 0; + for (p = (uint8_t *) str; *p != '\0'; p++) { + h = 37 * h + *p; + } + return h; +} + char * __fsa_fixpath(struct _reent *r, const char *path) { char *p; - char *fixedPath; if (!path) { r->_errno = EINVAL; - return nullptr; + return NULL; } p = strchr(path, ':') + 1; @@ -16,63 +92,95 @@ __fsa_fixpath(struct _reent *r, p = (char *) path; } - size_t pathLength = strlen(p); - if (pathLength > FS_MAX_PATH) { - r->_errno = ENAMETOOLONG; - return nullptr; - } - // wii u softlocks on empty strings so give expected error back - if (pathLength == 0) { + if (strlen(p) == 0) { r->_errno = ENOENT; - return nullptr; + return NULL; } - fixedPath = static_cast(memalign(0x40, FS_MAX_PATH + 1)); + int maxPathLength = PATH_MAX; + auto fixedPath = static_cast(memalign(0x40, maxPathLength)); if (!fixedPath) { + DEBUG_FUNCTION_LINE_ERR("__fsa_fixpath: failed to allocate memory for fixedPath"); r->_errno = ENOMEM; - return nullptr; + return NULL; } - if (p[0] == '/') { - auto *deviceData = (FSADeviceData *) r->deviceData; - strcpy(fixedPath, deviceData->mount_path); - strcat(fixedPath, p); + // Convert to an absolute path + if (p[0] != '\0' && p[0] != '\\' && p[0] != '/') { + __fsa_device_t *deviceData = (__fsa_device_t *) r->deviceData; + if (snprintf(fixedPath, maxPathLength, "%s/%s", deviceData->cwd, p) >= maxPathLength) { + DEBUG_FUNCTION_LINE_ERR("__fsa_fixpath: fixedPath snprintf result (relative) was truncated"); + } } else { - strcpy(fixedPath, p); + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); + if (snprintf(fixedPath, maxPathLength, "%s%s", deviceData->mountPath, p) >= maxPathLength) { + DEBUG_FUNCTION_LINE_ERR("__fsa_fixpath: fixedPath snprintf result (absolute) was truncated"); + } + } + + // Normalize path (resolve any ".", "..", or "//") + char *normalizedPath = strdup(fixedPath); + if (!normalizedPath) { + DEBUG_FUNCTION_LINE_ERR("__fsa_fixpath: failed to allocate memory for normalizedPath"); + free(fixedPath); + r->_errno = ENOMEM; + return NULL; + } + + char *resPath = __fsa_normpath(normalizedPath, fixedPath); + if (!resPath) { + DEBUG_FUNCTION_LINE_ERR("__fsa_fixpath: failed to normalize path"); + free(normalizedPath); + free(fixedPath); + r->_errno = EIO; + return NULL; + } + + if (snprintf(fixedPath, maxPathLength, "%s", resPath) >= maxPathLength) { + DEBUG_FUNCTION_LINE_ERR("__fsa_fixpath: fixedPath snprintf result (relative) was truncated"); + } + + free(normalizedPath); + + size_t pathLength = strlen(fixedPath); + if (pathLength > FS_MAX_PATH) { + free(fixedPath); + r->_errno = ENAMETOOLONG; + return NULL; } return fixedPath; } -mode_t __fsa_translate_stat_mode(FSAStat *fileStat) { +mode_t __fsa_translate_stat_mode(FSStat *fsStat) { mode_t retMode = 0; - if ((fileStat->flags & FS_STAT_LINK) == FS_STAT_LINK) { + if ((fsStat->flags & FS_STAT_LINK) == FS_STAT_LINK) { retMode |= S_IFLNK; - } else if ((fileStat->flags & FS_STAT_DIRECTORY) == FS_STAT_DIRECTORY) { + } else if ((fsStat->flags & FS_STAT_DIRECTORY) == FS_STAT_DIRECTORY) { retMode |= S_IFDIR; - } else if ((fileStat->flags & FS_STAT_FILE) == FS_STAT_FILE) { + } else if ((fsStat->flags & FS_STAT_FILE) == FS_STAT_FILE) { retMode |= S_IFREG; - } else if (fileStat->size == 0) { + } else if (fsStat->size == 0) { // Mounted paths like /vol/external01 have no flags set. // If no flag is set and the size is 0, it's a (root) dir retMode |= S_IFDIR; - } else if (fileStat->size > 0) { + } else if (fsStat->size > 0) { // Some regular Wii U files have no type info but will have a size retMode |= S_IFREG; } // Convert normal CafeOS hexadecimal permission bits into Unix octal permission bits - mode_t permissionMode = (((fileStat->mode >> 2) & S_IRWXU) | ((fileStat->mode >> 1) & S_IRWXG) | (fileStat->mode & S_IRWXO)); + mode_t permissionMode = (((fsStat->mode >> 2) & S_IRWXU) | ((fsStat->mode >> 1) & S_IRWXG) | (fsStat->mode & S_IRWXO)); return retMode | permissionMode; } -void __fsa_translate_stat(FSAStat *fsStat, struct stat *posStat) { +void __fsa_translate_stat(FSAClientHandle clientHandle, FSStat *fsStat, ino_t ino, struct stat *posStat) { memset(posStat, 0, sizeof(struct stat)); - posStat->st_dev = (dev_t) nullptr; - posStat->st_ino = fsStat->entryId; + posStat->st_dev = (dev_t) clientHandle; + posStat->st_ino = ino; posStat->st_mode = __fsa_translate_stat_mode(fsStat); posStat->st_nlink = 1; posStat->st_uid = fsStat->owner; @@ -106,11 +214,6 @@ time_t __fsa_translate_time(FSTime timeValue) { return (timeValue / 1000000) + EPOCH_DIFF_SECS(WIIU_FSTIME_EPOCH_YEAR); } -FSMode __fsa_translate_permission_mode(mode_t mode) { - // Convert normal Unix octal permission bits into CafeOS hexadecimal permission bits - return (FSMode) (((mode & S_IRWXU) << 2) | ((mode & S_IRWXG) << 1) | (mode & S_IRWXO)); -} - int __fsa_translate_error(FSError error) { switch (error) { case FS_ERROR_END_OF_DIR: @@ -139,56 +242,46 @@ int __fsa_translate_error(FSError error) { case FS_ERROR_NOT_FILE: return EISDIR; case FS_ERROR_OUT_OF_RANGE: - return ESPIPE; + return EINVAL; case FS_ERROR_UNSUPPORTED_COMMAND: return ENOTSUP; case FS_ERROR_WRITE_PROTECTED: return EROFS; case FS_ERROR_NOT_INIT: return ENODEV; - // TODO case FS_ERROR_MAX_MOUNT_POINTS: - break; case FS_ERROR_MAX_VOLUMES: - break; case FS_ERROR_MAX_CLIENTS: - break; case FS_ERROR_MAX_FILES: - break; case FS_ERROR_MAX_DIRS: - break; + return EMFILE; case FS_ERROR_ALREADY_OPEN: - break; + return EBUSY; case FS_ERROR_NOT_EMPTY: - break; + return ENOTEMPTY; case FS_ERROR_ACCESS_ERROR: - break; + return EACCES; case FS_ERROR_DATA_CORRUPTED: - break; + return EILSEQ; case FS_ERROR_JOURNAL_FULL: - break; + return EBUSY; case FS_ERROR_UNAVAILABLE_COMMAND: - break; + return EBUSY; case FS_ERROR_INVALID_PARAM: - break; + return EBUSY; case FS_ERROR_INVALID_BUFFER: - break; case FS_ERROR_INVALID_ALIGNMENT: - break; case FS_ERROR_INVALID_CLIENTHANDLE: - break; case FS_ERROR_INVALID_FILEHANDLE: - break; case FS_ERROR_INVALID_DIRHANDLE: - break; + return EINVAL; case FS_ERROR_OUT_OF_RESOURCES: - break; + return ENOMEM; case FS_ERROR_MEDIA_NOT_READY: - break; + return EIO; case FS_ERROR_INVALID_MEDIA: - break; + return EIO; default: - break; + return EIO; } - return (int) EIO; -} +} \ No newline at end of file diff --git a/source/devoptab/devoptab_fsa_write.cpp b/source/devoptab/devoptab_fsa_write.cpp index 39bc50c..247251e 100644 --- a/source/devoptab/devoptab_fsa_write.cpp +++ b/source/devoptab/devoptab_fsa_write.cpp @@ -1,17 +1,15 @@ +#include "../logger.h" #include "devoptab_fsa.h" -#include "logger.h" #include ssize_t __fsa_write(struct _reent *r, void *fd, const char *ptr, size_t len) { - FSError status; - if (!fd || !ptr) { r->_errno = EINVAL; return -1; } // Check that the file was opened with write access - auto *file = (__fsa_file_t *) fd; + auto *file = static_cast<__fsa_file_t *>(fd); if ((file->flags & O_ACCMODE) == O_RDONLY) { r->_errno = EBADF; return -1; @@ -20,9 +18,9 @@ ssize_t __fsa_write(struct _reent *r, void *fd, const char *ptr, size_t len) { // cache-aligned, cache-line-sized __attribute__((aligned(0x40))) uint8_t alignedBuffer[0x40]; - auto *deviceData = (FSADeviceData *) r->deviceData; + const auto deviceData = static_cast<__fsa_device_t *>(r->deviceData); - std::lock_guard lock(file->mutex); + std::scoped_lock lock(file->mutex); // If O_APPEND is set, we always write to the end of the file. // When writing we file->offset to the file size to keep in sync. @@ -37,13 +35,13 @@ ssize_t __fsa_write(struct _reent *r, void *fd, const char *ptr, size_t len) { uint8_t *tmp = (uint8_t *) ptr; size_t size = len - bytesWritten; - if ((uintptr_t) ptr & 0x3F) { + if (size < 0x40) { + // write partial cache-line back-end + tmp = alignedBuffer; + } else if ((uintptr_t) ptr & 0x3F) { // write partial cache-line front-end tmp = alignedBuffer; size = MIN(size, 0x40 - ((uintptr_t) ptr & 0x3F)); - } else if (size < 0x40) { - // write partial cache-line back-end - tmp = alignedBuffer; } else { // write whole cache lines size &= ~0x3F; @@ -58,7 +56,7 @@ ssize_t __fsa_write(struct _reent *r, void *fd, const char *ptr, size_t len) { memcpy(tmp, ptr, size); } - status = FSAWriteFile(deviceData->clientHandle, tmp, 1, size, file->fd, 0); + const FSError status = FSAWriteFile(deviceData->clientHandle, tmp, 1, size, file->fd, 0); if (status < 0) { DEBUG_FUNCTION_LINE_ERR("FSAWriteFile(0x%08X, %p, 1, 0x%08X, 0x%08X, 0) (%s) failed: %s", deviceData->clientHandle, tmp, size, file->fd, file->fullPath, FSAGetStatusStr(status)); diff --git a/source/fsa.cpp b/source/fsa.cpp index ceb358b..3f1876f 100644 --- a/source/fsa.cpp +++ b/source/fsa.cpp @@ -104,10 +104,10 @@ FSError FSAEx_RawReadEx(int clientHandle, void *data, uint32_t size_bytes, uint3 if ((uint32_t) data & 0x3F) { auto *alignedBuffer = memalign(0x40, ROUNDUP(size_bytes * cnt, 0x40)); if (!alignedBuffer) { - DEBUG_FUNCTION_LINE_ERR("Buffer not aligned (%p).\n", data); + DEBUG_FUNCTION_LINE_ERR("Buffer not aligned (%p).", data); return FS_ERROR_INVALID_ALIGNMENT; } - DEBUG_FUNCTION_LINE_WARN("Buffer not aligned (%p). Align to 0x40 for best performance\n", data); + DEBUG_FUNCTION_LINE_WARN("Buffer not aligned (%p). Align to 0x40 for best performance", data); tmp = alignedBuffer; }