[FL-2415] Storage: blocking file open (#1078)
* Storage: correct replacement for "/any" path in path holder * Unit tests: storage, blocking file open test * File stream: error getter * Storage: common copy and common remove now executes in external thread * Filesystems: got rid of unused functions * Storage: untangle dependencies, ram-frendly filesystem api * iButton: context assertions * Storage: pubsub messages * Storage: wait for the file to close if it was open * Storage: fix folder copying * Storage: unit test * Storage: pubsub documentation * Fix merge error * Fix memleak in storage test * Storage: remove unused define Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
cb7d43f7e1
commit
855f2584ab
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "file_worker.h"
|
#include "file_worker.h"
|
||||||
|
#include <m-array.h>
|
||||||
|
|
||||||
#define MAX_FILES 100 //temp
|
#define MAX_FILES 100 //temp
|
||||||
|
|
||||||
|
@ -94,11 +94,22 @@ void animation_manager_set_interact_callback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void animation_manager_check_blocking_callback(const void* message, void* context) {
|
static void animation_manager_check_blocking_callback(const void* message, void* context) {
|
||||||
|
const StorageEvent* storage_event = message;
|
||||||
|
|
||||||
|
switch(storage_event->type) {
|
||||||
|
case StorageEventTypeCardMount:
|
||||||
|
case StorageEventTypeCardUnmount:
|
||||||
|
case StorageEventTypeCardMountError:
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
AnimationManager* animation_manager = context;
|
AnimationManager* animation_manager = context;
|
||||||
if(animation_manager->check_blocking_callback) {
|
if(animation_manager->check_blocking_callback) {
|
||||||
animation_manager->check_blocking_callback(animation_manager->context);
|
animation_manager->check_blocking_callback(animation_manager->context);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void animation_manager_timer_callback(void* context) {
|
static void animation_manager_timer_callback(void* context) {
|
||||||
|
@ -63,6 +63,7 @@ void ibutton_cli_print_key_data(iButtonKey* key) {
|
|||||||
#define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0)
|
#define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0)
|
||||||
|
|
||||||
static void ibutton_cli_worker_read_cb(void* context) {
|
static void ibutton_cli_worker_read_cb(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
osEventFlagsId_t event = context;
|
osEventFlagsId_t event = context;
|
||||||
osEventFlagsSet(event, EVENT_FLAG_IBUTTON_COMPLETE);
|
osEventFlagsSet(event, EVENT_FLAG_IBUTTON_COMPLETE);
|
||||||
}
|
}
|
||||||
@ -112,6 +113,7 @@ typedef struct {
|
|||||||
} iButtonWriteContext;
|
} iButtonWriteContext;
|
||||||
|
|
||||||
static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) {
|
static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonWriteContext* write_context = (iButtonWriteContext*)context;
|
iButtonWriteContext* write_context = (iButtonWriteContext*)context;
|
||||||
write_context->result = result;
|
write_context->result = result;
|
||||||
osEventFlagsSet(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE);
|
osEventFlagsSet(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE);
|
||||||
|
@ -9,6 +9,7 @@ typedef enum {
|
|||||||
} SubmenuIndex;
|
} SubmenuIndex;
|
||||||
|
|
||||||
static void submenu_callback(void* context, uint32_t index) {
|
static void submenu_callback(void* context, uint32_t index) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
static void byte_input_callback(void* context) {
|
static void byte_input_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "../ibutton_app.h"
|
#include "../ibutton_app.h"
|
||||||
|
|
||||||
static void widget_callback(GuiButtonType result, InputType type, void* context) {
|
static void widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "../ibutton_app.h"
|
#include "../ibutton_app.h"
|
||||||
|
|
||||||
static void popup_callback(void* context) {
|
static void popup_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
event.type = iButtonEvent::Type::EventTypeBack;
|
event.type = iButtonEvent::Type::EventTypeBack;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
static void emulate_callback(void* context, bool emulated) {
|
static void emulate_callback(void* context, bool emulated) {
|
||||||
|
furi_assert(context);
|
||||||
if(emulated) {
|
if(emulated) {
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerEmulated};
|
iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerEmulated};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "../ibutton_app.h"
|
#include "../ibutton_app.h"
|
||||||
|
|
||||||
static void widget_callback(GuiButtonType result, InputType type, void* context) {
|
static void widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
static void read_callback(void* context) {
|
static void read_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerRead};
|
iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerRead};
|
||||||
app->get_view_manager()->send_event(&event);
|
app->get_view_manager()->send_event(&event);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <one_wire/maxim_crc.h>
|
#include <one_wire/maxim_crc.h>
|
||||||
|
|
||||||
static void dialog_ex_callback(DialogExResult result, void* context) {
|
static void dialog_ex_callback(DialogExResult result, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <one_wire/maxim_crc.h>
|
#include <one_wire/maxim_crc.h>
|
||||||
|
|
||||||
static void dialog_ex_callback(DialogExResult result, void* context) {
|
static void dialog_ex_callback(DialogExResult result, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
static void dialog_ex_callback(DialogExResult result, void* context) {
|
static void dialog_ex_callback(DialogExResult result, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ typedef enum {
|
|||||||
} SubmenuIndex;
|
} SubmenuIndex;
|
||||||
|
|
||||||
static void submenu_callback(void* context, uint32_t index) {
|
static void submenu_callback(void* context, uint32_t index) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <lib/toolbox/random_name.h>
|
#include <lib/toolbox/random_name.h>
|
||||||
|
|
||||||
static void text_input_callback(void* context) {
|
static void text_input_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
static void popup_callback(void* context) {
|
static void popup_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
event.type = iButtonEvent::Type::EventTypeBack;
|
event.type = iButtonEvent::Type::EventTypeBack;
|
||||||
|
@ -11,6 +11,7 @@ typedef enum {
|
|||||||
} SubmenuIndex;
|
} SubmenuIndex;
|
||||||
|
|
||||||
static void submenu_callback(void* context, uint32_t index) {
|
static void submenu_callback(void* context, uint32_t index) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ typedef enum {
|
|||||||
} SubmenuIndex;
|
} SubmenuIndex;
|
||||||
|
|
||||||
static void submenu_callback(void* context, uint32_t index) {
|
static void submenu_callback(void* context, uint32_t index) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "../ibutton_app.h"
|
#include "../ibutton_app.h"
|
||||||
|
|
||||||
static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) {
|
static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
event.type = iButtonEvent::Type::EventTypeWorkerWrite;
|
event.type = iButtonEvent::Type::EventTypeWorkerWrite;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "../ibutton_app.h"
|
#include "../ibutton_app.h"
|
||||||
|
|
||||||
static void popup_callback(void* context) {
|
static void popup_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
iButtonApp* app = static_cast<iButtonApp*>(context);
|
iButtonApp* app = static_cast<iButtonApp*>(context);
|
||||||
iButtonEvent event;
|
iButtonEvent event;
|
||||||
event.type = iButtonEvent::Type::EventTypeBack;
|
event.type = iButtonEvent::Type::EventTypeBack;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <furi.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -75,21 +75,21 @@ struct File {
|
|||||||
* @return end of file flag
|
* @return end of file flag
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool (*open)(
|
bool (*const open)(
|
||||||
void* context,
|
void* context,
|
||||||
File* file,
|
File* file,
|
||||||
const char* path,
|
const char* path,
|
||||||
FS_AccessMode access_mode,
|
FS_AccessMode access_mode,
|
||||||
FS_OpenMode open_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 (*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);
|
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);
|
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);
|
uint64_t (*size)(void* context, File* file);
|
||||||
bool (*sync)(void* context, File* file);
|
bool (*const sync)(void* context, File* file);
|
||||||
bool (*eof)(void* context, File* file);
|
bool (*const eof)(void* context, File* file);
|
||||||
} FS_File_Api;
|
} FS_File_Api;
|
||||||
|
|
||||||
/** Dir api structure
|
/** Dir api structure
|
||||||
@ -118,10 +118,15 @@ typedef struct {
|
|||||||
* @return success flag
|
* @return success flag
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool (*open)(void* context, File* file, const char* path);
|
bool (*const open)(void* context, File* file, const char* path);
|
||||||
bool (*close)(void* context, File* file);
|
bool (*const close)(void* context, File* file);
|
||||||
bool (*read)(void* context, File* file, FileInfo* fileinfo, char* name, uint16_t name_length);
|
bool (*const read)(
|
||||||
bool (*rewind)(void* context, File* file);
|
void* context,
|
||||||
|
File* file,
|
||||||
|
FileInfo* fileinfo,
|
||||||
|
char* name,
|
||||||
|
uint16_t name_length);
|
||||||
|
bool (*const rewind)(void* context, File* file);
|
||||||
} FS_Dir_Api;
|
} FS_Dir_Api;
|
||||||
|
|
||||||
/** Common api structure
|
/** Common api structure
|
||||||
@ -141,12 +146,6 @@ typedef struct {
|
|||||||
* @param path path to file/directory
|
* @param path path to file/directory
|
||||||
* @return FS_Error error info
|
* @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
|
* @var FS_Common_Api::mkdir
|
||||||
* @brief Create new directory
|
* @brief Create new directory
|
||||||
* @param path path to new directory
|
* @param path path to new directory
|
||||||
@ -160,31 +159,21 @@ typedef struct {
|
|||||||
* @return FS_Error error info
|
* @return FS_Error error info
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FS_Error (*stat)(void* context, const char* path, FileInfo* fileinfo);
|
FS_Error (*const stat)(void* context, const char* path, FileInfo* fileinfo);
|
||||||
FS_Error (*remove)(void* context, const char* path);
|
FS_Error (*const remove)(void* context, const char* path);
|
||||||
FS_Error (*rename)(void* context, const char* old_path, const char* new_path);
|
FS_Error (*const mkdir)(void* context, const char* path);
|
||||||
FS_Error (*mkdir)(void* context, const char* path);
|
FS_Error (*const fs_info)(
|
||||||
FS_Error (
|
void* context,
|
||||||
*fs_info)(void* context, const char* fs_path, uint64_t* total_space, uint64_t* free_space);
|
const char* fs_path,
|
||||||
|
uint64_t* total_space,
|
||||||
|
uint64_t* free_space);
|
||||||
} FS_Common_Api;
|
} 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 */
|
/** Full filesystem api structure */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FS_File_Api file;
|
const FS_File_Api file;
|
||||||
FS_Dir_Api dir;
|
const FS_Dir_Api dir;
|
||||||
FS_Common_Api common;
|
const FS_Common_Api common;
|
||||||
FS_Error_Api error;
|
|
||||||
void* context;
|
|
||||||
} FS_Api;
|
} FS_Api;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#define ICON_SD_MOUNTED &I_SDcardMounted_11x8
|
#define ICON_SD_MOUNTED &I_SDcardMounted_11x8
|
||||||
#define ICON_SD_ERROR &I_SDcardFail_11x8
|
#define ICON_SD_ERROR &I_SDcardFail_11x8
|
||||||
|
|
||||||
|
#define TAG "Storage"
|
||||||
|
|
||||||
static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) {
|
static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
furi_assert(context);
|
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)
|
// storage not enabled but was enabled (sd card unmount)
|
||||||
if(app->storage[ST_EXT].status == StorageStatusNotReady && app->sd_gui.enabled == true) {
|
if(app->storage[ST_EXT].status == StorageStatusNotReady && app->sd_gui.enabled == true) {
|
||||||
app->sd_gui.enabled = false;
|
app->sd_gui.enabled = false;
|
||||||
view_port_enabled_set(app->sd_gui.view_port, 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)
|
// 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 == false) {
|
||||||
app->sd_gui.enabled = true;
|
app->sd_gui.enabled = true;
|
||||||
view_port_enabled_set(app->sd_gui.view_port, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <furi.h>
|
#include <stdint.h>
|
||||||
|
#include <m-string.h>
|
||||||
#include "filesystem_api_defines.h"
|
#include "filesystem_api_defines.h"
|
||||||
#include "storage_sd_api.h"
|
#include "storage_sd_api.h"
|
||||||
|
|
||||||
@ -18,6 +19,23 @@ File* storage_file_alloc(Storage* storage);
|
|||||||
*/
|
*/
|
||||||
void storage_file_free(File* file);
|
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);
|
FuriPubSub* storage_get_pubsub(Storage* storage);
|
||||||
|
|
||||||
/******************* File Functions *******************/
|
/******************* File Functions *******************/
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "storage_i.h"
|
#include "storage_i.h"
|
||||||
#include "storage_message.h"
|
#include "storage_message.h"
|
||||||
|
#include <toolbox/stream/file_stream.h>
|
||||||
|
|
||||||
#define MAX_NAME_LENGTH 256
|
#define MAX_NAME_LENGTH 256
|
||||||
|
|
||||||
@ -49,9 +50,12 @@
|
|||||||
#define FILE_OPENED 1
|
#define FILE_OPENED 1
|
||||||
#define FILE_CLOSED 0
|
#define FILE_CLOSED 0
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
StorageEventFlagFileClose = (1 << 0),
|
||||||
|
} StorageEventFlag;
|
||||||
/****************** FILE ******************/
|
/****************** FILE ******************/
|
||||||
|
|
||||||
bool storage_file_open(
|
static bool storage_file_open_internal(
|
||||||
File* file,
|
File* file,
|
||||||
const char* path,
|
const char* path,
|
||||||
FS_AccessMode access_mode,
|
FS_AccessMode access_mode,
|
||||||
@ -75,6 +79,41 @@ bool storage_file_open(
|
|||||||
return S_RETURN_BOOL;
|
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) {
|
bool storage_file_close(File* file) {
|
||||||
S_FILE_API_PROLOGUE;
|
S_FILE_API_PROLOGUE;
|
||||||
S_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) {
|
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 = {
|
return error;
|
||||||
.cpaths = {
|
|
||||||
.old = old_path,
|
|
||||||
.new = new_path,
|
|
||||||
}};
|
|
||||||
|
|
||||||
S_API_MESSAGE(StorageCommandCommonRename);
|
|
||||||
S_API_EPILOGUE;
|
|
||||||
return S_RETURN_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) {
|
FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) {
|
||||||
S_API_PROLOGUE;
|
FS_Error error;
|
||||||
|
|
||||||
SAData data = {
|
FileInfo fileinfo;
|
||||||
.cpaths = {
|
error = storage_common_stat(storage, old_path, &fileinfo);
|
||||||
.old = old_path,
|
|
||||||
.new = new_path,
|
|
||||||
}};
|
|
||||||
|
|
||||||
S_API_MESSAGE(StorageCommandCommonCopy);
|
if(error == FSE_OK) {
|
||||||
S_API_EPILOGUE;
|
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||||
return S_RETURN_ERROR;
|
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) {
|
FS_Error storage_common_mkdir(Storage* storage, const char* path) {
|
||||||
|
@ -103,25 +103,7 @@ bool storage_has_file(const File* file, StorageData* storage_data) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageType storage_get_type_by_path(const char* path) {
|
bool storage_path_already_open(string_t path, StorageFileList_t array) {
|
||||||
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 open = false;
|
bool open = false;
|
||||||
|
|
||||||
StorageFileList_it_t it;
|
StorageFileList_it_t it;
|
||||||
@ -178,11 +160,7 @@ void* storage_get_storage_file_data(const File* file, StorageData* storage) {
|
|||||||
return founded_file->file_data;
|
return founded_file->file_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void storage_push_storage_file(
|
void storage_push_storage_file(File* file, string_t path, StorageType type, StorageData* storage) {
|
||||||
File* file,
|
|
||||||
const char* path,
|
|
||||||
StorageType type,
|
|
||||||
StorageData* storage) {
|
|
||||||
StorageFile* storage_file = StorageFileList_push_new(storage->files);
|
StorageFile* storage_file = StorageFileList_push_new(storage->files);
|
||||||
furi_check(storage_file != NULL);
|
furi_check(storage_file != NULL);
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include "filesystem_api_internal.h"
|
#include "filesystem_api_internal.h"
|
||||||
#include <m-string.h>
|
#include <m-string.h>
|
||||||
#include <m-array.h>
|
|
||||||
#include <m-list.h>
|
#include <m-list.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -54,7 +53,7 @@ LIST_DEF(
|
|||||||
CLEAR(API_2(storage_file_clear))))
|
CLEAR(API_2(storage_file_clear))))
|
||||||
|
|
||||||
struct StorageData {
|
struct StorageData {
|
||||||
FS_Api fs_api;
|
const FS_Api* fs_api;
|
||||||
StorageApi api;
|
StorageApi api;
|
||||||
void* data;
|
void* data;
|
||||||
osMutexId_t mutex;
|
osMutexId_t mutex;
|
||||||
@ -63,17 +62,12 @@ struct StorageData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool storage_has_file(const File* file, StorageData* storage_data);
|
bool storage_has_file(const File* file, StorageData* storage_data);
|
||||||
StorageType storage_get_type_by_path(const char* path);
|
bool storage_path_already_open(string_t path, StorageFileList_t files);
|
||||||
bool storage_path_already_open(const char* path, StorageFileList_t files);
|
|
||||||
|
|
||||||
void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage);
|
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_get_storage_file_data(const File* file, StorageData* storage);
|
||||||
|
|
||||||
void storage_push_storage_file(
|
void storage_push_storage_file(File* file, string_t path, StorageType type, StorageData* storage);
|
||||||
File* file,
|
|
||||||
const char* path,
|
|
||||||
StorageType type,
|
|
||||||
StorageData* storage);
|
|
||||||
bool storage_pop_storage_file(File* file, StorageData* storage);
|
bool storage_pop_storage_file(File* file, StorageData* storage);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -19,7 +19,6 @@ typedef struct {
|
|||||||
struct Storage {
|
struct Storage {
|
||||||
osMessageQueueId_t message_queue;
|
osMessageQueueId_t message_queue;
|
||||||
StorageData storage[STORAGE_COUNT];
|
StorageData storage[STORAGE_COUNT];
|
||||||
StorageStatus prev_ext_storage_status;
|
|
||||||
StorageSDGui sd_gui;
|
StorageSDGui sd_gui;
|
||||||
FuriPubSub* pubsub;
|
FuriPubSub* pubsub;
|
||||||
};
|
};
|
||||||
|
@ -47,11 +47,6 @@ typedef struct {
|
|||||||
FileInfo* fileinfo;
|
FileInfo* fileinfo;
|
||||||
} SADataCStat;
|
} SADataCStat;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char* old;
|
|
||||||
const char* new;
|
|
||||||
} SADataCPaths;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char* fs_path;
|
const char* fs_path;
|
||||||
uint64_t* total_space;
|
uint64_t* total_space;
|
||||||
@ -84,7 +79,6 @@ typedef union {
|
|||||||
SADataDRead dread;
|
SADataDRead dread;
|
||||||
|
|
||||||
SADataCStat cstat;
|
SADataCStat cstat;
|
||||||
SADataCPaths cpaths;
|
|
||||||
SADataCFSInfo cfsinfo;
|
SADataCFSInfo cfsinfo;
|
||||||
|
|
||||||
SADataError error;
|
SADataError error;
|
||||||
@ -120,8 +114,6 @@ typedef enum {
|
|||||||
StorageCommandDirRewind,
|
StorageCommandDirRewind,
|
||||||
StorageCommandCommonStat,
|
StorageCommandCommonStat,
|
||||||
StorageCommandCommonRemove,
|
StorageCommandCommonRemove,
|
||||||
StorageCommandCommonRename,
|
|
||||||
StorageCommandCommonCopy,
|
|
||||||
StorageCommandCommonMkDir,
|
StorageCommandCommonMkDir,
|
||||||
StorageCommandCommonFSInfo,
|
StorageCommandCommonFSInfo,
|
||||||
StorageCommandSDFormat,
|
StorageCommandSDFormat,
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
#include "storage_processing.h"
|
#include "storage_processing.h"
|
||||||
|
#include <m-list.h>
|
||||||
|
#include <m-dict.h>
|
||||||
|
#include <m-string.h>
|
||||||
|
|
||||||
#define FS_CALL(_storage, _fn) \
|
#define FS_CALL(_storage, _fn) \
|
||||||
storage_data_lock(_storage); \
|
storage_data_lock(_storage); \
|
||||||
ret = _storage->fs_api._fn; \
|
ret = _storage->fs_api->_fn; \
|
||||||
storage_data_unlock(_storage);
|
storage_data_unlock(_storage);
|
||||||
|
|
||||||
#define ST_CALL(_storage, _fn) \
|
#define ST_CALL(_storage, _fn) \
|
||||||
@ -11,18 +14,8 @@
|
|||||||
storage_data_unlock(_storage);
|
storage_data_unlock(_storage);
|
||||||
|
|
||||||
static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) {
|
static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) {
|
||||||
StorageData* storage;
|
furi_check(type == ST_EXT || type == ST_INT);
|
||||||
|
StorageData* storage = &app->storage[type];
|
||||||
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];
|
|
||||||
|
|
||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,10 +35,55 @@ static StorageData* get_storage_by_file(File* file, StorageData* storages) {
|
|||||||
return storage_data;
|
return storage_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* remove_vfs(const char* path) {
|
static const char* remove_vfs(const char* path) {
|
||||||
return path + MIN(4, strlen(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 *******************/
|
/******************* File Functions *******************/
|
||||||
|
|
||||||
bool storage_process_file_open(
|
bool storage_process_file_open(
|
||||||
@ -55,7 +93,7 @@ bool storage_process_file_open(
|
|||||||
FS_AccessMode access_mode,
|
FS_AccessMode access_mode,
|
||||||
FS_OpenMode open_mode) {
|
FS_OpenMode open_mode) {
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
StorageType type = storage_get_type_by_path(path);
|
StorageType type = storage_get_type_by_path(app, path);
|
||||||
StorageData* storage;
|
StorageData* storage;
|
||||||
file->error_id = FSE_OK;
|
file->error_id = FSE_OK;
|
||||||
|
|
||||||
@ -63,12 +101,18 @@ bool storage_process_file_open(
|
|||||||
file->error_id = FSE_INVALID_NAME;
|
file->error_id = FSE_INVALID_NAME;
|
||||||
} else {
|
} else {
|
||||||
storage = storage_get_storage_by_type(app, type);
|
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;
|
file->error_id = FSE_ALREADY_OPEN;
|
||||||
} else {
|
} 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));
|
FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string_clear(real_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -83,6 +127,9 @@ bool storage_process_file_close(Storage* app, File* file) {
|
|||||||
} else {
|
} else {
|
||||||
FS_CALL(storage, file.close(storage, file));
|
FS_CALL(storage, file.close(storage, file));
|
||||||
storage_pop_storage_file(file, storage);
|
storage_pop_storage_file(file, storage);
|
||||||
|
|
||||||
|
StorageEvent event = {.type = StorageEventTypeFileClose};
|
||||||
|
furi_pubsub_publish(app->pubsub, &event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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 storage_process_dir_open(Storage* app, File* file, const char* path) {
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
StorageType type = storage_get_type_by_path(path);
|
StorageType type = storage_get_type_by_path(app, path);
|
||||||
StorageData* storage;
|
StorageData* storage;
|
||||||
file->error_id = FSE_OK;
|
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;
|
file->error_id = FSE_INVALID_NAME;
|
||||||
} else {
|
} else {
|
||||||
storage = storage_get_storage_by_type(app, type);
|
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;
|
file->error_id = FSE_ALREADY_OPEN;
|
||||||
} else {
|
} 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)));
|
FS_CALL(storage, dir.open(storage, file, remove_vfs(path)));
|
||||||
}
|
}
|
||||||
|
string_clear(real_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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) {
|
static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) {
|
||||||
FS_Error ret = FSE_OK;
|
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)) {
|
if(storage_type_is_not_valid(type)) {
|
||||||
ret = FSE_INVALID_NAME;
|
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) {
|
static FS_Error storage_process_common_remove(Storage* app, const char* path) {
|
||||||
FS_Error ret = FSE_OK;
|
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 {
|
do {
|
||||||
if(storage_type_is_not_valid(type)) {
|
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);
|
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;
|
ret = FSE_ALREADY_OPEN;
|
||||||
break;
|
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)));
|
FS_CALL(storage, common.remove(storage, remove_vfs(path)));
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
|
string_clear(real_path);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FS_Error storage_process_common_mkdir(Storage* app, const char* path) {
|
static FS_Error storage_process_common_mkdir(Storage* app, const char* path) {
|
||||||
FS_Error ret = FSE_OK;
|
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)) {
|
if(storage_type_is_not_valid(type)) {
|
||||||
ret = FSE_INVALID_NAME;
|
ret = FSE_INVALID_NAME;
|
||||||
@ -321,86 +379,13 @@ static FS_Error storage_process_common_mkdir(Storage* app, const char* path) {
|
|||||||
return ret;
|
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(
|
static FS_Error storage_process_common_fs_info(
|
||||||
Storage* app,
|
Storage* app,
|
||||||
const char* fs_path,
|
const char* fs_path,
|
||||||
uint64_t* total_space,
|
uint64_t* total_space,
|
||||||
uint64_t* free_space) {
|
uint64_t* free_space) {
|
||||||
FS_Error ret = FSE_OK;
|
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)) {
|
if(storage_type_is_not_valid(type)) {
|
||||||
ret = FSE_INVALID_NAME;
|
ret = FSE_INVALID_NAME;
|
||||||
@ -472,8 +457,7 @@ static FS_Error storage_process_sd_status(Storage* app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/****************** API calls processing ******************/
|
/****************** API calls processing ******************/
|
||||||
|
void storage_process_message_internal(Storage* app, StorageMessage* message) {
|
||||||
void storage_process_message(Storage* app, StorageMessage* message) {
|
|
||||||
switch(message->command) {
|
switch(message->command) {
|
||||||
case StorageCommandFileOpen:
|
case StorageCommandFileOpen:
|
||||||
message->return_data->bool_value = storage_process_file_open(
|
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 =
|
message->return_data->error_value =
|
||||||
storage_process_common_remove(app, message->data->path.path);
|
storage_process_common_remove(app, message->data->path.path);
|
||||||
break;
|
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:
|
case StorageCommandCommonMkDir:
|
||||||
message->return_data->error_value =
|
message->return_data->error_value =
|
||||||
storage_process_common_mkdir(app, message->data->path.path);
|
storage_process_common_mkdir(app, message->data->path.path);
|
||||||
@ -592,3 +568,7 @@ void storage_process_message(Storage* app, StorageMessage* message) {
|
|||||||
|
|
||||||
osSemaphoreRelease(message->semaphore);
|
osSemaphoreRelease(message->semaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void storage_process_message(Storage* app, StorageMessage* message) {
|
||||||
|
storage_process_message_internal(app, message);
|
||||||
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include "filesystem_api_defines.h"
|
#include "filesystem_api_defines.h"
|
||||||
#include <fatfs.h>
|
|
||||||
#include "storage_glue.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -11,10 +9,11 @@ extern "C" {
|
|||||||
#define SD_LABEL_LENGTH 34
|
#define SD_LABEL_LENGTH 34
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FST_FAT12 = FS_FAT12,
|
FST_UNKNOWN,
|
||||||
FST_FAT16 = FS_FAT16,
|
FST_FAT12,
|
||||||
FST_FAT32 = FS_FAT32,
|
FST_FAT16,
|
||||||
FST_EXFAT = FS_EXFAT,
|
FST_FAT32,
|
||||||
|
FST_EXFAT,
|
||||||
} SDFsType;
|
} SDFsType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -164,7 +164,24 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
|
|||||||
sector_size = fs->ssize;
|
sector_size = fs->ssize;
|
||||||
#endif
|
#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_total = total_sectors / 1024 * sector_size;
|
||||||
sd_info->kb_free = free_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);
|
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) {
|
static FS_Error storage_ext_common_mkdir(void* ctx, const char* path) {
|
||||||
SDError result = f_mkdir(path);
|
SDError result = f_mkdir(path);
|
||||||
return storage_ext_parse_error(result);
|
return storage_ext_parse_error(result);
|
||||||
@ -510,6 +522,35 @@ static FS_Error storage_ext_common_fs_info(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/******************* Init Storage *******************/
|
/******************* 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) {
|
void storage_ext_init(StorageData* storage) {
|
||||||
SDData* sd_data = malloc(sizeof(SDData));
|
SDData* sd_data = malloc(sizeof(SDData));
|
||||||
@ -519,27 +560,7 @@ void storage_ext_init(StorageData* storage) {
|
|||||||
|
|
||||||
storage->data = sd_data;
|
storage->data = sd_data;
|
||||||
storage->api.tick = storage_ext_tick;
|
storage->api.tick = storage_ext_tick;
|
||||||
storage->fs_api.file.open = storage_ext_file_open;
|
storage->fs_api = &fs_api;
|
||||||
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;
|
|
||||||
|
|
||||||
hal_sd_detect_init();
|
hal_sd_detect_init();
|
||||||
|
|
||||||
|
@ -636,13 +636,6 @@ static FS_Error storage_int_common_remove(void* ctx, const char* path) {
|
|||||||
return storage_int_parse_error(result);
|
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) {
|
static FS_Error storage_int_common_mkdir(void* ctx, const char* path) {
|
||||||
StorageData* storage = ctx;
|
StorageData* storage = ctx;
|
||||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||||
@ -671,6 +664,35 @@ static FS_Error storage_int_common_fs_info(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/******************* Init Storage *******************/
|
/******************* 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) {
|
void storage_int_init(StorageData* storage) {
|
||||||
FURI_LOG_I(TAG, "Starting");
|
FURI_LOG_I(TAG, "Starting");
|
||||||
@ -689,25 +711,5 @@ void storage_int_init(StorageData* storage) {
|
|||||||
|
|
||||||
storage->data = lfs_data;
|
storage->data = lfs_data;
|
||||||
storage->api.tick = NULL;
|
storage->api.tick = NULL;
|
||||||
storage->fs_api.file.open = storage_int_file_open;
|
storage->fs_api = &fs_api;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
80
applications/tests/storage/storage_test.c
Normal file
80
applications/tests/storage/storage_test.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#include "../minunit.h"
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal_delay.h>
|
||||||
|
#include <storage/storage.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
@ -16,6 +16,7 @@ int run_minunit_test_rpc();
|
|||||||
int run_minunit_test_flipper_format();
|
int run_minunit_test_flipper_format();
|
||||||
int run_minunit_test_flipper_format_string();
|
int run_minunit_test_flipper_format_string();
|
||||||
int run_minunit_test_stream();
|
int run_minunit_test_stream();
|
||||||
|
int run_minunit_test_storage();
|
||||||
|
|
||||||
void minunit_print_progress(void) {
|
void minunit_print_progress(void) {
|
||||||
static char progress[] = {'\\', '|', '/', '-'};
|
static char progress[] = {'\\', '|', '/', '-'};
|
||||||
@ -53,11 +54,12 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) {
|
|||||||
uint32_t cycle_counter = DWT->CYCCNT;
|
uint32_t cycle_counter = DWT->CYCCNT;
|
||||||
|
|
||||||
test_result |= run_minunit();
|
test_result |= run_minunit();
|
||||||
test_result |= run_minunit_test_infrared_decoder_encoder();
|
test_result |= run_minunit_test_storage();
|
||||||
test_result |= run_minunit_test_rpc();
|
|
||||||
test_result |= run_minunit_test_stream();
|
test_result |= run_minunit_test_stream();
|
||||||
test_result |= run_minunit_test_flipper_format();
|
test_result |= run_minunit_test_flipper_format();
|
||||||
test_result |= run_minunit_test_flipper_format_string();
|
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);
|
cycle_counter = (DWT->CYCCNT - cycle_counter);
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "Consumed: %0.2fs", (float)cycle_counter / (SystemCoreClock));
|
FURI_LOG_I(TAG, "Consumed: %0.2fs", (float)cycle_counter / (SystemCoreClock));
|
||||||
|
@ -61,6 +61,13 @@ bool file_stream_close(Stream* _stream) {
|
|||||||
return storage_file_close(stream->file);
|
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) {
|
static void file_stream_free(FileStream* stream) {
|
||||||
storage_file_free(stream->file);
|
storage_file_free(stream->file);
|
||||||
free(stream);
|
free(stream);
|
||||||
|
@ -35,6 +35,13 @@ bool file_stream_open(
|
|||||||
*/
|
*/
|
||||||
bool file_stream_close(Stream* stream);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
Loading…
x
Reference in New Issue
Block a user