[FL-3055] Getter for application data path (#2181)
* Threads: application id * Unit tests: appsdata getter test * Unit tests: moar test cases for appsdata getter * Unit tests: remove folders after test * Storage: dir_is_exist, migrate, + unit_tests * Plugins: migration * Storage: common_exists, moar unit_tests 4 "common_migrate", "common_migrate" and "common_merge" bugfixes * Storage: use FuriString for path handling * Storage API: send caller thread id with path * Storage: remove StorageType field in storage file list * Storage: simplify processing * Storage API: send caller thread id with path everywhere * Storage: /app alias, unit tests and path creation * Storage, path helper: remove unused * Examples: app data example * App plugins: use new VFS path * Storage: file_info_is_dir * Services: handle alias if the service accepts a path. * App plugins: fixes * Make PVS happy * Storage: fix storage_merge_recursive * Storage: rename process_aliases to resolve_path. Rename APPS_DATA to APP_DATA. * Apps: use predefined macro instead of raw paths. Example Apps Data: README fixes. * Storage: rename storage_common_resolve_path to storage_common_resolve_path_and_ensure_app_directory * Api: fix version * Storage: rename alias message * Storage: do not create app folders in path resolving process in certain cases. --------- Co-authored-by: Astra <93453568+Astrrra@users.noreply.github.com> Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
@@ -36,3 +36,7 @@ const char* filesystem_api_error_get_desc(FS_Error error_id) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool file_info_is_dir(const FileInfo* file_info) {
|
||||
return (file_info->flags & FSF_DIRECTORY);
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -40,10 +41,10 @@ typedef enum {
|
||||
FSF_DIRECTORY = (1 << 0), /**< Directory */
|
||||
} FS_Flags;
|
||||
|
||||
/** Structure that hold file index and returned api errors */
|
||||
/** Structure that hold file index and returned api errors */
|
||||
typedef struct File File;
|
||||
|
||||
/** Structure that hold file info */
|
||||
/** Structure that hold file info */
|
||||
typedef struct {
|
||||
uint8_t flags; /**< flags from FS_Flags enum */
|
||||
uint64_t size; /**< file size */
|
||||
@@ -55,6 +56,12 @@ typedef struct {
|
||||
*/
|
||||
const char* filesystem_api_error_get_desc(FS_Error error_id);
|
||||
|
||||
/** Checks if file info is directory
|
||||
* @param file_info file info pointer
|
||||
* @return bool is directory
|
||||
*/
|
||||
bool file_info_is_dir(const FileInfo* file_info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -10,10 +10,12 @@ extern "C" {
|
||||
#define STORAGE_INT_PATH_PREFIX "/int"
|
||||
#define STORAGE_EXT_PATH_PREFIX "/ext"
|
||||
#define STORAGE_ANY_PATH_PREFIX "/any"
|
||||
#define STORAGE_APP_DATA_PATH_PREFIX "/app"
|
||||
|
||||
#define INT_PATH(path) STORAGE_INT_PATH_PREFIX "/" path
|
||||
#define EXT_PATH(path) STORAGE_EXT_PATH_PREFIX "/" path
|
||||
#define ANY_PATH(path) STORAGE_ANY_PATH_PREFIX "/" path
|
||||
#define APP_DATA_PATH(path) STORAGE_APP_DATA_PATH_PREFIX "/" path
|
||||
|
||||
#define RECORD_STORAGE "storage"
|
||||
|
||||
@@ -175,6 +177,15 @@ bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_
|
||||
*/
|
||||
bool storage_dir_rewind(File* file);
|
||||
|
||||
/**
|
||||
* @brief Check that dir exists
|
||||
*
|
||||
* @param storage
|
||||
* @param path
|
||||
* @return bool
|
||||
*/
|
||||
bool storage_dir_exists(Storage* storage, const char* path);
|
||||
|
||||
/******************* Common Functions *******************/
|
||||
|
||||
/** Retrieves unix timestamp of last access
|
||||
@@ -246,6 +257,36 @@ FS_Error storage_common_fs_info(
|
||||
uint64_t* total_space,
|
||||
uint64_t* free_space);
|
||||
|
||||
/**
|
||||
* @brief Parse aliases in path and replace them with real path
|
||||
* Also will create special folders if they are not exist
|
||||
*
|
||||
* @param storage
|
||||
* @param path
|
||||
* @return bool
|
||||
*/
|
||||
void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path);
|
||||
|
||||
/**
|
||||
* @brief Move content of one folder to another, with rename of all conflicting files.
|
||||
* Source folder will be deleted if the migration is successful.
|
||||
*
|
||||
* @param storage
|
||||
* @param source
|
||||
* @param dest
|
||||
* @return FS_Error
|
||||
*/
|
||||
FS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest);
|
||||
|
||||
/**
|
||||
* @brief Check that file or dir exists
|
||||
*
|
||||
* @param storage
|
||||
* @param path
|
||||
* @return bool
|
||||
*/
|
||||
bool storage_common_exists(Storage* storage, const char* path);
|
||||
|
||||
/******************* Error Functions *******************/
|
||||
|
||||
/** Retrieves the error text from the error id
|
||||
|
@@ -131,7 +131,7 @@ static void storage_cli_list(Cli* cli, FuriString* path) {
|
||||
|
||||
while(storage_dir_read(file, &fileinfo, name, MAX_NAME_LENGTH)) {
|
||||
read_done = true;
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
printf("\t[D] %s\r\n", name);
|
||||
} else {
|
||||
printf("\t[F] %s %lub\r\n", name, (uint32_t)(fileinfo.size));
|
||||
@@ -169,7 +169,7 @@ static void storage_cli_tree(Cli* cli, FuriString* path) {
|
||||
|
||||
while(dir_walk_read(dir_walk, name, &fileinfo) == DirWalkOK) {
|
||||
read_done = true;
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
printf("\t[D] %s\r\n", furi_string_get_cstr(name));
|
||||
} else {
|
||||
printf(
|
||||
@@ -383,7 +383,7 @@ static void storage_cli_stat(Cli* cli, FuriString* path) {
|
||||
FS_Error error = storage_common_stat(api, furi_string_get_cstr(path), &fileinfo);
|
||||
|
||||
if(error == FSE_OK) {
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
printf("Directory\r\n");
|
||||
} else {
|
||||
printf("File, size: %lub\r\n", (uint32_t)(fileinfo.size));
|
||||
|
@@ -39,12 +39,6 @@
|
||||
.file = file, \
|
||||
}};
|
||||
|
||||
#define S_API_DATA_PATH \
|
||||
SAData data = { \
|
||||
.path = { \
|
||||
.path = path, \
|
||||
}};
|
||||
|
||||
#define S_RETURN_BOOL (return_data.bool_value);
|
||||
#define S_RETURN_UINT16 (return_data.uint16_value);
|
||||
#define S_RETURN_UINT64 (return_data.uint64_value);
|
||||
@@ -70,6 +64,7 @@ static bool storage_file_open_internal(
|
||||
.path = path,
|
||||
.access_mode = access_mode,
|
||||
.open_mode = open_mode,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
}};
|
||||
|
||||
file->type = FileTypeOpenFile;
|
||||
@@ -249,7 +244,7 @@ bool storage_file_exists(Storage* storage, const char* path) {
|
||||
FileInfo fileinfo;
|
||||
FS_Error error = storage_common_stat(storage, path, &fileinfo);
|
||||
|
||||
if(error == FSE_OK && !(fileinfo.flags & FSF_DIRECTORY)) {
|
||||
if(error == FSE_OK && !file_info_is_dir(&fileinfo)) {
|
||||
exist = true;
|
||||
}
|
||||
|
||||
@@ -266,6 +261,7 @@ static bool storage_dir_open_internal(File* file, const char* path) {
|
||||
.dopen = {
|
||||
.file = file,
|
||||
.path = path,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
}};
|
||||
|
||||
file->type = FileTypeOpenDir;
|
||||
@@ -349,12 +345,28 @@ bool storage_dir_rewind(File* file) {
|
||||
return S_RETURN_BOOL;
|
||||
}
|
||||
|
||||
bool storage_dir_exists(Storage* storage, const char* path) {
|
||||
bool exist = false;
|
||||
FileInfo fileinfo;
|
||||
FS_Error error = storage_common_stat(storage, path, &fileinfo);
|
||||
|
||||
if(error == FSE_OK && file_info_is_dir(&fileinfo)) {
|
||||
exist = true;
|
||||
}
|
||||
|
||||
return exist;
|
||||
}
|
||||
/****************** COMMON ******************/
|
||||
|
||||
FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp) {
|
||||
S_API_PROLOGUE;
|
||||
|
||||
SAData data = {.ctimestamp = {.path = path, .timestamp = timestamp}};
|
||||
SAData data = {
|
||||
.ctimestamp = {
|
||||
.path = path,
|
||||
.timestamp = timestamp,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
}};
|
||||
|
||||
S_API_MESSAGE(StorageCommandCommonTimestamp);
|
||||
S_API_EPILOGUE;
|
||||
@@ -363,8 +375,12 @@ FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t*
|
||||
|
||||
FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) {
|
||||
S_API_PROLOGUE;
|
||||
|
||||
SAData data = {.cstat = {.path = path, .fileinfo = fileinfo}};
|
||||
SAData data = {
|
||||
.cstat = {
|
||||
.path = path,
|
||||
.fileinfo = fileinfo,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
}};
|
||||
|
||||
S_API_MESSAGE(StorageCommandCommonStat);
|
||||
S_API_EPILOGUE;
|
||||
@@ -373,7 +389,12 @@ FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* filei
|
||||
|
||||
FS_Error storage_common_remove(Storage* storage, const char* path) {
|
||||
S_API_PROLOGUE;
|
||||
S_API_DATA_PATH;
|
||||
SAData data = {
|
||||
.path = {
|
||||
.path = path,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
}};
|
||||
|
||||
S_API_MESSAGE(StorageCommandCommonRemove);
|
||||
S_API_EPILOGUE;
|
||||
return S_RETURN_ERROR;
|
||||
@@ -423,7 +444,7 @@ static FS_Error
|
||||
furi_string_right(path, strlen(old_path));
|
||||
furi_string_printf(tmp_new_path, "%s%s", new_path, furi_string_get_cstr(path));
|
||||
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
error = storage_common_mkdir(storage, furi_string_get_cstr(tmp_new_path));
|
||||
} else {
|
||||
error = storage_common_copy(
|
||||
@@ -452,7 +473,7 @@ FS_Error storage_common_copy(Storage* storage, const char* old_path, const char*
|
||||
error = storage_common_stat(storage, old_path, &fileinfo);
|
||||
|
||||
if(error == FSE_OK) {
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
error = storage_copy_recursive(storage, old_path, new_path);
|
||||
} else {
|
||||
Stream* stream_from = file_stream_alloc(storage);
|
||||
@@ -479,7 +500,7 @@ FS_Error storage_common_copy(Storage* storage, const char* old_path, const char*
|
||||
|
||||
static FS_Error
|
||||
storage_merge_recursive(Storage* storage, const char* old_path, const char* new_path) {
|
||||
FS_Error error = storage_common_mkdir(storage, new_path);
|
||||
FS_Error error = FSE_OK;
|
||||
DirWalk* dir_walk = dir_walk_alloc(storage);
|
||||
FuriString *path, *file_basename, *tmp_new_path;
|
||||
FileInfo fileinfo;
|
||||
@@ -488,7 +509,7 @@ static FS_Error
|
||||
tmp_new_path = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if((error != FSE_OK) && (error != FSE_EXIST)) break;
|
||||
if(!storage_simply_mkdir(storage, new_path)) break;
|
||||
|
||||
dir_walk_set_recursive(dir_walk, false);
|
||||
if(!dir_walk_open(dir_walk, old_path)) {
|
||||
@@ -508,13 +529,13 @@ static FS_Error
|
||||
path_extract_basename(furi_string_get_cstr(path), file_basename);
|
||||
path_concat(new_path, furi_string_get_cstr(file_basename), tmp_new_path);
|
||||
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
if(storage_common_stat(
|
||||
storage, furi_string_get_cstr(tmp_new_path), &fileinfo) == FSE_OK) {
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
error =
|
||||
storage_common_mkdir(storage, furi_string_get_cstr(tmp_new_path));
|
||||
if(error != FSE_OK) {
|
||||
if(error != FSE_OK && error != FSE_EXIST) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -548,7 +569,7 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char
|
||||
error = storage_common_stat(storage, old_path, &fileinfo);
|
||||
|
||||
if(error == FSE_OK) {
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
error = storage_merge_recursive(storage, old_path, new_path);
|
||||
} else {
|
||||
error = storage_common_stat(storage, new_path, &fileinfo);
|
||||
@@ -556,7 +577,7 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char
|
||||
furi_string_set(new_path_next, new_path);
|
||||
FuriString* dir_path;
|
||||
FuriString* filename;
|
||||
char extension[MAX_EXT_LEN];
|
||||
char extension[MAX_EXT_LEN] = {0};
|
||||
|
||||
dir_path = furi_string_alloc();
|
||||
filename = furi_string_alloc();
|
||||
@@ -608,7 +629,12 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char
|
||||
|
||||
FS_Error storage_common_mkdir(Storage* storage, const char* path) {
|
||||
S_API_PROLOGUE;
|
||||
S_API_DATA_PATH;
|
||||
SAData data = {
|
||||
.path = {
|
||||
.path = path,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
}};
|
||||
|
||||
S_API_MESSAGE(StorageCommandCommonMkDir);
|
||||
S_API_EPILOGUE;
|
||||
return S_RETURN_ERROR;
|
||||
@@ -626,6 +652,7 @@ FS_Error storage_common_fs_info(
|
||||
.fs_path = fs_path,
|
||||
.total_space = total_space,
|
||||
.free_space = free_space,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
}};
|
||||
|
||||
S_API_MESSAGE(StorageCommandCommonFSInfo);
|
||||
@@ -633,6 +660,38 @@ FS_Error storage_common_fs_info(
|
||||
return S_RETURN_ERROR;
|
||||
}
|
||||
|
||||
void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path) {
|
||||
S_API_PROLOGUE;
|
||||
|
||||
SAData data = {
|
||||
.cresolvepath = {
|
||||
.path = path,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
}};
|
||||
|
||||
S_API_MESSAGE(StorageCommandCommonResolvePath);
|
||||
S_API_EPILOGUE;
|
||||
}
|
||||
|
||||
FS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest) {
|
||||
if(!storage_common_exists(storage, source)) {
|
||||
return FSE_OK;
|
||||
}
|
||||
|
||||
FS_Error error = storage_common_merge(storage, source, dest);
|
||||
|
||||
if(error == FSE_OK) {
|
||||
storage_simply_remove_recursive(storage, source);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool storage_common_exists(Storage* storage, const char* path) {
|
||||
FileInfo file_info;
|
||||
return storage_common_stat(storage, path, &file_info) == FSE_OK;
|
||||
}
|
||||
|
||||
/****************** ERROR ******************/
|
||||
|
||||
const char* storage_error_get_desc(FS_Error error_id) {
|
||||
@@ -750,7 +809,7 @@ bool storage_simply_remove_recursive(Storage* storage, const char* path) {
|
||||
}
|
||||
|
||||
while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(file_info_is_dir(&fileinfo)) {
|
||||
furi_string_cat_printf(cur_dir, "/%s", name);
|
||||
go_deeper = true;
|
||||
break;
|
||||
|
@@ -5,21 +5,18 @@
|
||||
|
||||
void storage_file_init(StorageFile* obj) {
|
||||
obj->file = NULL;
|
||||
obj->type = ST_ERROR;
|
||||
obj->file_data = NULL;
|
||||
obj->path = furi_string_alloc();
|
||||
}
|
||||
|
||||
void storage_file_init_set(StorageFile* obj, const StorageFile* src) {
|
||||
obj->file = src->file;
|
||||
obj->type = src->type;
|
||||
obj->file_data = src->file_data;
|
||||
obj->path = furi_string_alloc_set(src->path);
|
||||
}
|
||||
|
||||
void storage_file_set(StorageFile* obj, const StorageFile* src) { //-V524
|
||||
obj->file = src->file;
|
||||
obj->type = src->type;
|
||||
obj->file_data = src->file_data;
|
||||
furi_string_set(obj->path, src->path);
|
||||
}
|
||||
@@ -150,16 +147,10 @@ void* storage_get_storage_file_data(const File* file, StorageData* storage) {
|
||||
return founded_file->file_data;
|
||||
}
|
||||
|
||||
void storage_push_storage_file(
|
||||
File* file,
|
||||
FuriString* path,
|
||||
StorageType type,
|
||||
StorageData* storage) {
|
||||
void storage_push_storage_file(File* file, FuriString* path, StorageData* storage) {
|
||||
StorageFile* storage_file = StorageFileList_push_new(storage->files);
|
||||
|
||||
file->file_id = (uint32_t)storage_file;
|
||||
storage_file->file = file;
|
||||
storage_file->type = type;
|
||||
furi_string_set(storage_file->path, path);
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,6 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
File* file;
|
||||
StorageType type;
|
||||
void* file_data;
|
||||
FuriString* path;
|
||||
} StorageFile;
|
||||
@@ -66,11 +65,7 @@ bool storage_path_already_open(FuriString* 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,
|
||||
FuriString* path,
|
||||
StorageType type,
|
||||
StorageData* storage);
|
||||
void storage_push_storage_file(File* file, FuriString* path, StorageData* storage);
|
||||
bool storage_pop_storage_file(File* file, StorageData* storage);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -12,6 +12,8 @@ extern "C" {
|
||||
|
||||
#define STORAGE_COUNT (ST_INT + 1)
|
||||
|
||||
#define APPS_DATA_PATH EXT_PATH("apps_data")
|
||||
|
||||
typedef struct {
|
||||
ViewPort* view_port;
|
||||
bool enabled;
|
||||
|
@@ -11,6 +11,7 @@ typedef struct {
|
||||
const char* path;
|
||||
FS_AccessMode access_mode;
|
||||
FS_OpenMode open_mode;
|
||||
FuriThreadId thread_id;
|
||||
} SADataFOpen;
|
||||
|
||||
typedef struct {
|
||||
@@ -34,6 +35,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
File* file;
|
||||
const char* path;
|
||||
FuriThreadId thread_id;
|
||||
} SADataDOpen;
|
||||
|
||||
typedef struct {
|
||||
@@ -46,25 +48,34 @@ typedef struct {
|
||||
typedef struct {
|
||||
const char* path;
|
||||
uint32_t* timestamp;
|
||||
FuriThreadId thread_id;
|
||||
} SADataCTimestamp;
|
||||
|
||||
typedef struct {
|
||||
const char* path;
|
||||
FileInfo* fileinfo;
|
||||
FuriThreadId thread_id;
|
||||
} SADataCStat;
|
||||
|
||||
typedef struct {
|
||||
const char* fs_path;
|
||||
uint64_t* total_space;
|
||||
uint64_t* free_space;
|
||||
FuriThreadId thread_id;
|
||||
} SADataCFSInfo;
|
||||
|
||||
typedef struct {
|
||||
FuriString* path;
|
||||
FuriThreadId thread_id;
|
||||
} SADataCResolvePath;
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
} SADataError;
|
||||
|
||||
typedef struct {
|
||||
const char* path;
|
||||
FuriThreadId thread_id;
|
||||
} SADataPath;
|
||||
|
||||
typedef struct {
|
||||
@@ -87,6 +98,7 @@ typedef union {
|
||||
SADataCTimestamp ctimestamp;
|
||||
SADataCStat cstat;
|
||||
SADataCFSInfo cfsinfo;
|
||||
SADataCResolvePath cresolvepath;
|
||||
|
||||
SADataError error;
|
||||
|
||||
@@ -128,6 +140,7 @@ typedef enum {
|
||||
StorageCommandSDUnmount,
|
||||
StorageCommandSDInfo,
|
||||
StorageCommandSDStatus,
|
||||
StorageCommandCommonResolvePath,
|
||||
} StorageCommand;
|
||||
|
||||
typedef struct {
|
||||
|
@@ -4,17 +4,11 @@
|
||||
|
||||
#define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn;
|
||||
|
||||
static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) {
|
||||
furi_check(type == ST_EXT || type == ST_INT);
|
||||
StorageData* storage = &app->storage[type];
|
||||
return storage;
|
||||
}
|
||||
|
||||
static bool storage_type_is_not_valid(StorageType type) {
|
||||
static bool storage_type_is_valid(StorageType type) {
|
||||
#ifdef FURI_RAM_EXEC
|
||||
return type != ST_EXT;
|
||||
return type == ST_EXT;
|
||||
#else
|
||||
return type >= ST_ERROR;
|
||||
return type < ST_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -30,25 +24,21 @@ static StorageData* get_storage_by_file(File* file, StorageData* storages) {
|
||||
return storage_data;
|
||||
}
|
||||
|
||||
static const char* remove_vfs(const char* path) {
|
||||
return path + MIN(4u, strlen(path));
|
||||
static const char* cstr_path_without_vfs_prefix(FuriString* path) {
|
||||
const char* path_cstr = furi_string_get_cstr(path);
|
||||
return path_cstr + MIN(4u, strlen(path_cstr));
|
||||
}
|
||||
|
||||
static StorageType storage_get_type_by_path(Storage* app, const char* path) {
|
||||
static StorageType storage_get_type_by_path(FuriString* path) {
|
||||
StorageType type = ST_ERROR;
|
||||
if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
|
||||
type = ST_EXT;
|
||||
} else if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
|
||||
type = ST_INT;
|
||||
} else if(memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
|
||||
type = ST_ANY;
|
||||
}
|
||||
const char* path_cstr = furi_string_get_cstr(path);
|
||||
|
||||
if(type == ST_ANY) {
|
||||
if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
|
||||
type = ST_EXT;
|
||||
} else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
|
||||
type = ST_INT;
|
||||
if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) {
|
||||
type = ST_EXT;
|
||||
}
|
||||
} else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
|
||||
type = ST_ANY;
|
||||
}
|
||||
|
||||
return type;
|
||||
@@ -71,38 +61,51 @@ static void storage_path_change_to_real_storage(FuriString* path, StorageType re
|
||||
}
|
||||
}
|
||||
|
||||
FS_Error storage_get_data(Storage* app, FuriString* path, StorageData** storage) {
|
||||
StorageType type = storage_get_type_by_path(path);
|
||||
|
||||
if(storage_type_is_valid(type)) {
|
||||
if(type == ST_ANY) {
|
||||
type = ST_INT;
|
||||
if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) {
|
||||
type = ST_EXT;
|
||||
}
|
||||
storage_path_change_to_real_storage(path, type);
|
||||
}
|
||||
|
||||
furi_assert(type == ST_EXT || type == ST_INT);
|
||||
*storage = &app->storage[type];
|
||||
|
||||
return FSE_OK;
|
||||
} else {
|
||||
return FSE_INVALID_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* File Functions *******************/
|
||||
|
||||
bool storage_process_file_open(
|
||||
Storage* app,
|
||||
File* file,
|
||||
const char* path,
|
||||
FuriString* path,
|
||||
FS_AccessMode access_mode,
|
||||
FS_OpenMode open_mode) {
|
||||
bool ret = false;
|
||||
StorageType type = storage_get_type_by_path(app, path);
|
||||
StorageData* storage;
|
||||
file->error_id = FSE_OK;
|
||||
file->error_id = storage_get_data(app, path, &storage);
|
||||
|
||||
if(storage_type_is_not_valid(type)) {
|
||||
file->error_id = FSE_INVALID_NAME;
|
||||
} else {
|
||||
storage = storage_get_storage_by_type(app, type);
|
||||
FuriString* real_path;
|
||||
real_path = furi_string_alloc_set(path);
|
||||
storage_path_change_to_real_storage(real_path, type);
|
||||
|
||||
if(storage_path_already_open(real_path, storage->files)) {
|
||||
if(file->error_id == FSE_OK) {
|
||||
if(storage_path_already_open(path, storage->files)) {
|
||||
file->error_id = FSE_ALREADY_OPEN;
|
||||
} else {
|
||||
if(access_mode & FSAM_WRITE) {
|
||||
storage_data_timestamp(storage);
|
||||
}
|
||||
storage_push_storage_file(file, real_path, type, storage);
|
||||
FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode));
|
||||
}
|
||||
storage_push_storage_file(file, path, storage);
|
||||
|
||||
furi_string_free(real_path);
|
||||
const char* path_cstr_no_vfs = cstr_path_without_vfs_prefix(path);
|
||||
FS_CALL(storage, file.open(storage, file, path_cstr_no_vfs, access_mode, open_mode));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -243,27 +246,18 @@ static bool storage_process_file_eof(Storage* app, File* file) {
|
||||
|
||||
/******************* Dir Functions *******************/
|
||||
|
||||
bool storage_process_dir_open(Storage* app, File* file, const char* path) {
|
||||
bool storage_process_dir_open(Storage* app, File* file, FuriString* path) {
|
||||
bool ret = false;
|
||||
StorageType type = storage_get_type_by_path(app, path);
|
||||
StorageData* storage;
|
||||
file->error_id = FSE_OK;
|
||||
file->error_id = storage_get_data(app, path, &storage);
|
||||
|
||||
if(storage_type_is_not_valid(type)) {
|
||||
file->error_id = FSE_INVALID_NAME;
|
||||
} else {
|
||||
storage = storage_get_storage_by_type(app, type);
|
||||
FuriString* real_path;
|
||||
real_path = furi_string_alloc_set(path);
|
||||
storage_path_change_to_real_storage(real_path, type);
|
||||
|
||||
if(storage_path_already_open(real_path, storage->files)) {
|
||||
if(file->error_id == FSE_OK) {
|
||||
if(storage_path_already_open(path, storage->files)) {
|
||||
file->error_id = FSE_ALREADY_OPEN;
|
||||
} else {
|
||||
storage_push_storage_file(file, real_path, type, storage);
|
||||
FS_CALL(storage, dir.open(storage, file, remove_vfs(path)));
|
||||
storage_push_storage_file(file, path, storage);
|
||||
FS_CALL(storage, dir.open(storage, file, cstr_path_without_vfs_prefix(path)));
|
||||
}
|
||||
furi_string_free(real_path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -320,73 +314,52 @@ bool storage_process_dir_rewind(Storage* app, File* file) {
|
||||
/******************* Common FS Functions *******************/
|
||||
|
||||
static FS_Error
|
||||
storage_process_common_timestamp(Storage* app, const char* path, uint32_t* timestamp) {
|
||||
FS_Error ret = FSE_OK;
|
||||
StorageType type = storage_get_type_by_path(app, path);
|
||||
storage_process_common_timestamp(Storage* app, FuriString* path, uint32_t* timestamp) {
|
||||
StorageData* storage;
|
||||
FS_Error ret = storage_get_data(app, path, &storage);
|
||||
|
||||
if(storage_type_is_not_valid(type)) {
|
||||
ret = FSE_INVALID_NAME;
|
||||
} else {
|
||||
StorageData* storage = storage_get_storage_by_type(app, type);
|
||||
if(ret == FSE_OK) {
|
||||
*timestamp = storage_data_get_timestamp(storage);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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(app, path);
|
||||
static FS_Error storage_process_common_stat(Storage* app, FuriString* path, FileInfo* fileinfo) {
|
||||
StorageData* storage;
|
||||
FS_Error ret = storage_get_data(app, path, &storage);
|
||||
|
||||
if(storage_type_is_not_valid(type)) {
|
||||
ret = FSE_INVALID_NAME;
|
||||
} else {
|
||||
StorageData* storage = storage_get_storage_by_type(app, type);
|
||||
FS_CALL(storage, common.stat(storage, remove_vfs(path), fileinfo));
|
||||
if(ret == FSE_OK) {
|
||||
FS_CALL(storage, common.stat(storage, cstr_path_without_vfs_prefix(path), fileinfo));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static FS_Error storage_process_common_remove(Storage* app, const char* path) {
|
||||
FS_Error ret = FSE_OK;
|
||||
StorageType type = storage_get_type_by_path(app, path);
|
||||
|
||||
FuriString* real_path;
|
||||
real_path = furi_string_alloc_set(path);
|
||||
storage_path_change_to_real_storage(real_path, type);
|
||||
static FS_Error storage_process_common_remove(Storage* app, FuriString* path) {
|
||||
StorageData* storage;
|
||||
FS_Error ret = storage_get_data(app, path, &storage);
|
||||
|
||||
do {
|
||||
if(storage_type_is_not_valid(type)) {
|
||||
ret = FSE_INVALID_NAME;
|
||||
break;
|
||||
}
|
||||
|
||||
StorageData* storage = storage_get_storage_by_type(app, type);
|
||||
if(storage_path_already_open(real_path, storage->files)) {
|
||||
if(storage_path_already_open(path, storage->files)) {
|
||||
ret = FSE_ALREADY_OPEN;
|
||||
break;
|
||||
}
|
||||
|
||||
storage_data_timestamp(storage);
|
||||
FS_CALL(storage, common.remove(storage, remove_vfs(path)));
|
||||
FS_CALL(storage, common.remove(storage, cstr_path_without_vfs_prefix(path)));
|
||||
} while(false);
|
||||
|
||||
furi_string_free(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(app, path);
|
||||
static FS_Error storage_process_common_mkdir(Storage* app, FuriString* path) {
|
||||
StorageData* storage;
|
||||
FS_Error ret = storage_get_data(app, path, &storage);
|
||||
|
||||
if(storage_type_is_not_valid(type)) {
|
||||
ret = FSE_INVALID_NAME;
|
||||
} else {
|
||||
StorageData* storage = storage_get_storage_by_type(app, type);
|
||||
if(ret == FSE_OK) {
|
||||
storage_data_timestamp(storage);
|
||||
FS_CALL(storage, common.mkdir(storage, remove_vfs(path)));
|
||||
FS_CALL(storage, common.mkdir(storage, cstr_path_without_vfs_prefix(path)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -394,17 +367,16 @@ static FS_Error storage_process_common_mkdir(Storage* app, const char* path) {
|
||||
|
||||
static FS_Error storage_process_common_fs_info(
|
||||
Storage* app,
|
||||
const char* fs_path,
|
||||
FuriString* path,
|
||||
uint64_t* total_space,
|
||||
uint64_t* free_space) {
|
||||
FS_Error ret = FSE_OK;
|
||||
StorageType type = storage_get_type_by_path(app, fs_path);
|
||||
StorageData* storage;
|
||||
FS_Error ret = storage_get_data(app, path, &storage);
|
||||
|
||||
if(storage_type_is_not_valid(type)) {
|
||||
ret = FSE_INVALID_NAME;
|
||||
} else {
|
||||
StorageData* storage = storage_get_storage_by_type(app, type);
|
||||
FS_CALL(storage, common.fs_info(storage, remove_vfs(fs_path), total_space, free_space));
|
||||
if(ret == FSE_OK) {
|
||||
FS_CALL(
|
||||
storage,
|
||||
common.fs_info(storage, cstr_path_without_vfs_prefix(path), total_space, free_space));
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -471,14 +443,52 @@ static FS_Error storage_process_sd_status(Storage* app) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************** Aliases processing *******************/
|
||||
|
||||
void storage_process_alias(
|
||||
Storage* app,
|
||||
FuriString* path,
|
||||
FuriThreadId thread_id,
|
||||
bool create_folders) {
|
||||
if(furi_string_start_with(path, STORAGE_APP_DATA_PATH_PREFIX)) {
|
||||
FuriString* apps_data_path_with_appsid = furi_string_alloc_set(APPS_DATA_PATH "/");
|
||||
furi_string_cat(apps_data_path_with_appsid, furi_thread_get_appid(thread_id));
|
||||
|
||||
// "/app" -> "/ext/apps_data/appsid"
|
||||
furi_string_replace_at(
|
||||
path,
|
||||
0,
|
||||
strlen(STORAGE_APP_DATA_PATH_PREFIX),
|
||||
furi_string_get_cstr(apps_data_path_with_appsid));
|
||||
|
||||
// Create app data folder if not exists
|
||||
if(create_folders &&
|
||||
storage_process_common_stat(app, apps_data_path_with_appsid, NULL) != FSE_OK) {
|
||||
furi_string_set(apps_data_path_with_appsid, APPS_DATA_PATH);
|
||||
storage_process_common_mkdir(app, apps_data_path_with_appsid);
|
||||
furi_string_cat(apps_data_path_with_appsid, "/");
|
||||
furi_string_cat(apps_data_path_with_appsid, furi_thread_get_appid(thread_id));
|
||||
storage_process_common_mkdir(app, apps_data_path_with_appsid);
|
||||
}
|
||||
|
||||
furi_string_free(apps_data_path_with_appsid);
|
||||
}
|
||||
}
|
||||
|
||||
/****************** API calls processing ******************/
|
||||
|
||||
void storage_process_message_internal(Storage* app, StorageMessage* message) {
|
||||
FuriString* path = NULL;
|
||||
|
||||
switch(message->command) {
|
||||
// File operations
|
||||
case StorageCommandFileOpen:
|
||||
path = furi_string_alloc_set(message->data->fopen.path);
|
||||
storage_process_alias(app, path, message->data->fopen.thread_id, true);
|
||||
message->return_data->bool_value = storage_process_file_open(
|
||||
app,
|
||||
message->data->fopen.file,
|
||||
message->data->fopen.path,
|
||||
path,
|
||||
message->data->fopen.access_mode,
|
||||
message->data->fopen.open_mode);
|
||||
break;
|
||||
@@ -527,9 +537,12 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
|
||||
message->return_data->bool_value = storage_process_file_eof(app, message->data->file.file);
|
||||
break;
|
||||
|
||||
// Dir operations
|
||||
case StorageCommandDirOpen:
|
||||
path = furi_string_alloc_set(message->data->dopen.path);
|
||||
storage_process_alias(app, path, message->data->dopen.thread_id, true);
|
||||
message->return_data->bool_value =
|
||||
storage_process_dir_open(app, message->data->dopen.file, message->data->dopen.path);
|
||||
storage_process_dir_open(app, message->data->dopen.file, path);
|
||||
break;
|
||||
case StorageCommandDirClose:
|
||||
message->return_data->bool_value =
|
||||
@@ -547,29 +560,42 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
|
||||
message->return_data->bool_value =
|
||||
storage_process_dir_rewind(app, message->data->file.file);
|
||||
break;
|
||||
|
||||
// Common operations
|
||||
case StorageCommandCommonTimestamp:
|
||||
message->return_data->error_value = storage_process_common_timestamp(
|
||||
app, message->data->ctimestamp.path, message->data->ctimestamp.timestamp);
|
||||
path = furi_string_alloc_set(message->data->ctimestamp.path);
|
||||
storage_process_alias(app, path, message->data->ctimestamp.thread_id, false);
|
||||
message->return_data->error_value =
|
||||
storage_process_common_timestamp(app, path, message->data->ctimestamp.timestamp);
|
||||
break;
|
||||
case StorageCommandCommonStat:
|
||||
message->return_data->error_value = storage_process_common_stat(
|
||||
app, message->data->cstat.path, message->data->cstat.fileinfo);
|
||||
path = furi_string_alloc_set(message->data->cstat.path);
|
||||
storage_process_alias(app, path, message->data->cstat.thread_id, false);
|
||||
message->return_data->error_value =
|
||||
storage_process_common_stat(app, path, message->data->cstat.fileinfo);
|
||||
break;
|
||||
case StorageCommandCommonRemove:
|
||||
message->return_data->error_value =
|
||||
storage_process_common_remove(app, message->data->path.path);
|
||||
path = furi_string_alloc_set(message->data->path.path);
|
||||
storage_process_alias(app, path, message->data->path.thread_id, false);
|
||||
message->return_data->error_value = storage_process_common_remove(app, path);
|
||||
break;
|
||||
case StorageCommandCommonMkDir:
|
||||
message->return_data->error_value =
|
||||
storage_process_common_mkdir(app, message->data->path.path);
|
||||
path = furi_string_alloc_set(message->data->path.path);
|
||||
storage_process_alias(app, path, message->data->path.thread_id, true);
|
||||
message->return_data->error_value = storage_process_common_mkdir(app, path);
|
||||
break;
|
||||
case StorageCommandCommonFSInfo:
|
||||
path = furi_string_alloc_set(message->data->cfsinfo.fs_path);
|
||||
storage_process_alias(app, path, message->data->cfsinfo.thread_id, false);
|
||||
message->return_data->error_value = storage_process_common_fs_info(
|
||||
app,
|
||||
message->data->cfsinfo.fs_path,
|
||||
message->data->cfsinfo.total_space,
|
||||
message->data->cfsinfo.free_space);
|
||||
app, path, message->data->cfsinfo.total_space, message->data->cfsinfo.free_space);
|
||||
break;
|
||||
case StorageCommandCommonResolvePath:
|
||||
storage_process_alias(
|
||||
app, message->data->cresolvepath.path, message->data->cresolvepath.thread_id, true);
|
||||
break;
|
||||
|
||||
// SD operations
|
||||
case StorageCommandSDFormat:
|
||||
message->return_data->error_value = storage_process_sd_format(app);
|
||||
break;
|
||||
@@ -585,6 +611,10 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(path != NULL) { //-V547
|
||||
furi_string_free(path);
|
||||
}
|
||||
|
||||
api_lock_unlock(message->lock);
|
||||
}
|
||||
|
||||
|
@@ -1,341 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "StorageTest"
|
||||
#define BYTES_COUNT 16
|
||||
#define TEST_STRING "TestDataStringProvidedByDiceRoll"
|
||||
#define SEEK_OFFSET_FROM_START 10
|
||||
#define SEEK_OFFSET_INCREASE 12
|
||||
#define SEEK_OFFSET_SUM (SEEK_OFFSET_FROM_START + SEEK_OFFSET_INCREASE)
|
||||
|
||||
static void do_file_test(Storage* api, const char* path) {
|
||||
File* file = storage_file_alloc(api);
|
||||
bool result;
|
||||
uint8_t bytes[BYTES_COUNT + 1];
|
||||
uint8_t bytes_count;
|
||||
uint64_t position;
|
||||
uint64_t size;
|
||||
|
||||
FURI_LOG_I(TAG, "--------- FILE \"%s\" ---------", path);
|
||||
|
||||
// open
|
||||
result = storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "open");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
// write
|
||||
bytes_count = storage_file_write(file, TEST_STRING, strlen(TEST_STRING));
|
||||
if(bytes_count == 0) {
|
||||
FURI_LOG_E(TAG, "write, %s", storage_file_get_error_desc(file));
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "write");
|
||||
}
|
||||
|
||||
// sync
|
||||
result = storage_file_sync(file);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "sync");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "sync, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
// eof #1
|
||||
result = storage_file_eof(file);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "eof #1");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "eof #1, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
// seek from start and tell
|
||||
result = storage_file_seek(file, SEEK_OFFSET_FROM_START, true);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "seek #1");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "seek #1, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
position = storage_file_tell(file);
|
||||
if(position != SEEK_OFFSET_FROM_START) {
|
||||
FURI_LOG_E(TAG, "tell #1, %s", storage_file_get_error_desc(file));
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "tell #1");
|
||||
}
|
||||
|
||||
// size
|
||||
size = storage_file_size(file);
|
||||
if(size != strlen(TEST_STRING)) {
|
||||
FURI_LOG_E(TAG, "size #1, %s", storage_file_get_error_desc(file));
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "size #1");
|
||||
}
|
||||
|
||||
// seek and tell
|
||||
result = storage_file_seek(file, SEEK_OFFSET_INCREASE, false);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "seek #2");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "seek #2, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
position = storage_file_tell(file);
|
||||
if(position != SEEK_OFFSET_SUM) {
|
||||
FURI_LOG_E(TAG, "tell #2, %s", storage_file_get_error_desc(file));
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "tell #2");
|
||||
}
|
||||
|
||||
// eof #2
|
||||
result = storage_file_eof(file);
|
||||
if(!result) {
|
||||
FURI_LOG_I(TAG, "eof #2");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "eof #2, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
// truncate
|
||||
result = storage_file_truncate(file);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "truncate");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "truncate, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
size = storage_file_size(file);
|
||||
if(size != SEEK_OFFSET_SUM) {
|
||||
FURI_LOG_E(TAG, "size #2, %s", storage_file_get_error_desc(file));
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "size #2");
|
||||
}
|
||||
|
||||
// close
|
||||
result = storage_file_close(file);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "close");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "close, error");
|
||||
}
|
||||
|
||||
// open
|
||||
result = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "open");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
// read
|
||||
memset(bytes, 0, BYTES_COUNT + 1);
|
||||
bytes_count = storage_file_read(file, bytes, BYTES_COUNT);
|
||||
if(bytes_count == 0) {
|
||||
FURI_LOG_E(TAG, "read, %s", storage_file_get_error_desc(file));
|
||||
} else {
|
||||
if(memcmp(TEST_STRING, bytes, bytes_count) == 0) {
|
||||
FURI_LOG_I(TAG, "read");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "read, garbage");
|
||||
}
|
||||
}
|
||||
|
||||
// close
|
||||
result = storage_file_close(file);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "close");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "close, error");
|
||||
}
|
||||
|
||||
storage_file_free(file);
|
||||
}
|
||||
|
||||
static void do_dir_test(Storage* api, const char* path) {
|
||||
File* file = storage_file_alloc(api);
|
||||
bool result;
|
||||
|
||||
FURI_LOG_I(TAG, "--------- DIR \"%s\" ---------", path);
|
||||
|
||||
// open
|
||||
result = storage_dir_open(file, path);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "open");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
// read
|
||||
const uint8_t filename_size = 100;
|
||||
char* filename = malloc(filename_size);
|
||||
FileInfo fileinfo;
|
||||
|
||||
do {
|
||||
result = storage_dir_read(file, &fileinfo, filename, filename_size);
|
||||
if(result) {
|
||||
if(strlen(filename)) {
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"read #1, [%s]%s",
|
||||
((fileinfo.flags & FSF_DIRECTORY) ? "D" : "F"),
|
||||
filename);
|
||||
}
|
||||
} else if(storage_file_get_error(file) != FSE_NOT_EXIST) {
|
||||
FURI_LOG_E(TAG, "read #1, %s", storage_file_get_error_desc(file));
|
||||
break;
|
||||
}
|
||||
|
||||
} while(result);
|
||||
|
||||
// rewind
|
||||
result = storage_dir_rewind(file);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "rewind");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "rewind, %s", storage_file_get_error_desc(file));
|
||||
}
|
||||
|
||||
// read
|
||||
do {
|
||||
result = storage_dir_read(file, &fileinfo, filename, filename_size);
|
||||
if(result) {
|
||||
if(strlen(filename)) {
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"read #2, [%s]%s",
|
||||
((fileinfo.flags & FSF_DIRECTORY) ? "D" : "F"),
|
||||
filename);
|
||||
}
|
||||
} else if(storage_file_get_error(file) != FSE_NOT_EXIST) {
|
||||
FURI_LOG_E(TAG, "read #2, %s", storage_file_get_error_desc(file));
|
||||
break;
|
||||
}
|
||||
|
||||
} while((strlen(filename)));
|
||||
|
||||
// close
|
||||
result = storage_dir_close(file);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "close");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "close, error");
|
||||
}
|
||||
|
||||
storage_file_free(file);
|
||||
free(filename);
|
||||
}
|
||||
|
||||
static void do_test_start(Storage* api, const char* path) {
|
||||
FuriString* str_path = furi_string_alloc_printf("%s/test-folder", path);
|
||||
|
||||
FURI_LOG_I(TAG, "--------- START \"%s\" ---------", path);
|
||||
|
||||
// mkdir
|
||||
FS_Error result = storage_common_mkdir(api, furi_string_get_cstr(str_path));
|
||||
|
||||
if(result == FSE_OK) {
|
||||
FURI_LOG_I(TAG, "mkdir ok");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "mkdir, %s", storage_error_get_desc(result));
|
||||
}
|
||||
|
||||
// stat
|
||||
FileInfo fileinfo;
|
||||
result = storage_common_stat(api, furi_string_get_cstr(str_path), &fileinfo);
|
||||
|
||||
if(result == FSE_OK) {
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
FURI_LOG_I(TAG, "stat #1 ok");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "stat #1, %s", storage_error_get_desc(result));
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "stat #1, %s", storage_error_get_desc(result));
|
||||
}
|
||||
|
||||
furi_string_free(str_path);
|
||||
}
|
||||
|
||||
static void do_test_end(Storage* api, const char* path) {
|
||||
uint64_t total_space;
|
||||
uint64_t free_space;
|
||||
FuriString* str_path_1 = furi_string_alloc_printf("%s/test-folder", path);
|
||||
FuriString* str_path_2 = furi_string_alloc_printf("%s/test-folder2", path);
|
||||
|
||||
FURI_LOG_I(TAG, "--------- END \"%s\" ---------", path);
|
||||
|
||||
// fs stat
|
||||
FS_Error result = storage_common_fs_info(api, path, &total_space, &free_space);
|
||||
|
||||
if(result == FSE_OK) {
|
||||
uint32_t total_kb = total_space / 1024;
|
||||
uint32_t free_kb = free_space / 1024;
|
||||
FURI_LOG_I(TAG, "fs_info: total %luk, free %luk", total_kb, free_kb);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "fs_info, %s", storage_error_get_desc(result));
|
||||
}
|
||||
|
||||
// rename #1
|
||||
result = storage_common_rename(
|
||||
api, furi_string_get_cstr(str_path_1), furi_string_get_cstr(str_path_2));
|
||||
if(result == FSE_OK) {
|
||||
FURI_LOG_I(TAG, "rename #1 ok");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "rename #1, %s", storage_error_get_desc(result));
|
||||
}
|
||||
|
||||
// remove #1
|
||||
result = storage_common_remove(api, furi_string_get_cstr(str_path_2));
|
||||
if(result == FSE_OK) {
|
||||
FURI_LOG_I(TAG, "remove #1 ok");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "remove #1, %s", storage_error_get_desc(result));
|
||||
}
|
||||
|
||||
// rename #2
|
||||
furi_string_printf(str_path_1, "%s/test.txt", path);
|
||||
furi_string_printf(str_path_2, "%s/test2.txt", path);
|
||||
|
||||
result = storage_common_rename(
|
||||
api, furi_string_get_cstr(str_path_1), furi_string_get_cstr(str_path_2));
|
||||
if(result == FSE_OK) {
|
||||
FURI_LOG_I(TAG, "rename #2 ok");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "rename #2, %s", storage_error_get_desc(result));
|
||||
}
|
||||
|
||||
// remove #2
|
||||
result = storage_common_remove(api, furi_string_get_cstr(str_path_2));
|
||||
if(result == FSE_OK) {
|
||||
FURI_LOG_I(TAG, "remove #2 ok");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "remove #2, %s", storage_error_get_desc(result));
|
||||
}
|
||||
|
||||
furi_string_free(str_path_1);
|
||||
furi_string_free(str_path_2);
|
||||
}
|
||||
|
||||
int32_t storage_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
do_test_start(api, STORAGE_INT_PATH_PREFIX);
|
||||
do_test_start(api, STORAGE_ANY_PATH_PREFIX);
|
||||
do_test_start(api, STORAGE_EXT_PATH_PREFIX);
|
||||
|
||||
do_file_test(api, INT_PATH("test.txt"));
|
||||
do_file_test(api, ANY_PATH("test.txt"));
|
||||
do_file_test(api, EXT_PATH("test.txt"));
|
||||
|
||||
do_dir_test(api, STORAGE_INT_PATH_PREFIX);
|
||||
do_dir_test(api, STORAGE_ANY_PATH_PREFIX);
|
||||
do_dir_test(api, STORAGE_EXT_PATH_PREFIX);
|
||||
|
||||
do_test_end(api, STORAGE_INT_PATH_PREFIX);
|
||||
do_test_end(api, STORAGE_ANY_PATH_PREFIX);
|
||||
do_test_end(api, STORAGE_EXT_PATH_PREFIX);
|
||||
|
||||
while(true) {
|
||||
furi_delay_ms(1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user