storage_processing.c 17 KB

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