Add devoptab support to replace the libioushax devoptab

This commit is contained in:
Maschell 2022-07-22 14:46:29 +02:00
parent 8d650220b2
commit 11f76790e4
33 changed files with 1556 additions and 11 deletions

View File

@ -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

View File

@ -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.

View File

@ -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"

View File

@ -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 <br>
* MOCHA_RESULT_MAX_CLIENT: The maximum number of FSAClients have been registered.<br>
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
* MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.<br>
* 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 <br>
* MOCHA_RESULT_INVALID_ARGUMENT: <br>
* 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

View File

@ -0,0 +1,20 @@
#pragma once
#include <coreinit/fastmutex.h>
class FastLockWrapper {
public:
FastLockWrapper() {
OSFastMutex_Init(&mutex, "generic lock");
}
void lock() {
OSFastMutex_Lock(&mutex);
}
void unlock() {
OSFastMutex_Unlock(&mutex);
}
private:
OSFastMutex mutex{};
};

View File

@ -0,0 +1,202 @@
#include "devoptab_fs.h"
#include "logger.h"
#include "mocha/mocha.h"
#include <coreinit/cache.h>
#include <coreinit/filesystem_fsa.h>
#include <mutex>
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<std::mutex> 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<std::mutex> 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<FSError>(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;
}

View File

@ -0,0 +1,109 @@
#pragma once
#include <coreinit/filesystem_fsa.h>
#include "FastLockWrapper.h"
#include <cerrno>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <malloc.h>
#include <sys/dirent.h>
#include <sys/iosupport.h>
#include <sys/param.h>
#include <unistd.h>
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

View File

@ -0,0 +1,32 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,38 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
#include <sys/stat.h>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,29 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,29 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,52 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,41 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,28 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,27 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,41 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,29 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -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;
}

View File

@ -0,0 +1,38 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,95 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
// 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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,77 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
#include <sys/param.h>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,45 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,71 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,73 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,50 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,50 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,38 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -0,0 +1,35 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,73 @@
#include "devoptab_fs.h"
#include "logger.h"
#include <mutex>
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<FastLockWrapper> 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;
}

View File

@ -1,4 +1,5 @@
#include "mocha/fsa.h"
#include "logger.h"
#include "utils.h"
#include <coreinit/debug.h>
#include <coreinit/filesystem.h>
@ -6,14 +7,14 @@
#include <cstring>
#include <malloc.h>
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;
}

View File

@ -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: