diff --git a/applications/archive/helpers/archive_files.h b/applications/archive/helpers/archive_files.h index c8b16c53..64549d09 100644 --- a/applications/archive/helpers/archive_files.h +++ b/applications/archive/helpers/archive_files.h @@ -1,5 +1,6 @@ #pragma once #include "file_worker.h" +#include #define MAX_FILES 100 //temp diff --git a/applications/desktop/animations/animation_manager.c b/applications/desktop/animations/animation_manager.c index afc9c42e..4c99724e 100644 --- a/applications/desktop/animations/animation_manager.c +++ b/applications/desktop/animations/animation_manager.c @@ -94,10 +94,21 @@ void animation_manager_set_interact_callback( } static void animation_manager_check_blocking_callback(const void* message, void* context) { - furi_assert(context); - AnimationManager* animation_manager = context; - if(animation_manager->check_blocking_callback) { - animation_manager->check_blocking_callback(animation_manager->context); + const StorageEvent* storage_event = message; + + switch(storage_event->type) { + case StorageEventTypeCardMount: + case StorageEventTypeCardUnmount: + case StorageEventTypeCardMountError: + furi_assert(context); + AnimationManager* animation_manager = context; + if(animation_manager->check_blocking_callback) { + animation_manager->check_blocking_callback(animation_manager->context); + } + break; + + default: + break; } } diff --git a/applications/ibutton/ibutton_cli.c b/applications/ibutton/ibutton_cli.c index 44d7025c..243ee36b 100644 --- a/applications/ibutton/ibutton_cli.c +++ b/applications/ibutton/ibutton_cli.c @@ -63,6 +63,7 @@ void ibutton_cli_print_key_data(iButtonKey* key) { #define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0) static void ibutton_cli_worker_read_cb(void* context) { + furi_assert(context); osEventFlagsId_t event = context; osEventFlagsSet(event, EVENT_FLAG_IBUTTON_COMPLETE); } @@ -112,6 +113,7 @@ typedef struct { } iButtonWriteContext; static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) { + furi_assert(context); iButtonWriteContext* write_context = (iButtonWriteContext*)context; write_context->result = result; osEventFlagsSet(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE); diff --git a/applications/ibutton/scene/ibutton_scene_add_type.cpp b/applications/ibutton/scene/ibutton_scene_add_type.cpp index aa76d3c0..c9537768 100644 --- a/applications/ibutton/scene/ibutton_scene_add_type.cpp +++ b/applications/ibutton/scene/ibutton_scene_add_type.cpp @@ -9,6 +9,7 @@ typedef enum { } SubmenuIndex; static void submenu_callback(void* context, uint32_t index) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_add_value.cpp b/applications/ibutton/scene/ibutton_scene_add_value.cpp index 95d5f82f..cb1e287c 100755 --- a/applications/ibutton/scene/ibutton_scene_add_value.cpp +++ b/applications/ibutton/scene/ibutton_scene_add_value.cpp @@ -3,6 +3,7 @@ #include static void byte_input_callback(void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_delete_confirm.cpp b/applications/ibutton/scene/ibutton_scene_delete_confirm.cpp index 91e471df..8b1aa9f5 100755 --- a/applications/ibutton/scene/ibutton_scene_delete_confirm.cpp +++ b/applications/ibutton/scene/ibutton_scene_delete_confirm.cpp @@ -2,6 +2,7 @@ #include "../ibutton_app.h" static void widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_delete_success.cpp b/applications/ibutton/scene/ibutton_scene_delete_success.cpp index e2b64999..b2241141 100644 --- a/applications/ibutton/scene/ibutton_scene_delete_success.cpp +++ b/applications/ibutton/scene/ibutton_scene_delete_success.cpp @@ -2,6 +2,7 @@ #include "../ibutton_app.h" static void popup_callback(void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; event.type = iButtonEvent::Type::EventTypeBack; diff --git a/applications/ibutton/scene/ibutton_scene_emulate.cpp b/applications/ibutton/scene/ibutton_scene_emulate.cpp index 8f517c78..e1b08e84 100644 --- a/applications/ibutton/scene/ibutton_scene_emulate.cpp +++ b/applications/ibutton/scene/ibutton_scene_emulate.cpp @@ -3,6 +3,7 @@ #include static void emulate_callback(void* context, bool emulated) { + furi_assert(context); if(emulated) { iButtonApp* app = static_cast(context); iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerEmulated}; diff --git a/applications/ibutton/scene/ibutton_scene_info.cpp b/applications/ibutton/scene/ibutton_scene_info.cpp index f4cbd809..afc8c050 100755 --- a/applications/ibutton/scene/ibutton_scene_info.cpp +++ b/applications/ibutton/scene/ibutton_scene_info.cpp @@ -2,6 +2,7 @@ #include "../ibutton_app.h" static void widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_read.cpp b/applications/ibutton/scene/ibutton_scene_read.cpp index b5c7ac8f..1353ef96 100644 --- a/applications/ibutton/scene/ibutton_scene_read.cpp +++ b/applications/ibutton/scene/ibutton_scene_read.cpp @@ -3,6 +3,7 @@ #include static void read_callback(void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerRead}; app->get_view_manager()->send_event(&event); diff --git a/applications/ibutton/scene/ibutton_scene_read_crc_error.cpp b/applications/ibutton/scene/ibutton_scene_read_crc_error.cpp index 9d67e442..1371334b 100644 --- a/applications/ibutton/scene/ibutton_scene_read_crc_error.cpp +++ b/applications/ibutton/scene/ibutton_scene_read_crc_error.cpp @@ -3,6 +3,7 @@ #include static void dialog_ex_callback(DialogExResult result, void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_read_not_key_error.cpp b/applications/ibutton/scene/ibutton_scene_read_not_key_error.cpp index fab3c3f2..b88f4655 100644 --- a/applications/ibutton/scene/ibutton_scene_read_not_key_error.cpp +++ b/applications/ibutton/scene/ibutton_scene_read_not_key_error.cpp @@ -3,6 +3,7 @@ #include static void dialog_ex_callback(DialogExResult result, void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_read_success.cpp b/applications/ibutton/scene/ibutton_scene_read_success.cpp index 818f7e38..6eb95721 100644 --- a/applications/ibutton/scene/ibutton_scene_read_success.cpp +++ b/applications/ibutton/scene/ibutton_scene_read_success.cpp @@ -3,6 +3,7 @@ #include static void dialog_ex_callback(DialogExResult result, void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_readed_key_menu.cpp b/applications/ibutton/scene/ibutton_scene_readed_key_menu.cpp index 63af10ad..19eec4e2 100644 --- a/applications/ibutton/scene/ibutton_scene_readed_key_menu.cpp +++ b/applications/ibutton/scene/ibutton_scene_readed_key_menu.cpp @@ -9,6 +9,7 @@ typedef enum { } SubmenuIndex; static void submenu_callback(void* context, uint32_t index) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_save_name.cpp b/applications/ibutton/scene/ibutton_scene_save_name.cpp index bde6133d..94ca45d4 100644 --- a/applications/ibutton/scene/ibutton_scene_save_name.cpp +++ b/applications/ibutton/scene/ibutton_scene_save_name.cpp @@ -3,6 +3,7 @@ #include static void text_input_callback(void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_save_success.cpp b/applications/ibutton/scene/ibutton_scene_save_success.cpp index 891b020a..0a1fc153 100644 --- a/applications/ibutton/scene/ibutton_scene_save_success.cpp +++ b/applications/ibutton/scene/ibutton_scene_save_success.cpp @@ -3,6 +3,7 @@ #include static void popup_callback(void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; event.type = iButtonEvent::Type::EventTypeBack; diff --git a/applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp b/applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp index 1d87e85f..d9d042d9 100644 --- a/applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp +++ b/applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp @@ -11,6 +11,7 @@ typedef enum { } SubmenuIndex; static void submenu_callback(void* context, uint32_t index) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_start.cpp b/applications/ibutton/scene/ibutton_scene_start.cpp index 2dbe9878..4b877dd2 100644 --- a/applications/ibutton/scene/ibutton_scene_start.cpp +++ b/applications/ibutton/scene/ibutton_scene_start.cpp @@ -8,6 +8,7 @@ typedef enum { } SubmenuIndex; static void submenu_callback(void* context, uint32_t index) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; diff --git a/applications/ibutton/scene/ibutton_scene_write.cpp b/applications/ibutton/scene/ibutton_scene_write.cpp index af86a148..847e7f01 100644 --- a/applications/ibutton/scene/ibutton_scene_write.cpp +++ b/applications/ibutton/scene/ibutton_scene_write.cpp @@ -2,6 +2,7 @@ #include "../ibutton_app.h" static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; event.type = iButtonEvent::Type::EventTypeWorkerWrite; diff --git a/applications/ibutton/scene/ibutton_scene_write_success.cpp b/applications/ibutton/scene/ibutton_scene_write_success.cpp index 48acaaa4..dbeb595d 100644 --- a/applications/ibutton/scene/ibutton_scene_write_success.cpp +++ b/applications/ibutton/scene/ibutton_scene_write_success.cpp @@ -2,6 +2,7 @@ #include "../ibutton_app.h" static void popup_callback(void* context) { + furi_assert(context); iButtonApp* app = static_cast(context); iButtonEvent event; event.type = iButtonEvent::Type::EventTypeBack; diff --git a/applications/storage/filesystem_api_defines.h b/applications/storage/filesystem_api_defines.h index e90e642f..cc33de26 100644 --- a/applications/storage/filesystem_api_defines.h +++ b/applications/storage/filesystem_api_defines.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/storage/filesystem_api_internal.h b/applications/storage/filesystem_api_internal.h index c67e5395..29098e59 100644 --- a/applications/storage/filesystem_api_internal.h +++ b/applications/storage/filesystem_api_internal.h @@ -75,21 +75,21 @@ struct File { * @return end of file flag */ typedef struct { - bool (*open)( + bool (*const open)( void* context, File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode); - bool (*close)(void* context, File* file); + bool (*const close)(void* context, File* file); uint16_t (*read)(void* context, File* file, void* buff, uint16_t bytes_to_read); uint16_t (*write)(void* context, File* file, const void* buff, uint16_t bytes_to_write); - bool (*seek)(void* context, File* file, uint32_t offset, bool from_start); + bool (*const seek)(void* context, File* file, uint32_t offset, bool from_start); uint64_t (*tell)(void* context, File* file); - bool (*truncate)(void* context, File* file); + bool (*const truncate)(void* context, File* file); uint64_t (*size)(void* context, File* file); - bool (*sync)(void* context, File* file); - bool (*eof)(void* context, File* file); + bool (*const sync)(void* context, File* file); + bool (*const eof)(void* context, File* file); } FS_File_Api; /** Dir api structure @@ -118,10 +118,15 @@ typedef struct { * @return success flag */ typedef struct { - bool (*open)(void* context, File* file, const char* path); - bool (*close)(void* context, File* file); - bool (*read)(void* context, File* file, FileInfo* fileinfo, char* name, uint16_t name_length); - bool (*rewind)(void* context, File* file); + bool (*const open)(void* context, File* file, const char* path); + bool (*const close)(void* context, File* file); + bool (*const read)( + void* context, + File* file, + FileInfo* fileinfo, + char* name, + uint16_t name_length); + bool (*const rewind)(void* context, File* file); } FS_Dir_Api; /** Common api structure @@ -141,12 +146,6 @@ typedef struct { * @param path path to file/directory * @return FS_Error error info * - * @var FS_Common_Api::rename - * @brief Rename file/directory, - * file/directory must not be opened - * @param path path to file/directory - * @return FS_Error error info - * * @var FS_Common_Api::mkdir * @brief Create new directory * @param path path to new directory @@ -160,31 +159,21 @@ typedef struct { * @return FS_Error error info */ typedef struct { - FS_Error (*stat)(void* context, const char* path, FileInfo* fileinfo); - FS_Error (*remove)(void* context, const char* path); - FS_Error (*rename)(void* context, const char* old_path, const char* new_path); - FS_Error (*mkdir)(void* context, const char* path); - FS_Error ( - *fs_info)(void* context, const char* fs_path, uint64_t* total_space, uint64_t* free_space); + FS_Error (*const stat)(void* context, const char* path, FileInfo* fileinfo); + FS_Error (*const remove)(void* context, const char* path); + FS_Error (*const mkdir)(void* context, const char* path); + FS_Error (*const fs_info)( + void* context, + const char* fs_path, + uint64_t* total_space, + uint64_t* free_space); } FS_Common_Api; -/** Errors api structure - * @var FS_Error_Api::get_desc - * @brief Get error description text - * @param error_id FS_Error error id (for fire/dir functions result can be obtained from File.error_id) - * @return pointer to description text - */ -typedef struct { - const char* (*get_desc)(void* context, FS_Error error_id); -} FS_Error_Api; - /** Full filesystem api structure */ typedef struct { - FS_File_Api file; - FS_Dir_Api dir; - FS_Common_Api common; - FS_Error_Api error; - void* context; + const FS_File_Api file; + const FS_Dir_Api dir; + const FS_Common_Api common; } FS_Api; #ifdef __cplusplus diff --git a/applications/storage/storage.c b/applications/storage/storage.c index 9ce8d958..4aa5f105 100644 --- a/applications/storage/storage.c +++ b/applications/storage/storage.c @@ -11,6 +11,8 @@ #define ICON_SD_MOUNTED &I_SDcardMounted_11x8 #define ICON_SD_ERROR &I_SDcardFail_11x8 +#define TAG "Storage" + static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) { furi_assert(canvas); furi_assert(context); @@ -63,15 +65,14 @@ void storage_tick(Storage* app) { } } - if(app->storage[ST_EXT].status != app->prev_ext_storage_status) { - app->prev_ext_storage_status = app->storage[ST_EXT].status; - furi_pubsub_publish(app->pubsub, &app->storage[ST_EXT].status); - } - // storage not enabled but was enabled (sd card unmount) if(app->storage[ST_EXT].status == StorageStatusNotReady && app->sd_gui.enabled == true) { app->sd_gui.enabled = false; view_port_enabled_set(app->sd_gui.view_port, false); + + FURI_LOG_I(TAG, "SD card unmount"); + StorageEvent event = {.type = StorageEventTypeCardUnmount}; + furi_pubsub_publish(app->pubsub, &event); } // storage enabled (or in error state) but was not enabled (sd card mount) @@ -83,6 +84,16 @@ void storage_tick(Storage* app) { app->sd_gui.enabled == false) { app->sd_gui.enabled = true; view_port_enabled_set(app->sd_gui.view_port, true); + + if(app->storage[ST_EXT].status == StorageStatusOK) { + FURI_LOG_I(TAG, "SD card mount"); + StorageEvent event = {.type = StorageEventTypeCardMount}; + furi_pubsub_publish(app->pubsub, &event); + } else { + FURI_LOG_I(TAG, "SD card mount error"); + StorageEvent event = {.type = StorageEventTypeCardMountError}; + furi_pubsub_publish(app->pubsub, &event); + } } } diff --git a/applications/storage/storage.h b/applications/storage/storage.h index f70827cb..cf18d485 100644 --- a/applications/storage/storage.h +++ b/applications/storage/storage.h @@ -1,5 +1,6 @@ #pragma once -#include +#include +#include #include "filesystem_api_defines.h" #include "storage_sd_api.h" @@ -18,6 +19,23 @@ File* storage_file_alloc(Storage* storage); */ void storage_file_free(File* file); +typedef enum { + StorageEventTypeCardMount, + StorageEventTypeCardUnmount, + StorageEventTypeCardMountError, + StorageEventTypeFileClose, +} StorageEventType; + +typedef struct { + StorageEventType type; +} StorageEvent; + +/** + * Get storage pubsub. + * Storage will send StorageEvent messages. + * @param storage + * @return FuriPubSub* + */ FuriPubSub* storage_get_pubsub(Storage* storage); /******************* File Functions *******************/ diff --git a/applications/storage/storage_external_api.c b/applications/storage/storage_external_api.c index 9acc9ea4..927bc80e 100644 --- a/applications/storage/storage_external_api.c +++ b/applications/storage/storage_external_api.c @@ -3,6 +3,7 @@ #include "storage.h" #include "storage_i.h" #include "storage_message.h" +#include #define MAX_NAME_LENGTH 256 @@ -49,9 +50,12 @@ #define FILE_OPENED 1 #define FILE_CLOSED 0 +typedef enum { + StorageEventFlagFileClose = (1 << 0), +} StorageEventFlag; /****************** FILE ******************/ -bool storage_file_open( +static bool storage_file_open_internal( File* file, const char* path, FS_AccessMode access_mode, @@ -75,6 +79,41 @@ bool storage_file_open( return S_RETURN_BOOL; } +static void storage_file_close_callback(const void* message, void* context) { + const StorageEvent* storage_event = message; + + if(storage_event->type == StorageEventTypeFileClose) { + furi_assert(context); + osEventFlagsId_t event = context; + osEventFlagsSet(event, StorageEventFlagFileClose); + } +} + +bool storage_file_open( + File* file, + const char* path, + FS_AccessMode access_mode, + FS_OpenMode open_mode) { + bool result; + osEventFlagsId_t event = osEventFlagsNew(NULL); + FuriPubSubSubscription* subscription = furi_pubsub_subscribe( + storage_get_pubsub(file->storage), storage_file_close_callback, event); + + do { + result = storage_file_open_internal(file, path, access_mode, open_mode); + + if(!result && file->error_id == FSE_ALREADY_OPEN) { + osEventFlagsWait(event, StorageEventFlagFileClose, osFlagsWaitAny, osWaitForever); + } else { + break; + } + } while(true); + + furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); + osEventFlagsDelete(event); + return result; +} + bool storage_file_close(File* file) { S_FILE_API_PROLOGUE; S_API_PROLOGUE; @@ -259,31 +298,44 @@ FS_Error storage_common_remove(Storage* storage, const char* path) { } FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { - S_API_PROLOGUE; + FS_Error error = storage_common_copy(storage, old_path, new_path); + if(error == FSE_OK) { + error = storage_common_remove(storage, old_path); + } - SAData data = { - .cpaths = { - .old = old_path, - .new = new_path, - }}; - - S_API_MESSAGE(StorageCommandCommonRename); - S_API_EPILOGUE; - return S_RETURN_ERROR; + return error; } FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) { - S_API_PROLOGUE; + FS_Error error; - SAData data = { - .cpaths = { - .old = old_path, - .new = new_path, - }}; + FileInfo fileinfo; + error = storage_common_stat(storage, old_path, &fileinfo); - S_API_MESSAGE(StorageCommandCommonCopy); - S_API_EPILOGUE; - return S_RETURN_ERROR; + if(error == FSE_OK) { + if(fileinfo.flags & FSF_DIRECTORY) { + error = storage_common_mkdir(storage, new_path); + } else { + Stream* stream_from = file_stream_alloc(storage); + Stream* stream_to = file_stream_alloc(storage); + + do { + if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break; + if(!file_stream_open(stream_to, new_path, FSAM_WRITE, FSOM_CREATE_NEW)) break; + stream_copy_full(stream_from, stream_to); + } while(false); + + error = file_stream_get_error(stream_from); + if(error == FSE_OK) { + error = file_stream_get_error(stream_to); + } + + stream_free(stream_from); + stream_free(stream_to); + } + } + + return error; } FS_Error storage_common_mkdir(Storage* storage, const char* path) { diff --git a/applications/storage/storage_glue.c b/applications/storage/storage_glue.c index 6271fbd8..7ce8ec2c 100644 --- a/applications/storage/storage_glue.c +++ b/applications/storage/storage_glue.c @@ -103,25 +103,7 @@ bool storage_has_file(const File* file, StorageData* storage_data) { return result; } -StorageType storage_get_type_by_path(const char* path) { - StorageType type = ST_ERROR; - - const char* ext_path = "/ext"; - const char* int_path = "/int"; - const char* any_path = "/any"; - - if(strlen(path) >= strlen(ext_path) && memcmp(path, ext_path, strlen(ext_path)) == 0) { - type = ST_EXT; - } else if(strlen(path) >= strlen(int_path) && memcmp(path, int_path, strlen(int_path)) == 0) { - type = ST_INT; - } else if(strlen(path) >= strlen(any_path) && memcmp(path, any_path, strlen(any_path)) == 0) { - type = ST_ANY; - } - - return type; -} - -bool storage_path_already_open(const char* path, StorageFileList_t array) { +bool storage_path_already_open(string_t path, StorageFileList_t array) { bool open = false; StorageFileList_it_t it; @@ -178,11 +160,7 @@ void* storage_get_storage_file_data(const File* file, StorageData* storage) { return founded_file->file_data; } -void storage_push_storage_file( - File* file, - const char* path, - StorageType type, - StorageData* storage) { +void storage_push_storage_file(File* file, string_t path, StorageType type, StorageData* storage) { StorageFile* storage_file = StorageFileList_push_new(storage->files); furi_check(storage_file != NULL); diff --git a/applications/storage/storage_glue.h b/applications/storage/storage_glue.h index bd8f317b..21147dce 100644 --- a/applications/storage/storage_glue.h +++ b/applications/storage/storage_glue.h @@ -3,7 +3,6 @@ #include #include "filesystem_api_internal.h" #include -#include #include #ifdef __cplusplus @@ -54,7 +53,7 @@ LIST_DEF( CLEAR(API_2(storage_file_clear)))) struct StorageData { - FS_Api fs_api; + const FS_Api* fs_api; StorageApi api; void* data; osMutexId_t mutex; @@ -63,17 +62,12 @@ struct StorageData { }; bool storage_has_file(const File* file, StorageData* storage_data); -StorageType storage_get_type_by_path(const char* path); -bool storage_path_already_open(const char* path, StorageFileList_t files); +bool storage_path_already_open(string_t path, StorageFileList_t files); void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage); void* storage_get_storage_file_data(const File* file, StorageData* storage); -void storage_push_storage_file( - File* file, - const char* path, - StorageType type, - StorageData* storage); +void storage_push_storage_file(File* file, string_t path, StorageType type, StorageData* storage); bool storage_pop_storage_file(File* file, StorageData* storage); #ifdef __cplusplus diff --git a/applications/storage/storage_i.h b/applications/storage/storage_i.h index d69a0ef8..0db5218d 100644 --- a/applications/storage/storage_i.h +++ b/applications/storage/storage_i.h @@ -19,7 +19,6 @@ typedef struct { struct Storage { osMessageQueueId_t message_queue; StorageData storage[STORAGE_COUNT]; - StorageStatus prev_ext_storage_status; StorageSDGui sd_gui; FuriPubSub* pubsub; }; diff --git a/applications/storage/storage_message.h b/applications/storage/storage_message.h index 0d409587..0e58fff2 100644 --- a/applications/storage/storage_message.h +++ b/applications/storage/storage_message.h @@ -47,11 +47,6 @@ typedef struct { FileInfo* fileinfo; } SADataCStat; -typedef struct { - const char* old; - const char* new; -} SADataCPaths; - typedef struct { const char* fs_path; uint64_t* total_space; @@ -84,7 +79,6 @@ typedef union { SADataDRead dread; SADataCStat cstat; - SADataCPaths cpaths; SADataCFSInfo cfsinfo; SADataError error; @@ -120,8 +114,6 @@ typedef enum { StorageCommandDirRewind, StorageCommandCommonStat, StorageCommandCommonRemove, - StorageCommandCommonRename, - StorageCommandCommonCopy, StorageCommandCommonMkDir, StorageCommandCommonFSInfo, StorageCommandSDFormat, diff --git a/applications/storage/storage_processing.c b/applications/storage/storage_processing.c index ccf7de9a..a96d9cfc 100644 --- a/applications/storage/storage_processing.c +++ b/applications/storage/storage_processing.c @@ -1,8 +1,11 @@ #include "storage_processing.h" +#include +#include +#include #define FS_CALL(_storage, _fn) \ storage_data_lock(_storage); \ - ret = _storage->fs_api._fn; \ + ret = _storage->fs_api->_fn; \ storage_data_unlock(_storage); #define ST_CALL(_storage, _fn) \ @@ -11,18 +14,8 @@ storage_data_unlock(_storage); static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) { - StorageData* storage; - - if(type == ST_ANY) { - type = ST_INT; - StorageData* ext_storage = &app->storage[ST_EXT]; - - if(storage_data_status(ext_storage) == StorageStatusOK) { - type = ST_EXT; - } - } - storage = &app->storage[type]; - + furi_check(type == ST_EXT || type == ST_INT); + StorageData* storage = &app->storage[type]; return storage; } @@ -42,10 +35,55 @@ static StorageData* get_storage_by_file(File* file, StorageData* storages) { return storage_data; } -const char* remove_vfs(const char* path) { +static const char* remove_vfs(const char* path) { return path + MIN(4, strlen(path)); } +static const char* ext_path = "/ext"; +static const char* int_path = "/int"; +static const char* any_path = "/any"; + +static StorageType storage_get_type_by_path(Storage* app, const char* path) { + StorageType type = ST_ERROR; + if(strlen(path) >= strlen(ext_path) && memcmp(path, ext_path, strlen(ext_path)) == 0) { + type = ST_EXT; + } else if(strlen(path) >= strlen(int_path) && memcmp(path, int_path, strlen(int_path)) == 0) { + type = ST_INT; + } else if(strlen(path) >= strlen(any_path) && memcmp(path, any_path, strlen(any_path)) == 0) { + type = ST_ANY; + } + + if(type == ST_ANY) { + type = ST_INT; + if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) { + type = ST_EXT; + } + } + + return type; +} + +static void storage_path_change_to_real_storage(string_t path, StorageType real_storage) { + if(memcmp(string_get_cstr(path), any_path, strlen(any_path)) == 0) { + switch(real_storage) { + case ST_EXT: + string_set_char(path, 0, ext_path[0]); + string_set_char(path, 1, ext_path[1]); + string_set_char(path, 2, ext_path[2]); + string_set_char(path, 3, ext_path[3]); + break; + case ST_INT: + string_set_char(path, 0, int_path[0]); + string_set_char(path, 1, int_path[1]); + string_set_char(path, 2, int_path[2]); + string_set_char(path, 3, int_path[3]); + break; + default: + break; + } + } +} + /******************* File Functions *******************/ bool storage_process_file_open( @@ -55,7 +93,7 @@ bool storage_process_file_open( FS_AccessMode access_mode, FS_OpenMode open_mode) { bool ret = false; - StorageType type = storage_get_type_by_path(path); + StorageType type = storage_get_type_by_path(app, path); StorageData* storage; file->error_id = FSE_OK; @@ -63,12 +101,18 @@ bool storage_process_file_open( file->error_id = FSE_INVALID_NAME; } else { storage = storage_get_storage_by_type(app, type); - if(storage_path_already_open(path, storage->files)) { + string_t real_path; + string_init_set(real_path, path); + storage_path_change_to_real_storage(real_path, type); + + if(storage_path_already_open(real_path, storage->files)) { file->error_id = FSE_ALREADY_OPEN; } else { - storage_push_storage_file(file, path, type, storage); + storage_push_storage_file(file, real_path, type, storage); FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode)); } + + string_clear(real_path); } return ret; @@ -83,6 +127,9 @@ bool storage_process_file_close(Storage* app, File* file) { } else { FS_CALL(storage, file.close(storage, file)); storage_pop_storage_file(file, storage); + + StorageEvent event = {.type = StorageEventTypeFileClose}; + furi_pubsub_publish(app->pubsub, &event); } return ret; @@ -205,7 +252,7 @@ static bool storage_process_file_eof(Storage* app, File* file) { bool storage_process_dir_open(Storage* app, File* file, const char* path) { bool ret = false; - StorageType type = storage_get_type_by_path(path); + StorageType type = storage_get_type_by_path(app, path); StorageData* storage; file->error_id = FSE_OK; @@ -213,12 +260,17 @@ bool storage_process_dir_open(Storage* app, File* file, const char* path) { file->error_id = FSE_INVALID_NAME; } else { storage = storage_get_storage_by_type(app, type); - if(storage_path_already_open(path, storage->files)) { + string_t real_path; + string_init_set(real_path, path); + storage_path_change_to_real_storage(real_path, type); + + if(storage_path_already_open(real_path, storage->files)) { file->error_id = FSE_ALREADY_OPEN; } else { - storage_push_storage_file(file, path, type, storage); + storage_push_storage_file(file, real_path, type, storage); FS_CALL(storage, dir.open(storage, file, remove_vfs(path))); } + string_clear(real_path); } return ret; @@ -273,7 +325,7 @@ bool storage_process_dir_rewind(Storage* app, File* file) { static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) { FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(path); + StorageType type = storage_get_type_by_path(app, path); if(storage_type_is_not_valid(type)) { ret = FSE_INVALID_NAME; @@ -287,7 +339,11 @@ static FS_Error storage_process_common_stat(Storage* app, const char* path, File static FS_Error storage_process_common_remove(Storage* app, const char* path) { FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(path); + StorageType type = storage_get_type_by_path(app, path); + + string_t real_path; + string_init_set(real_path, path); + storage_path_change_to_real_storage(real_path, type); do { if(storage_type_is_not_valid(type)) { @@ -296,7 +352,7 @@ static FS_Error storage_process_common_remove(Storage* app, const char* path) { } StorageData* storage = storage_get_storage_by_type(app, type); - if(storage_path_already_open(path, storage->files)) { + if(storage_path_already_open(real_path, storage->files)) { ret = FSE_ALREADY_OPEN; break; } @@ -304,12 +360,14 @@ static FS_Error storage_process_common_remove(Storage* app, const char* path) { FS_CALL(storage, common.remove(storage, remove_vfs(path))); } while(false); + string_clear(real_path); + return ret; } static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(path); + StorageType type = storage_get_type_by_path(app, path); if(storage_type_is_not_valid(type)) { ret = FSE_INVALID_NAME; @@ -321,86 +379,13 @@ static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { return ret; } -static FS_Error storage_process_common_copy(Storage* app, const char* old, const char* new) { - FS_Error ret = FSE_INTERNAL; - File file_old; - File file_new; - - FileInfo fileinfo; - ret = storage_process_common_stat(app, old, &fileinfo); - - if(ret == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { - ret = storage_process_common_mkdir(app, new); - } else { - do { - if(!storage_process_file_open(app, &file_old, old, FSAM_READ, FSOM_OPEN_EXISTING)) { - ret = storage_file_get_error(&file_old); - storage_process_file_close(app, &file_old); - break; - } - - if(!storage_process_file_open(app, &file_new, new, FSAM_WRITE, FSOM_CREATE_NEW)) { - ret = storage_file_get_error(&file_new); - storage_process_file_close(app, &file_new); - storage_process_file_close(app, &file_old); - break; - } - - const uint16_t buffer_size = 64; - uint8_t* buffer = malloc(buffer_size); - uint16_t readed_size = 0; - uint16_t writed_size = 0; - - while(true) { - readed_size = storage_process_file_read(app, &file_old, buffer, buffer_size); - ret = storage_file_get_error(&file_old); - if(readed_size == 0) break; - - writed_size = storage_process_file_write(app, &file_new, buffer, readed_size); - ret = storage_file_get_error(&file_new); - if(writed_size < readed_size) break; - } - - free(buffer); - storage_process_file_close(app, &file_old); - storage_process_file_close(app, &file_new); - } while(false); - } - } - - return ret; -} - -static FS_Error storage_process_common_rename(Storage* app, const char* old, const char* new) { - FS_Error ret = FSE_INTERNAL; - StorageType type_old = storage_get_type_by_path(old); - StorageType type_new = storage_get_type_by_path(new); - - if(storage_type_is_not_valid(type_old) || storage_type_is_not_valid(type_new)) { - ret = FSE_INVALID_NAME; - } else { - if(type_old != type_new) { - ret = storage_process_common_copy(app, old, new); - if(ret == FSE_OK) { - ret = storage_process_common_remove(app, old); - } - } else { - StorageData* storage = storage_get_storage_by_type(app, type_old); - FS_CALL(storage, common.rename(storage, remove_vfs(old), remove_vfs(new))); - } - } - - return ret; -} - static FS_Error storage_process_common_fs_info( Storage* app, const char* fs_path, uint64_t* total_space, uint64_t* free_space) { FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(fs_path); + StorageType type = storage_get_type_by_path(app, fs_path); if(storage_type_is_not_valid(type)) { ret = FSE_INVALID_NAME; @@ -472,8 +457,7 @@ static FS_Error storage_process_sd_status(Storage* app) { } /****************** API calls processing ******************/ - -void storage_process_message(Storage* app, StorageMessage* message) { +void storage_process_message_internal(Storage* app, StorageMessage* message) { switch(message->command) { case StorageCommandFileOpen: message->return_data->bool_value = storage_process_file_open( @@ -556,14 +540,6 @@ void storage_process_message(Storage* app, StorageMessage* message) { message->return_data->error_value = storage_process_common_remove(app, message->data->path.path); break; - case StorageCommandCommonRename: - message->return_data->error_value = storage_process_common_rename( - app, message->data->cpaths.old, message->data->cpaths.new); - break; - case StorageCommandCommonCopy: - message->return_data->error_value = - storage_process_common_copy(app, message->data->cpaths.old, message->data->cpaths.new); - break; case StorageCommandCommonMkDir: message->return_data->error_value = storage_process_common_mkdir(app, message->data->path.path); @@ -592,3 +568,7 @@ void storage_process_message(Storage* app, StorageMessage* message) { osSemaphoreRelease(message->semaphore); } + +void storage_process_message(Storage* app, StorageMessage* message) { + storage_process_message_internal(app, message); +} diff --git a/applications/storage/storage_sd_api.h b/applications/storage/storage_sd_api.h index fe6f94af..2db35866 100644 --- a/applications/storage/storage_sd_api.h +++ b/applications/storage/storage_sd_api.h @@ -1,8 +1,6 @@ #pragma once #include #include "filesystem_api_defines.h" -#include -#include "storage_glue.h" #ifdef __cplusplus extern "C" { @@ -11,10 +9,11 @@ extern "C" { #define SD_LABEL_LENGTH 34 typedef enum { - FST_FAT12 = FS_FAT12, - FST_FAT16 = FS_FAT16, - FST_FAT32 = FS_FAT32, - FST_EXFAT = FS_EXFAT, + FST_UNKNOWN, + FST_FAT12, + FST_FAT16, + FST_FAT32, + FST_EXFAT, } SDFsType; typedef struct { diff --git a/applications/storage/storages/storage_ext.c b/applications/storage/storages/storage_ext.c index 1b76ef6c..8a3d21ca 100644 --- a/applications/storage/storages/storage_ext.c +++ b/applications/storage/storages/storage_ext.c @@ -164,7 +164,24 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { sector_size = fs->ssize; #endif - sd_info->fs_type = fs->fs_type; + switch(fs->fs_type) { + case FS_FAT12: + sd_info->fs_type = FST_FAT12; + break; + case FS_FAT16: + sd_info->fs_type = FST_FAT16; + break; + case FS_FAT32: + sd_info->fs_type = FST_FAT32; + break; + case FS_EXFAT: + sd_info->fs_type = FST_EXFAT; + break; + + default: + sd_info->fs_type = FST_UNKNOWN; + break; + } sd_info->kb_total = total_sectors / 1024 * sector_size; sd_info->kb_free = free_sectors / 1024 * sector_size; @@ -466,11 +483,6 @@ static FS_Error storage_ext_common_remove(void* ctx, const char* path) { return storage_ext_parse_error(result); } -static FS_Error storage_ext_common_rename(void* ctx, const char* old_path, const char* new_path) { - SDError result = f_rename(old_path, new_path); - return storage_ext_parse_error(result); -} - static FS_Error storage_ext_common_mkdir(void* ctx, const char* path) { SDError result = f_mkdir(path); return storage_ext_parse_error(result); @@ -510,6 +522,35 @@ static FS_Error storage_ext_common_fs_info( } /******************* Init Storage *******************/ +static const FS_Api fs_api = { + .file = + { + .open = storage_ext_file_open, + .close = storage_ext_file_close, + .read = storage_ext_file_read, + .write = storage_ext_file_write, + .seek = storage_ext_file_seek, + .tell = storage_ext_file_tell, + .truncate = storage_ext_file_truncate, + .size = storage_ext_file_size, + .sync = storage_ext_file_sync, + .eof = storage_ext_file_eof, + }, + .dir = + { + .open = storage_ext_dir_open, + .close = storage_ext_dir_close, + .read = storage_ext_dir_read, + .rewind = storage_ext_dir_rewind, + }, + .common = + { + .stat = storage_ext_common_stat, + .mkdir = storage_ext_common_mkdir, + .remove = storage_ext_common_remove, + .fs_info = storage_ext_common_fs_info, + }, +}; void storage_ext_init(StorageData* storage) { SDData* sd_data = malloc(sizeof(SDData)); @@ -519,27 +560,7 @@ void storage_ext_init(StorageData* storage) { storage->data = sd_data; storage->api.tick = storage_ext_tick; - storage->fs_api.file.open = storage_ext_file_open; - storage->fs_api.file.close = storage_ext_file_close; - storage->fs_api.file.read = storage_ext_file_read; - storage->fs_api.file.write = storage_ext_file_write; - storage->fs_api.file.seek = storage_ext_file_seek; - storage->fs_api.file.tell = storage_ext_file_tell; - storage->fs_api.file.truncate = storage_ext_file_truncate; - storage->fs_api.file.size = storage_ext_file_size; - storage->fs_api.file.sync = storage_ext_file_sync; - storage->fs_api.file.eof = storage_ext_file_eof; - - storage->fs_api.dir.open = storage_ext_dir_open; - storage->fs_api.dir.close = storage_ext_dir_close; - storage->fs_api.dir.read = storage_ext_dir_read; - storage->fs_api.dir.rewind = storage_ext_dir_rewind; - - storage->fs_api.common.stat = storage_ext_common_stat; - storage->fs_api.common.mkdir = storage_ext_common_mkdir; - storage->fs_api.common.rename = storage_ext_common_rename; - storage->fs_api.common.remove = storage_ext_common_remove; - storage->fs_api.common.fs_info = storage_ext_common_fs_info; + storage->fs_api = &fs_api; hal_sd_detect_init(); diff --git a/applications/storage/storages/storage_int.c b/applications/storage/storages/storage_int.c index 811a1d6a..2f610929 100644 --- a/applications/storage/storages/storage_int.c +++ b/applications/storage/storages/storage_int.c @@ -636,13 +636,6 @@ static FS_Error storage_int_common_remove(void* ctx, const char* path) { return storage_int_parse_error(result); } -static FS_Error storage_int_common_rename(void* ctx, const char* old_path, const char* new_path) { - StorageData* storage = ctx; - lfs_t* lfs = lfs_get_from_storage(storage); - int result = lfs_rename(lfs, old_path, new_path); - return storage_int_parse_error(result); -} - static FS_Error storage_int_common_mkdir(void* ctx, const char* path) { StorageData* storage = ctx; lfs_t* lfs = lfs_get_from_storage(storage); @@ -671,6 +664,35 @@ static FS_Error storage_int_common_fs_info( } /******************* Init Storage *******************/ +static const FS_Api fs_api = { + .file = + { + .open = storage_int_file_open, + .close = storage_int_file_close, + .read = storage_int_file_read, + .write = storage_int_file_write, + .seek = storage_int_file_seek, + .tell = storage_int_file_tell, + .truncate = storage_int_file_truncate, + .size = storage_int_file_size, + .sync = storage_int_file_sync, + .eof = storage_int_file_eof, + }, + .dir = + { + .open = storage_int_dir_open, + .close = storage_int_dir_close, + .read = storage_int_dir_read, + .rewind = storage_int_dir_rewind, + }, + .common = + { + .stat = storage_int_common_stat, + .mkdir = storage_int_common_mkdir, + .remove = storage_int_common_remove, + .fs_info = storage_int_common_fs_info, + }, +}; void storage_int_init(StorageData* storage) { FURI_LOG_I(TAG, "Starting"); @@ -689,25 +711,5 @@ void storage_int_init(StorageData* storage) { storage->data = lfs_data; storage->api.tick = NULL; - storage->fs_api.file.open = storage_int_file_open; - storage->fs_api.file.close = storage_int_file_close; - storage->fs_api.file.read = storage_int_file_read; - storage->fs_api.file.write = storage_int_file_write; - storage->fs_api.file.seek = storage_int_file_seek; - storage->fs_api.file.tell = storage_int_file_tell; - storage->fs_api.file.truncate = storage_int_file_truncate; - storage->fs_api.file.size = storage_int_file_size; - storage->fs_api.file.sync = storage_int_file_sync; - storage->fs_api.file.eof = storage_int_file_eof; - - storage->fs_api.dir.open = storage_int_dir_open; - storage->fs_api.dir.close = storage_int_dir_close; - storage->fs_api.dir.read = storage_int_dir_read; - storage->fs_api.dir.rewind = storage_int_dir_rewind; - - storage->fs_api.common.stat = storage_int_common_stat; - storage->fs_api.common.mkdir = storage_int_common_mkdir; - storage->fs_api.common.rename = storage_int_common_rename; - storage->fs_api.common.remove = storage_int_common_remove; - storage->fs_api.common.fs_info = storage_int_common_fs_info; + storage->fs_api = &fs_api; } diff --git a/applications/tests/storage/storage_test.c b/applications/tests/storage/storage_test.c new file mode 100644 index 00000000..e6ae8c81 --- /dev/null +++ b/applications/tests/storage/storage_test.c @@ -0,0 +1,80 @@ +#include "../minunit.h" +#include +#include +#include + +#define STORAGE_LOCKED_FILE "/ext/locked_file.test" + +static void storage_file_open_lock_setup() { + Storage* storage = furi_record_open("storage"); + File* file = storage_file_alloc(storage); + storage_simply_remove(storage, STORAGE_LOCKED_FILE); + mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_WRITE, FSOM_CREATE_NEW)); + mu_check(storage_file_write(file, "0123", 4) == 4); + mu_check(storage_file_close(file)); + storage_file_free(file); + furi_record_close("storage"); +} + +static void storage_file_open_lock_teardown() { + Storage* storage = furi_record_open("storage"); + mu_check(storage_simply_remove(storage, STORAGE_LOCKED_FILE)); + furi_record_close("storage"); +} + +static int32_t storage_file_locker(void* ctx) { + Storage* storage = furi_record_open("storage"); + osSemaphoreId_t semaphore = ctx; + File* file = storage_file_alloc(storage); + furi_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); + osSemaphoreRelease(semaphore); + furi_hal_delay_ms(1000); + + furi_check(storage_file_close(file)); + furi_record_close("storage"); + storage_file_free(file); + return 0; +} + +MU_TEST(storage_file_open_lock) { + Storage* storage = furi_record_open("storage"); + bool result = false; + osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); + File* file = storage_file_alloc(storage); + + // file_locker thread start + FuriThread* locker_thread = furi_thread_alloc(); + furi_thread_set_name(locker_thread, "StorageFileLocker"); + furi_thread_set_stack_size(locker_thread, 2048); + furi_thread_set_context(locker_thread, semaphore); + furi_thread_set_callback(locker_thread, storage_file_locker); + mu_check(furi_thread_start(locker_thread)); + + // wait for file lock + osSemaphoreAcquire(semaphore, osWaitForever); + osSemaphoreDelete(semaphore); + + result = storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING); + storage_file_close(file); + + // file_locker thread stop + mu_check(furi_thread_join(locker_thread) == osOK); + furi_thread_free(locker_thread); + + // clean data + storage_file_free(file); + furi_record_close("storage"); + + mu_assert(result, "cannot open locked file"); +} + +MU_TEST_SUITE(storage_file) { + storage_file_open_lock_setup(); + MU_RUN_TEST(storage_file_open_lock); + storage_file_open_lock_teardown(); +} + +int run_minunit_test_storage() { + MU_RUN_SUITE(storage_file); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/tests/test_index.c b/applications/tests/test_index.c index 391f759e..16e75414 100644 --- a/applications/tests/test_index.c +++ b/applications/tests/test_index.c @@ -16,6 +16,7 @@ int run_minunit_test_rpc(); int run_minunit_test_flipper_format(); int run_minunit_test_flipper_format_string(); int run_minunit_test_stream(); +int run_minunit_test_storage(); void minunit_print_progress(void) { static char progress[] = {'\\', '|', '/', '-'}; @@ -53,11 +54,12 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) { uint32_t cycle_counter = DWT->CYCCNT; test_result |= run_minunit(); - test_result |= run_minunit_test_infrared_decoder_encoder(); - test_result |= run_minunit_test_rpc(); + test_result |= run_minunit_test_storage(); test_result |= run_minunit_test_stream(); test_result |= run_minunit_test_flipper_format(); test_result |= run_minunit_test_flipper_format_string(); + test_result |= run_minunit_test_infrared_decoder_encoder(); + test_result |= run_minunit_test_rpc(); cycle_counter = (DWT->CYCCNT - cycle_counter); FURI_LOG_I(TAG, "Consumed: %0.2fs", (float)cycle_counter / (SystemCoreClock)); diff --git a/lib/toolbox/stream/file_stream.c b/lib/toolbox/stream/file_stream.c index 56dc1de8..f38ae898 100644 --- a/lib/toolbox/stream/file_stream.c +++ b/lib/toolbox/stream/file_stream.c @@ -61,6 +61,13 @@ bool file_stream_close(Stream* _stream) { return storage_file_close(stream->file); } +FS_Error file_stream_get_error(Stream* _stream) { + furi_assert(_stream); + FileStream* stream = (FileStream*)_stream; + furi_check(stream->stream_base.vtable == &file_stream_vtable); + return storage_file_get_error(stream->file); +} + static void file_stream_free(FileStream* stream) { storage_file_free(stream->file); free(stream); diff --git a/lib/toolbox/stream/file_stream.h b/lib/toolbox/stream/file_stream.h index 3f19d07b..ec371a53 100644 --- a/lib/toolbox/stream/file_stream.h +++ b/lib/toolbox/stream/file_stream.h @@ -35,6 +35,13 @@ bool file_stream_open( */ bool file_stream_close(Stream* stream); +/** + * Retrieves the error id from the file object + * @param stream pointer to stream object. + * @return FS_Error error id + */ +FS_Error file_stream_get_error(Stream* stream); + #ifdef __cplusplus } #endif \ No newline at end of file