From 11f76790e4b712f68e3db7692ca0e3dd5613e89b Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 22 Jul 2022 14:46:29 +0200 Subject: [PATCH] Add devoptab support to replace the libioushax devoptab --- Makefile | 5 +- README.md | 2 + include/mocha/fsa.h | 10 +- include/mocha/mocha.h | 29 ++++ source/devoptab/FastLockWrapper.h | 20 +++ source/devoptab/devoptab_fs.cpp | 202 ++++++++++++++++++++++ source/devoptab/devoptab_fs.h | 109 ++++++++++++ source/devoptab/devoptab_fsa_chdir.cpp | 32 ++++ source/devoptab/devoptab_fsa_chmod.cpp | 38 ++++ source/devoptab/devoptab_fsa_close.cpp | 29 ++++ source/devoptab/devoptab_fsa_dirclose.cpp | 29 ++++ source/devoptab/devoptab_fsa_dirnext.cpp | 52 ++++++ source/devoptab/devoptab_fsa_diropen.cpp | 41 +++++ source/devoptab/devoptab_fsa_dirreset.cpp | 28 +++ source/devoptab/devoptab_fsa_fchmod.cpp | 27 +++ source/devoptab/devoptab_fsa_fstat.cpp | 41 +++++ source/devoptab/devoptab_fsa_fsync.cpp | 29 ++++ source/devoptab/devoptab_fsa_link.cpp | 8 + source/devoptab/devoptab_fsa_mkdir.cpp | 38 ++++ source/devoptab/devoptab_fsa_open.cpp | 95 ++++++++++ source/devoptab/devoptab_fsa_read.cpp | 77 +++++++++ source/devoptab/devoptab_fsa_rename.cpp | 45 +++++ source/devoptab/devoptab_fsa_rmdir.cpp | 71 ++++++++ source/devoptab/devoptab_fsa_seek.cpp | 73 ++++++++ source/devoptab/devoptab_fsa_stat.cpp | 50 ++++++ source/devoptab/devoptab_fsa_statvfs.cpp | 50 ++++++ source/devoptab/devoptab_fsa_truncate.cpp | 38 ++++ source/devoptab/devoptab_fsa_unlink.cpp | 35 ++++ source/devoptab/devoptab_fsa_utils.cpp | 169 ++++++++++++++++++ source/devoptab/devoptab_fsa_utimes.cpp | 9 + source/devoptab/devoptab_fsa_write.cpp | 73 ++++++++ source/fsa.cpp | 9 +- source/utils.cpp | 4 + 33 files changed, 1556 insertions(+), 11 deletions(-) create mode 100644 source/devoptab/FastLockWrapper.h create mode 100644 source/devoptab/devoptab_fs.cpp create mode 100644 source/devoptab/devoptab_fs.h create mode 100644 source/devoptab/devoptab_fsa_chdir.cpp create mode 100644 source/devoptab/devoptab_fsa_chmod.cpp create mode 100644 source/devoptab/devoptab_fsa_close.cpp create mode 100644 source/devoptab/devoptab_fsa_dirclose.cpp create mode 100644 source/devoptab/devoptab_fsa_dirnext.cpp create mode 100644 source/devoptab/devoptab_fsa_diropen.cpp create mode 100644 source/devoptab/devoptab_fsa_dirreset.cpp create mode 100644 source/devoptab/devoptab_fsa_fchmod.cpp create mode 100644 source/devoptab/devoptab_fsa_fstat.cpp create mode 100644 source/devoptab/devoptab_fsa_fsync.cpp create mode 100644 source/devoptab/devoptab_fsa_link.cpp create mode 100644 source/devoptab/devoptab_fsa_mkdir.cpp create mode 100644 source/devoptab/devoptab_fsa_open.cpp create mode 100644 source/devoptab/devoptab_fsa_read.cpp create mode 100644 source/devoptab/devoptab_fsa_rename.cpp create mode 100644 source/devoptab/devoptab_fsa_rmdir.cpp create mode 100644 source/devoptab/devoptab_fsa_seek.cpp create mode 100644 source/devoptab/devoptab_fsa_stat.cpp create mode 100644 source/devoptab/devoptab_fsa_statvfs.cpp create mode 100644 source/devoptab/devoptab_fsa_truncate.cpp create mode 100644 source/devoptab/devoptab_fsa_unlink.cpp create mode 100644 source/devoptab/devoptab_fsa_utils.cpp create mode 100644 source/devoptab/devoptab_fsa_utimes.cpp create mode 100644 source/devoptab/devoptab_fsa_write.cpp diff --git a/Makefile b/Makefile index e343a58..2c9fcb2 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,8 @@ VERSION := $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH) #------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build -SOURCES := source +SOURCES := source \ + source/devoptab DATA := data INCLUDES := source \ include \ @@ -38,7 +39,7 @@ CFLAGS := -Wall -Werror -save-temps \ $(MACHDEP) \ $(BUILD_CFLAGS) -CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -fno-exceptions CXXFLAGS := $(CFLAGS) -std=gnu++20 diff --git a/README.md b/README.md index d81bdcb..06be463 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ [![Publish Docker Image](https://github.com/wiiu-env/libmocha/actions/workflows/push_image.yml/badge.svg)](https://github.com/wiiu-env/libmocha/actions/workflows/push_image.yml) +**This library is still WIP, may not work as expected or have breaking changes in the near future** + # libmocha Requires the [MochaPayload](https://github.com/wiiu-env/MochaPayload) to be running via [EnvironmentLoader](https://github.com/wiiu-env/EnvironmentLoader). Requires [wut](https://github.com/devkitPro/wut) for building. diff --git a/include/mocha/fsa.h b/include/mocha/fsa.h index 6abb28a..076c59f 100644 --- a/include/mocha/fsa.h +++ b/include/mocha/fsa.h @@ -15,7 +15,7 @@ extern "C" { * @param outHandle pointer where the handle of the raw device will be stored * @return */ -FSError FSAEx_RawOpen(FSClient *client, char *device_path, int32_t *outHandle); +FSError FSAEx_RawOpen(FSClient *client, const char *device_path, int32_t *outHandle); /** * Opens a device for raw read/write @@ -24,7 +24,7 @@ FSError FSAEx_RawOpen(FSClient *client, char *device_path, int32_t *outHandle); * @param outHandle pointer where the handle of the raw device will be stored * @return */ -FSError FSAEx_RawOpenEx(int clientHandle, char *device_path, int32_t *outHandle); +FSError FSAEx_RawOpenEx(FSAClientHandle clientHandle, const char *device_path, int32_t *outHandle); /** * Closes a devices that was previously opened via FSAEx_RawOpen @@ -40,7 +40,7 @@ FSError FSAEx_RawClose(FSClient *client, int32_t device_handle); * @param device_handle device handle * @return */ -FSError FSAEx_RawCloseEx(int clientHandle, int32_t device_handle); +FSError FSAEx_RawCloseEx(FSAClientHandle clientHandle, int32_t device_handle); /** * Read data from a device handle. @@ -66,7 +66,7 @@ FSError FSAEx_RawRead(FSClient *client, void *data, uint32_t size_bytes, uint32_ * @param device_handle valid device handle. * @return */ -FSError FSAEx_RawReadEx(int clientHandle, void *data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle); +FSError FSAEx_RawReadEx(FSAClientHandle clientHandle, void *data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle); /** @@ -93,7 +93,7 @@ FSError FSAEx_RawWrite(FSClient *client, const void *data, uint32_t size_bytes, * @param device_handle valid device handle. * @return */ -FSError FSAEx_RawWriteEx(int clientHandle, const void *data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle); +FSError FSAEx_RawWriteEx(FSAClientHandle clientHandle, const void *data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle); #ifdef __cplusplus } // extern "C" diff --git a/include/mocha/mocha.h b/include/mocha/mocha.h index 1dfe9b3..83d6a2b 100644 --- a/include/mocha/mocha.h +++ b/include/mocha/mocha.h @@ -13,6 +13,8 @@ typedef enum MochaUtilsStatus { MOCHA_RESULT_INVALID_ARGUMENT = -0x01, MOCHA_RESULT_MAX_CLIENT = -0x02, MOCHA_RESULT_OUT_OF_MEMORY = -0x03, + MOCHA_RESULT_ALREADY_EXISTS = -0x04, + MOCHA_RESULT_ADD_DEVOPTAB_FAILED = -0x05, MOCHA_RESULT_NOT_FOUND = -0x06, MOCHA_RESULT_UNSUPPORTED_API_VERSION = -0x10, MOCHA_RESULT_UNSUPPORTED_COMMAND = -0x11, @@ -253,6 +255,33 @@ MochaUtilsStatus Mocha_ODMGetDiscKey(WUDDiscKey *discKey); */ MochaUtilsStatus Mocha_SEEPROMRead(uint8_t *out_buffer, uint32_t offset, uint32_t size); +/** + * Mounts a device (dev_path) to a given path (mount_path) and make a accessible via the + * newlib devoptab (standard POSIX file I/O) + * + * Requires Mocha API Version: 1 + * @param virt_name Name which should be used for the devoptab. When choosing e.g. "storage_usb" the mounted device can be accessed via "storage_usb:/". + * @param dev_path (optional) Cafe OS internal device path (e.g. /dev/slc01). If the given dev_path is NULL, an existing mount will be used (and is expected) + * @param mount_path Path where CafeOS should mount the device to. Must be globally unique and start with "/vol/storage_" + * @return MOCHA_RESULT_SUCCESS: The device has been mounted successfully
+ * MOCHA_RESULT_MAX_CLIENT: The maximum number of FSAClients have been registered.
+ * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.
+ * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.
+ * MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path. + */ +MochaUtilsStatus Mocha_MountFS(const char *virt_name, const char *dev_path, const char *mount_path); + +MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, const char *mount_path, FSAMountFlags mountFlags, void *mountArgBuf, int mountArgBufLen); + +/** + * Unmounts a mount by it's name. + * @param virt_name Name of the mount. + * @return MOCHA_RESULT_SUCCESS: The unmount was successful
+ * MOCHA_RESULT_INVALID_ARGUMENT:
+ * MOCHA_RESULT_NOT_FOUND: No mount with the given name has been found. + */ +MochaUtilsStatus Mocha_UnmountFS(const char *virt_name); + #ifdef __cplusplus } // extern "C" #endif \ No newline at end of file diff --git a/source/devoptab/FastLockWrapper.h b/source/devoptab/FastLockWrapper.h new file mode 100644 index 0000000..46f3a98 --- /dev/null +++ b/source/devoptab/FastLockWrapper.h @@ -0,0 +1,20 @@ +#pragma once + +#include +class FastLockWrapper { +public: + FastLockWrapper() { + OSFastMutex_Init(&mutex, "generic lock"); + } + + void lock() { + OSFastMutex_Lock(&mutex); + } + + void unlock() { + OSFastMutex_Unlock(&mutex); + } + +private: + OSFastMutex mutex{}; +}; diff --git a/source/devoptab/devoptab_fs.cpp b/source/devoptab/devoptab_fs.cpp new file mode 100644 index 0000000..c8ceefb --- /dev/null +++ b/source/devoptab/devoptab_fs.cpp @@ -0,0 +1,202 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include "mocha/mocha.h" +#include +#include +#include + +static const devoptab_t fsa_default_devoptab = { + .structSize = sizeof(__fsa_file_t), + .open_r = __fsa_open, + .close_r = __fsa_close, + .write_r = __fsa_write, + .read_r = __fsa_read, + .seek_r = __fsa_seek, + .fstat_r = __fsa_fstat, + .stat_r = __fsa_stat, + .link_r = __fsa_link, + .unlink_r = __fsa_unlink, + .chdir_r = __fsa_chdir, + .rename_r = __fsa_rename, + .mkdir_r = __fsa_mkdir, + .dirStateSize = sizeof(__fsa_dir_t), + .diropen_r = __fsa_diropen, + .dirreset_r = __fsa_dirreset, + .dirnext_r = __fsa_dirnext, + .dirclose_r = __fsa_dirclose, + .statvfs_r = __fsa_statvfs, + .ftruncate_r = __fsa_ftruncate, + .fsync_r = __fsa_fsync, + .chmod_r = __fsa_chmod, + .fchmod_r = __fsa_fchmod, + .rmdir_r = __fsa_rmdir, + .lstat_r = __fsa_stat, + .utimes_r = __fsa_utimes, +}; + +static bool fsa_initialised = false; +static FSADeviceData fsa_mounts[8]; + +static void fsaResetMount(FSADeviceData *mount, uint32_t id) { + *mount = {}; + memcpy(&mount->device, &fsa_default_devoptab, sizeof(fsa_default_devoptab)); + mount->device.name = mount->name; + mount->device.deviceData = mount; + mount->id = id; + mount->setup = false; + mount->mounted = false; + mount->clientHandle = -1; + mount->deviceSizeInSectors = 0; + mount->deviceSectorSize = 0; + memset(mount->mount_path, 0, sizeof(mount->mount_path)); + 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]); + for (uint32_t i = 0; i < total; i++) { + fsaResetMount(&fsa_mounts[i], i); + } + fsa_initialised = true; + } +} + +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) { + return mount; + } + } + + return nullptr; +} + +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_ERR("FSAUnmount %s for %s failed: %s", mount->mount_path, mount->name, FSAGetStatusStr(res)); + } + } + res = FSADelClient(mount->clientHandle); + if (res < 0) { + DEBUG_FUNCTION_LINE_ERR("FSADelClient for %s failed: %d", mount->name, FSAGetStatusStr(res)); + } + fsaResetMount(mount, mount->id); +} + +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]); + + fsaInit(); + + for (uint32_t i = 0; i < total; i++) { + FSADeviceData *mount = &fsa_mounts[i]; + if (!mount->setup) { + continue; + } + if (strcmp(mount->name, virt_name) == 0) { + RemoveDevice(mount->name); + fsa_free(mount); + return MOCHA_RESULT_SUCCESS; + } + } + + DEBUG_FUNCTION_LINE_ERR("Failed to find fsa mount data for %s", virt_name); + return MOCHA_RESULT_NOT_FOUND; +} +extern int mochaInitDone; + +MochaUtilsStatus Mocha_MountFS(const char *virt_name, const char *dev_path, const char *mount_path) { + return Mocha_MountFSEx(virt_name, dev_path, mount_path, FSA_MOUNT_FLAG_GLOBAL_MOUNT, nullptr, 0); +} + +MochaUtilsStatus Mocha_MountFSEx(const char *virt_name, const char *dev_path, const char *mount_path, FSAMountFlags mountFlags, void *mountArgBuf, int mountArgBufLen) { + if (!mochaInitDone) { + if (Mocha_InitLibrary() != MOCHA_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Mocha_InitLibrary failed"); + return MOCHA_RESULT_UNSUPPORTED_COMMAND; + } + } + + FSAInit(); + std::lock_guard lock(fsaMutex); + + FSADeviceData *mount = fsa_alloc(); + if (mount == nullptr) { + DEBUG_FUNCTION_LINE_ERR("fsa_alloc() failed"); + OSMemoryBarrier(); + return MOCHA_RESULT_OUT_OF_MEMORY; + } + + mount->clientHandle = FSAAddClient(nullptr); + if (mount->clientHandle < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAAddClient() failed: %s", FSAGetStatusStr(static_cast(mount->clientHandle))); + fsa_free(mount); + return MOCHA_RESULT_MAX_CLIENT; + } + + if (Mocha_UnlockFSClientEx(mount->clientHandle) != MOCHA_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Mocha_UnlockFSClientEx failed"); + return MOCHA_RESULT_UNSUPPORTED_COMMAND; + } + + mount->mounted = false; + + strncpy(mount->name, virt_name, sizeof(mount->name) - 1); + strncpy(mount->mount_path, mount_path, sizeof(mount->mount_path) - 1); + FSError res; + if (dev_path) { + res = FSAMount(mount->clientHandle, dev_path, mount_path, mountFlags, mountArgBuf, mountArgBufLen); + if (res < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAMount(0x%08X, %s, %s, FSA_MOUNT_FLAG_GLOBAL_MOUNT, nullptr, 0) failed: %s", mount->clientHandle, dev_path, mount_path, FSAGetStatusStr(res)); + fsa_free(mount); + if (res == FS_ERROR_ALREADY_EXISTS) { + return MOCHA_RESULT_ALREADY_EXISTS; + } + return MOCHA_RESULT_UNKNOWN_ERROR; + } + mount->mounted = true; + } else { + mount->mounted = false; + } + + if ((res = FSAChangeDir(mount->clientHandle, mount->mount_path)) < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAChangeDir(0x%08X, %s) failed: %s", mount->clientHandle, mount->mount_path, FSAGetStatusStr(res)); + } + + FSADeviceInfo deviceInfo; + if ((res = FSAGetDeviceInfo(mount->clientHandle, mount_path, &deviceInfo)) >= 0) { + mount->deviceSizeInSectors = deviceInfo.deviceSizeInSectors; + mount->deviceSectorSize = deviceInfo.deviceSectorSize; + } else { + mount->deviceSizeInSectors = 0xFFFFFFFF; + mount->deviceSectorSize = 512; + DEBUG_FUNCTION_LINE_ERR("Failed to get DeviceInfo for %s: %s", mount_path, FSAGetStatusStr(res)); + } + + if (AddDevice(&mount->device) < 0) { + DEBUG_FUNCTION_LINE_ERR("AddDevice failed for %s.", virt_name); + fsa_free(mount); + return MOCHA_RESULT_ADD_DEVOPTAB_FAILED; + } + + mount->setup = true; + + return MOCHA_RESULT_SUCCESS; +} diff --git a/source/devoptab/devoptab_fs.h b/source/devoptab/devoptab_fs.h new file mode 100644 index 0000000..bd815e9 --- /dev/null +++ b/source/devoptab/devoptab_fs.h @@ -0,0 +1,109 @@ +#pragma once +#include + +#include "FastLockWrapper.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct FSADeviceData { + devoptab_t device{}; + bool setup{}; + bool mounted{}; + uint32_t id{}; + char name[32]{}; + char mount_path[256]{}; + uint64_t deviceSizeInSectors{}; + uint32_t deviceSectorSize{}; + FSAClientHandle clientHandle{}; + FastLockWrapper mutex; +} FSADeviceData; + +/** + * Open file struct + */ +typedef struct +{ + //! FS handle + FSAFileHandle fd; + + //! Flags used in open(2) + int flags; + + //! Current file offset + uint32_t offset; + + //! Path stored for internal path tracking + char path[FS_MAX_PATH + 1]; +} __fsa_file_t; + +/** + * Open directory struct + */ +typedef struct +{ + //! Should be set to FS_DIRITER_MAGIC + uint32_t magic; + + //! FS handle + FSADirectoryHandle fd; + + //! Temporary storage for reading entries + FSADirectoryEntry entry_data; +} __fsa_dir_t; + +#define FS_DIRITER_MAGIC 0x77696975 + + +#ifdef __cplusplus +extern "C" { +#endif + +int __fsa_open(struct _reent *r, void *fileStruct, const char *path, + int flags, int mode); +int __fsa_close(struct _reent *r, void *fd); +ssize_t __fsa_write(struct _reent *r, void *fd, const char *ptr, + size_t len); +ssize_t __fsa_read(struct _reent *r, void *fd, char *ptr, size_t len); +off_t __fsa_seek(struct _reent *r, void *fd, off_t pos, int dir); +int __fsa_fstat(struct _reent *r, void *fd, struct stat *st); +int __fsa_stat(struct _reent *r, const char *file, struct stat *st); +int __fsa_link(struct _reent *r, const char *existing, + const char *newLink); +int __fsa_unlink(struct _reent *r, const char *name); +int __fsa_chdir(struct _reent *r, const char *name); +int __fsa_rename(struct _reent *r, const char *oldName, + const char *newName); +int __fsa_mkdir(struct _reent *r, const char *path, int mode); +DIR_ITER *__fsa_diropen(struct _reent *r, DIR_ITER *dirState, + const char *path); +int __fsa_dirreset(struct _reent *r, DIR_ITER *dirState); +int __fsa_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, + struct stat *filestat); +int __fsa_dirclose(struct _reent *r, DIR_ITER *dirState); +int __fsa_statvfs(struct _reent *r, const char *path, + struct statvfs *buf); +int __fsa_ftruncate(struct _reent *r, void *fd, off_t len); +int __fsa_fsync(struct _reent *r, void *fd); +int __fsa_chmod(struct _reent *r, const char *path, mode_t mode); +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 +char *__fsa_fixpath(struct _reent *r, const char *path); +int __fsa_translate_error(FSError error); +time_t __fsa_translate_time(FSTime timeValue); +FSMode __fsa_translate_permission_mode(int mode); +mode_t __fsa_translate_stat_mode(FSStat *fileStat); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/source/devoptab/devoptab_fsa_chdir.cpp b/source/devoptab/devoptab_fsa_chdir.cpp new file mode 100644 index 0000000..c3984a5 --- /dev/null +++ b/source/devoptab/devoptab_fsa_chdir.cpp @@ -0,0 +1,32 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_chdir(struct _reent *r, + const char *path) { + FSError status; + + if (!path) { + r->_errno = EINVAL; + return -1; + } + + char *fixedPath = __fsa_fixpath(r, path); + if (!fixedPath) { + r->_errno = ENOMEM; + return -1; + } + auto *deviceData = (FSADeviceData *) r->deviceData; + std::lock_guard lock(deviceData->mutex); + + 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; + } + free(fixedPath); + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_chmod.cpp b/source/devoptab/devoptab_fsa_chmod.cpp new file mode 100644 index 0000000..67cb152 --- /dev/null +++ b/source/devoptab/devoptab_fsa_chmod.cpp @@ -0,0 +1,38 @@ +#include "devoptab_fs.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; + } + + char *fixedPath = __fsa_fixpath(r, path); + if (!fixedPath) { + r->_errno = ENOMEM; + return -1; + } + + FSMode translatedMode = __fsa_translate_permission_mode(mode); + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + 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)); + free(fixedPath); + r->_errno = __fsa_translate_error(status); + return -1; + } + free(fixedPath); + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_close.cpp b/source/devoptab/devoptab_fsa_close.cpp new file mode 100644 index 0000000..0fe482c --- /dev/null +++ b/source/devoptab/devoptab_fsa_close.cpp @@ -0,0 +1,29 @@ +#include "devoptab_fs.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; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + 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)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_dirclose.cpp b/source/devoptab/devoptab_fsa_dirclose.cpp new file mode 100644 index 0000000..f9ee986 --- /dev/null +++ b/source/devoptab/devoptab_fsa_dirclose.cpp @@ -0,0 +1,29 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_dirclose(struct _reent *r, + DIR_ITER *dirState) { + FSError status; + __fsa_dir_t *dir; + + if (!dirState) { + r->_errno = EINVAL; + return -1; + } + + dir = (__fsa_dir_t *) (dirState->dirStruct); + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + status = FSACloseDir(deviceData->clientHandle, dir->fd); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSACloseDir(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, dir->fd, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_dirnext.cpp b/source/devoptab/devoptab_fsa_dirnext.cpp new file mode 100644 index 0000000..3cfc19f --- /dev/null +++ b/source/devoptab/devoptab_fsa_dirnext.cpp @@ -0,0 +1,52 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_dirnext(struct _reent *r, + DIR_ITER *dirState, + char *filename, + struct stat *filestat) { + FSError status; + __fsa_dir_t *dir; + + if (!dirState || !filename || !filestat) { + r->_errno = EINVAL; + return -1; + } + + dir = (__fsa_dir_t *) (dirState->dirStruct); + memset(&dir->entry_data, 0, sizeof(dir->entry_data)); + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + 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, 0x%08X) failed: %s", deviceData->clientHandle, dir->fd, &dir->entry_data, FSAGetStatusStr(status)); + } + r->_errno = __fsa_translate_error(status); + return -1; + } + + // Fill in the stat info + memset(filestat, 0, sizeof(struct stat)); + filestat->st_dev = (dev_t) deviceData->clientHandle; + filestat->st_ino = dir->entry_data.info.entryId; + filestat->st_mode = __fsa_translate_stat_mode(&dir->entry_data.info); + filestat->st_nlink = 1; + filestat->st_uid = dir->entry_data.info.owner; + filestat->st_gid = dir->entry_data.info.group; + filestat->st_rdev = filestat->st_dev; + filestat->st_size = dir->entry_data.info.size; + filestat->st_blksize = deviceData->deviceSectorSize; + filestat->st_blocks = (filestat->st_size + filestat->st_blksize - 1) / filestat->st_blksize; + filestat->st_atime = __fsa_translate_time(dir->entry_data.info.modified); + filestat->st_ctime = __fsa_translate_time(dir->entry_data.info.created); + filestat->st_mtime = __fsa_translate_time(dir->entry_data.info.modified); + + memset(filename, 0, NAME_MAX); + strcpy(filename, dir->entry_data.name); + return 0; +} diff --git a/source/devoptab/devoptab_fsa_diropen.cpp b/source/devoptab/devoptab_fsa_diropen.cpp new file mode 100644 index 0000000..25f0b34 --- /dev/null +++ b/source/devoptab/devoptab_fsa_diropen.cpp @@ -0,0 +1,41 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +DIR_ITER * +__fsa_diropen(struct _reent *r, + DIR_ITER *dirState, + const char *path) { + FSADirectoryHandle fd; + FSError status; + __fsa_dir_t *dir; + + if (!dirState || !path) { + r->_errno = EINVAL; + return NULL; + } + + char *fixedPath = __fsa_fixpath(r, path); + if (!fixedPath) { + return NULL; + } + dir = (__fsa_dir_t *) (dirState->dirStruct); + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + status = FSAOpenDir(deviceData->clientHandle, fixedPath, &fd); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAOpenDir(0x%08X, %s, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, &fd, FSAGetStatusStr(status)); + free(fixedPath); + r->_errno = __fsa_translate_error(status); + return NULL; + } + free(fixedPath); + + dir->magic = FS_DIRITER_MAGIC; + dir->fd = fd; + memset(&dir->entry_data, 0, sizeof(dir->entry_data)); + return dirState; +} diff --git a/source/devoptab/devoptab_fsa_dirreset.cpp b/source/devoptab/devoptab_fsa_dirreset.cpp new file mode 100644 index 0000000..d7372c5 --- /dev/null +++ b/source/devoptab/devoptab_fsa_dirreset.cpp @@ -0,0 +1,28 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_dirreset(struct _reent *r, + DIR_ITER *dirState) { + FSError status; + __fsa_dir_t *dir; + + if (!dirState) { + r->_errno = EINVAL; + return -1; + } + + dir = (__fsa_dir_t *) (dirState->dirStruct); + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + status = FSARewindDir(deviceData->clientHandle, dir->fd); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSARewindDir(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, dir->fd, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_fchmod.cpp b/source/devoptab/devoptab_fsa_fchmod.cpp new file mode 100644 index 0000000..dd89934 --- /dev/null +++ b/source/devoptab/devoptab_fsa_fchmod.cpp @@ -0,0 +1,27 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_fchmod(struct _reent *r, + void *fd, + mode_t mode) { + FSError status; + __fsa_file_t *file; + + file = (__fsa_file_t *) fd; + + FSMode translatedMode = __fsa_translate_permission_mode(mode); + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + status = FSAChangeMode(deviceData->clientHandle, file->path, translatedMode); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAChangeMode(0x%08X, %s, 0x%X) failed: %s", deviceData->clientHandle, file->path, translatedMode, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_fstat.cpp b/source/devoptab/devoptab_fsa_fstat.cpp new file mode 100644 index 0000000..77c0e7d --- /dev/null +++ b/source/devoptab/devoptab_fsa_fstat.cpp @@ -0,0 +1,41 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_fstat(struct _reent *r, + void *fd, + struct stat *st) { + FSError status; + FSAStat fsStat; + __fsa_file_t *file; + + if (!fd || !st) { + r->_errno = EINVAL; + return -1; + } + + file = (__fsa_file_t *) fd; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + status = FSAGetStatFile(deviceData->clientHandle, file->fd, &fsStat); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSARewindDir(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, &fsStat, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + memset(st, 0, sizeof(struct stat)); + st->st_size = fsStat.size; + st->st_uid = fsStat.owner; + st->st_gid = fsStat.group; + st->st_nlink = 1; + st->st_mode = __fsa_translate_stat_mode(&fsStat); + st->st_atime = __fsa_translate_time(fsStat.modified); + st->st_ctime = __fsa_translate_time(fsStat.created); + st->st_mtime = __fsa_translate_time(fsStat.modified); + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_fsync.cpp b/source/devoptab/devoptab_fsa_fsync.cpp new file mode 100644 index 0000000..43c9ec2 --- /dev/null +++ b/source/devoptab/devoptab_fsa_fsync.cpp @@ -0,0 +1,29 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_fsync(struct _reent *r, + void *fd) { + FSError status; + __fsa_file_t *file; + + if (!fd) { + r->_errno = EINVAL; + return -1; + } + + file = (__fsa_file_t *) fd; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + status = FSAFlushFile(deviceData->clientHandle, file->fd); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAFlushFile(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_link.cpp b/source/devoptab/devoptab_fsa_link.cpp new file mode 100644 index 0000000..8680fed --- /dev/null +++ b/source/devoptab/devoptab_fsa_link.cpp @@ -0,0 +1,8 @@ +#include "devoptab_fs.h" + +int __fsa_link(struct _reent *r, + const char *existing, + const char *newLink) { + r->_errno = ENOSYS; + return -1; +} diff --git a/source/devoptab/devoptab_fsa_mkdir.cpp b/source/devoptab/devoptab_fsa_mkdir.cpp new file mode 100644 index 0000000..6108f15 --- /dev/null +++ b/source/devoptab/devoptab_fsa_mkdir.cpp @@ -0,0 +1,38 @@ +#include "devoptab_fs.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); + if (!fixedPath) { + r->_errno = ENOMEM; + return -1; + } + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + FSMode translatedMode = __fsa_translate_permission_mode(mode); + + 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)); + free(fixedPath); + r->_errno = __fsa_translate_error(status); + return -1; + } + free(fixedPath); + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_open.cpp b/source/devoptab/devoptab_fsa_open.cpp new file mode 100644 index 0000000..6a49b81 --- /dev/null +++ b/source/devoptab/devoptab_fsa_open.cpp @@ -0,0 +1,95 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +// Use this extended hidden flag value to provide FS_OPEN_FLAG_ENCRYPTED in underlying FSOpenFileEx() call +#define O_ENCRYPTED 0x4000000 + +int __fsa_open(struct _reent *r, + void *fileStruct, + const char *path, + int flags, + int mode) { + FSAFileHandle fd; + FSError status; + const char *fsMode; + __fsa_file_t *file; + + if (!fileStruct || !path) { + r->_errno = EINVAL; + return -1; + } + + // Map flags to open modes + if ((flags & O_ACCMODE) == O_RDONLY) { + fsMode = "r"; + if (flags & O_APPEND) { + r->_errno = EINVAL; + return -1; + } + } else if ((flags & O_ACCMODE) == O_WRONLY) { + if (flags & O_APPEND) { + fsMode = "a"; + } else if (flags & O_TRUNC) { + fsMode = "w"; + } else { + r->_errno = EINVAL; + return -1; + } + } else if ((flags & O_ACCMODE) == O_RDWR) { + if (flags & O_APPEND) { + fsMode = "a+"; + } else if (flags & O_TRUNC) { + fsMode = "w+"; + } else { + fsMode = "r+"; + } + } else { + r->_errno = EINVAL; + return -1; + } + + char *fixedPath = __fsa_fixpath(r, path); + if (!fixedPath) { + r->_errno = ENOMEM; + return -1; + } + + // Open the file + FSOpenFileFlags openFlags = (mode & O_ENCRYPTED) ? FS_OPEN_FLAG_ENCRYPTED : FS_OPEN_FLAG_NONE; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + FSMode translatedMode = __fsa_translate_permission_mode(mode); + + uint32_t preAllocSize = 0; + + status = FSAOpenFileEx(deviceData->clientHandle, fixedPath, fsMode, translatedMode, openFlags, preAllocSize, &fd); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAOpenFileEx(0x%08X, %s, %s, 0x%X, 0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, fsMode, translatedMode, openFlags, preAllocSize, &fd, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + free(fixedPath); + return -1; + } + + file = (__fsa_file_t *) fileStruct; + file->fd = fd; + file->flags = (flags & (O_ACCMODE | O_APPEND | O_SYNC)); + strncpy(file->path, fixedPath, FS_MAX_PATH); + free(fixedPath); + + if (flags & O_APPEND) { + status = FSAGetPosFile(deviceData->clientHandle, fd, &file->offset); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAGetPosFile(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, fd, &file->offset, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + if (FSACloseFile(deviceData->clientHandle, fd) < 0) { + DEBUG_FUNCTION_LINE_ERR("FSACloseFile(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, fd, FSAGetStatusStr(status)); + } + return -1; + } + } + return 0; +} \ No newline at end of file diff --git a/source/devoptab/devoptab_fsa_read.cpp b/source/devoptab/devoptab_fsa_read.cpp new file mode 100644 index 0000000..0fb4ff5 --- /dev/null +++ b/source/devoptab/devoptab_fsa_read.cpp @@ -0,0 +1,77 @@ +#include "devoptab_fs.h" +#include "logger.h" + +#include +#include + +ssize_t __fsa_read(struct _reent *r, void *fd, char *ptr, size_t len) { + if (!fd || !ptr) { + r->_errno = EINVAL; + return -1; + } + + // Check that the file was opened with read access + __fsa_file_t *file = (__fsa_file_t *) fd; + if ((file->flags & O_ACCMODE) == O_WRONLY) { + r->_errno = EBADF; + return -1; + } + + // cache-aligned, cache-line-sized + __attribute__((aligned(0x40))) uint8_t alignedBuffer[0x40]; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + size_t bytesRead = 0; + while (bytesRead < len) { + // only use input buffer if cache-aligned and read size is a multiple of cache line size + // otherwise read into alignedBuffer + uint8_t *tmp = (uint8_t *) ptr; + size_t size = len - bytesRead; + + 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; + } + + // Limit each request to 1 MiB + if (size > 0x100000) { + size = 0x100000; + } + + FSError status = FSAReadFile(deviceData->clientHandle, tmp, 1, size, file->fd, 0); + + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAReadFile(0x%08X, 0x%08X, 1, 0x%08X, 0x%08X, 0) failed: %s", deviceData->clientHandle, tmp, size, file->fd, FSAGetStatusStr(status)); + if (bytesRead != 0) { + return bytesRead; // error after partial read + } + + r->_errno = __fsa_translate_error(status); + return -1; + } + + if (tmp == alignedBuffer) { + memcpy(ptr, alignedBuffer, status); + } + + bytesRead += status; + ptr += status; + + + if ((size_t) status != size) { + return bytesRead; // partial read + } + } + + return bytesRead; +} \ No newline at end of file diff --git a/source/devoptab/devoptab_fsa_rename.cpp b/source/devoptab/devoptab_fsa_rename.cpp new file mode 100644 index 0000000..4b295e9 --- /dev/null +++ b/source/devoptab/devoptab_fsa_rename.cpp @@ -0,0 +1,45 @@ +#include "devoptab_fs.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); + if (!fixedOldPath) { + r->_errno = ENOMEM; + return -1; + } + + fixedNewPath = __fsa_fixpath(r, newName); + if (!fixedNewPath) { + free(fixedOldPath); + r->_errno = ENOMEM; + return -1; + } + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + 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)); + free(fixedOldPath); + free(fixedNewPath); + r->_errno = __fsa_translate_error(status); + return -1; + } + free(fixedOldPath); + free(fixedNewPath); + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_rmdir.cpp b/source/devoptab/devoptab_fsa_rmdir.cpp new file mode 100644 index 0000000..7cd180d --- /dev/null +++ b/source/devoptab/devoptab_fsa_rmdir.cpp @@ -0,0 +1,71 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_rmdir(struct _reent *r, + const char *name) { + FSError status; + + if (!name) { + r->_errno = EINVAL; + return -1; + } + + char *fixedPath = __fsa_fixpath(r, name); + if (!fixedPath) { + r->_errno = ENOMEM; + return -1; + } + + // Check if directory still has files in which case return error + FSDirectoryHandle dirHandle = 0; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + status = FSAOpenDir(deviceData->clientHandle, fixedPath, &dirHandle); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAOpenDir(0x%08X, %s, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, &dirHandle, FSAGetStatusStr(status)); + free(fixedPath); + r->_errno = __fsa_translate_error(status); + return -1; + } + + auto *dir_entry = (FSADirectoryEntry *) malloc(sizeof(FSADirectoryEntry)); + if (dir_entry == nullptr) { + DEBUG_FUNCTION_LINE_ERR("malloc(sizeof(FSADirectoryEntry)) failed"); + free(fixedPath); + FSACloseDir(deviceData->clientHandle, dirHandle); + r->_errno = ENOMEM; + return -1; + } + + status = FSAReadDir(deviceData->clientHandle, dirHandle, dir_entry); + FSACloseDir(deviceData->clientHandle, dirHandle); + if (status == FS_ERROR_OK) { + free(fixedPath); + free(dir_entry); + r->_errno = ENOTEMPTY; + return -1; + } else if (status != FS_ERROR_END_OF_DIR) { + DEBUG_FUNCTION_LINE_ERR("FSAReadDir(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, dirHandle, dir_entry, FSAGetStatusStr(status)); + free(fixedPath); + free(dir_entry); + r->_errno = __fsa_translate_error(status); + return -1; + } + free(dir_entry); + + // Remove folder since folder is empty + 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 = __fsa_translate_error(status); + return -1; + } + free(fixedPath); + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_seek.cpp b/source/devoptab/devoptab_fsa_seek.cpp new file mode 100644 index 0000000..182d891 --- /dev/null +++ b/source/devoptab/devoptab_fsa_seek.cpp @@ -0,0 +1,73 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +off_t __fsa_seek(struct _reent *r, + void *fd, + off_t pos, + int whence) { + FSError status; + FSAStat fsStat; + uint64_t offset; + __fsa_file_t *file; + + if (!fd) { + r->_errno = EINVAL; + return -1; + } + + file = (__fsa_file_t *) fd; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + status = FSAGetStatFile(deviceData->clientHandle, file->fd, &fsStat); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAGetStatFile(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, &fsStat, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + // Find the offset to see from + switch (whence) { + // Set absolute position; start offset is 0 + case SEEK_SET: + offset = 0; + break; + + // Set position relative to the current position + case SEEK_CUR: + offset = file->offset; + break; + + // Set position relative to the end of the file + case SEEK_END: + offset = fsStat.size; + break; + + // An invalid option was provided + default: + r->_errno = EINVAL; + return -1; + } + + // TODO: A better check that prevents overflow. + if (pos < 0 && (off_t) offset < -pos) { + // Don't allow seek to before the beginning of the file + r->_errno = EINVAL; + return -1; + } + + uint32_t old_pos = file->offset; + file->offset = offset + pos; + + status = FSASetPosFile(deviceData->clientHandle, file->fd, file->offset); + if (status < 0) { + file->offset = old_pos; + DEBUG_FUNCTION_LINE_ERR("FSASetPosFile(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, file->offset, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + return file->offset; +} diff --git a/source/devoptab/devoptab_fsa_stat.cpp b/source/devoptab/devoptab_fsa_stat.cpp new file mode 100644 index 0000000..75cc7dc --- /dev/null +++ b/source/devoptab/devoptab_fsa_stat.cpp @@ -0,0 +1,50 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_stat(struct _reent *r, + const char *path, + struct stat *st) { + FSError status; + FSAStat fsStat; + + if (!path || !st) { + r->_errno = EINVAL; + return -1; + } + + char *fixedPath = __fsa_fixpath(r, path); + if (!fixedPath) { + r->_errno = ENOMEM; + return -1; + } + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + status = FSAGetStat(deviceData->clientHandle, fixedPath, &fsStat); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAGetStat(0x%08X, %s, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, &fsStat, FSAGetStatusStr(status)); + free(fixedPath); + r->_errno = __fsa_translate_error(status); + return -1; + } + free(fixedPath); + + memset(st, 0, sizeof(struct stat)); + st->st_dev = (dev_t) deviceData->clientHandle; + st->st_ino = fsStat.entryId; + st->st_mode = __fsa_translate_stat_mode(&fsStat); + st->st_nlink = 1; + st->st_uid = fsStat.owner; + st->st_gid = fsStat.group; + st->st_rdev = st->st_dev; + st->st_size = fsStat.size; + st->st_blksize = deviceData->deviceSectorSize; + st->st_blocks = (st->st_size + st->st_blksize - 1) / st->st_blksize; + st->st_atime = __fsa_translate_time(fsStat.modified); + st->st_ctime = __fsa_translate_time(fsStat.created); + st->st_mtime = __fsa_translate_time(fsStat.modified); + return 0; +} \ No newline at end of file diff --git a/source/devoptab/devoptab_fsa_statvfs.cpp b/source/devoptab/devoptab_fsa_statvfs.cpp new file mode 100644 index 0000000..5fee11b --- /dev/null +++ b/source/devoptab/devoptab_fsa_statvfs.cpp @@ -0,0 +1,50 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_statvfs(struct _reent *r, + const char *path, + struct statvfs *buf) { + FSError status; + uint64_t freeSpace; + memset(buf, 0, sizeof(struct statvfs)); + + char *fixedPath = __fsa_fixpath(r, path); + if (!fixedPath) { + r->_errno = ENOMEM; + return -1; + } + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + status = FSAGetFreeSpaceSize(deviceData->clientHandle, fixedPath, &freeSpace); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAGetFreeSpaceSize(0x%08X, %s, 0x%08X) failed: %s", deviceData->clientHandle, fixedPath, &freeSpace, FSAGetStatusStr(status)); + free(fixedPath); + r->_errno = __fsa_translate_error(status); + return -1; + } + free(fixedPath); + + // File system block size + buf->f_bsize = deviceData->deviceSectorSize; + // Fundamental file system block size + buf->f_frsize = deviceData->deviceSectorSize; + // Total number of blocks on file system in units of f_frsize + buf->f_blocks = deviceData->deviceSizeInSectors; + // Free blocks available for all and for non-privileged processes + buf->f_bfree = buf->f_bavail = (uint32_t) (freeSpace / buf->f_frsize); + // Number of inodes at this point in time + buf->f_files = 0xFFFFFFFF; + // Free inodes available for all and for non-privileged processes + buf->f_ffree = 0xFFFFFFFF; + // File system id + buf->f_fsid = (unsigned long) deviceData->clientHandle; + // Bit mask of f_flag values. + buf->f_flag = 0; + // Maximum length of filenames + 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 new file mode 100644 index 0000000..ac030de --- /dev/null +++ b/source/devoptab/devoptab_fsa_truncate.cpp @@ -0,0 +1,38 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +int __fsa_ftruncate(struct _reent *r, + void *fd, + off_t len) { + FSError status; + __fsa_file_t *file; + + // Make sure length is non-negative + if (!fd || len < 0) { + r->_errno = EINVAL; + return -1; + } + + // Set the new file size + file = (__fsa_file_t *) fd; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + status = FSASetPosFile(deviceData->clientHandle, file->fd, len); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSASetPosFile(0x%08X, 0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, len, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + status = FSATruncateFile(deviceData->clientHandle, file->fd); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSATruncateFile(0x%08X, 0x%08X) failed: %s", deviceData->clientHandle, file->fd, FSAGetStatusStr(status)); + r->_errno = __fsa_translate_error(status); + return -1; + } + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_unlink.cpp b/source/devoptab/devoptab_fsa_unlink.cpp new file mode 100644 index 0000000..60c517b --- /dev/null +++ b/source/devoptab/devoptab_fsa_unlink.cpp @@ -0,0 +1,35 @@ +#include "devoptab_fs.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); + if (!fixedPath) { + r->_errno = ENOMEM; + return -1; + } + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + 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 = __fsa_translate_error(status); + return -1; + } + + free(fixedPath); + + return 0; +} diff --git a/source/devoptab/devoptab_fsa_utils.cpp b/source/devoptab/devoptab_fsa_utils.cpp new file mode 100644 index 0000000..4afe577 --- /dev/null +++ b/source/devoptab/devoptab_fsa_utils.cpp @@ -0,0 +1,169 @@ +#include "devoptab_fs.h" + +char * +__fsa_fixpath(struct _reent *r, + const char *path) { + char *p; + char *fixedPath; + + if (!path) { + r->_errno = EINVAL; + return nullptr; + } + + p = strchr(path, ':') + 1; + if (!strchr(path, ':')) { + p = (char *) path; + } + + if (strlen(p) > FS_MAX_PATH) { + r->_errno = ENAMETOOLONG; + return nullptr; + } + + fixedPath = (char *) memalign(0x40, FS_MAX_PATH + 1); + if (!fixedPath) { + r->_errno = ENOMEM; + return nullptr; + } + + if (p[0] == '/') { + auto *deviceData = (FSADeviceData *) r->deviceData; + strcpy(fixedPath, deviceData->mount_path); + strcat(fixedPath, p); + } else { + strcpy(fixedPath, p); + } + + return fixedPath; +} + +mode_t __fsa_translate_stat_mode(FSStat *fileStat) { + mode_t retMode = 0; + + if ((fileStat->flags & FS_STAT_LINK) == FS_STAT_LINK) { + retMode |= S_IFLNK; + } else if ((fileStat->flags & FS_STAT_DIRECTORY) == FS_STAT_DIRECTORY) { + retMode |= S_IFDIR; + } else if ((fileStat->flags & FS_STAT_FILE) == FS_STAT_FILE) { + retMode |= S_IFREG; + } else if (fileStat->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)); + + return retMode | permissionMode; +} + +// The Wii U FSTime epoch is at 1980, so we must map it to 1970 for gettime +#define WIIU_FSTIME_EPOCH_YEAR (1980) + +#define EPOCH_YEAR (1970) +#define EPOCH_YEARS_SINCE_LEAP 2 +#define EPOCH_YEARS_SINCE_CENTURY 70 +#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370 + +#define EPOCH_DIFF_YEARS(year) (year - EPOCH_YEAR) +#define EPOCH_DIFF_DAYS(year) \ + ((EPOCH_DIFF_YEARS(year) * 365) + \ + (EPOCH_DIFF_YEARS(year) - 1 + EPOCH_YEARS_SINCE_LEAP) / 4 - \ + (EPOCH_DIFF_YEARS(year) - 1 + EPOCH_YEARS_SINCE_CENTURY) / 100 + \ + (EPOCH_DIFF_YEARS(year) - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) / 400) +#define EPOCH_DIFF_SECS(year) (60ull * 60ull * 24ull * (uint64_t) EPOCH_DIFF_DAYS(year)) + +time_t __fsa_translate_time(FSTime timeValue) { + return (timeValue / 1000000) + EPOCH_DIFF_SECS(WIIU_FSTIME_EPOCH_YEAR); +} + + +FSMode +__fsa_translate_permission_mode(int 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: + case FS_ERROR_END_OF_FILE: + return ENOENT; + case FS_ERROR_ALREADY_EXISTS: + return EEXIST; + case FS_ERROR_MEDIA_ERROR: + return EIO; + case FS_ERROR_NOT_FOUND: + return ENOENT; + case FS_ERROR_PERMISSION_ERROR: + return EPERM; + case FS_ERROR_STORAGE_FULL: + return ENOSPC; + case FS_ERROR_BUSY: + return EBUSY; + case FS_ERROR_CANCELLED: + return ECANCELED; + case FS_ERROR_FILE_TOO_BIG: + return EFBIG; + case FS_ERROR_INVALID_PATH: + return ENAMETOOLONG; + case FS_ERROR_NOT_DIR: + return ENOTDIR; + case FS_ERROR_NOT_FILE: + return EISDIR; + case FS_ERROR_OUT_OF_RANGE: + return ESPIPE; + 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; + case FS_ERROR_ALREADY_OPEN: + break; + case FS_ERROR_NOT_EMPTY: + break; + case FS_ERROR_ACCESS_ERROR: + break; + case FS_ERROR_DATA_CORRUPTED: + break; + case FS_ERROR_JOURNAL_FULL: + break; + case FS_ERROR_UNAVAILABLE_COMMAND: + break; + case FS_ERROR_INVALID_PARAM: + break; + 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; + case FS_ERROR_OUT_OF_RESOURCES: + break; + case FS_ERROR_MEDIA_NOT_READY: + break; + case FS_ERROR_INVALID_MEDIA: + break; + default: + break; + } + return (int) EIO; +} diff --git a/source/devoptab/devoptab_fsa_utimes.cpp b/source/devoptab/devoptab_fsa_utimes.cpp new file mode 100644 index 0000000..ae60d1d --- /dev/null +++ b/source/devoptab/devoptab_fsa_utimes.cpp @@ -0,0 +1,9 @@ +#include "devoptab_fs.h" + +int __fsa_utimes(struct _reent *r, + const char *filename, + const struct timeval times[2]) { + // TODO: FSChangeMode and FSStatFile? + r->_errno = ENOSYS; + return -1; +} diff --git a/source/devoptab/devoptab_fsa_write.cpp b/source/devoptab/devoptab_fsa_write.cpp new file mode 100644 index 0000000..8867f67 --- /dev/null +++ b/source/devoptab/devoptab_fsa_write.cpp @@ -0,0 +1,73 @@ +#include "devoptab_fs.h" +#include "logger.h" +#include + +ssize_t __fsa_write(struct _reent *r, void *fd, const char *ptr, size_t len) { + if (!fd || !ptr) { + r->_errno = EINVAL; + return -1; + } + + // Check that the file was opened with write access + __fsa_file_t *file = (__fsa_file_t *) fd; + if ((file->flags & O_ACCMODE) == O_RDONLY) { + r->_errno = EBADF; + return -1; + } + + // cache-aligned, cache-line-sized + __attribute__((aligned(0x40))) uint8_t alignedBuffer[0x40]; + + auto *deviceData = (FSADeviceData *) r->deviceData; + + std::lock_guard lock(deviceData->mutex); + + size_t bytesWritten = 0; + while (bytesWritten < len) { + // only use input buffer if cache-aligned and write size is a multiple of cache line size + // otherwise write from alignedBuffer + uint8_t *tmp = (uint8_t *) ptr; + size_t size = len - bytesWritten; + + 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; + } + + // Limit each request to 256 KiB + if (size > 0x40000) { + size = 0x40000; + } + + if (tmp == alignedBuffer) { + memcpy(tmp, ptr, size); + } + + FSError status = FSAWriteFile(deviceData->clientHandle, tmp, 1, size, file->fd, 0); + if (status < 0) { + DEBUG_FUNCTION_LINE_ERR("FSAWriteFile(0x%08X, 0x%08X, 1, 0x%08X, 0x%08X, 0) failed: %s", deviceData->clientHandle, tmp, size, file->fd, FSAGetStatusStr(status)); + if (bytesWritten != 0) { + return bytesWritten; // error after partial write + } + + r->_errno = __fsa_translate_error(status); + return -1; + } + + bytesWritten += status; + ptr += status; + + if ((size_t) status != size) { + return bytesWritten; // partial write + } + } + + return bytesWritten; +} diff --git a/source/fsa.cpp b/source/fsa.cpp index fe14ab3..e273f40 100644 --- a/source/fsa.cpp +++ b/source/fsa.cpp @@ -1,4 +1,5 @@ #include "mocha/fsa.h" +#include "logger.h" #include "utils.h" #include #include @@ -6,14 +7,14 @@ #include #include -FSError FSAEx_RawOpen(FSClient *client, char *device_path, int32_t *outHandle) { +FSError FSAEx_RawOpen(FSClient *client, const char *device_path, int32_t *outHandle) { if (!client) { return FS_ERROR_INVALID_CLIENTHANDLE; } return FSAEx_RawOpenEx(FSGetClientBody(client)->clientHandle, device_path, outHandle); } -FSError FSAEx_RawOpenEx(int clientHandle, char *device_path, int32_t *outHandle) { +FSError FSAEx_RawOpenEx(int clientHandle, const char *device_path, int32_t *outHandle) { if (!outHandle) { return FS_ERROR_INVALID_BUFFER; } @@ -103,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) { - OSReport("## ERROR: FSAEx_RawReadEx buffer not aligned (%08X).\n", data); + DEBUG_FUNCTION_LINE_ERR("FSAEx_RawReadEx buffer not aligned (%08X).\n", data); return FS_ERROR_INVALID_ALIGNMENT; } - OSReport("## WARNING: FSAEx_RawReadEx buffer not aligned (%08X). Align to 0x40 for best performance\n", data); + DEBUG_FUNCTION_LINE_WARN("FSAEx_RawReadEx buffer not aligned (%08X). Align to 0x40 for best performance\n", data); tmp = alignedBuffer; } diff --git a/source/utils.cpp b/source/utils.cpp index 11f0828..4b6df26 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -33,6 +33,10 @@ const char *Mocha_GetStatusStr(MochaUtilsStatus status) { return "MOCHA_RESULT_MAX_CLIENT"; case MOCHA_RESULT_OUT_OF_MEMORY: return "MOCHA_RESULT_OUT_OF_MEMORY"; + case MOCHA_RESULT_ALREADY_EXISTS: + return "MOCHA_RESULT_ALREADY_EXISTS"; + case MOCHA_RESULT_ADD_DEVOPTAB_FAILED: + return "MOCHA_RESULT_ADD_DEVOPTAB_FAILED"; case MOCHA_RESULT_NOT_FOUND: return "MOCHA_RESULT_NOT_FOUND"; case MOCHA_RESULT_UNSUPPORTED_API_VERSION: