application_assets.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. #include "application_assets.h"
  2. #include <toolbox/path.h>
  3. #include <storage/storage_i.h>
  4. // #define ELF_ASSETS_DEBUG_LOG 1
  5. #ifndef ELF_ASSETS_DEBUG_LOG
  6. #undef FURI_LOG_D
  7. #define FURI_LOG_D(...)
  8. #undef FURI_LOG_E
  9. #define FURI_LOG_E(...)
  10. #endif
  11. #define FLIPPER_APPLICATION_ASSETS_MAGIC 0x4F4C5A44
  12. #define FLIPPER_APPLICATION_ASSETS_VERSION 1
  13. #define FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME ".assets.signature"
  14. #define BUFFER_SIZE 512
  15. #define TAG "fap_assets"
  16. #pragma pack(push, 1)
  17. typedef struct {
  18. uint32_t magic;
  19. uint32_t version;
  20. uint32_t dirs_count;
  21. uint32_t files_count;
  22. } FlipperApplicationAssetsHeader;
  23. #pragma pack(pop)
  24. typedef enum {
  25. AssetsSignatureResultEqual,
  26. AssetsSignatureResultNotEqual,
  27. AssetsSignatureResultError,
  28. } AssetsSignatureResult;
  29. static FuriString* flipper_application_assets_alloc_app_full_path(FuriString* app_name) {
  30. furi_assert(app_name);
  31. FuriString* full_path = furi_string_alloc_set(APPS_ASSETS_PATH "/");
  32. furi_string_cat(full_path, app_name);
  33. return full_path;
  34. }
  35. static FuriString* flipper_application_assets_alloc_signature_file_path(FuriString* app_name) {
  36. furi_assert(app_name);
  37. FuriString* signature_file_path = flipper_application_assets_alloc_app_full_path(app_name);
  38. furi_string_cat(signature_file_path, "/" FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME);
  39. return signature_file_path;
  40. }
  41. static uint8_t* flipper_application_assets_alloc_and_load_data(File* file, size_t* size) {
  42. furi_assert(file);
  43. uint8_t* data = NULL;
  44. uint32_t length = 0;
  45. // read data length
  46. if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) {
  47. return NULL;
  48. }
  49. data = malloc(length);
  50. // read data
  51. if(storage_file_read(file, (void*)data, length) != length) {
  52. free((void*)data);
  53. return NULL;
  54. }
  55. if(size != NULL) {
  56. *size = length;
  57. }
  58. return data;
  59. }
  60. static bool flipper_application_assets_process_files(
  61. Storage* storage,
  62. File* file,
  63. FuriString* app_name,
  64. uint32_t files_count) {
  65. furi_assert(storage);
  66. furi_assert(file);
  67. furi_assert(app_name);
  68. UNUSED(storage);
  69. bool success = false;
  70. uint32_t length = 0;
  71. char* path = NULL;
  72. FuriString* file_path = furi_string_alloc();
  73. File* destination = storage_file_alloc(storage);
  74. FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
  75. for(uint32_t i = 0; i < files_count; i++) {
  76. path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL);
  77. if(path == NULL) {
  78. break;
  79. }
  80. // read file size
  81. if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) {
  82. break;
  83. }
  84. furi_string_set(file_path, full_path);
  85. furi_string_cat(file_path, "/");
  86. furi_string_cat(file_path, path);
  87. if(!storage_file_open(
  88. destination, furi_string_get_cstr(file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  89. FURI_LOG_E(TAG, "Can't create file: %s", furi_string_get_cstr(file_path));
  90. break;
  91. }
  92. // copy data to file
  93. if(!storage_file_copy_to_file(file, destination, length)) {
  94. FURI_LOG_E(TAG, "Can't copy data to file: %s", furi_string_get_cstr(file_path));
  95. break;
  96. }
  97. storage_file_close(destination);
  98. free(path);
  99. path = NULL;
  100. if(i == files_count - 1) {
  101. success = true;
  102. }
  103. }
  104. if(path != NULL) {
  105. free(path);
  106. }
  107. storage_file_free(destination);
  108. furi_string_free(file_path);
  109. return success;
  110. }
  111. static bool flipper_application_assets_process_dirs(
  112. Storage* storage,
  113. File* file,
  114. FuriString* app_name,
  115. uint32_t dirs_count) {
  116. furi_assert(storage);
  117. furi_assert(file);
  118. furi_assert(app_name);
  119. bool success = false;
  120. FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
  121. do {
  122. if(!storage_simply_mkdir(storage, APPS_ASSETS_PATH)) {
  123. break;
  124. }
  125. if(!storage_simply_mkdir(storage, furi_string_get_cstr(full_path))) {
  126. break;
  127. }
  128. FuriString* dir_path = furi_string_alloc();
  129. char* path = NULL;
  130. for(uint32_t i = 0; i < dirs_count; i++) {
  131. path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL);
  132. if(path == NULL) {
  133. break;
  134. }
  135. furi_string_set(dir_path, full_path);
  136. furi_string_cat(dir_path, "/");
  137. furi_string_cat(dir_path, path);
  138. if(!storage_simply_mkdir(storage, furi_string_get_cstr(dir_path))) {
  139. FURI_LOG_E(TAG, "Can't create directory: %s", furi_string_get_cstr(dir_path));
  140. break;
  141. }
  142. free(path);
  143. path = NULL;
  144. if(i == dirs_count - 1) {
  145. success = true;
  146. }
  147. }
  148. if(path != NULL) {
  149. free(path);
  150. }
  151. furi_string_free(dir_path);
  152. } while(false);
  153. furi_string_free(full_path);
  154. return success;
  155. }
  156. static AssetsSignatureResult flipper_application_assets_process_signature(
  157. Storage* storage,
  158. File* file,
  159. FuriString* app_name,
  160. uint8_t** signature_data,
  161. size_t* signature_data_size) {
  162. furi_assert(storage);
  163. furi_assert(file);
  164. furi_assert(app_name);
  165. furi_assert(signature_data);
  166. furi_assert(signature_data_size);
  167. AssetsSignatureResult result = AssetsSignatureResultError;
  168. File* signature_file = storage_file_alloc(storage);
  169. FuriString* signature_file_path =
  170. flipper_application_assets_alloc_signature_file_path(app_name);
  171. do {
  172. // read signature
  173. *signature_data =
  174. flipper_application_assets_alloc_and_load_data(file, signature_data_size);
  175. if(*signature_data == NULL) { //-V547
  176. FURI_LOG_E(TAG, "Can't read signature");
  177. break;
  178. }
  179. result = AssetsSignatureResultNotEqual;
  180. if(!storage_file_open(
  181. signature_file,
  182. furi_string_get_cstr(signature_file_path),
  183. FSAM_READ_WRITE,
  184. FSOM_OPEN_EXISTING)) {
  185. FURI_LOG_E(TAG, "Can't open signature file");
  186. break;
  187. }
  188. size_t signature_size = storage_file_size(signature_file);
  189. uint8_t* signature_file_data = malloc(signature_size);
  190. if(storage_file_read(signature_file, signature_file_data, signature_size) !=
  191. signature_size) {
  192. FURI_LOG_E(TAG, "Can't read signature file");
  193. free(signature_file_data);
  194. break;
  195. }
  196. if(memcmp(*signature_data, signature_file_data, signature_size) == 0) {
  197. FURI_LOG_D(TAG, "Assets signature is equal");
  198. result = AssetsSignatureResultEqual;
  199. }
  200. free(signature_file_data);
  201. } while(0);
  202. storage_file_free(signature_file);
  203. furi_string_free(signature_file_path);
  204. return result;
  205. }
  206. bool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size) {
  207. UNUSED(size);
  208. furi_assert(file);
  209. furi_assert(elf_path);
  210. FlipperApplicationAssetsHeader header;
  211. bool result = false;
  212. Storage* storage = furi_record_open(RECORD_STORAGE);
  213. uint8_t* signature_data = NULL;
  214. size_t signature_data_size = 0;
  215. FuriString* app_name = furi_string_alloc();
  216. path_extract_filename_no_ext(elf_path, app_name);
  217. FURI_LOG_D(TAG, "Loading assets for %s", furi_string_get_cstr(app_name));
  218. do {
  219. if(!storage_file_seek(file, offset, true)) {
  220. break;
  221. }
  222. // read header
  223. if(storage_file_read(file, &header, sizeof(header)) != sizeof(header)) {
  224. break;
  225. }
  226. if(header.magic != FLIPPER_APPLICATION_ASSETS_MAGIC) {
  227. break;
  228. }
  229. if(header.version != FLIPPER_APPLICATION_ASSETS_VERSION) {
  230. break;
  231. }
  232. // process signature
  233. AssetsSignatureResult signature_result = flipper_application_assets_process_signature(
  234. storage, file, app_name, &signature_data, &signature_data_size);
  235. if(signature_result == AssetsSignatureResultError) {
  236. FURI_LOG_E(TAG, "Assets signature error");
  237. break;
  238. } else if(signature_result == AssetsSignatureResultEqual) {
  239. FURI_LOG_D(TAG, "Assets signature equal, skip loading");
  240. result = true;
  241. break;
  242. } else {
  243. FURI_LOG_D(TAG, "Assets signature not equal, loading");
  244. // remove old assets
  245. FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name);
  246. storage_simply_remove_recursive(storage, furi_string_get_cstr(full_path));
  247. furi_string_free(full_path);
  248. FURI_LOG_D(TAG, "Assets removed");
  249. }
  250. // process directories
  251. if(!flipper_application_assets_process_dirs(storage, file, app_name, header.dirs_count)) {
  252. break;
  253. }
  254. // process files
  255. if(!flipper_application_assets_process_files(storage, file, app_name, header.files_count)) {
  256. break;
  257. }
  258. // write signature
  259. FuriString* signature_file_path =
  260. flipper_application_assets_alloc_signature_file_path(app_name);
  261. File* signature_file = storage_file_alloc(storage);
  262. if(storage_file_open(
  263. signature_file,
  264. furi_string_get_cstr(signature_file_path),
  265. FSAM_WRITE,
  266. FSOM_CREATE_ALWAYS)) {
  267. storage_file_write(signature_file, signature_data, signature_data_size);
  268. }
  269. storage_file_free(signature_file);
  270. furi_string_free(signature_file_path);
  271. result = true;
  272. } while(false);
  273. if(signature_data != NULL) {
  274. free(signature_data);
  275. }
  276. furi_record_close(RECORD_STORAGE);
  277. furi_string_free(app_name);
  278. FURI_LOG_D(TAG, "Assets loading %s", result ? "success" : "failed");
  279. return result;
  280. }