rpc_storage.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. #include "flipper.pb.h"
  2. #include "furi/common_defines.h"
  3. #include "furi/memmgr.h"
  4. #include "furi/record.h"
  5. #include "pb_decode.h"
  6. #include "rpc/rpc.h"
  7. #include "rpc_i.h"
  8. #include "storage.pb.h"
  9. #include "storage/filesystem_api_defines.h"
  10. #include "storage/storage.h"
  11. #include <stdint.h>
  12. #include <lib/toolbox/md5.h>
  13. #include <update_util/lfs_backup.h>
  14. #define TAG "RpcStorage"
  15. #define MAX_NAME_LENGTH 255
  16. static const size_t MAX_DATA_SIZE = 512;
  17. typedef enum {
  18. RpcStorageStateIdle = 0,
  19. RpcStorageStateWriting,
  20. } RpcStorageState;
  21. typedef struct {
  22. RpcSession* session;
  23. Storage* api;
  24. File* file;
  25. RpcStorageState state;
  26. uint32_t current_command_id;
  27. } RpcStorageSystem;
  28. static void rpc_system_storage_reset_state(
  29. RpcStorageSystem* rpc_storage,
  30. RpcSession* session,
  31. bool send_error) {
  32. furi_assert(rpc_storage);
  33. if(rpc_storage->state != RpcStorageStateIdle) {
  34. if(send_error) {
  35. rpc_send_and_release_empty(
  36. session,
  37. rpc_storage->current_command_id,
  38. PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED);
  39. }
  40. if(rpc_storage->state == RpcStorageStateWriting) {
  41. storage_file_close(rpc_storage->file);
  42. storage_file_free(rpc_storage->file);
  43. furi_record_close("storage");
  44. }
  45. rpc_storage->state = RpcStorageStateIdle;
  46. }
  47. }
  48. PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error) {
  49. PB_CommandStatus pb_error;
  50. switch(fs_error) {
  51. case FSE_OK:
  52. pb_error = PB_CommandStatus_OK;
  53. break;
  54. case FSE_INVALID_NAME:
  55. pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME;
  56. break;
  57. case FSE_INVALID_PARAMETER:
  58. pb_error = PB_CommandStatus_ERROR_STORAGE_INVALID_PARAMETER;
  59. break;
  60. case FSE_INTERNAL:
  61. pb_error = PB_CommandStatus_ERROR_STORAGE_INTERNAL;
  62. break;
  63. case FSE_ALREADY_OPEN:
  64. pb_error = PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN;
  65. break;
  66. case FSE_DENIED:
  67. pb_error = PB_CommandStatus_ERROR_STORAGE_DENIED;
  68. break;
  69. case FSE_EXIST:
  70. pb_error = PB_CommandStatus_ERROR_STORAGE_EXIST;
  71. break;
  72. case FSE_NOT_EXIST:
  73. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_EXIST;
  74. break;
  75. case FSE_NOT_READY:
  76. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_READY;
  77. break;
  78. case FSE_NOT_IMPLEMENTED:
  79. pb_error = PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED;
  80. break;
  81. default:
  82. pb_error = PB_CommandStatus_ERROR;
  83. break;
  84. }
  85. return pb_error;
  86. }
  87. static PB_CommandStatus rpc_system_storage_get_file_error(File* file) {
  88. return rpc_system_storage_get_error(storage_file_get_error(file));
  89. }
  90. static void rpc_system_storage_info_process(const PB_Main* request, void* context) {
  91. furi_assert(request);
  92. furi_assert(context);
  93. furi_assert(request->which_content == PB_Main_storage_info_request_tag);
  94. FURI_LOG_D(TAG, "Info");
  95. RpcStorageSystem* rpc_storage = context;
  96. RpcSession* session = rpc_storage->session;
  97. furi_assert(session);
  98. rpc_system_storage_reset_state(rpc_storage, session, true);
  99. PB_Main* response = malloc(sizeof(PB_Main));
  100. response->command_id = request->command_id;
  101. Storage* fs_api = furi_record_open("storage");
  102. FS_Error error = storage_common_fs_info(
  103. fs_api,
  104. request->content.storage_info_request.path,
  105. &response->content.storage_info_response.total_space,
  106. &response->content.storage_info_response.free_space);
  107. response->command_status = rpc_system_storage_get_error(error);
  108. if(error == FSE_OK) {
  109. response->which_content = PB_Main_storage_info_response_tag;
  110. } else {
  111. response->which_content = PB_Main_empty_tag;
  112. }
  113. rpc_send_and_release(session, response);
  114. free(response);
  115. furi_record_close("storage");
  116. }
  117. static void rpc_system_storage_stat_process(const PB_Main* request, void* context) {
  118. furi_assert(request);
  119. furi_assert(context);
  120. furi_assert(request->which_content == PB_Main_storage_stat_request_tag);
  121. FURI_LOG_D(TAG, "Stat");
  122. RpcStorageSystem* rpc_storage = context;
  123. RpcSession* session = rpc_storage->session;
  124. furi_assert(session);
  125. rpc_system_storage_reset_state(rpc_storage, session, true);
  126. PB_Main* response = malloc(sizeof(PB_Main));
  127. response->command_id = request->command_id;
  128. Storage* fs_api = furi_record_open("storage");
  129. const char* path = request->content.storage_stat_request.path;
  130. FileInfo fileinfo;
  131. FS_Error error = storage_common_stat(fs_api, path, &fileinfo);
  132. response->command_status = rpc_system_storage_get_error(error);
  133. response->which_content = PB_Main_empty_tag;
  134. if(error == FSE_OK) {
  135. response->which_content = PB_Main_storage_stat_response_tag;
  136. response->content.storage_stat_response.has_file = true;
  137. response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ?
  138. PB_Storage_File_FileType_DIR :
  139. PB_Storage_File_FileType_FILE;
  140. response->content.storage_stat_response.file.size = fileinfo.size;
  141. }
  142. rpc_send_and_release(session, response);
  143. free(response);
  144. furi_record_close("storage");
  145. }
  146. static void rpc_system_storage_list_root(const PB_Main* request, void* context) {
  147. RpcStorageSystem* rpc_storage = context;
  148. RpcSession* session = rpc_storage->session;
  149. furi_assert(session);
  150. const char* hard_coded_dirs[] = {"any", "int", "ext"};
  151. PB_Main response = {
  152. .has_next = false,
  153. .command_id = request->command_id,
  154. .command_status = PB_CommandStatus_OK,
  155. .which_content = PB_Main_storage_list_response_tag,
  156. };
  157. furi_assert(COUNT_OF(hard_coded_dirs) < COUNT_OF(response.content.storage_list_response.file));
  158. for(uint32_t i = 0; i < COUNT_OF(hard_coded_dirs); ++i) {
  159. ++response.content.storage_list_response.file_count;
  160. response.content.storage_list_response.file[i].data = NULL;
  161. response.content.storage_list_response.file[i].size = 0;
  162. response.content.storage_list_response.file[i].type = PB_Storage_File_FileType_DIR;
  163. char* str = malloc(strlen(hard_coded_dirs[i]) + 1);
  164. strcpy(str, hard_coded_dirs[i]);
  165. response.content.storage_list_response.file[i].name = str;
  166. }
  167. rpc_send_and_release(session, &response);
  168. }
  169. static void rpc_system_storage_list_process(const PB_Main* request, void* context) {
  170. furi_assert(request);
  171. furi_assert(context);
  172. furi_assert(request->which_content == PB_Main_storage_list_request_tag);
  173. FURI_LOG_D(TAG, "List");
  174. RpcStorageSystem* rpc_storage = context;
  175. RpcSession* session = rpc_storage->session;
  176. furi_assert(session);
  177. rpc_system_storage_reset_state(rpc_storage, session, true);
  178. if(!strcmp(request->content.storage_list_request.path, "/")) {
  179. rpc_system_storage_list_root(request, context);
  180. return;
  181. }
  182. Storage* fs_api = furi_record_open("storage");
  183. File* dir = storage_file_alloc(fs_api);
  184. PB_Main response = {
  185. .command_id = request->command_id,
  186. .has_next = false,
  187. .which_content = PB_Main_storage_list_response_tag,
  188. .command_status = PB_CommandStatus_OK,
  189. };
  190. PB_Storage_ListResponse* list = &response.content.storage_list_response;
  191. bool finish = false;
  192. int i = 0;
  193. if(!storage_dir_open(dir, request->content.storage_list_request.path)) {
  194. response.command_status = rpc_system_storage_get_file_error(dir);
  195. response.which_content = PB_Main_empty_tag;
  196. finish = true;
  197. }
  198. while(!finish) {
  199. FileInfo fileinfo;
  200. char* name = malloc(MAX_NAME_LENGTH + 1);
  201. if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
  202. if(i == COUNT_OF(list->file)) {
  203. list->file_count = i;
  204. response.has_next = true;
  205. rpc_send_and_release(session, &response);
  206. i = 0;
  207. }
  208. list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? PB_Storage_File_FileType_DIR :
  209. PB_Storage_File_FileType_FILE;
  210. list->file[i].size = fileinfo.size;
  211. list->file[i].data = NULL;
  212. list->file[i].name = name;
  213. ++i;
  214. } else {
  215. list->file_count = i;
  216. finish = true;
  217. free(name);
  218. }
  219. }
  220. response.has_next = false;
  221. rpc_send_and_release(session, &response);
  222. storage_dir_close(dir);
  223. storage_file_free(dir);
  224. furi_record_close("storage");
  225. }
  226. static void rpc_system_storage_read_process(const PB_Main* request, void* context) {
  227. furi_assert(request);
  228. furi_assert(context);
  229. furi_assert(request->which_content == PB_Main_storage_read_request_tag);
  230. FURI_LOG_D(TAG, "Read");
  231. RpcStorageSystem* rpc_storage = context;
  232. RpcSession* session = rpc_storage->session;
  233. furi_assert(session);
  234. rpc_system_storage_reset_state(rpc_storage, session, true);
  235. /* use same message memory to send reponse */
  236. PB_Main* response = malloc(sizeof(PB_Main));
  237. const char* path = request->content.storage_read_request.path;
  238. Storage* fs_api = furi_record_open("storage");
  239. File* file = storage_file_alloc(fs_api);
  240. bool result = false;
  241. if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  242. size_t size_left = storage_file_size(file);
  243. do {
  244. response->command_id = request->command_id;
  245. response->which_content = PB_Main_storage_read_response_tag;
  246. response->command_status = PB_CommandStatus_OK;
  247. response->content.storage_read_response.has_file = true;
  248. response->content.storage_read_response.file.data =
  249. malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE)));
  250. uint8_t* buffer = response->content.storage_read_response.file.data->bytes;
  251. uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size;
  252. size_t read_size = MIN(size_left, MAX_DATA_SIZE);
  253. *read_size_msg = storage_file_read(file, buffer, read_size);
  254. size_left -= read_size;
  255. result = (*read_size_msg == read_size);
  256. if(result) {
  257. response->has_next = (size_left > 0);
  258. rpc_send_and_release(session, response);
  259. }
  260. } while((size_left != 0) && result);
  261. if(!result) {
  262. rpc_send_and_release_empty(
  263. session, request->command_id, rpc_system_storage_get_file_error(file));
  264. }
  265. } else {
  266. rpc_send_and_release_empty(
  267. session, request->command_id, rpc_system_storage_get_file_error(file));
  268. }
  269. free(response);
  270. storage_file_close(file);
  271. storage_file_free(file);
  272. furi_record_close("storage");
  273. }
  274. static void rpc_system_storage_write_process(const PB_Main* request, void* context) {
  275. furi_assert(request);
  276. furi_assert(context);
  277. furi_assert(request->which_content == PB_Main_storage_write_request_tag);
  278. FURI_LOG_D(TAG, "Write");
  279. RpcStorageSystem* rpc_storage = context;
  280. RpcSession* session = rpc_storage->session;
  281. furi_assert(session);
  282. bool result = true;
  283. if((request->command_id != rpc_storage->current_command_id) &&
  284. (rpc_storage->state == RpcStorageStateWriting)) {
  285. rpc_system_storage_reset_state(rpc_storage, session, true);
  286. }
  287. if(rpc_storage->state != RpcStorageStateWriting) {
  288. rpc_storage->api = furi_record_open("storage");
  289. rpc_storage->file = storage_file_alloc(rpc_storage->api);
  290. rpc_storage->current_command_id = request->command_id;
  291. rpc_storage->state = RpcStorageStateWriting;
  292. const char* path = request->content.storage_write_request.path;
  293. result = storage_file_open(rpc_storage->file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS);
  294. }
  295. File* file = rpc_storage->file;
  296. if(result) {
  297. uint8_t* buffer = request->content.storage_write_request.file.data->bytes;
  298. size_t buffer_size = request->content.storage_write_request.file.data->size;
  299. uint16_t written_size = storage_file_write(file, buffer, buffer_size);
  300. result = (written_size == buffer_size);
  301. if(result && !request->has_next) {
  302. rpc_send_and_release_empty(
  303. session, rpc_storage->current_command_id, PB_CommandStatus_OK);
  304. rpc_system_storage_reset_state(rpc_storage, session, false);
  305. }
  306. }
  307. if(!result) {
  308. rpc_send_and_release_empty(
  309. session, rpc_storage->current_command_id, rpc_system_storage_get_file_error(file));
  310. rpc_system_storage_reset_state(rpc_storage, session, false);
  311. }
  312. }
  313. static bool rpc_system_storage_is_dir_is_empty(Storage* fs_api, const char* path) {
  314. FileInfo fileinfo;
  315. bool is_dir_is_empty = false;
  316. FS_Error error = storage_common_stat(fs_api, path, &fileinfo);
  317. if((error == FSE_OK) && (fileinfo.flags & FSF_DIRECTORY)) {
  318. File* dir = storage_file_alloc(fs_api);
  319. if(storage_dir_open(dir, path)) {
  320. char* name = malloc(MAX_NAME_LENGTH);
  321. is_dir_is_empty = !storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH);
  322. free(name);
  323. }
  324. storage_dir_close(dir);
  325. storage_file_free(dir);
  326. }
  327. return is_dir_is_empty;
  328. }
  329. static void rpc_system_storage_delete_process(const PB_Main* request, void* context) {
  330. furi_assert(request);
  331. furi_assert(request->which_content == PB_Main_storage_delete_request_tag);
  332. furi_assert(context);
  333. FURI_LOG_D(TAG, "Delete");
  334. RpcStorageSystem* rpc_storage = context;
  335. RpcSession* session = rpc_storage->session;
  336. furi_assert(session);
  337. PB_CommandStatus status = PB_CommandStatus_ERROR;
  338. rpc_system_storage_reset_state(rpc_storage, session, true);
  339. Storage* fs_api = furi_record_open("storage");
  340. char* path = request->content.storage_delete_request.path;
  341. if(!path) {
  342. status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
  343. } else {
  344. FS_Error error_remove = storage_common_remove(fs_api, path);
  345. // FSE_DENIED is for empty directory, but not only for this
  346. // that's why we have to check it
  347. if((error_remove == FSE_DENIED) && !rpc_system_storage_is_dir_is_empty(fs_api, path)) {
  348. if(request->content.storage_delete_request.recursive) {
  349. bool deleted = storage_simply_remove_recursive(fs_api, path);
  350. status = deleted ? PB_CommandStatus_OK : PB_CommandStatus_ERROR;
  351. } else {
  352. status = PB_CommandStatus_ERROR_STORAGE_DIR_NOT_EMPTY;
  353. }
  354. } else if(error_remove == FSE_NOT_EXIST) {
  355. status = PB_CommandStatus_OK;
  356. } else {
  357. status = rpc_system_storage_get_error(error_remove);
  358. }
  359. }
  360. furi_record_close("storage");
  361. rpc_send_and_release_empty(session, request->command_id, status);
  362. }
  363. static void rpc_system_storage_mkdir_process(const PB_Main* request, void* context) {
  364. furi_assert(request);
  365. furi_assert(request->which_content == PB_Main_storage_mkdir_request_tag);
  366. furi_assert(context);
  367. FURI_LOG_D(TAG, "Mkdir");
  368. RpcStorageSystem* rpc_storage = context;
  369. RpcSession* session = rpc_storage->session;
  370. furi_assert(session);
  371. PB_CommandStatus status;
  372. rpc_system_storage_reset_state(rpc_storage, session, true);
  373. Storage* fs_api = furi_record_open("storage");
  374. char* path = request->content.storage_mkdir_request.path;
  375. if(path) {
  376. FS_Error error = storage_common_mkdir(fs_api, path);
  377. status = rpc_system_storage_get_error(error);
  378. } else {
  379. status = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
  380. }
  381. furi_record_close("storage");
  382. rpc_send_and_release_empty(session, request->command_id, status);
  383. }
  384. static void rpc_system_storage_md5sum_process(const PB_Main* request, void* context) {
  385. furi_assert(request);
  386. furi_assert(request->which_content == PB_Main_storage_md5sum_request_tag);
  387. furi_assert(context);
  388. FURI_LOG_D(TAG, "Md5sum");
  389. RpcStorageSystem* rpc_storage = context;
  390. RpcSession* session = rpc_storage->session;
  391. furi_assert(session);
  392. rpc_system_storage_reset_state(rpc_storage, session, true);
  393. const char* filename = request->content.storage_md5sum_request.path;
  394. if(!filename) {
  395. rpc_send_and_release_empty(
  396. session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);
  397. return;
  398. }
  399. Storage* fs_api = furi_record_open("storage");
  400. File* file = storage_file_alloc(fs_api);
  401. if(storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) {
  402. const uint16_t size_to_read = 512;
  403. const uint8_t hash_size = 16;
  404. uint8_t* data = malloc(size_to_read);
  405. uint8_t* hash = malloc(sizeof(uint8_t) * hash_size);
  406. md5_context* md5_ctx = malloc(sizeof(md5_context));
  407. md5_starts(md5_ctx);
  408. while(true) {
  409. uint16_t read_size = storage_file_read(file, data, size_to_read);
  410. if(read_size == 0) break;
  411. md5_update(md5_ctx, data, read_size);
  412. }
  413. md5_finish(md5_ctx, hash);
  414. free(md5_ctx);
  415. PB_Main response = {
  416. .command_id = request->command_id,
  417. .command_status = PB_CommandStatus_OK,
  418. .which_content = PB_Main_storage_md5sum_response_tag,
  419. .has_next = false,
  420. };
  421. char* md5sum = response.content.storage_md5sum_response.md5sum;
  422. size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum);
  423. (void)md5sum_size;
  424. furi_assert(hash_size <= ((md5sum_size - 1) / 2));
  425. for(uint8_t i = 0; i < hash_size; i++) {
  426. md5sum += sprintf(md5sum, "%02x", hash[i]);
  427. }
  428. free(hash);
  429. free(data);
  430. storage_file_close(file);
  431. rpc_send_and_release(session, &response);
  432. } else {
  433. rpc_send_and_release_empty(
  434. session, request->command_id, rpc_system_storage_get_file_error(file));
  435. }
  436. storage_file_free(file);
  437. furi_record_close("storage");
  438. }
  439. static void rpc_system_storage_rename_process(const PB_Main* request, void* context) {
  440. furi_assert(request);
  441. furi_assert(request->which_content == PB_Main_storage_rename_request_tag);
  442. furi_assert(context);
  443. FURI_LOG_D(TAG, "Rename");
  444. RpcStorageSystem* rpc_storage = context;
  445. RpcSession* session = rpc_storage->session;
  446. furi_assert(session);
  447. PB_CommandStatus status;
  448. rpc_system_storage_reset_state(rpc_storage, session, true);
  449. Storage* fs_api = furi_record_open("storage");
  450. FS_Error error = storage_common_rename(
  451. fs_api,
  452. request->content.storage_rename_request.old_path,
  453. request->content.storage_rename_request.new_path);
  454. status = rpc_system_storage_get_error(error);
  455. furi_record_close("storage");
  456. rpc_send_and_release_empty(session, request->command_id, status);
  457. }
  458. static void rpc_system_storage_backup_create_process(const PB_Main* request, void* context) {
  459. furi_assert(request);
  460. furi_assert(request->which_content == PB_Main_storage_backup_create_request_tag);
  461. FURI_LOG_D(TAG, "BackupCreate");
  462. RpcSession* session = (RpcSession*)context;
  463. furi_assert(session);
  464. PB_Main* response = malloc(sizeof(PB_Main));
  465. response->command_id = request->command_id;
  466. response->has_next = false;
  467. Storage* fs_api = furi_record_open("storage");
  468. bool backup_ok =
  469. lfs_backup_create(fs_api, request->content.storage_backup_create_request.archive_path);
  470. response->command_status = backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR;
  471. furi_record_close("storage");
  472. rpc_send_and_release(session, response);
  473. free(response);
  474. }
  475. static void rpc_system_storage_backup_restore_process(const PB_Main* request, void* context) {
  476. furi_assert(request);
  477. furi_assert(request->which_content == PB_Main_storage_backup_restore_request_tag);
  478. FURI_LOG_D(TAG, "BackupRestore");
  479. RpcSession* session = (RpcSession*)context;
  480. furi_assert(session);
  481. PB_Main* response = malloc(sizeof(PB_Main));
  482. response->command_id = request->command_id;
  483. response->has_next = false;
  484. response->command_status = PB_CommandStatus_OK;
  485. Storage* fs_api = furi_record_open("storage");
  486. bool backup_ok =
  487. lfs_backup_unpack(fs_api, request->content.storage_backup_restore_request.archive_path);
  488. response->command_status = backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR;
  489. furi_record_close("storage");
  490. rpc_send_and_release(session, response);
  491. free(response);
  492. }
  493. void* rpc_system_storage_alloc(RpcSession* session) {
  494. furi_assert(session);
  495. RpcStorageSystem* rpc_storage = malloc(sizeof(RpcStorageSystem));
  496. rpc_storage->api = furi_record_open("storage");
  497. rpc_storage->session = session;
  498. rpc_storage->state = RpcStorageStateIdle;
  499. RpcHandler rpc_handler = {
  500. .message_handler = NULL,
  501. .decode_submessage = NULL,
  502. .context = rpc_storage,
  503. };
  504. rpc_handler.message_handler = rpc_system_storage_info_process;
  505. rpc_add_handler(session, PB_Main_storage_info_request_tag, &rpc_handler);
  506. rpc_handler.message_handler = rpc_system_storage_stat_process;
  507. rpc_add_handler(session, PB_Main_storage_stat_request_tag, &rpc_handler);
  508. rpc_handler.message_handler = rpc_system_storage_list_process;
  509. rpc_add_handler(session, PB_Main_storage_list_request_tag, &rpc_handler);
  510. rpc_handler.message_handler = rpc_system_storage_read_process;
  511. rpc_add_handler(session, PB_Main_storage_read_request_tag, &rpc_handler);
  512. rpc_handler.message_handler = rpc_system_storage_write_process;
  513. rpc_add_handler(session, PB_Main_storage_write_request_tag, &rpc_handler);
  514. rpc_handler.message_handler = rpc_system_storage_delete_process;
  515. rpc_add_handler(session, PB_Main_storage_delete_request_tag, &rpc_handler);
  516. rpc_handler.message_handler = rpc_system_storage_mkdir_process;
  517. rpc_add_handler(session, PB_Main_storage_mkdir_request_tag, &rpc_handler);
  518. rpc_handler.message_handler = rpc_system_storage_md5sum_process;
  519. rpc_add_handler(session, PB_Main_storage_md5sum_request_tag, &rpc_handler);
  520. rpc_handler.message_handler = rpc_system_storage_rename_process;
  521. rpc_add_handler(session, PB_Main_storage_rename_request_tag, &rpc_handler);
  522. rpc_handler.message_handler = rpc_system_storage_backup_create_process;
  523. rpc_add_handler(session, PB_Main_storage_backup_create_request_tag, &rpc_handler);
  524. rpc_handler.message_handler = rpc_system_storage_backup_restore_process;
  525. rpc_add_handler(session, PB_Main_storage_backup_restore_request_tag, &rpc_handler);
  526. return rpc_storage;
  527. }
  528. void rpc_system_storage_free(void* context) {
  529. RpcStorageSystem* rpc_storage = context;
  530. RpcSession* session = rpc_storage->session;
  531. furi_assert(session);
  532. rpc_system_storage_reset_state(rpc_storage, session, false);
  533. free(rpc_storage);
  534. }