diff --git a/applications/services/storage/storage.c b/applications/services/storage/storage.c index 733980a6b..ad1bfd31a 100644 --- a/applications/services/storage/storage.c +++ b/applications/services/storage/storage.c @@ -43,6 +43,7 @@ Storage* storage_app_alloc() { } #ifndef FURI_RAM_EXEC + storage_mnt_init(&app->storage[ST_MNT]); storage_int_init(&app->storage[ST_INT]); #endif storage_ext_init(&app->storage[ST_EXT]); diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index bcca46e30..e278902a3 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -14,6 +14,7 @@ extern "C" { #define STORAGE_INT_PATH_PREFIX "/int" #define STORAGE_EXT_PATH_PREFIX "/ext" +#define STORAGE_MNT_PATH_PREFIX "/mnt" #define STORAGE_ANY_PATH_PREFIX "/any" #define STORAGE_APP_DATA_PATH_PREFIX "/data" #define STORAGE_APP_ASSETS_PATH_PREFIX "/assets" @@ -21,6 +22,7 @@ extern "C" { #define INT_PATH(path) STORAGE_INT_PATH_PREFIX "/" path #define EXT_PATH(path) STORAGE_EXT_PATH_PREFIX "/" path +#define MNT_PATH(path) STORAGE_MNT_PATH_PREFIX "/" path #define ANY_PATH(path) STORAGE_ANY_PATH_PREFIX "/" path #define APP_DATA_PATH(path) STORAGE_APP_DATA_PATH_PREFIX "/" path #define APP_ASSETS_PATH(path) STORAGE_APP_ASSETS_PATH_PREFIX "/" path @@ -555,6 +557,58 @@ FS_Error storage_int_backup(Storage* storage, const char* dstname); */ FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter); +/******************* FatFs Virtual Mount Functions *******************/ + +/** + * @brief Initialize virual API with given disk image. + * + * @param storage pointer to a storage API instance. + * @param image pointer to a File instance to base virtual mount on. + * @return FSE_OK if the image was setup successfully. + * @return FSE_ALREADY_OPEN if virtual API is already initialized. + */ +FS_Error storage_virtual_init(Storage* storage, File* image); + +/** + * @brief Format the virtual image. + * + * @param storage pointer to a storage API instance. + * @return FSE_OK if the image was formatted successfully. + * @return FSE_NOT_READY if virtual API is not initialized. + * @return FSE_INTERNAL if an unknown error occurred. + */ +FS_Error storage_virtual_format(Storage* storage); + +/** + * @brief Mount the virtual image to /mnt. + * + * @param storage pointer to a storage API instance. + * @return FSE_OK if the image was mounted successfully. + * @return FSE_NOT_READY if virtual API is not initialized. + * @return FSE_INVALID_PARAMETER if image has no supported filesystem. + * @return FSE_INTERNAL if an unknown error occurred. + */ +FS_Error storage_virtual_mount(Storage* storage); + +/** + * @brief Unmount the virtual image from /mnt. + * + * @param storage pointer to a storage API instance. + * @return FSE_OK if the image was unmounted successfully. + * @return FSE_NOT_READY if virtual API is not initialized. + * @return FSE_INTERNAL if an unknown error occurred. + */ +FS_Error storage_virtual_unmount(Storage* storage); + +/** + * @brief Quit virtual mount API, reset to allow new init. + * + * @param storage pointer to a storage API instance. + * @return FSE_OK if the image was unloaded successfully. + * @return FSE_NOT_READY if virtual API is not initialized. + */ +FS_Error storage_virtual_quit(Storage* storage); + /***************** Simplified Functions ******************/ /** diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 0c6ff2295..ade9a974f 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -979,6 +979,49 @@ FS_Error storage_sd_status(Storage* storage) { return S_RETURN_ERROR; } +FS_Error storage_virtual_init(Storage* storage, File* image) { + S_API_PROLOGUE; + SAData data = { + .virtualinit = { + .image = image, + }}; + S_API_MESSAGE(StorageCommandVirtualInit); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_virtual_format(Storage* storage) { + S_API_PROLOGUE; + SAData data = {}; + S_API_MESSAGE(StorageCommandVirtualFormat); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_virtual_mount(Storage* storage) { + S_API_PROLOGUE; + SAData data = {}; + S_API_MESSAGE(StorageCommandVirtualMount); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_virtual_unmount(Storage* storage) { + S_API_PROLOGUE; + SAData data = {}; + S_API_MESSAGE(StorageCommandVirtualUnmount); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_virtual_quit(Storage* storage) { + S_API_PROLOGUE; + SAData data = {}; + S_API_MESSAGE(StorageCommandVirtualQuit); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + File* storage_file_alloc(Storage* storage) { File* file = malloc(sizeof(File)); file->type = FileTypeClosed; diff --git a/applications/services/storage/storage_glue.h b/applications/services/storage/storage_glue.h index 4323296cf..fbc08ebbf 100644 --- a/applications/services/storage/storage_glue.h +++ b/applications/services/storage/storage_glue.h @@ -8,7 +8,7 @@ extern "C" { #endif -typedef enum { ST_EXT = 0, ST_INT = 1, ST_ANY, ST_ERROR } StorageType; +typedef enum { ST_EXT = 0, ST_INT = 1, ST_MNT = 2, ST_ANY, ST_ERROR } StorageType; typedef struct StorageData StorageData; diff --git a/applications/services/storage/storage_message.h b/applications/services/storage/storage_message.h index 2893d835b..31910fde3 100644 --- a/applications/services/storage/storage_message.h +++ b/applications/services/storage/storage_message.h @@ -104,6 +104,10 @@ typedef struct { SDInfo* info; } SAInfo; +typedef struct { + File* image; +} SAVirtualInit; + typedef union { SADataFOpen fopen; SADataFRead fread; @@ -127,6 +131,8 @@ typedef union { SADataRename rename; SAInfo sdinfo; + + SAVirtualInit virtualinit; } SAData; typedef union { @@ -167,6 +173,11 @@ typedef enum { StorageCommandFileExpand, StorageCommandCommonRename, + StorageCommandVirtualInit, + StorageCommandVirtualFormat, + StorageCommandVirtualMount, + StorageCommandVirtualUnmount, + StorageCommandVirtualQuit, } StorageCommand; typedef struct { diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 84d9d0011..89b507c92 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -54,6 +54,8 @@ static StorageType storage_get_type_by_path(FuriString* path) { type = ST_EXT; } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { type = ST_INT; + } else if(memcmp(path_cstr, STORAGE_MNT_PATH_PREFIX, strlen(STORAGE_MNT_PATH_PREFIX)) == 0) { + type = ST_MNT; } else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { type = ST_ANY; } @@ -787,6 +789,25 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { case StorageCommandSDStatus: message->return_data->error_value = storage_process_sd_status(app); break; + + // Virtual operations + case StorageCommandVirtualInit: + File* image = message->data->virtualinit.image; + StorageData* image_storage = get_storage_by_file(image, app->storage); + message->return_data->error_value = storage_process_virtual_init(image_storage, image); + break; + case StorageCommandVirtualFormat: + message->return_data->error_value = storage_process_virtual_format(&app->storage[ST_MNT]); + break; + case StorageCommandVirtualMount: + message->return_data->error_value = storage_process_virtual_mount(&app->storage[ST_MNT]); + break; + case StorageCommandVirtualUnmount: + message->return_data->error_value = storage_process_virtual_unmount(&app->storage[ST_MNT]); + break; + case StorageCommandVirtualQuit: + message->return_data->error_value = storage_process_virtual_quit(&app->storage[ST_MNT]); + break; } if(path != NULL) { //-V547 diff --git a/applications/services/storage/storages/storage_ext.c b/applications/services/storage/storages/storage_ext.c index 923095845..0bb313335 100644 --- a/applications/services/storage/storages/storage_ext.c +++ b/applications/services/storage/storages/storage_ext.c @@ -714,3 +714,181 @@ void storage_ext_init(StorageData* storage) { // do not notify on first launch, notifications app is waiting for our thread to read settings storage_ext_tick_internal(storage, false); } + +#include "fatfs/ff_gen_drv.h" + +#define SCSI_BLOCK_SIZE (0x200UL) +static File* mnt_image = NULL; +static StorageData* mnt_image_storage = NULL; +bool mnt_mounted = false; +; + +FS_Error storage_process_virtual_init(StorageData* image_storage, File* image) { + if(mnt_image) return FSE_ALREADY_OPEN; + mnt_image = image; + mnt_image_storage = image_storage; + return FSE_OK; +} + +FS_Error storage_process_virtual_format(StorageData* storage) { +#ifdef FURI_RAM_EXEC + UNUSED(storage); + return FSE_NOT_READY; +#else + if(!mnt_image) return FSE_NOT_READY; + SDData* sd_data = storage->data; + uint8_t* work = malloc(_MAX_SS); + SDError error = f_mkfs(sd_data->path, FM_ANY, 0, work, _MAX_SS); + free(work); + if(error != FR_OK) return FSE_INTERNAL; + return FSE_OK; +#endif +} + +FS_Error storage_process_virtual_mount(StorageData* storage) { + if(!mnt_image) return FSE_NOT_READY; + SDData* sd_data = storage->data; + SDError error = f_mount(sd_data->fs, sd_data->path, 1); + if(error == FR_NO_FILESYSTEM) return FSE_INVALID_PARAMETER; + if(error != FR_OK) return FSE_INTERNAL; + mnt_mounted = true; + return FSE_OK; +} + +FS_Error storage_process_virtual_unmount(StorageData* storage) { + if(!mnt_image) return FSE_NOT_READY; + SDData* sd_data = storage->data; + SDError error = f_mount(0, sd_data->path, 0); + if(error != FR_OK) return FSE_INTERNAL; + mnt_mounted = false; + return FSE_OK; +} + +FS_Error storage_process_virtual_quit(StorageData* storage) { + if(!mnt_image) return FSE_NOT_READY; + if(mnt_mounted) storage_process_virtual_unmount(storage); + mnt_image = NULL; + mnt_image_storage = NULL; + return FSE_OK; +} + +/** + * @brief Initializes a Drive + * @param pdrv: Physical drive number (0..) + * @retval DSTATUS: Operation status + */ +static DSTATUS mnt_driver_initialize(BYTE pdrv) { + UNUSED(pdrv); + return RES_OK; +} + +/** + * @brief Gets Disk Status + * @param pdrv: Physical drive number (0..) + * @retval DSTATUS: Operation status + */ +static DSTATUS mnt_driver_status(BYTE pdrv) { + UNUSED(pdrv); + if(!mnt_image) return STA_NOINIT; + return RES_OK; +} + +/** + * @brief Reads Sector(s) + * @param pdrv: Physical drive number (0..) + * @param *buff: Data buffer to store read data + * @param sector: Sector address (LBA) + * @param count: Number of sectors to read (1..128) + * @retval DRESULT: Operation result + */ +static DRESULT mnt_driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { + UNUSED(pdrv); + if(!storage_ext_file_seek(mnt_image_storage, mnt_image, sector * SCSI_BLOCK_SIZE, true)) { + return RES_ERROR; + } + size_t size = count * SCSI_BLOCK_SIZE; + size_t read = storage_ext_file_read(mnt_image_storage, mnt_image, buff, size); + return read == size ? RES_OK : RES_ERROR; +} + +/** + * @brief Writes Sector(s) + * @param pdrv: Physical drive number (0..) + * @param *buff: Data to be written + * @param sector: Sector address (LBA) + * @param count: Number of sectors to write (1..128) + * @retval DRESULT: Operation result + */ +static DRESULT mnt_driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { + UNUSED(pdrv); + if(!storage_ext_file_seek(mnt_image_storage, mnt_image, sector * SCSI_BLOCK_SIZE, true)) { + return RES_ERROR; + } + size_t size = count * SCSI_BLOCK_SIZE; + size_t wrote = storage_ext_file_write(mnt_image_storage, mnt_image, buff, size); + return wrote == size ? RES_OK : RES_ERROR; +} + +/** + * @brief I/O control operation + * @param pdrv: Physical drive number (0..) + * @param cmd: Control code + * @param *buff: Buffer to send/receive control data + * @retval DRESULT: Operation result + */ +static DRESULT mnt_driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) { + DRESULT res = RES_ERROR; + + DSTATUS status = mnt_driver_status(pdrv); + if(status & STA_NOINIT) return RES_NOTRDY; + + switch(cmd) { + /* Make sure that no pending write process */ + case CTRL_SYNC: + res = RES_OK; + break; + + /* Get number of sectors on the disk (DWORD) */ + case GET_SECTOR_COUNT: + *(DWORD*)buff = storage_ext_file_size(mnt_image_storage, mnt_image) / SCSI_BLOCK_SIZE; + res = RES_OK; + break; + + /* Get R/W sector size (WORD) */ + case GET_SECTOR_SIZE: + *(WORD*)buff = SCSI_BLOCK_SIZE; + res = RES_OK; + break; + + /* Get erase block size in unit of sector (DWORD) */ + case GET_BLOCK_SIZE: + *(DWORD*)buff = SCSI_BLOCK_SIZE; + res = RES_OK; + break; + + default: + res = RES_PARERR; + } + + return res; +} + +static Diskio_drvTypeDef mnt_driver = { + mnt_driver_initialize, + mnt_driver_status, + mnt_driver_read, + mnt_driver_write, + mnt_driver_ioctl, +}; + +void storage_mnt_init(StorageData* storage) { + char path[4] = {0}; + FATFS_LinkDriver(&mnt_driver, path); + + SDData* sd_data = malloc(sizeof(SDData)); + sd_data->fs = malloc(sizeof(FATFS)); + sd_data->path = strdup(path); + + storage->data = sd_data; + storage->fs_api = &fs_api; +} diff --git a/applications/services/storage/storages/storage_ext.h b/applications/services/storage/storages/storage_ext.h index 18d1f7143..b598283e6 100644 --- a/applications/services/storage/storages/storage_ext.h +++ b/applications/services/storage/storages/storage_ext.h @@ -12,6 +12,12 @@ FS_Error sd_mount_card(StorageData* storage, bool notify); FS_Error sd_unmount_card(StorageData* storage); FS_Error sd_format_card(StorageData* storage); FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info); +void storage_mnt_init(StorageData* storage); +FS_Error storage_process_virtual_init(StorageData* image_storage, File* image); +FS_Error storage_process_virtual_format(StorageData* storage); +FS_Error storage_process_virtual_mount(StorageData* storage); +FS_Error storage_process_virtual_unmount(StorageData* storage); +FS_Error storage_process_virtual_quit(StorageData* storage); #ifdef __cplusplus } #endif diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 44e39cf8c..ff4ed9217 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3188,6 +3188,11 @@ Function,+,storage_sd_unmount,FS_Error,Storage* Function,+,storage_simply_mkdir,_Bool,"Storage*, const char*" Function,+,storage_simply_remove,_Bool,"Storage*, const char*" Function,+,storage_simply_remove_recursive,_Bool,"Storage*, const char*" +Function,+,storage_virtual_format,FS_Error,Storage* +Function,+,storage_virtual_init,FS_Error,"Storage*, File*" +Function,+,storage_virtual_mount,FS_Error,Storage* +Function,+,storage_virtual_quit,FS_Error,Storage* +Function,+,storage_virtual_unmount,FS_Error,Storage* Function,-,stpcpy,char*,"char*, const char*" Function,-,stpncpy,char*,"char*, const char*, size_t" Function,+,strcasecmp,int,"const char*, const char*" diff --git a/targets/f7/fatfs/ffconf.h b/targets/f7/fatfs/ffconf.h index 887ad18e8..f1c32db59 100644 --- a/targets/f7/fatfs/ffconf.h +++ b/targets/f7/fatfs/ffconf.h @@ -163,7 +163,7 @@ / Drive/Volume Configurations /----------------------------------------------------------------------------*/ -#define _VOLUMES 1 +#define _VOLUMES 2 /* Number of volumes (logical drives) to be used. */ /* USER CODE BEGIN Volumes */