diff --git a/.gitmodules b/.gitmodules index 671e7e2c4..4fba0483e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,7 @@ [submodule "assets/protobuf"] path = assets/protobuf url = https://github.com/flipperdevices/flipperzero-protobuf.git + shallow = false [submodule "lib/libusb_stm32"] path = lib/libusb_stm32 url = https://github.com/flipperdevices/libusb_stm32.git diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 167266a84..533a8a9ca 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -287,7 +287,8 @@ static void test_rpc_create_simple_message( PB_Main* message, uint16_t tag, const char* str, - uint32_t command_id) { + uint32_t command_id, + bool flag) { furi_check(message); char* str_copy = NULL; @@ -308,6 +309,7 @@ static void test_rpc_create_simple_message( break; case PB_Main_storage_list_request_tag: message->content.storage_list_request.path = str_copy; + message->content.storage_list_request.include_md5 = flag; break; case PB_Main_storage_mkdir_request_tag: message->content.storage_mkdir_request.path = str_copy; @@ -419,6 +421,7 @@ static void } mu_check(result_msg_file->size == expected_msg_file->size); mu_check(result_msg_file->type == expected_msg_file->type); + mu_assert_string_eq(expected_msg_file->md5sum, result_msg_file->md5sum); if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) { mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF??? @@ -430,10 +433,10 @@ static void } static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) { - mu_check(result->command_id == expected->command_id); - mu_check(result->command_status == expected->command_status); - mu_check(result->has_next == expected->has_next); - mu_check(result->which_content == expected->which_content); + mu_assert_int_eq(expected->command_id, result->command_id); + mu_assert_int_eq(expected->command_status, result->command_status); + mu_assert_int_eq(expected->has_next, result->has_next); + mu_assert_int_eq(expected->which_content, result->which_content); if(result->command_status != PB_CommandStatus_OK) { mu_check(result->which_content == PB_Main_empty_tag); } @@ -573,10 +576,15 @@ static void static void test_rpc_storage_list_create_expected_list( MsgList_t msg_list, const char* path, - uint32_t command_id) { + uint32_t command_id, + bool append_md5) { Storage* fs_api = furi_record_open(RECORD_STORAGE); File* dir = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + PB_Main response = { .command_id = command_id, .has_next = false, @@ -614,6 +622,17 @@ static void test_rpc_storage_list_create_expected_list( list->file[i].data = NULL; /* memory free inside rpc_encode_and_send() -> pb_release() */ list->file[i].name = name; + + if(append_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf(md5_path, "%s/%s", path, name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } } else { @@ -626,6 +645,10 @@ static void test_rpc_storage_list_create_expected_list( response.has_next = false; MsgList_push_back(msg_list, response); + furi_string_free(md5); + furi_string_free(md5_path); + storage_file_free(file); + storage_dir_close(dir); storage_file_free(dir); @@ -675,16 +698,17 @@ static void test_rpc_free_msg_list(MsgList_t msg_list) { MsgList_clear(msg_list); } -static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { +static void test_rpc_storage_list_run(const char* path, uint32_t command_id, bool md5) { PB_Main request; MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_list_request_tag, path, command_id, md5); if(!strcmp(path, "/")) { test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id); } else { - test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id); + test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id, md5); } test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -694,15 +718,25 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { } MU_TEST(test_storage_list) { - test_rpc_storage_list_run("/", ++command_id); - test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id); + test_rpc_storage_list_run("/", ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false); + test_rpc_storage_list_run("error_path", ++command_id, false); +} - test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id); - test_rpc_storage_list_run("error_path", ++command_id); +MU_TEST(test_storage_list_md5) { + test_rpc_storage_list_run("/", ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true); + test_rpc_storage_list_run("error_path", ++command_id, true); } static void @@ -770,7 +804,8 @@ static void test_storage_read_run(const char* path, uint32_t command_id) { MsgList_init(expected_msg_list); test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id); - test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_read_request_tag, path, command_id, false); test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -824,7 +859,8 @@ static void test_rpc_storage_info_run(const char* path, uint32_t command_id) { MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_info_request_tag, path, command_id, false); PB_Main* response = MsgList_push_new(expected_msg_list); response->command_id = command_id; @@ -856,7 +892,8 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) { MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_stat_request_tag, path, command_id, false); Storage* fs_api = furi_record_open(RECORD_STORAGE); FileInfo fileinfo; @@ -968,7 +1005,11 @@ static void test_storage_write_read_run( test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id); test_rpc_create_simple_message( - MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id); + MsgList_push_raw(input_msg_list), + PB_Main_storage_read_request_tag, + path, + ++*command_id, + false); test_rpc_add_read_or_write_to_list( expected_msg_list, READ_RESPONSE, @@ -1041,7 +1082,8 @@ MU_TEST(test_storage_interrupt_continuous_same_system) { MsgList_push_new(input_msg_list), PB_Main_storage_mkdir_request_tag, TEST_DIR "dir1", - command_id + 1); + command_id + 1, + false); test_rpc_add_read_or_write_to_list( input_msg_list, WRITE_REQUEST, @@ -1121,7 +1163,8 @@ static void test_storage_delete_run( MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_delete_request_tag, path, command_id, false); request.content.storage_delete_request.recursive = recursive; test_rpc_add_empty_to_list(expected_msg_list, status, command_id); @@ -1202,7 +1245,8 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_mkdir_request_tag, path, command_id, false); test_rpc_add_empty_to_list(expected_msg_list, status, command_id); test_rpc_encode_and_feed_one(&request, 0); @@ -1229,33 +1273,15 @@ MU_TEST(test_storage_mkdir) { static void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) { Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); - if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t once_read_size = 512; - const uint8_t hash_size = MD5SUM_SIZE; - uint8_t* data = malloc(once_read_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, once_read_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } - - free(hash); - free(data); + if(md5_string_calc_file(file, path, md5, NULL)) { + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); } else { furi_check(0); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); @@ -1271,11 +1297,12 @@ static void test_storage_md5sum_run( MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_md5sum_request_tag, path, command_id, false); if(status == PB_CommandStatus_OK) { PB_Main* response = MsgList_push_new(expected_msg_list); test_rpc_create_simple_message( - response, PB_Main_storage_md5sum_response_tag, md5sum, command_id); + response, PB_Main_storage_md5sum_response_tag, md5sum, command_id, false); response->command_status = status; } else { test_rpc_add_empty_to_list(expected_msg_list, status, command_id); @@ -1433,6 +1460,7 @@ MU_TEST_SUITE(test_rpc_storage) { MU_RUN_TEST(test_storage_info); MU_RUN_TEST(test_storage_stat); MU_RUN_TEST(test_storage_list); + MU_RUN_TEST(test_storage_list_md5); MU_RUN_TEST(test_storage_read); MU_RUN_TEST(test_storage_write_read); MU_RUN_TEST(test_storage_write); @@ -1731,7 +1759,8 @@ MU_TEST(test_rpc_multisession_storage) { MsgList_push_raw(input_0), PB_Main_storage_read_request_tag, TEST_DIR "file0.txt", - ++command_id); + ++command_id, + false); test_rpc_add_read_or_write_to_list( expected_0, READ_RESPONSE, TEST_DIR "file0.txt", pattern, sizeof(pattern), 1, command_id); @@ -1739,7 +1768,8 @@ MU_TEST(test_rpc_multisession_storage) { MsgList_push_raw(input_1), PB_Main_storage_read_request_tag, TEST_DIR "file1.txt", - ++command_id); + ++command_id, + false); test_rpc_add_read_or_write_to_list( expected_1, READ_RESPONSE, TEST_DIR "file1.txt", pattern, sizeof(pattern), 1, command_id); diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c index f0b45c598..13188e5e0 100644 --- a/applications/debug/unit_tests/storage/storage_test.c +++ b/applications/debug/unit_tests/storage/storage_test.c @@ -582,6 +582,49 @@ MU_TEST(test_storage_common_migrate) { furi_record_close(RECORD_STORAGE); } +#define MD5_HASH_SIZE (16) +#include + +MU_TEST(test_md5_calc) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + const char* path = UNIT_TESTS_PATH("storage/md5.txt"); + const char* md5_cstr = "2a456fa43e75088fdde41c93159d62a2"; + const uint8_t md5[MD5_HASH_SIZE] = { + 0x2a, + 0x45, + 0x6f, + 0xa4, + 0x3e, + 0x75, + 0x08, + 0x8f, + 0xdd, + 0xe4, + 0x1c, + 0x93, + 0x15, + 0x9d, + 0x62, + 0xa2, + }; + + uint8_t md5_output[MD5_HASH_SIZE]; + FuriString* md5_output_str = furi_string_alloc(); + memset(md5_output, 0, MD5_HASH_SIZE); + + mu_check(md5_calc_file(file, path, md5_output, NULL)); + mu_check(md5_string_calc_file(file, path, md5_output_str, NULL)); + + mu_assert_mem_eq(md5, md5_output, MD5_HASH_SIZE); + mu_assert_string_eq(md5_cstr, furi_string_get_cstr(md5_output_str)); + + storage_file_free(file); + furi_string_free(md5_output_str); + furi_record_close(RECORD_STORAGE); +} + MU_TEST_SUITE(test_data_path) { MU_RUN_TEST(test_storage_data_path); MU_RUN_TEST(test_storage_data_path_apps); @@ -591,11 +634,16 @@ MU_TEST_SUITE(test_storage_common) { MU_RUN_TEST(test_storage_common_migrate); } +MU_TEST_SUITE(test_md5_calc_suite) { + MU_RUN_TEST(test_md5_calc); +} + int run_minunit_test_storage() { MU_RUN_SUITE(storage_file); MU_RUN_SUITE(storage_dir); MU_RUN_SUITE(storage_rename); MU_RUN_SUITE(test_data_path); MU_RUN_SUITE(test_storage_common); + MU_RUN_SUITE(test_md5_calc_suite); return MU_EXIT_CODE; } diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index c3a4a0470..93c7043e8 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -9,7 +9,7 @@ #include "storage/filesystem_api_defines.h" #include "storage/storage.h" #include -#include +#include #include #include @@ -271,6 +271,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex }; PB_Storage_ListResponse* list = &response.content.storage_list_response; + bool include_md5 = request->content.storage_list_request.include_md5; + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + bool finish = false; int i = 0; @@ -296,6 +301,21 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex list->file[i].size = fileinfo.size; list->file[i].data = NULL; list->file[i].name = name; + + if(include_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf( //-V576 + md5_path, + "%s/%s", + request->content.storage_list_request.path, + name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } else { free(name); @@ -310,8 +330,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex response.has_next = false; rpc_send_and_release(session, &response); + furi_string_free(md5); + furi_string_free(md5_path); storage_dir_close(dir); storage_file_free(dir); + storage_file_free(file); furi_record_close(RECORD_STORAGE); } @@ -569,23 +592,10 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t size_to_read = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(size_to_read); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, size_to_read); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - + if(md5_string_calc_file(file, filename, md5, &file_error)) { PB_Main response = { .command_id = request->command_id, .command_status = PB_CommandStatus_OK, @@ -595,21 +605,15 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont char* md5sum = response.content.storage_md5sum_response.md5sum; size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); - (void)md5sum_size; - furi_assert(hash_size <= ((md5sum_size - 1) / 2)); //-V547 - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); - free(hash); - free(data); - storage_file_close(file); rpc_send_and_release(session, &response); } else { rpc_send_and_release_empty( - session, request->command_id, rpc_system_storage_get_file_error(file)); + session, request->command_id, rpc_system_storage_get_error(file_error)); } + furi_string_free(md5); storage_file_free(file); furi_record_close(RECORD_STORAGE); diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 8e2dcdbbb..74bcf2d92 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -482,34 +482,16 @@ static void storage_cli_md5(Cli* cli, FuriString* path) { UNUSED(cli); Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t buffer_size = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(buffer_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, buffer_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - printf("%02x", hash[i]); - } - printf("\r\n"); - - free(hash); - free(data); + if(md5_string_calc_file(file, furi_string_get_cstr(path), md5, &file_error)) { + printf("%s\r\n", furi_string_get_cstr(md5)); } else { - storage_cli_print_error(storage_file_get_error(file)); + storage_cli_print_error(file_error); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); diff --git a/assets/protobuf b/assets/protobuf index 08a907d95..7e011a958 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 08a907d95733600becc41c0602ef5ee4c4baf782 +Subproject commit 7e011a95863716e72e7c6b5d552bca241d688304 diff --git a/assets/unit_tests/storage/md5.txt b/assets/unit_tests/storage/md5.txt new file mode 100644 index 000000000..777e390be --- /dev/null +++ b/assets/unit_tests/storage/md5.txt @@ -0,0 +1 @@ +Yo dawg, I heard you like md5... \ No newline at end of file diff --git a/fbt b/fbt index ef41cc056..471285a76 100755 --- a/fbt +++ b/fbt @@ -29,7 +29,7 @@ if [ -z "$FBT_NO_SYNC" ]; then echo "\".git\" directory not found, please clone repo via \"git clone\""; exit 1; fi - git submodule update --init --depth 1 --jobs "$N_GIT_THREADS"; + git submodule update --init --jobs "$N_GIT_THREADS"; fi $SCONS_EP $SCONS_DEFAULT_FLAGS "$@" diff --git a/furi/core/thread.c b/furi/core/thread.c index 657b867d1..de50bde7a 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -92,10 +92,10 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); - furi_log_print_format( //-V576 + furi_log_print_format( thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, TAG, - "%s allocation balance: %u", + "%s allocation balance: %zu", thread->name ? thread->name : "Thread", thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index fc9dd06ba..539a48c85 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -892,7 +892,7 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); total_size += itref->value.size; } - FURI_LOG_I(TAG, "Total size of loaded sections: %u", total_size); //-V576 + FURI_LOG_I(TAG, "Total size of loaded sections: %zu", total_size); } return status; diff --git a/lib/toolbox/md5_calc.c b/lib/toolbox/md5_calc.c new file mode 100644 index 000000000..b050295a1 --- /dev/null +++ b/lib/toolbox/md5_calc.c @@ -0,0 +1,44 @@ +#include "md5.h" +#include "md5_calc.h" + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error) { + bool result = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + const uint16_t size_to_read = 512; + uint8_t* data = malloc(size_to_read); + md5_context* md5_ctx = malloc(sizeof(md5_context)); + + md5_starts(md5_ctx); + while(true) { + uint16_t read_size = storage_file_read(file, data, size_to_read); + if(read_size == 0) break; + md5_update(md5_ctx, data, read_size); + } + md5_finish(md5_ctx, output); + free(md5_ctx); + free(data); + } + + if(file_error != NULL) { + *file_error = storage_file_get_error(file); + } + + storage_file_close(file); + return result; +} + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error) { + const size_t hash_size = 16; + unsigned char hash[hash_size]; + bool result = md5_calc_file(file, path, hash, file_error); + + if(result) { + furi_string_set(output, ""); + for(size_t i = 0; i < hash_size; i++) { + furi_string_cat_printf(output, "%02x", hash[i]); + } + } + + return result; +} \ No newline at end of file diff --git a/lib/toolbox/md5_calc.h b/lib/toolbox/md5_calc.h new file mode 100644 index 000000000..cf82e3718 --- /dev/null +++ b/lib/toolbox/md5_calc.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error); + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error); + +#ifdef __cplusplus +} +#endif