[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:
SG 2022-04-01 22:21:31 +10:00 committed by GitHub
parent cb7d43f7e1
commit 855f2584ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 443 additions and 281 deletions

View File

@ -1,5 +1,6 @@
#pragma once
#include "file_worker.h"
#include <m-array.h>
#define MAX_FILES 100 //temp

View File

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

View File

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

View File

@ -9,6 +9,7 @@ typedef enum {
} SubmenuIndex;
static void submenu_callback(void* context, uint32_t index) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -3,6 +3,7 @@
#include <dolphin/dolphin.h>
static void byte_input_callback(void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -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<iButtonApp*>(context);
iButtonEvent event;

View File

@ -2,6 +2,7 @@
#include "../ibutton_app.h"
static void popup_callback(void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;
event.type = iButtonEvent::Type::EventTypeBack;

View File

@ -3,6 +3,7 @@
#include <dolphin/dolphin.h>
static void emulate_callback(void* context, bool emulated) {
furi_assert(context);
if(emulated) {
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerEmulated};

View File

@ -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<iButtonApp*>(context);
iButtonEvent event;

View File

@ -3,6 +3,7 @@
#include <dolphin/dolphin.h>
static void read_callback(void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerRead};
app->get_view_manager()->send_event(&event);

View File

@ -3,6 +3,7 @@
#include <one_wire/maxim_crc.h>
static void dialog_ex_callback(DialogExResult result, void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -3,6 +3,7 @@
#include <one_wire/maxim_crc.h>
static void dialog_ex_callback(DialogExResult result, void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -3,6 +3,7 @@
#include <dolphin/dolphin.h>
static void dialog_ex_callback(DialogExResult result, void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -9,6 +9,7 @@ typedef enum {
} SubmenuIndex;
static void submenu_callback(void* context, uint32_t index) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -3,6 +3,7 @@
#include <lib/toolbox/random_name.h>
static void text_input_callback(void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -3,6 +3,7 @@
#include <dolphin/dolphin.h>
static void popup_callback(void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;
event.type = iButtonEvent::Type::EventTypeBack;

View File

@ -11,6 +11,7 @@ typedef enum {
} SubmenuIndex;
static void submenu_callback(void* context, uint32_t index) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -8,6 +8,7 @@ typedef enum {
} SubmenuIndex;
static void submenu_callback(void* context, uint32_t index) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;

View File

@ -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<iButtonApp*>(context);
iButtonEvent event;
event.type = iButtonEvent::Type::EventTypeWorkerWrite;

View File

@ -2,6 +2,7 @@
#include "../ibutton_app.h"
static void popup_callback(void* context) {
furi_assert(context);
iButtonApp* app = static_cast<iButtonApp*>(context);
iButtonEvent event;
event.type = iButtonEvent::Type::EventTypeBack;

View File

@ -1,5 +1,5 @@
#pragma once
#include <furi.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {

View File

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

View File

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

View File

@ -1,5 +1,6 @@
#pragma once
#include <furi.h>
#include <stdint.h>
#include <m-string.h>
#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 *******************/

View File

@ -3,6 +3,7 @@
#include "storage.h"
#include "storage_i.h"
#include "storage_message.h"
#include <toolbox/stream/file_stream.h>
#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) {

View File

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

View File

@ -3,7 +3,6 @@
#include <furi.h>
#include "filesystem_api_internal.h"
#include <m-string.h>
#include <m-array.h>
#include <m-list.h>
#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

View File

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

View File

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

View File

@ -1,8 +1,11 @@
#include "storage_processing.h"
#include <m-list.h>
#include <m-dict.h>
#include <m-string.h>
#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);
}

View File

@ -1,8 +1,6 @@
#pragma once
#include <furi.h>
#include "filesystem_api_defines.h"
#include <fatfs.h>
#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 {

View File

@ -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();

View File

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

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

View File

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

View File

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

View File

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