storage_processing.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. #include "storage_processing.h"
  2. #include <m-list.h>
  3. #include <m-dict.h>
  4. #define FS_CALL(_storage, _fn) \
  5. storage_data_lock(_storage); \
  6. ret = _storage->fs_api->_fn; \
  7. storage_data_unlock(_storage);
  8. #define ST_CALL(_storage, _fn) \
  9. storage_data_lock(_storage); \
  10. ret = _storage->api._fn; \
  11. storage_data_unlock(_storage);
  12. static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) {
  13. furi_check(type == ST_EXT || type == ST_INT);
  14. StorageData* storage = &app->storage[type];
  15. return storage;
  16. }
  17. static bool storage_type_is_not_valid(StorageType type) {
  18. #ifdef FURI_RAM_EXEC
  19. return type != ST_EXT;
  20. #else
  21. return type >= ST_ERROR;
  22. #endif
  23. }
  24. static StorageData* get_storage_by_file(File* file, StorageData* storages) {
  25. StorageData* storage_data = NULL;
  26. for(uint8_t i = 0; i < STORAGE_COUNT; i++) {
  27. if(storage_has_file(file, &storages[i])) {
  28. storage_data = &storages[i];
  29. }
  30. }
  31. return storage_data;
  32. }
  33. static const char* remove_vfs(const char* path) {
  34. return path + MIN(4u, strlen(path));
  35. }
  36. static StorageType storage_get_type_by_path(Storage* app, const char* path) {
  37. StorageType type = ST_ERROR;
  38. if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) &&
  39. memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
  40. type = ST_EXT;
  41. } else if(
  42. strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) &&
  43. memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
  44. type = ST_INT;
  45. } else if(
  46. strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) &&
  47. memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
  48. type = ST_ANY;
  49. }
  50. if(type == ST_ANY) {
  51. type = ST_INT;
  52. if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) {
  53. type = ST_EXT;
  54. }
  55. }
  56. return type;
  57. }
  58. static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) {
  59. if(memcmp(
  60. furi_string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) ==
  61. 0) {
  62. switch(real_storage) {
  63. case ST_EXT:
  64. furi_string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]);
  65. furi_string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]);
  66. furi_string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]);
  67. furi_string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]);
  68. break;
  69. case ST_INT:
  70. furi_string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]);
  71. furi_string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]);
  72. furi_string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]);
  73. furi_string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]);
  74. break;
  75. default:
  76. break;
  77. }
  78. }
  79. }
  80. /******************* File Functions *******************/
  81. bool storage_process_file_open(
  82. Storage* app,
  83. File* file,
  84. const char* path,
  85. FS_AccessMode access_mode,
  86. FS_OpenMode open_mode) {
  87. bool ret = false;
  88. StorageType type = storage_get_type_by_path(app, path);
  89. StorageData* storage;
  90. file->error_id = FSE_OK;
  91. if(storage_type_is_not_valid(type)) {
  92. file->error_id = FSE_INVALID_NAME;
  93. } else {
  94. storage = storage_get_storage_by_type(app, type);
  95. FuriString* real_path;
  96. real_path = furi_string_alloc_set(path);
  97. storage_path_change_to_real_storage(real_path, type);
  98. if(storage_path_already_open(real_path, storage->files)) {
  99. file->error_id = FSE_ALREADY_OPEN;
  100. } else {
  101. if(access_mode & FSAM_WRITE) {
  102. storage_data_timestamp(storage);
  103. }
  104. storage_push_storage_file(file, real_path, type, storage);
  105. FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode));
  106. }
  107. furi_string_free(real_path);
  108. }
  109. return ret;
  110. }
  111. bool storage_process_file_close(Storage* app, File* file) {
  112. bool ret = false;
  113. StorageData* storage = get_storage_by_file(file, app->storage);
  114. if(storage == NULL) {
  115. file->error_id = FSE_INVALID_PARAMETER;
  116. } else {
  117. FS_CALL(storage, file.close(storage, file));
  118. storage_pop_storage_file(file, storage);
  119. StorageEvent event = {.type = StorageEventTypeFileClose};
  120. furi_pubsub_publish(app->pubsub, &event);
  121. }
  122. return ret;
  123. }
  124. static uint16_t
  125. storage_process_file_read(Storage* app, File* file, void* buff, uint16_t const bytes_to_read) {
  126. uint16_t ret = 0;
  127. StorageData* storage = get_storage_by_file(file, app->storage);
  128. if(storage == NULL) {
  129. file->error_id = FSE_INVALID_PARAMETER;
  130. } else {
  131. FS_CALL(storage, file.read(storage, file, buff, bytes_to_read));
  132. }
  133. return ret;
  134. }
  135. static uint16_t storage_process_file_write(
  136. Storage* app,
  137. File* file,
  138. const void* buff,
  139. uint16_t const bytes_to_write) {
  140. uint16_t ret = 0;
  141. StorageData* storage = get_storage_by_file(file, app->storage);
  142. if(storage == NULL) {
  143. file->error_id = FSE_INVALID_PARAMETER;
  144. } else {
  145. storage_data_timestamp(storage);
  146. FS_CALL(storage, file.write(storage, file, buff, bytes_to_write));
  147. }
  148. return ret;
  149. }
  150. static bool storage_process_file_seek(
  151. Storage* app,
  152. File* file,
  153. const uint32_t offset,
  154. const bool from_start) {
  155. bool ret = false;
  156. StorageData* storage = get_storage_by_file(file, app->storage);
  157. if(storage == NULL) {
  158. file->error_id = FSE_INVALID_PARAMETER;
  159. } else {
  160. FS_CALL(storage, file.seek(storage, file, offset, from_start));
  161. }
  162. return ret;
  163. }
  164. static uint64_t storage_process_file_tell(Storage* app, File* file) {
  165. uint64_t ret = 0;
  166. StorageData* storage = get_storage_by_file(file, app->storage);
  167. if(storage == NULL) {
  168. file->error_id = FSE_INVALID_PARAMETER;
  169. } else {
  170. FS_CALL(storage, file.tell(storage, file));
  171. }
  172. return ret;
  173. }
  174. static bool storage_process_file_truncate(Storage* app, File* file) {
  175. bool ret = false;
  176. StorageData* storage = get_storage_by_file(file, app->storage);
  177. if(storage == NULL) {
  178. file->error_id = FSE_INVALID_PARAMETER;
  179. } else {
  180. storage_data_timestamp(storage);
  181. FS_CALL(storage, file.truncate(storage, file));
  182. }
  183. return ret;
  184. }
  185. static bool storage_process_file_sync(Storage* app, File* file) {
  186. bool ret = false;
  187. StorageData* storage = get_storage_by_file(file, app->storage);
  188. if(storage == NULL) {
  189. file->error_id = FSE_INVALID_PARAMETER;
  190. } else {
  191. storage_data_timestamp(storage);
  192. FS_CALL(storage, file.sync(storage, file));
  193. }
  194. return ret;
  195. }
  196. static uint64_t storage_process_file_size(Storage* app, File* file) {
  197. uint64_t ret = 0;
  198. StorageData* storage = get_storage_by_file(file, app->storage);
  199. if(storage == NULL) {
  200. file->error_id = FSE_INVALID_PARAMETER;
  201. } else {
  202. FS_CALL(storage, file.size(storage, file));
  203. }
  204. return ret;
  205. }
  206. static bool storage_process_file_eof(Storage* app, File* file) {
  207. bool ret = false;
  208. StorageData* storage = get_storage_by_file(file, app->storage);
  209. if(storage == NULL) {
  210. file->error_id = FSE_INVALID_PARAMETER;
  211. } else {
  212. FS_CALL(storage, file.eof(storage, file));
  213. }
  214. return ret;
  215. }
  216. /******************* Dir Functions *******************/
  217. bool storage_process_dir_open(Storage* app, File* file, const char* path) {
  218. bool ret = false;
  219. StorageType type = storage_get_type_by_path(app, path);
  220. StorageData* storage;
  221. file->error_id = FSE_OK;
  222. if(storage_type_is_not_valid(type)) {
  223. file->error_id = FSE_INVALID_NAME;
  224. } else {
  225. storage = storage_get_storage_by_type(app, type);
  226. FuriString* real_path;
  227. real_path = furi_string_alloc_set(path);
  228. storage_path_change_to_real_storage(real_path, type);
  229. if(storage_path_already_open(real_path, storage->files)) {
  230. file->error_id = FSE_ALREADY_OPEN;
  231. } else {
  232. storage_push_storage_file(file, real_path, type, storage);
  233. FS_CALL(storage, dir.open(storage, file, remove_vfs(path)));
  234. }
  235. furi_string_free(real_path);
  236. }
  237. return ret;
  238. }
  239. bool storage_process_dir_close(Storage* app, File* file) {
  240. bool ret = false;
  241. StorageData* storage = get_storage_by_file(file, app->storage);
  242. if(storage == NULL) {
  243. file->error_id = FSE_INVALID_PARAMETER;
  244. } else {
  245. FS_CALL(storage, dir.close(storage, file));
  246. storage_pop_storage_file(file, storage);
  247. StorageEvent event = {.type = StorageEventTypeDirClose};
  248. furi_pubsub_publish(app->pubsub, &event);
  249. }
  250. return ret;
  251. }
  252. bool storage_process_dir_read(
  253. Storage* app,
  254. File* file,
  255. FileInfo* fileinfo,
  256. char* name,
  257. const uint16_t name_length) {
  258. bool ret = false;
  259. StorageData* storage = get_storage_by_file(file, app->storage);
  260. if(storage == NULL) {
  261. file->error_id = FSE_INVALID_PARAMETER;
  262. } else {
  263. FS_CALL(storage, dir.read(storage, file, fileinfo, name, name_length));
  264. }
  265. return ret;
  266. }
  267. bool storage_process_dir_rewind(Storage* app, File* file) {
  268. bool ret = false;
  269. StorageData* storage = get_storage_by_file(file, app->storage);
  270. if(storage == NULL) {
  271. file->error_id = FSE_INVALID_PARAMETER;
  272. } else {
  273. FS_CALL(storage, dir.rewind(storage, file));
  274. }
  275. return ret;
  276. }
  277. /******************* Common FS Functions *******************/
  278. static FS_Error
  279. storage_process_common_timestamp(Storage* app, const char* path, uint32_t* timestamp) {
  280. FS_Error ret = FSE_OK;
  281. StorageType type = storage_get_type_by_path(app, path);
  282. if(storage_type_is_not_valid(type)) {
  283. ret = FSE_INVALID_NAME;
  284. } else {
  285. StorageData* storage = storage_get_storage_by_type(app, type);
  286. *timestamp = storage_data_get_timestamp(storage);
  287. }
  288. return ret;
  289. }
  290. static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) {
  291. FS_Error ret = FSE_OK;
  292. StorageType type = storage_get_type_by_path(app, path);
  293. if(storage_type_is_not_valid(type)) {
  294. ret = FSE_INVALID_NAME;
  295. } else {
  296. StorageData* storage = storage_get_storage_by_type(app, type);
  297. FS_CALL(storage, common.stat(storage, remove_vfs(path), fileinfo));
  298. }
  299. return ret;
  300. }
  301. static FS_Error storage_process_common_remove(Storage* app, const char* path) {
  302. FS_Error ret = FSE_OK;
  303. StorageType type = storage_get_type_by_path(app, path);
  304. FuriString* real_path;
  305. real_path = furi_string_alloc_set(path);
  306. storage_path_change_to_real_storage(real_path, type);
  307. do {
  308. if(storage_type_is_not_valid(type)) {
  309. ret = FSE_INVALID_NAME;
  310. break;
  311. }
  312. StorageData* storage = storage_get_storage_by_type(app, type);
  313. if(storage_path_already_open(real_path, storage->files)) {
  314. ret = FSE_ALREADY_OPEN;
  315. break;
  316. }
  317. storage_data_timestamp(storage);
  318. FS_CALL(storage, common.remove(storage, remove_vfs(path)));
  319. } while(false);
  320. furi_string_free(real_path);
  321. return ret;
  322. }
  323. static FS_Error storage_process_common_mkdir(Storage* app, const char* path) {
  324. FS_Error ret = FSE_OK;
  325. StorageType type = storage_get_type_by_path(app, path);
  326. if(storage_type_is_not_valid(type)) {
  327. ret = FSE_INVALID_NAME;
  328. } else {
  329. StorageData* storage = storage_get_storage_by_type(app, type);
  330. storage_data_timestamp(storage);
  331. FS_CALL(storage, common.mkdir(storage, remove_vfs(path)));
  332. }
  333. return ret;
  334. }
  335. static FS_Error storage_process_common_fs_info(
  336. Storage* app,
  337. const char* fs_path,
  338. uint64_t* total_space,
  339. uint64_t* free_space) {
  340. FS_Error ret = FSE_OK;
  341. StorageType type = storage_get_type_by_path(app, fs_path);
  342. if(storage_type_is_not_valid(type)) {
  343. ret = FSE_INVALID_NAME;
  344. } else {
  345. StorageData* storage = storage_get_storage_by_type(app, type);
  346. FS_CALL(storage, common.fs_info(storage, remove_vfs(fs_path), total_space, free_space));
  347. }
  348. return ret;
  349. }
  350. /****************** Raw SD API ******************/
  351. // TODO think about implementing a custom storage API to split that kind of api linkage
  352. #include "storages/storage_ext.h"
  353. static FS_Error storage_process_sd_format(Storage* app) {
  354. FS_Error ret = FSE_OK;
  355. if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) {
  356. ret = FSE_NOT_READY;
  357. } else {
  358. ret = sd_format_card(&app->storage[ST_EXT]);
  359. storage_data_timestamp(&app->storage[ST_EXT]);
  360. }
  361. return ret;
  362. }
  363. static FS_Error storage_process_sd_unmount(Storage* app) {
  364. FS_Error ret = FSE_OK;
  365. if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) {
  366. ret = FSE_NOT_READY;
  367. } else {
  368. sd_unmount_card(&app->storage[ST_EXT]);
  369. storage_data_timestamp(&app->storage[ST_EXT]);
  370. }
  371. return ret;
  372. }
  373. static FS_Error storage_process_sd_info(Storage* app, SDInfo* info) {
  374. FS_Error ret = FSE_OK;
  375. if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) {
  376. ret = FSE_NOT_READY;
  377. } else {
  378. ret = sd_card_info(&app->storage[ST_EXT], info);
  379. }
  380. return ret;
  381. }
  382. static FS_Error storage_process_sd_status(Storage* app) {
  383. FS_Error ret;
  384. StorageStatus status = storage_data_status(&app->storage[ST_EXT]);
  385. switch(status) {
  386. case StorageStatusOK:
  387. ret = FSE_OK;
  388. break;
  389. case StorageStatusNotReady:
  390. ret = FSE_NOT_READY;
  391. break;
  392. default:
  393. ret = FSE_INTERNAL;
  394. break;
  395. }
  396. return ret;
  397. }
  398. /****************** API calls processing ******************/
  399. void storage_process_message_internal(Storage* app, StorageMessage* message) {
  400. switch(message->command) {
  401. case StorageCommandFileOpen:
  402. message->return_data->bool_value = storage_process_file_open(
  403. app,
  404. message->data->fopen.file,
  405. message->data->fopen.path,
  406. message->data->fopen.access_mode,
  407. message->data->fopen.open_mode);
  408. break;
  409. case StorageCommandFileClose:
  410. message->return_data->bool_value =
  411. storage_process_file_close(app, message->data->fopen.file);
  412. break;
  413. case StorageCommandFileRead:
  414. message->return_data->uint16_value = storage_process_file_read(
  415. app,
  416. message->data->fread.file,
  417. message->data->fread.buff,
  418. message->data->fread.bytes_to_read);
  419. break;
  420. case StorageCommandFileWrite:
  421. message->return_data->uint16_value = storage_process_file_write(
  422. app,
  423. message->data->fwrite.file,
  424. message->data->fwrite.buff,
  425. message->data->fwrite.bytes_to_write);
  426. break;
  427. case StorageCommandFileSeek:
  428. message->return_data->bool_value = storage_process_file_seek(
  429. app,
  430. message->data->fseek.file,
  431. message->data->fseek.offset,
  432. message->data->fseek.from_start);
  433. break;
  434. case StorageCommandFileTell:
  435. message->return_data->uint64_value =
  436. storage_process_file_tell(app, message->data->file.file);
  437. break;
  438. case StorageCommandFileTruncate:
  439. message->return_data->bool_value =
  440. storage_process_file_truncate(app, message->data->file.file);
  441. break;
  442. case StorageCommandFileSync:
  443. message->return_data->bool_value =
  444. storage_process_file_sync(app, message->data->file.file);
  445. break;
  446. case StorageCommandFileSize:
  447. message->return_data->uint64_value =
  448. storage_process_file_size(app, message->data->file.file);
  449. break;
  450. case StorageCommandFileEof:
  451. message->return_data->bool_value = storage_process_file_eof(app, message->data->file.file);
  452. break;
  453. case StorageCommandDirOpen:
  454. message->return_data->bool_value =
  455. storage_process_dir_open(app, message->data->dopen.file, message->data->dopen.path);
  456. break;
  457. case StorageCommandDirClose:
  458. message->return_data->bool_value =
  459. storage_process_dir_close(app, message->data->file.file);
  460. break;
  461. case StorageCommandDirRead:
  462. message->return_data->bool_value = storage_process_dir_read(
  463. app,
  464. message->data->dread.file,
  465. message->data->dread.fileinfo,
  466. message->data->dread.name,
  467. message->data->dread.name_length);
  468. break;
  469. case StorageCommandDirRewind:
  470. message->return_data->bool_value =
  471. storage_process_dir_rewind(app, message->data->file.file);
  472. break;
  473. case StorageCommandCommonTimestamp:
  474. message->return_data->error_value = storage_process_common_timestamp(
  475. app, message->data->ctimestamp.path, message->data->ctimestamp.timestamp);
  476. break;
  477. case StorageCommandCommonStat:
  478. message->return_data->error_value = storage_process_common_stat(
  479. app, message->data->cstat.path, message->data->cstat.fileinfo);
  480. break;
  481. case StorageCommandCommonRemove:
  482. message->return_data->error_value =
  483. storage_process_common_remove(app, message->data->path.path);
  484. break;
  485. case StorageCommandCommonMkDir:
  486. message->return_data->error_value =
  487. storage_process_common_mkdir(app, message->data->path.path);
  488. break;
  489. case StorageCommandCommonFSInfo:
  490. message->return_data->error_value = storage_process_common_fs_info(
  491. app,
  492. message->data->cfsinfo.fs_path,
  493. message->data->cfsinfo.total_space,
  494. message->data->cfsinfo.free_space);
  495. break;
  496. case StorageCommandSDFormat:
  497. message->return_data->error_value = storage_process_sd_format(app);
  498. break;
  499. case StorageCommandSDUnmount:
  500. message->return_data->error_value = storage_process_sd_unmount(app);
  501. break;
  502. case StorageCommandSDInfo:
  503. message->return_data->error_value =
  504. storage_process_sd_info(app, message->data->sdinfo.info);
  505. break;
  506. case StorageCommandSDStatus:
  507. message->return_data->error_value = storage_process_sd_status(app);
  508. break;
  509. }
  510. furi_semaphore_release(message->semaphore);
  511. }
  512. void storage_process_message(Storage* app, StorageMessage* message) {
  513. storage_process_message_internal(app, message);
  514. }