[FL-2675] /int space reservation (#1448)
* storage: added global #defines for /int, /ext & /any * storage: introduced PATH_EXT, PATH_INT& PATH_ANY macros * core apps: moved hardcoded config files names to separate headers; prefixed them with "."; updater: added file name migration to new naming convention on backup extraction * storage: fixed storage_merge_recursive handling of complex directory structures; storage_move_to_sd: changed data migration logic to all non-dot files & all folders * core: added macro aliases for core record names * Bumped protobuf commit pointer * storage: reserved 5 pages in /int; denying write&creation of non-dot files when running out of free space Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
#define ICON_SD_MOUNTED &I_SDcardMounted_11x8
|
||||
#define ICON_SD_ERROR &I_SDcardFail_11x8
|
||||
|
||||
#define TAG "Storage"
|
||||
#define TAG RECORD_STORAGE
|
||||
|
||||
static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(canvas);
|
||||
@@ -52,9 +52,9 @@ Storage* storage_app_alloc() {
|
||||
view_port_draw_callback_set(app->sd_gui.view_port, storage_app_sd_icon_draw_callback, app);
|
||||
view_port_enabled_set(app->sd_gui.view_port, false);
|
||||
|
||||
Gui* gui = furi_record_open("gui");
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, app->sd_gui.view_port, GuiLayerStatusBarLeft);
|
||||
furi_record_close("gui");
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ void storage_tick(Storage* app) {
|
||||
int32_t storage_srv(void* p) {
|
||||
UNUSED(p);
|
||||
Storage* app = storage_app_alloc();
|
||||
furi_record_create("storage", app);
|
||||
furi_record_create(RECORD_STORAGE, app);
|
||||
|
||||
StorageMessage message;
|
||||
while(1) {
|
||||
|
@@ -8,6 +8,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define STORAGE_INT_PATH_PREFIX "/int"
|
||||
#define STORAGE_EXT_PATH_PREFIX "/ext"
|
||||
#define STORAGE_ANY_PATH_PREFIX "/any"
|
||||
|
||||
#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 RECORD_STORAGE "storage"
|
||||
|
||||
typedef struct Storage Storage;
|
||||
|
||||
/** Allocates and initializes a file descriptor
|
||||
@@ -273,6 +283,8 @@ FS_Error storage_sd_status(Storage* api);
|
||||
|
||||
/******************* Internal LFS Functions *******************/
|
||||
|
||||
typedef void (*Storage_name_converter)(string_t);
|
||||
|
||||
/** Backs up internal storage to a tar archive
|
||||
* @param api pointer to the api
|
||||
* @param dstmane destination archive path
|
||||
@@ -283,9 +295,10 @@ FS_Error storage_int_backup(Storage* api, const char* dstname);
|
||||
/** Restores internal storage from a tar archive
|
||||
* @param api pointer to the api
|
||||
* @param dstmane archive path
|
||||
* @param converter pointer to filename conversion function, may be NULL
|
||||
* @return FS_Error operation result
|
||||
*/
|
||||
FS_Error storage_int_restore(Storage* api, const char* dstname);
|
||||
FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter);
|
||||
|
||||
/***************** Simplified Functions ******************/
|
||||
|
||||
|
@@ -40,12 +40,13 @@ static void storage_cli_print_error(FS_Error error) {
|
||||
|
||||
static void storage_cli_info(Cli* cli, string_t path) {
|
||||
UNUSED(cli);
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
if(string_cmp_str(path, "/int") == 0) {
|
||||
if(string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) {
|
||||
uint64_t total_space;
|
||||
uint64_t free_space;
|
||||
FS_Error error = storage_common_fs_info(api, "/int", &total_space, &free_space);
|
||||
FS_Error error =
|
||||
storage_common_fs_info(api, STORAGE_INT_PATH_PREFIX, &total_space, &free_space);
|
||||
|
||||
if(error != FSE_OK) {
|
||||
storage_cli_print_error(error);
|
||||
@@ -56,7 +57,7 @@ static void storage_cli_info(Cli* cli, string_t path) {
|
||||
(uint32_t)(total_space / 1024),
|
||||
(uint32_t)(free_space / 1024));
|
||||
}
|
||||
} else if(string_cmp_str(path, "/ext") == 0) {
|
||||
} else if(string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {
|
||||
SDInfo sd_info;
|
||||
FS_Error error = storage_sd_info(api, &sd_info);
|
||||
|
||||
@@ -74,17 +75,17 @@ static void storage_cli_info(Cli* cli, string_t path) {
|
||||
storage_cli_print_usage();
|
||||
}
|
||||
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
};
|
||||
|
||||
static void storage_cli_format(Cli* cli, string_t path) {
|
||||
if(string_cmp_str(path, "/int") == 0) {
|
||||
if(string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) {
|
||||
storage_cli_print_error(FSE_NOT_IMPLEMENTED);
|
||||
} else if(string_cmp_str(path, "/ext") == 0) {
|
||||
} else if(string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {
|
||||
printf("Formatting SD card, all data will be lost. Are you sure (y/n)?\r\n");
|
||||
char answer = cli_getc(cli);
|
||||
if(answer == 'y' || answer == 'Y') {
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
printf("Formatting, please wait...\r\n");
|
||||
|
||||
FS_Error error = storage_sd_format(api);
|
||||
@@ -94,7 +95,7 @@ static void storage_cli_format(Cli* cli, string_t path) {
|
||||
} else {
|
||||
printf("SD card was successfully formatted.\r\n");
|
||||
}
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
} else {
|
||||
printf("Cancelled.\r\n");
|
||||
}
|
||||
@@ -110,7 +111,7 @@ static void storage_cli_list(Cli* cli, string_t path) {
|
||||
printf("\t[D] ext\r\n");
|
||||
printf("\t[D] any\r\n");
|
||||
} else {
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
|
||||
if(storage_dir_open(file, string_get_cstr(path))) {
|
||||
@@ -136,18 +137,18 @@ static void storage_cli_list(Cli* cli, string_t path) {
|
||||
|
||||
storage_dir_close(file);
|
||||
storage_file_free(file);
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
}
|
||||
|
||||
static void storage_cli_tree(Cli* cli, string_t path) {
|
||||
if(string_cmp_str(path, "/") == 0) {
|
||||
string_set(path, "/int");
|
||||
string_set(path, STORAGE_INT_PATH_PREFIX);
|
||||
storage_cli_tree(cli, path);
|
||||
string_set(path, "/ext");
|
||||
string_set(path, STORAGE_EXT_PATH_PREFIX);
|
||||
storage_cli_tree(cli, path);
|
||||
} else {
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
DirWalk* dir_walk = dir_walk_alloc(api);
|
||||
string_t name;
|
||||
string_init(name);
|
||||
@@ -174,13 +175,13 @@ static void storage_cli_tree(Cli* cli, string_t path) {
|
||||
|
||||
string_clear(name);
|
||||
dir_walk_free(dir_walk);
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
}
|
||||
|
||||
static void storage_cli_read(Cli* cli, string_t path) {
|
||||
UNUSED(cli);
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
|
||||
if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
@@ -206,11 +207,11 @@ static void storage_cli_read(Cli* cli, string_t path) {
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_write(Cli* cli, string_t path) {
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
|
||||
const uint16_t buffer_size = 512;
|
||||
@@ -260,11 +261,11 @@ static void storage_cli_write(Cli* cli, string_t path) {
|
||||
|
||||
free(buffer);
|
||||
storage_file_free(file);
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_read_chunks(Cli* cli, string_t path, string_t args) {
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
|
||||
uint32_t buffer_size;
|
||||
@@ -298,11 +299,11 @@ static void storage_cli_read_chunks(Cli* cli, string_t path, string_t args) {
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_write_chunk(Cli* cli, string_t path, string_t args) {
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
|
||||
uint32_t buffer_size;
|
||||
@@ -334,18 +335,19 @@ static void storage_cli_write_chunk(Cli* cli, string_t path, string_t args) {
|
||||
}
|
||||
|
||||
storage_file_free(file);
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_stat(Cli* cli, string_t path) {
|
||||
UNUSED(cli);
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
if(string_cmp_str(path, "/") == 0) {
|
||||
printf("Storage\r\n");
|
||||
} else if(
|
||||
string_cmp_str(path, "/ext") == 0 || string_cmp_str(path, "/int") == 0 ||
|
||||
string_cmp_str(path, "/any") == 0) {
|
||||
string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0 ||
|
||||
string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0 ||
|
||||
string_cmp_str(path, STORAGE_ANY_PATH_PREFIX) == 0) {
|
||||
uint64_t total_space;
|
||||
uint64_t free_space;
|
||||
FS_Error error =
|
||||
@@ -374,12 +376,12 @@ static void storage_cli_stat(Cli* cli, string_t path) {
|
||||
}
|
||||
}
|
||||
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_copy(Cli* cli, string_t old_path, string_t args) {
|
||||
UNUSED(cli);
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
string_t new_path;
|
||||
string_init(new_path);
|
||||
|
||||
@@ -395,24 +397,24 @@ static void storage_cli_copy(Cli* cli, string_t old_path, string_t args) {
|
||||
}
|
||||
|
||||
string_clear(new_path);
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_remove(Cli* cli, string_t path) {
|
||||
UNUSED(cli);
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
FS_Error error = storage_common_remove(api, string_get_cstr(path));
|
||||
|
||||
if(error != FSE_OK) {
|
||||
storage_cli_print_error(error);
|
||||
}
|
||||
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_rename(Cli* cli, string_t old_path, string_t args) {
|
||||
UNUSED(cli);
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
string_t new_path;
|
||||
string_init(new_path);
|
||||
|
||||
@@ -428,24 +430,24 @@ static void storage_cli_rename(Cli* cli, string_t old_path, string_t args) {
|
||||
}
|
||||
|
||||
string_clear(new_path);
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_mkdir(Cli* cli, string_t path) {
|
||||
UNUSED(cli);
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
FS_Error error = storage_common_mkdir(api, string_get_cstr(path));
|
||||
|
||||
if(error != FSE_OK) {
|
||||
storage_cli_print_error(error);
|
||||
}
|
||||
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_cli_md5(Cli* cli, string_t path) {
|
||||
UNUSED(cli);
|
||||
Storage* api = furi_record_open("storage");
|
||||
Storage* api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(api);
|
||||
|
||||
if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
@@ -478,7 +480,7 @@ static void storage_cli_md5(Cli* cli, string_t path) {
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
furi_record_close("storage");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
void storage_cli(Cli* cli, string_t args, void* context) {
|
||||
@@ -592,11 +594,11 @@ static void storage_cli_factory_reset(Cli* cli, string_t args, void* context) {
|
||||
|
||||
void storage_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open("cli");
|
||||
cli_add_command(cli, "storage", CliCommandFlagParallelSafe, storage_cli, NULL);
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, RECORD_STORAGE, CliCommandFlagParallelSafe, storage_cli, NULL);
|
||||
cli_add_command(
|
||||
cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL);
|
||||
furi_record_close("cli");
|
||||
furi_record_close(RECORD_CLI);
|
||||
#else
|
||||
UNUSED(storage_cli_factory_reset);
|
||||
#endif
|
||||
|
@@ -447,17 +447,16 @@ 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);
|
||||
DirWalk* dir_walk = dir_walk_alloc(storage);
|
||||
string_t path;
|
||||
string_t tmp_new_path;
|
||||
string_t tmp_old_path;
|
||||
string_t path, file_basename, tmp_new_path;
|
||||
FileInfo fileinfo;
|
||||
string_init(path);
|
||||
string_init(file_basename);
|
||||
string_init(tmp_new_path);
|
||||
string_init(tmp_old_path);
|
||||
|
||||
do {
|
||||
if((error != FSE_OK) && (error != FSE_EXIST)) break;
|
||||
|
||||
dir_walk_set_recursive(dir_walk, false);
|
||||
if(!dir_walk_open(dir_walk, old_path)) {
|
||||
error = dir_walk_get_error(dir_walk);
|
||||
break;
|
||||
@@ -472,30 +471,33 @@ static FS_Error
|
||||
} else if(res == DirWalkLast) {
|
||||
break;
|
||||
} else {
|
||||
string_set(tmp_old_path, path);
|
||||
string_right(path, strlen(old_path));
|
||||
string_printf(tmp_new_path, "%s%s", new_path, string_get_cstr(path));
|
||||
path_extract_basename(string_get_cstr(path), file_basename);
|
||||
path_concat(new_path, string_get_cstr(file_basename), tmp_new_path);
|
||||
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
if(storage_common_stat(storage, string_get_cstr(tmp_new_path), &fileinfo) ==
|
||||
FSE_OK) {
|
||||
if(fileinfo.flags & FSF_DIRECTORY) {
|
||||
error = storage_common_mkdir(storage, string_get_cstr(tmp_new_path));
|
||||
if(error != FSE_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error = storage_common_merge(
|
||||
storage, string_get_cstr(tmp_old_path), string_get_cstr(tmp_new_path));
|
||||
}
|
||||
error = storage_common_merge(
|
||||
storage, string_get_cstr(path), string_get_cstr(tmp_new_path));
|
||||
|
||||
if(error != FSE_OK) break;
|
||||
if(error != FSE_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
string_clear(tmp_new_path);
|
||||
string_clear(tmp_old_path);
|
||||
string_clear(file_basename);
|
||||
string_clear(path);
|
||||
dir_walk_free(dir_walk);
|
||||
return error;
|
||||
|
@@ -3,20 +3,19 @@
|
||||
#include "storage.h"
|
||||
#include <toolbox/tar/tar_archive.h>
|
||||
|
||||
#define INT_PATH "/int"
|
||||
|
||||
FS_Error storage_int_backup(Storage* api, const char* dstname) {
|
||||
TarArchive* archive = tar_archive_alloc(api);
|
||||
bool success = tar_archive_open(archive, dstname, TAR_OPEN_MODE_WRITE) &&
|
||||
tar_archive_add_dir(archive, INT_PATH, "") && tar_archive_finalize(archive);
|
||||
tar_archive_add_dir(archive, STORAGE_INT_PATH_PREFIX, "") &&
|
||||
tar_archive_finalize(archive);
|
||||
tar_archive_free(archive);
|
||||
return success ? FSE_OK : FSE_INTERNAL;
|
||||
}
|
||||
|
||||
FS_Error storage_int_restore(Storage* api, const char* srcname) {
|
||||
FS_Error storage_int_restore(Storage* api, const char* srcname, Storage_name_converter converter) {
|
||||
TarArchive* archive = tar_archive_alloc(api);
|
||||
bool success = tar_archive_open(archive, srcname, TAR_OPEN_MODE_READ) &&
|
||||
tar_archive_unpack_to(archive, INT_PATH);
|
||||
tar_archive_unpack_to(archive, STORAGE_INT_PATH_PREFIX, converter);
|
||||
tar_archive_free(archive);
|
||||
return success ? FSE_OK : FSE_INTERNAL;
|
||||
}
|
||||
|
@@ -43,17 +43,18 @@ static const char* remove_vfs(const char* path) {
|
||||
return path + MIN(4u, 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) {
|
||||
if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) &&
|
||||
memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
|
||||
type = ST_EXT;
|
||||
} else if(strlen(path) >= strlen(int_path) && memcmp(path, int_path, strlen(int_path)) == 0) {
|
||||
} else if(
|
||||
strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) &&
|
||||
memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
|
||||
type = ST_INT;
|
||||
} else if(strlen(path) >= strlen(any_path) && memcmp(path, any_path, strlen(any_path)) == 0) {
|
||||
} else if(
|
||||
strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) &&
|
||||
memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
|
||||
type = ST_ANY;
|
||||
}
|
||||
|
||||
@@ -68,19 +69,20 @@ static StorageType storage_get_type_by_path(Storage* app, const char* path) {
|
||||
}
|
||||
|
||||
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) {
|
||||
if(memcmp(string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) ==
|
||||
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]);
|
||||
string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]);
|
||||
string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]);
|
||||
string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]);
|
||||
string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[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]);
|
||||
string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]);
|
||||
string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]);
|
||||
string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]);
|
||||
string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@@ -317,22 +317,22 @@ static void do_test_end(Storage* api, const char* path) {
|
||||
|
||||
int32_t storage_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
Storage* api = furi_record_open("storage");
|
||||
do_test_start(api, "/int");
|
||||
do_test_start(api, "/any");
|
||||
do_test_start(api, "/ext");
|
||||
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/test.txt");
|
||||
do_file_test(api, "/any/test.txt");
|
||||
do_file_test(api, "/ext/test.txt");
|
||||
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, "/int");
|
||||
do_dir_test(api, "/any");
|
||||
do_dir_test(api, "/ext");
|
||||
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, "/int");
|
||||
do_test_end(api, "/any");
|
||||
do_test_end(api, "/ext");
|
||||
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);
|
||||
|
@@ -11,7 +11,7 @@ typedef FILINFO SDFileInfo;
|
||||
typedef FRESULT SDError;
|
||||
|
||||
#define TAG "StorageExt"
|
||||
#define STORAGE_PATH "/ext"
|
||||
|
||||
/********************* Definitions ********************/
|
||||
|
||||
typedef struct {
|
||||
@@ -35,9 +35,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
|
||||
|
||||
while(result == false && counter > 0 && hal_sd_detect()) {
|
||||
if(notify) {
|
||||
NotificationApp* notification = furi_record_open("notification");
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
sd_notify_wait(notification);
|
||||
furi_record_close("notification");
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
if((counter % 2) == 0) {
|
||||
@@ -78,9 +78,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
|
||||
}
|
||||
|
||||
if(notify) {
|
||||
NotificationApp* notification = furi_record_open("notification");
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
sd_notify_wait_off(notification);
|
||||
furi_record_close("notification");
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
@@ -224,16 +224,16 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) {
|
||||
if(storage->status != StorageStatusOK) {
|
||||
FURI_LOG_E(TAG, "sd init error: %s", storage_data_status_text(storage));
|
||||
if(notify) {
|
||||
NotificationApp* notification = furi_record_open("notification");
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
sd_notify_error(notification);
|
||||
furi_record_close("notification");
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "card mounted");
|
||||
if(notify) {
|
||||
NotificationApp* notification = furi_record_open("notification");
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
sd_notify_success(notification);
|
||||
furi_record_close("notification");
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,9 +252,9 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) {
|
||||
|
||||
sd_unmount_card(storage);
|
||||
if(notify) {
|
||||
NotificationApp* notification = furi_record_open("notification");
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
sd_notify_eject(notification);
|
||||
furi_record_close("notification");
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,16 @@
|
||||
#include "storage_int.h"
|
||||
#include <lfs.h>
|
||||
#include <furi_hal.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
#define TAG "StorageInt"
|
||||
#define STORAGE_PATH "/int"
|
||||
#define STORAGE_PATH STORAGE_INT_PATH_PREFIX
|
||||
#define LFS_CLEAN_FINGERPRINT 0
|
||||
|
||||
/* When less than LFS_RESERVED_PAGES_COUNT are left free, creation &
|
||||
* modification of non-dot files is restricted */
|
||||
#define LFS_RESERVED_PAGES_COUNT 5
|
||||
|
||||
typedef struct {
|
||||
const size_t start_address;
|
||||
const size_t start_page;
|
||||
@@ -297,6 +302,20 @@ static FS_Error storage_int_parse_error(int error) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Returns false if less than reserved space is left free */
|
||||
static bool storage_int_check_for_free_space(StorageData* storage) {
|
||||
LFSData* lfs_data = lfs_data_get_from_storage(storage);
|
||||
|
||||
lfs_ssize_t result = lfs_fs_size(lfs_get_from_storage(storage));
|
||||
if(result >= 0) {
|
||||
lfs_size_t free_space =
|
||||
(lfs_data->config.block_count - result) * lfs_data->config.block_size;
|
||||
|
||||
return (free_space > LFS_RESERVED_PAGES_COUNT * furi_hal_flash_get_page_size());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/******************* File Functions *******************/
|
||||
|
||||
static bool storage_int_file_open(
|
||||
@@ -308,6 +327,8 @@ static bool storage_int_file_open(
|
||||
StorageData* storage = ctx;
|
||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||
|
||||
bool enough_free_space = storage_int_check_for_free_space(storage);
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if(access_mode & FSAM_READ) flags |= LFS_O_RDONLY;
|
||||
@@ -321,6 +342,23 @@ static bool storage_int_file_open(
|
||||
|
||||
LFSHandle* handle = lfs_handle_alloc_file();
|
||||
storage_set_storage_file_data(file, handle, storage);
|
||||
|
||||
if(!enough_free_space) {
|
||||
string_t filename;
|
||||
string_init(filename);
|
||||
path_extract_basename(path, filename);
|
||||
bool is_dot_file = (!string_empty_p(filename) && (string_get_char(filename, 0) == '.'));
|
||||
string_clear(filename);
|
||||
|
||||
/* Restrict write & creation access to all non-dot files */
|
||||
if(!is_dot_file && (flags & (LFS_O_CREAT | LFS_O_WRONLY))) {
|
||||
file->internal_error_id = LFS_ERR_NOSPC;
|
||||
file->error_id = FSE_DENIED;
|
||||
FURI_LOG_W(TAG, "Denied access to '%s': no free space", path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
file->internal_error_id = lfs_file_open(lfs, lfs_handle_get_file(handle), path, flags);
|
||||
|
||||
if(file->internal_error_id >= LFS_ERR_OK) {
|
||||
@@ -328,6 +366,7 @@ static bool storage_int_file_open(
|
||||
}
|
||||
|
||||
file->error_id = storage_int_parse_error(file->internal_error_id);
|
||||
|
||||
return (file->error_id == FSE_OK);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user