mirror of
https://github.com/wiiu-env/libmocha.git
synced 2024-11-16 00:35:04 +01:00
Add libiosuhax features into libmocha
This commit is contained in:
parent
f8703950aa
commit
41226ab3ca
@ -1,6 +1,6 @@
|
||||
FROM wiiuenv/devkitppc:20220605
|
||||
|
||||
RUN git clone --depth 1 --single-branch -b filesystemstructs https://github.com/Maschell/wut && cd wut && git reset --hard a060c981e23cdd7e622624692df2d789693432ce && make install && cd .. && rm -rf wut
|
||||
RUN git clone --depth 1 --single-branch -b filesystemstructs https://github.com/Maschell/wut && cd wut && git reset --hard f73f75225b62fd546fedc7b366cee1d87b4b72a5 && make install && cd .. && rm -rf wut
|
||||
|
||||
WORKDIR tmp_build
|
||||
COPY . .
|
||||
|
@ -1,5 +1,5 @@
|
||||
FROM wiiuenv/devkitppc:20220605
|
||||
|
||||
RUN git clone --depth 1 --single-branch -b filesystemstructs https://github.com/Maschell/wut && cd wut && git reset --hard a060c981e23cdd7e622624692df2d789693432ce && make install && cd .. && rm -rf wut
|
||||
RUN git clone --depth 1 --single-branch -b filesystemstructs https://github.com/Maschell/wut && cd wut && git reset --hard f73f75225b62fd546fedc7b366cee1d87b4b72a5 && make install && cd .. && rm -rf wut
|
||||
|
||||
WORKDIR project
|
5
Makefile
5
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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -1,66 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <coreinit/filesystem.h>
|
||||
#include <stdint.h>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum FSAMountFlags {
|
||||
FSA_MOUNT_FLAG_LOCAL_MOUNT = 0,
|
||||
FSA_MOUNT_FLAG_BIND_MOUNT = 1,
|
||||
FSA_MOUNT_FLAG_GLOBAL_MOUNT = 2,
|
||||
} FSAMountFlags;
|
||||
|
||||
typedef enum FSAUnmountFlags {
|
||||
FSA_UNMOUNT_FLAG_BIND_MOUNT = 0x80000000,
|
||||
} FSAUnmountFlags;
|
||||
|
||||
/**
|
||||
* Mounts a source to a given path for a given FSClient (or globally if FSA_MOUNT_FLAG_GLOBAL_MOUNT is set)
|
||||
*
|
||||
* @param client valid FSClient pointer with unlocked permissions
|
||||
* @param source Mount source e.g. /dev/sdcard01
|
||||
* @param target Must not start with /vol/storage_ if FSA_MOUNT_FLAG_GLOBAL_MOUNT is **not** set. Requires to start with "/vol/storage_" if FSA_MOUNT_FLAG_GLOBAL_MOUNT or FSA_UNMOUNT_FLAG_BIND_MOUNT is set
|
||||
* @param flags Determines the mount type.
|
||||
* @param arg_buf unknown
|
||||
* @param arg_len unknown
|
||||
* @return
|
||||
*/
|
||||
FSError FSAEx_Mount(FSClient *client, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len);
|
||||
|
||||
/**
|
||||
* Mounts a source to a given path for a given FSClient (or globally if FSA_MOUNT_FLAG_GLOBAL_MOUNT is set)
|
||||
*
|
||||
* @param clientHandle valid /dev/fsa handle with unlocked permissions
|
||||
* @param source Mount source e.g. /dev/sdcard01
|
||||
* @param target Must not start with /vol/storage_ if FSA_MOUNT_FLAG_GLOBAL_MOUNT is **not** set. Requires to start with "/vol/storage_" if FSA_MOUNT_FLAG_GLOBAL_MOUNT is set
|
||||
* @param flags Determines the mount type.
|
||||
* @param arg_buf unknown
|
||||
* @param arg_len unknown
|
||||
* @return
|
||||
*/
|
||||
FSError FSAEx_MountEx(int clientHandle, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len);
|
||||
|
||||
/**
|
||||
* Unmounts a given path
|
||||
* @param client valid FSClient pointer with unlocked permissions
|
||||
* @param mountedTarget path where the mount is mounted to.
|
||||
* @param flags FSA_UNMOUNT_FLAG_BIND_MOUNT is expected for a BindMount
|
||||
* @return
|
||||
*/
|
||||
FSError FSAEx_Unmount(FSClient *client, const char *mountedTarget, FSAUnmountFlags flags);
|
||||
|
||||
/**
|
||||
* Unmounts a given path
|
||||
* @param clientHandle valid /dev/fsa handle with unlocked permissions
|
||||
* @param mountedTarget path where the mount is mounted to.
|
||||
* @param flags FSA_UNMOUNT_FLAG_BIND_MOUNT is expected for a BindMount
|
||||
* @return
|
||||
*/
|
||||
FSError FSAEx_UnmountEx(int clientHandle, const char *mountedTarget, FSAUnmountFlags flags);
|
||||
|
||||
/**
|
||||
* Opens a device for raw read/write
|
||||
* @param client valid FSClient pointer with unlocked permissions
|
||||
@ -77,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, char *device_path, int32_t *outHandle);
|
||||
|
||||
/**
|
||||
* Closes a devices that was previously opened via FSAEx_RawOpen
|
||||
@ -93,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.
|
||||
@ -119,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);
|
||||
|
||||
|
||||
/**
|
||||
@ -146,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"
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <coreinit/filesystem.h>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <mocha/commands.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@ -12,6 +13,10 @@ enum MochaUtilsStatus {
|
||||
MOCHA_RESULT_SUCCESS = 0,
|
||||
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,
|
||||
MOCHA_RESULT_LIB_UNINITIALIZED = -0x20,
|
||||
@ -25,7 +30,11 @@ enum MochaUtilsStatus {
|
||||
*/
|
||||
MochaUtilsStatus Mocha_InitLibrary();
|
||||
|
||||
MochaUtilsStatus Mocha_DeInitLibrary();
|
||||
/**
|
||||
* Deinitializes the mocha lib
|
||||
* @return
|
||||
*/
|
||||
MochaUtilsStatus Mocha_DeinitLibrary();
|
||||
|
||||
/**
|
||||
* Retrieves the API Version of the running mocha.
|
||||
@ -38,42 +47,129 @@ MochaUtilsStatus Mocha_DeInitLibrary();
|
||||
*/
|
||||
MochaUtilsStatus Mocha_CheckAPIVersion(uint32_t *outVersion);
|
||||
|
||||
/***
|
||||
/**
|
||||
* Copies data within IOSU with kernel permission.
|
||||
* @param dst - Destination address
|
||||
* @param src - Source address
|
||||
* @param size - Bytes to copy.
|
||||
* @return MOCHA_RESULT_SUCCESS: The data has been copied successfully<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
|
||||
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
|
||||
*/
|
||||
MochaUtilsStatus Mocha_IOSUKernelMemcpy(uint32_t dst, uint32_t src, uint32_t size);
|
||||
|
||||
/**
|
||||
* Writes data to a given address with kernel permission.
|
||||
* @param address - Address where the data will be written to.
|
||||
* @param buffer - Pointer to the data which should be written.
|
||||
* @param size - Bytes to write.
|
||||
* @return MOCHA_RESULT_SUCCESS: The data has been written successfully<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
|
||||
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
|
||||
*/
|
||||
MochaUtilsStatus Mocha_IOSUKernelWrite(uint32_t address, const uint8_t *buffer, uint32_t size);
|
||||
|
||||
/**
|
||||
* Reads data from a given address with kernel permission.
|
||||
* @param address - Address where the data will be read from.
|
||||
* @param buffer - Pointer to the buffer where the read will be stored
|
||||
* @param size - Bytes to read.
|
||||
* @return MOCHA_RESULT_SUCCESS: The data has been read successfully<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
|
||||
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
|
||||
*/
|
||||
MochaUtilsStatus Mocha_IOSUKernelRead(uint32_t address, uint8_t *out_buffer, uint32_t size);
|
||||
|
||||
/**
|
||||
* Write 4 bytes with IOSU kernel permission
|
||||
* @param address Address where the value will be written.
|
||||
* @param value Value that will be written to address.
|
||||
* @return MOCHA_RESULT_SUCCESS: The data has been written successfully<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
|
||||
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
|
||||
*/
|
||||
|
||||
MochaUtilsStatus Mocha_IOSUKernelWrite32(uint32_t address, uint32_t value);
|
||||
|
||||
/**
|
||||
* Reads 4 bytes with IOSU kernel permission
|
||||
* @param address Address from which the data will be read.
|
||||
* @param out_buffer Pointer where the result will be stored
|
||||
* @return MOCHA_RESULT_SUCCESS: The data has been read successfully<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: invalid version pointer<br>
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR: Unknown error<br>
|
||||
* MOCHA_RESULT_UNSUPPORTED_API_VERSION: Failed to get the API version caused by an outdated mocha version.
|
||||
*/
|
||||
MochaUtilsStatus Mocha_IOSUKernelRead32(uint32_t address, uint32_t *out_buffer);
|
||||
|
||||
typedef struct WiiUConsoleOTP {
|
||||
uint8_t data[0x400];
|
||||
} WiiUConsoleOTP;
|
||||
|
||||
/**
|
||||
* Read the consoles OTP into the given buffer.
|
||||
*
|
||||
* @param out_buffer Buffer where the result will be stored.
|
||||
* @return MOCHA_RESULT_SUCCESS: The OTP has been read into the buffer<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: invalid environmentPathBuffer pointer or bufferLen \< 0x100<br>
|
||||
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to retrieve the environment path.
|
||||
*/
|
||||
MochaUtilsStatus Mocha_ReadOTP(WiiUConsoleOTP *out_buffer);
|
||||
|
||||
/**
|
||||
* Calls an iosu SVC.
|
||||
* @param svc_id
|
||||
* @param args array of argument with the length arg_cnt
|
||||
* @param arg_cnt number of arguments
|
||||
* @param outResult
|
||||
* @return MOCHA_RESULT_SUCCESS: The SVC has been called successfully, the result has been stored in outResult.<br>
|
||||
* MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.<br>
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR
|
||||
*/
|
||||
int Mocha_IOSUCallSVC(uint32_t svc_id, uint32_t *args, uint32_t arg_cnt, int32_t *outResult);
|
||||
|
||||
/**
|
||||
* Returns the path of the currently loaded environment
|
||||
* @param environmentPathBuffer: buffer where the result will be stored
|
||||
* @param bufferLen: length of the buffer. Required to be >= 0x100
|
||||
* @return MOCHA_RESULT_SUCCESS: The environment path has been stored in environmentPathBuffer<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: invalid environmentPathBuffer pointer or bufferLen \< 0x100<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.
|
||||
* @return MOCHA_RESULT_SUCCESS: The environment path has been stored in environmentPathBuffer<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: invalid environmentPathBuffer pointer or bufferLen \< 0x100<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_GetEnvironmentPath(char *environmentPathBuffer, uint32_t bufferLen);
|
||||
|
||||
/***
|
||||
/**
|
||||
* Signals Mocha to not redirect the men.rpx to root.rpx anymore
|
||||
* @return MOCHA_RESULT_SUCCESS
|
||||
* 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.
|
||||
* @return MOCHA_RESULT_SUCCESS: <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_RPXHookCompleted();
|
||||
|
||||
/***
|
||||
* Starts the MCP Thread in mocha to allows usage of /dev/iosuhax and wupclient
|
||||
* @return MOCHA_RESULT_SUCCESS
|
||||
* 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.
|
||||
/**
|
||||
* Starts the MCP thread of mocha to allows usage of /dev/iosuhax and wupclient
|
||||
* @return MOCHA_RESULT_SUCCESS: Thread has been started<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_StartMCPThread();
|
||||
|
||||
/***
|
||||
* Starts the MCP Thread in mocha to allows usage of /dev/iosuhax and wupclient
|
||||
* @return MOCHA_RESULT_SUCCESS
|
||||
* 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.
|
||||
/**
|
||||
* Enables logging via USB (FTDI FT232 chipset only) via OSReport and friends.
|
||||
* @param avoidLogCatchup If set to true, the log start at the moment this function is called (for the first time)
|
||||
* @return MOCHA_RESULT_SUCCESS: Logging via USB starts or has already been started<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_StartUSBLogging(bool avoidLogCatchup);
|
||||
|
||||
@ -81,11 +177,12 @@ MochaUtilsStatus Mocha_StartUSBLogging(bool avoidLogCatchup);
|
||||
* Gives a FSClient full permissions. <br>
|
||||
* Requires Mocha API Version: 1
|
||||
* @param client The FSClient that should have full permission
|
||||
* @return MOCHA_RESULT_SUCCESS: The has been unlocked successfully.
|
||||
* @return MOCHA_RESULT_SUCCESS: The has been unlocked successfully. <br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: client was NULL <br>
|
||||
* MOCHA_RESULT_MAX_CLIENT: The maximum number of FS Clients have been unlocked.<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.
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to unlock a given FSClient
|
||||
*/
|
||||
MochaUtilsStatus Mocha_UnlockFSClient(FSClient *client);
|
||||
|
||||
@ -93,24 +190,88 @@ MochaUtilsStatus Mocha_UnlockFSClient(FSClient *client);
|
||||
* Gives a /dev/fsa handle full permissions. <br>
|
||||
* Requires Mocha API Version: 1
|
||||
* @param client The /dev/fsa handle that should have full permission
|
||||
* @return MOCHA_RESULT_SUCCESS: The has been unlocked successfully.
|
||||
* @return MOCHA_RESULT_SUCCESS: The has been unlocked successfully. <br>
|
||||
* MOCHA_RESULT_MAX_CLIENT: The maximum number of FS Clients have been unlocked.<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.
|
||||
* MOCHA_RESULT_UNKNOWN_ERROR: Failed to unlock the given client
|
||||
*/
|
||||
MochaUtilsStatus Mocha_UnlockFSClientEx(int clientHandle);
|
||||
|
||||
/**
|
||||
* After successfully calling this function, the next loaded .rpx will be replaced according to the given loadInfo.<br>
|
||||
* <br>
|
||||
* Loading a .rpx from within a file (archive e.g. a WUHB) is supported. <br>
|
||||
* To achieve this, the fileoffset (offset inside file specified via path) and filesize (size of the .rpx) need to be set. <br>
|
||||
* If filesize is set to 0, the whole file (starting at fileoffset) will be loaded as .rpx <br>
|
||||
*
|
||||
* The path is **relative** to the root of the given target device.
|
||||
*
|
||||
* @param loadInfo Information about the .rpx replacement.
|
||||
* @return MOCHA_RESULT_SUCCESS: Loading the next RPX will be redirected. <br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: The given loadInfo was NULL <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 setup a redirect of RPX.
|
||||
*/
|
||||
MochaUtilsStatus Mocha_LoadRPXOnNextLaunch(MochaRPXLoadInfo *loadInfo);
|
||||
|
||||
typedef struct WUDDiscKey {
|
||||
uint8_t key[0x10];
|
||||
} WUDDiscKey;
|
||||
|
||||
/**
|
||||
* Reads the disc key (used to decrypt the SI partition) of the inserted disc.
|
||||
*
|
||||
* @param discKey target buffer where the result will be stored.
|
||||
* @return MOCHA_RESULT_SUCCESS: The disc key of the inserted disc has been read into the given buffer.<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: The given discKey buffer was NULL <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 read the discKey.
|
||||
*/
|
||||
MochaUtilsStatus Mocha_ODMGetDiscKey(WUDDiscKey *discKey);
|
||||
|
||||
/**
|
||||
* Reads *size* bytes from *offset* from the SEEPROM of the console. Total size of SEEPROM is 0x200
|
||||
* @param out_buffer buffer where the result will be stored
|
||||
* @param offset offset in bytes. Must be an even number.
|
||||
* @param size size in bytes
|
||||
* @return MOCHA_RESULT_SUCCESS: The SEEPROM has been read into the given buffer.<br>
|
||||
* MOCHA_RESULT_INVALID_ARGUMENT: The given out_buffer was NULL or the offset was < 0 or an odd value<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 read the seeprom.
|
||||
*/
|
||||
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
|
20
source/devoptab/FastLockWrapper.h
Normal file
20
source/devoptab/FastLockWrapper.h
Normal 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{};
|
||||
};
|
203
source/devoptab/devoptab_fs.cpp
Normal file
203
source/devoptab/devoptab_fs.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include "devoptab_fs.h"
|
||||
#include "logger.h"
|
||||
#include "mocha/mocha.h"
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <mocha/mocha.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;
|
||||
}
|
109
source/devoptab/devoptab_fs.h
Normal file
109
source/devoptab/devoptab_fs.h
Normal 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
|
32
source/devoptab/devoptab_fsa_chdir.cpp
Normal file
32
source/devoptab/devoptab_fsa_chdir.cpp
Normal 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;
|
||||
}
|
38
source/devoptab/devoptab_fsa_chmod.cpp
Normal file
38
source/devoptab/devoptab_fsa_chmod.cpp
Normal 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;
|
||||
}
|
29
source/devoptab/devoptab_fsa_close.cpp
Normal file
29
source/devoptab/devoptab_fsa_close.cpp
Normal 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;
|
||||
}
|
29
source/devoptab/devoptab_fsa_dirclose.cpp
Normal file
29
source/devoptab/devoptab_fsa_dirclose.cpp
Normal 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;
|
||||
}
|
52
source/devoptab/devoptab_fsa_dirnext.cpp
Normal file
52
source/devoptab/devoptab_fsa_dirnext.cpp
Normal 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;
|
||||
}
|
41
source/devoptab/devoptab_fsa_diropen.cpp
Normal file
41
source/devoptab/devoptab_fsa_diropen.cpp
Normal 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;
|
||||
}
|
28
source/devoptab/devoptab_fsa_dirreset.cpp
Normal file
28
source/devoptab/devoptab_fsa_dirreset.cpp
Normal 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;
|
||||
}
|
27
source/devoptab/devoptab_fsa_fchmod.cpp
Normal file
27
source/devoptab/devoptab_fsa_fchmod.cpp
Normal 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;
|
||||
}
|
41
source/devoptab/devoptab_fsa_fstat.cpp
Normal file
41
source/devoptab/devoptab_fsa_fstat.cpp
Normal 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;
|
||||
}
|
29
source/devoptab/devoptab_fsa_fsync.cpp
Normal file
29
source/devoptab/devoptab_fsa_fsync.cpp
Normal 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;
|
||||
}
|
8
source/devoptab/devoptab_fsa_link.cpp
Normal file
8
source/devoptab/devoptab_fsa_link.cpp
Normal 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;
|
||||
}
|
38
source/devoptab/devoptab_fsa_mkdir.cpp
Normal file
38
source/devoptab/devoptab_fsa_mkdir.cpp
Normal 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;
|
||||
}
|
92
source/devoptab/devoptab_fsa_open.cpp
Normal file
92
source/devoptab/devoptab_fsa_open.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#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);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
77
source/devoptab/devoptab_fsa_read.cpp
Normal file
77
source/devoptab/devoptab_fsa_read.cpp
Normal 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;
|
||||
}
|
45
source/devoptab/devoptab_fsa_rename.cpp
Normal file
45
source/devoptab/devoptab_fsa_rename.cpp
Normal 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;
|
||||
}
|
71
source/devoptab/devoptab_fsa_rmdir.cpp
Normal file
71
source/devoptab/devoptab_fsa_rmdir.cpp
Normal 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;
|
||||
}
|
72
source/devoptab/devoptab_fsa_seek.cpp
Normal file
72
source/devoptab/devoptab_fsa_seek.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
status = FSASetPosFile(deviceData->clientHandle, file->fd, file->offset);
|
||||
if (status < 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Update the current offset
|
||||
file->offset = offset + pos;
|
||||
|
||||
return file->offset;
|
||||
}
|
50
source/devoptab/devoptab_fsa_stat.cpp
Normal file
50
source/devoptab/devoptab_fsa_stat.cpp
Normal 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;
|
||||
}
|
50
source/devoptab/devoptab_fsa_statvfs.cpp
Normal file
50
source/devoptab/devoptab_fsa_statvfs.cpp
Normal 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;
|
||||
}
|
38
source/devoptab/devoptab_fsa_truncate.cpp
Normal file
38
source/devoptab/devoptab_fsa_truncate.cpp
Normal 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;
|
||||
}
|
35
source/devoptab/devoptab_fsa_unlink.cpp
Normal file
35
source/devoptab/devoptab_fsa_unlink.cpp
Normal 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;
|
||||
}
|
169
source/devoptab/devoptab_fsa_utils.cpp
Normal file
169
source/devoptab/devoptab_fsa_utils.cpp
Normal 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;
|
||||
}
|
9
source/devoptab/devoptab_fsa_utimes.cpp
Normal file
9
source/devoptab/devoptab_fsa_utimes.cpp
Normal 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;
|
||||
}
|
73
source/devoptab/devoptab_fsa_write.cpp
Normal file
73
source/devoptab/devoptab_fsa_write.cpp
Normal 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;
|
||||
}
|
@ -1,75 +1,11 @@
|
||||
#include "mocha/fsa.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/filesystem.h>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
#include <mocha/fsa.h>
|
||||
#include <string_view>
|
||||
|
||||
FSError FSAEx_Mount(FSClient *client, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len) {
|
||||
if (!client) {
|
||||
return FS_ERROR_INVALID_CLIENTHANDLE;
|
||||
}
|
||||
return FSAEx_MountEx(FSGetClientBody(client)->clientHandle, source, target, flags, arg_buf, arg_len);
|
||||
}
|
||||
|
||||
FSError FSAEx_MountEx(int clientHandle, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len) {
|
||||
auto *buffer = (FSAShimBuffer *) memalign(0x40, sizeof(FSAShimBuffer));
|
||||
if (!buffer) {
|
||||
return FS_ERROR_INVALID_BUFFER;
|
||||
}
|
||||
|
||||
// Check if source and target path is valid.
|
||||
if (!std::string_view(target).starts_with("/vol/") || !std::string_view(source).starts_with("/dev/")) {
|
||||
return FS_ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
if (flags == FSA_MOUNT_FLAG_BIND_MOUNT || flags == FSA_MOUNT_FLAG_GLOBAL_MOUNT) {
|
||||
// Return error if target path DOES NOT start with "/vol/storage_"
|
||||
if (!std::string_view(target).starts_with("/vol/storage_")) {
|
||||
return FS_ERROR_INVALID_PATH;
|
||||
}
|
||||
} else {
|
||||
// Return error if target path starts with "/vol/storage_"
|
||||
if (std::string_view(target).starts_with("/vol/storage_")) {
|
||||
return FS_ERROR_INVALID_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
auto res = __FSAShimSetupRequestMount(buffer, clientHandle, source, target, 2, arg_buf, arg_len);
|
||||
if (res != 0) {
|
||||
free(buffer);
|
||||
return res;
|
||||
}
|
||||
res = __FSAShimSend(buffer, 0);
|
||||
free(buffer);
|
||||
return res;
|
||||
}
|
||||
|
||||
FSError FSAEx_Unmount(FSClient *client, const char *mountedTarget, FSAUnmountFlags flags) {
|
||||
if (!client) {
|
||||
return FS_ERROR_INVALID_CLIENTHANDLE;
|
||||
}
|
||||
return FSAEx_UnmountEx(FSGetClientBody(client)->clientHandle, mountedTarget, flags);
|
||||
}
|
||||
|
||||
FSError FSAEx_UnmountEx(int clientHandle, const char *mountedTarget, FSAUnmountFlags flags) {
|
||||
auto *buffer = (FSAShimBuffer *) memalign(0x40, sizeof(FSAShimBuffer));
|
||||
if (!buffer) {
|
||||
return FS_ERROR_INVALID_BUFFER;
|
||||
}
|
||||
|
||||
auto res = __FSAShimSetupRequestUnmount(buffer, clientHandle, mountedTarget, flags);
|
||||
if (res != 0) {
|
||||
free(buffer);
|
||||
return res;
|
||||
}
|
||||
res = __FSAShimSend(buffer, 0);
|
||||
free(buffer);
|
||||
return res;
|
||||
}
|
||||
|
||||
FSError FSAEx_RawOpen(FSClient *client, char *device_path, int32_t *outHandle) {
|
||||
if (!client) {
|
||||
@ -168,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;
|
||||
}
|
||||
|
||||
|
24
source/logger.h
Normal file
24
source/logger.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <coreinit/debug.h>
|
||||
#include <cstring>
|
||||
|
||||
#define __FILENAME__ ({ \
|
||||
const char *__filename = __FILE__; \
|
||||
const char *__pos = strrchr(__filename, '/'); \
|
||||
if (!__pos) __pos = strrchr(__filename, '\\'); \
|
||||
__pos ? __pos + 1 : __filename; \
|
||||
})
|
||||
|
||||
#define LOG_APP_TYPE "L"
|
||||
#define LOG_APP_NAME "libmocha"
|
||||
|
||||
#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \
|
||||
do { \
|
||||
LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS)
|
||||
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARNING## ", "\n", FMT, ##ARGS)
|
184
source/utils.cpp
184
source/utils.cpp
@ -1,19 +1,58 @@
|
||||
#include "utils.h"
|
||||
#include "logger.h"
|
||||
#include "mocha/commands.h"
|
||||
#include "mocha/mocha.h"
|
||||
#include <coreinit/ios.h>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int iosuhaxHandle = -1;
|
||||
int mochaInitDone = 0;
|
||||
uint32_t mochaApiVersion = 0;
|
||||
|
||||
#define IOCTL_MEM_WRITE 0x00
|
||||
#define IOCTL_MEM_READ 0x01
|
||||
#define IOCTL_SVC 0x02
|
||||
#define IOCTL_MEMCPY 0x04
|
||||
#define IOCTL_REPEATED_WRITE 0x05
|
||||
#define IOCTL_KERN_READ32 0x06
|
||||
#define IOCTL_KERN_WRITE32 0x07
|
||||
#define IOCTL_READ_OTP 0x08
|
||||
#define IOCTL_CHECK_IF_IOSUHAX 0x5B
|
||||
#define IOSUHAX_MAGIC_WORD 0x4E696365
|
||||
|
||||
MochaUtilsStatus Mocha_InitLibrary() {
|
||||
if (mochaInitDone) {
|
||||
return MOCHA_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
if (iosuhaxHandle < 0) {
|
||||
int haxHandle = IOS_Open((char *) ("/dev/iosuhax"), static_cast<IOSOpenMode>(0));
|
||||
if (haxHandle >= 0) {
|
||||
ALIGN_0x40 int res[0x40 >> 2];
|
||||
*res = 0;
|
||||
|
||||
IOS_Ioctl(haxHandle, IOCTL_CHECK_IF_IOSUHAX, (void *) nullptr, 0, res, 4);
|
||||
if (*res != IOSUHAX_MAGIC_WORD) {
|
||||
IOS_Close(haxHandle);
|
||||
DEBUG_FUNCTION_LINE_ERR("Unexpected /dev/iosuhax magic");
|
||||
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
|
||||
}
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to open /dev/iosuhax");
|
||||
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
|
||||
}
|
||||
iosuhaxHandle = haxHandle;
|
||||
}
|
||||
|
||||
mochaInitDone = 1;
|
||||
mochaApiVersion = 0;
|
||||
uint32_t version = 0;
|
||||
if (Mocha_CheckAPIVersion(&version) != MOCHA_RESULT_SUCCESS) {
|
||||
return MOCHA_RESULT_SUCCESS;
|
||||
IOS_Close(iosuhaxHandle);
|
||||
iosuhaxHandle = -1;
|
||||
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
|
||||
}
|
||||
|
||||
mochaApiVersion = version;
|
||||
@ -21,10 +60,14 @@ MochaUtilsStatus Mocha_InitLibrary() {
|
||||
return MOCHA_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
MochaUtilsStatus Mocha_DeInitLibrary() {
|
||||
MochaUtilsStatus Mocha_DeinitLibrary() {
|
||||
mochaInitDone = 0;
|
||||
mochaApiVersion = 0;
|
||||
|
||||
if (iosuhaxHandle >= 0) {
|
||||
IOS_Close(iosuhaxHandle);
|
||||
}
|
||||
|
||||
return MOCHA_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -53,6 +96,138 @@ MochaUtilsStatus Mocha_CheckAPIVersion(uint32_t *version) {
|
||||
return res;
|
||||
}
|
||||
|
||||
MochaUtilsStatus Mocha_IOSUKernelMemcpy(uint32_t dst, uint32_t src, uint32_t size) {
|
||||
if (size == 0) {
|
||||
return MOCHA_RESULT_SUCCESS;
|
||||
}
|
||||
if (dst == 0 || src == 0) {
|
||||
return MOCHA_RESULT_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!mochaInitDone || iosuhaxHandle < 0) {
|
||||
return MOCHA_RESULT_LIB_UNINITIALIZED;
|
||||
}
|
||||
|
||||
ALIGN_0x40 uint32_t io_buf[0x40 >> 2];
|
||||
io_buf[0] = dst;
|
||||
io_buf[1] = src;
|
||||
io_buf[2] = size;
|
||||
|
||||
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_MEMCPY, io_buf, 3 * sizeof(uint32_t), nullptr, 0);
|
||||
return res >= 0 ? MOCHA_RESULT_SUCCESS : MOCHA_RESULT_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
MochaUtilsStatus Mocha_IOSUKernelWrite(uint32_t address, const uint8_t *buffer, uint32_t size) {
|
||||
if (size == 0) {
|
||||
return MOCHA_RESULT_SUCCESS;
|
||||
}
|
||||
if (address == 0 || buffer == nullptr) {
|
||||
return MOCHA_RESULT_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!mochaInitDone || iosuhaxHandle < 0) {
|
||||
return MOCHA_RESULT_LIB_UNINITIALIZED;
|
||||
}
|
||||
|
||||
auto *io_buf = (uint32_t *) memalign(0x40, ROUNDUP(size + 4, 0x40));
|
||||
if (!io_buf) {
|
||||
return MOCHA_RESULT_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
io_buf[0] = address;
|
||||
memcpy(io_buf + 1, buffer, size);
|
||||
|
||||
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_MEM_WRITE, io_buf, size + 4, nullptr, 0);
|
||||
|
||||
free(io_buf);
|
||||
return res >= 0 ? MOCHA_RESULT_SUCCESS : MOCHA_RESULT_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
MochaUtilsStatus Mocha_IOSUKernelRead(uint32_t address, uint8_t *out_buffer, uint32_t size) {
|
||||
if (size == 0) {
|
||||
return MOCHA_RESULT_SUCCESS;
|
||||
}
|
||||
if (address == 0 || out_buffer == nullptr) {
|
||||
return MOCHA_RESULT_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!mochaInitDone || iosuhaxHandle < 0) {
|
||||
return MOCHA_RESULT_LIB_UNINITIALIZED;
|
||||
}
|
||||
|
||||
ALIGN_0x40 uint32_t io_buf[0x40 >> 2];
|
||||
io_buf[0] = address;
|
||||
|
||||
void *tmp_buf = nullptr;
|
||||
|
||||
if (((uintptr_t) out_buffer & 0x3F) || (size & 0x3F)) {
|
||||
tmp_buf = (uint32_t *) memalign(0x40, ROUNDUP(size, 0x40));
|
||||
if (!tmp_buf) {
|
||||
return MOCHA_RESULT_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_MEM_READ, io_buf, sizeof(address), tmp_buf ? tmp_buf : out_buffer, size);
|
||||
|
||||
if (res >= 0 && tmp_buf) {
|
||||
memcpy(out_buffer, tmp_buf, size);
|
||||
}
|
||||
|
||||
free(tmp_buf);
|
||||
return res >= 0 ? MOCHA_RESULT_SUCCESS : MOCHA_RESULT_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
MochaUtilsStatus Mocha_IOSUKernelWrite32(uint32_t address, uint32_t value) {
|
||||
return Mocha_IOSUKernelWrite(address, reinterpret_cast<const uint8_t *>(&value), 4);
|
||||
}
|
||||
|
||||
MochaUtilsStatus Mocha_IOSUKernelRead32(uint32_t address, uint32_t *out_buffer) {
|
||||
return Mocha_IOSUKernelRead(address, reinterpret_cast<uint8_t *>(out_buffer), 4);
|
||||
}
|
||||
|
||||
MochaUtilsStatus Mocha_ReadOTP(WiiUConsoleOTP *out_buffer) {
|
||||
if (out_buffer == nullptr) {
|
||||
return MOCHA_RESULT_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!mochaInitDone || iosuhaxHandle < 0) {
|
||||
return MOCHA_RESULT_LIB_UNINITIALIZED;
|
||||
}
|
||||
|
||||
ALIGN_0x40 uint32_t io_buf[0x400 >> 2];
|
||||
|
||||
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_READ_OTP, nullptr, 0, io_buf, 0x400);
|
||||
|
||||
if (res < 0) {
|
||||
return MOCHA_RESULT_UNKNOWN_ERROR;
|
||||
}
|
||||
memcpy(out_buffer, io_buf, 0x400);
|
||||
|
||||
return MOCHA_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
int Mocha_IOSUCallSVC(uint32_t svc_id, uint32_t *args, uint32_t arg_cnt, int32_t *outResult) {
|
||||
if (!mochaInitDone || iosuhaxHandle < 0) {
|
||||
return MOCHA_RESULT_LIB_UNINITIALIZED;
|
||||
}
|
||||
|
||||
ALIGN_0x40 uint32_t arguments[0x40 >> 2];
|
||||
arguments[0] = svc_id;
|
||||
|
||||
if (args && arg_cnt) {
|
||||
if (arg_cnt > 8) {
|
||||
arg_cnt = 8;
|
||||
}
|
||||
|
||||
memcpy(arguments + 1, args, arg_cnt * 4);
|
||||
}
|
||||
|
||||
ALIGN_0x40 int result[0x40 >> 2];
|
||||
int res = IOS_Ioctl(iosuhaxHandle, IOCTL_SVC, arguments, (1 + arg_cnt) * 4, result, 4);
|
||||
|
||||
if (res >= 0) {
|
||||
*outResult = *result;
|
||||
}
|
||||
|
||||
return res >= 0 ? MOCHA_RESULT_SUCCESS : MOCHA_RESULT_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
MochaUtilsStatus Mocha_GetEnvironmentPath(char *environmentPathBuffer, uint32_t bufferLen) {
|
||||
if (!mochaInitDone) {
|
||||
return MOCHA_RESULT_LIB_UNINITIALIZED;
|
||||
@ -169,6 +344,9 @@ MochaUtilsStatus Mocha_LoadRPXOnNextLaunch(MochaRPXLoadInfo *loadInfo) {
|
||||
if (mochaApiVersion < 1) {
|
||||
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
|
||||
}
|
||||
if (!loadInfo) {
|
||||
return MOCHA_RESULT_INVALID_ARGUMENT;
|
||||
}
|
||||
MochaUtilsStatus res = MOCHA_RESULT_UNKNOWN_ERROR;
|
||||
int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0);
|
||||
if (mcpFd >= 0) {
|
||||
@ -212,7 +390,7 @@ MochaUtilsStatus Mocha_ODMGetDiscKey(WUDDiscKey *discKey) {
|
||||
return res;
|
||||
}
|
||||
|
||||
extern int bspRead(const char *, uint32_t, const char *, uint32_t, uint16_t *);
|
||||
extern "C" int bspRead(const char *, uint32_t, const char *, uint32_t, uint16_t *);
|
||||
MochaUtilsStatus Mocha_SEEPROMRead(uint8_t *out_buffer, uint32_t offset, uint32_t size) {
|
||||
if (!mochaInitDone) {
|
||||
return MOCHA_RESULT_LIB_UNINITIALIZED;
|
||||
|
Loading…
Reference in New Issue
Block a user