storage_external_api.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. #include <furi/record.h>
  2. #include <m-string.h>
  3. #include "storage.h"
  4. #include "storage_i.h"
  5. #include "storage_message.h"
  6. #include <toolbox/stream/file_stream.h>
  7. #define MAX_NAME_LENGTH 256
  8. #define S_API_PROLOGUE \
  9. osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); \
  10. furi_check(semaphore != NULL);
  11. #define S_FILE_API_PROLOGUE \
  12. Storage* storage = file->storage; \
  13. furi_assert(storage);
  14. #define S_API_EPILOGUE \
  15. furi_check(osMessageQueuePut(storage->message_queue, &message, 0, osWaitForever) == osOK); \
  16. osSemaphoreAcquire(semaphore, osWaitForever); \
  17. osSemaphoreDelete(semaphore);
  18. #define S_API_MESSAGE(_command) \
  19. SAReturn return_data; \
  20. StorageMessage message = { \
  21. .semaphore = semaphore, \
  22. .command = _command, \
  23. .data = &data, \
  24. .return_data = &return_data, \
  25. };
  26. #define S_API_DATA_FILE \
  27. SAData data = { \
  28. .file = { \
  29. .file = file, \
  30. }};
  31. #define S_API_DATA_PATH \
  32. SAData data = { \
  33. .path = { \
  34. .path = path, \
  35. }};
  36. #define S_RETURN_BOOL (return_data.bool_value);
  37. #define S_RETURN_UINT16 (return_data.uint16_value);
  38. #define S_RETURN_UINT64 (return_data.uint64_value);
  39. #define S_RETURN_ERROR (return_data.error_value);
  40. #define S_RETURN_CSTRING (return_data.cstring_value);
  41. #define FILE_OPENED_FILE 1
  42. #define FILE_OPENED_DIR 2
  43. #define FILE_CLOSED 0
  44. typedef enum {
  45. StorageEventFlagFileClose = (1 << 0),
  46. } StorageEventFlag;
  47. /****************** FILE ******************/
  48. static bool storage_file_open_internal(
  49. File* file,
  50. const char* path,
  51. FS_AccessMode access_mode,
  52. FS_OpenMode open_mode) {
  53. S_FILE_API_PROLOGUE;
  54. S_API_PROLOGUE;
  55. SAData data = {
  56. .fopen = {
  57. .file = file,
  58. .path = path,
  59. .access_mode = access_mode,
  60. .open_mode = open_mode,
  61. }};
  62. file->file_id = FILE_OPENED_FILE;
  63. S_API_MESSAGE(StorageCommandFileOpen);
  64. S_API_EPILOGUE;
  65. return S_RETURN_BOOL;
  66. }
  67. static void storage_file_close_callback(const void* message, void* context) {
  68. const StorageEvent* storage_event = message;
  69. if(storage_event->type == StorageEventTypeFileClose ||
  70. storage_event->type == StorageEventTypeDirClose) {
  71. furi_assert(context);
  72. osEventFlagsId_t event = context;
  73. osEventFlagsSet(event, StorageEventFlagFileClose);
  74. }
  75. }
  76. bool storage_file_open(
  77. File* file,
  78. const char* path,
  79. FS_AccessMode access_mode,
  80. FS_OpenMode open_mode) {
  81. bool result;
  82. osEventFlagsId_t event = osEventFlagsNew(NULL);
  83. FuriPubSubSubscription* subscription = furi_pubsub_subscribe(
  84. storage_get_pubsub(file->storage), storage_file_close_callback, event);
  85. do {
  86. result = storage_file_open_internal(file, path, access_mode, open_mode);
  87. if(!result && file->error_id == FSE_ALREADY_OPEN) {
  88. osEventFlagsWait(event, StorageEventFlagFileClose, osFlagsWaitAny, osWaitForever);
  89. } else {
  90. break;
  91. }
  92. } while(true);
  93. furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription);
  94. osEventFlagsDelete(event);
  95. return result;
  96. }
  97. bool storage_file_close(File* file) {
  98. S_FILE_API_PROLOGUE;
  99. S_API_PROLOGUE;
  100. S_API_DATA_FILE;
  101. S_API_MESSAGE(StorageCommandFileClose);
  102. S_API_EPILOGUE;
  103. file->file_id = FILE_CLOSED;
  104. return S_RETURN_BOOL;
  105. }
  106. uint16_t storage_file_read(File* file, void* buff, uint16_t bytes_to_read) {
  107. S_FILE_API_PROLOGUE;
  108. S_API_PROLOGUE;
  109. SAData data = {
  110. .fread = {
  111. .file = file,
  112. .buff = buff,
  113. .bytes_to_read = bytes_to_read,
  114. }};
  115. S_API_MESSAGE(StorageCommandFileRead);
  116. S_API_EPILOGUE;
  117. return S_RETURN_UINT16;
  118. }
  119. uint16_t storage_file_write(File* file, const void* buff, uint16_t bytes_to_write) {
  120. S_FILE_API_PROLOGUE;
  121. S_API_PROLOGUE;
  122. SAData data = {
  123. .fwrite = {
  124. .file = file,
  125. .buff = buff,
  126. .bytes_to_write = bytes_to_write,
  127. }};
  128. S_API_MESSAGE(StorageCommandFileWrite);
  129. S_API_EPILOGUE;
  130. return S_RETURN_UINT16;
  131. }
  132. bool storage_file_seek(File* file, uint32_t offset, bool from_start) {
  133. S_FILE_API_PROLOGUE;
  134. S_API_PROLOGUE;
  135. SAData data = {
  136. .fseek = {
  137. .file = file,
  138. .offset = offset,
  139. .from_start = from_start,
  140. }};
  141. S_API_MESSAGE(StorageCommandFileSeek);
  142. S_API_EPILOGUE;
  143. return S_RETURN_BOOL;
  144. }
  145. uint64_t storage_file_tell(File* file) {
  146. S_FILE_API_PROLOGUE;
  147. S_API_PROLOGUE;
  148. S_API_DATA_FILE;
  149. S_API_MESSAGE(StorageCommandFileTell);
  150. S_API_EPILOGUE;
  151. return S_RETURN_UINT64;
  152. }
  153. bool storage_file_truncate(File* file) {
  154. S_FILE_API_PROLOGUE;
  155. S_API_PROLOGUE;
  156. S_API_DATA_FILE;
  157. S_API_MESSAGE(StorageCommandFileTruncate);
  158. S_API_EPILOGUE;
  159. return S_RETURN_BOOL;
  160. }
  161. uint64_t storage_file_size(File* file) {
  162. S_FILE_API_PROLOGUE;
  163. S_API_PROLOGUE;
  164. S_API_DATA_FILE;
  165. S_API_MESSAGE(StorageCommandFileSize);
  166. S_API_EPILOGUE;
  167. return S_RETURN_UINT64;
  168. }
  169. bool storage_file_sync(File* file) {
  170. S_FILE_API_PROLOGUE;
  171. S_API_PROLOGUE;
  172. S_API_DATA_FILE;
  173. S_API_MESSAGE(StorageCommandFileSync);
  174. S_API_EPILOGUE;
  175. return S_RETURN_BOOL;
  176. }
  177. bool storage_file_eof(File* file) {
  178. S_FILE_API_PROLOGUE;
  179. S_API_PROLOGUE;
  180. S_API_DATA_FILE;
  181. S_API_MESSAGE(StorageCommandFileEof);
  182. S_API_EPILOGUE;
  183. return S_RETURN_BOOL;
  184. }
  185. /****************** DIR ******************/
  186. static bool storage_dir_open_internal(File* file, const char* path) {
  187. S_FILE_API_PROLOGUE;
  188. S_API_PROLOGUE;
  189. SAData data = {
  190. .dopen = {
  191. .file = file,
  192. .path = path,
  193. }};
  194. file->file_id = FILE_OPENED_DIR;
  195. S_API_MESSAGE(StorageCommandDirOpen);
  196. S_API_EPILOGUE;
  197. return S_RETURN_BOOL;
  198. }
  199. bool storage_dir_open(File* file, const char* path) {
  200. bool result;
  201. osEventFlagsId_t event = osEventFlagsNew(NULL);
  202. FuriPubSubSubscription* subscription = furi_pubsub_subscribe(
  203. storage_get_pubsub(file->storage), storage_file_close_callback, event);
  204. do {
  205. result = storage_dir_open_internal(file, path);
  206. if(!result && file->error_id == FSE_ALREADY_OPEN) {
  207. osEventFlagsWait(event, StorageEventFlagFileClose, osFlagsWaitAny, osWaitForever);
  208. } else {
  209. break;
  210. }
  211. } while(true);
  212. furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription);
  213. osEventFlagsDelete(event);
  214. return result;
  215. }
  216. bool storage_dir_close(File* file) {
  217. S_FILE_API_PROLOGUE;
  218. S_API_PROLOGUE;
  219. S_API_DATA_FILE;
  220. S_API_MESSAGE(StorageCommandDirClose);
  221. S_API_EPILOGUE;
  222. file->file_id = FILE_CLOSED;
  223. return S_RETURN_BOOL;
  224. }
  225. bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length) {
  226. S_FILE_API_PROLOGUE;
  227. S_API_PROLOGUE;
  228. SAData data = {
  229. .dread = {
  230. .file = file,
  231. .fileinfo = fileinfo,
  232. .name = name,
  233. .name_length = name_length,
  234. }};
  235. S_API_MESSAGE(StorageCommandDirRead);
  236. S_API_EPILOGUE;
  237. return S_RETURN_BOOL;
  238. }
  239. bool storage_dir_rewind(File* file) {
  240. S_FILE_API_PROLOGUE;
  241. S_API_PROLOGUE;
  242. S_API_DATA_FILE;
  243. S_API_MESSAGE(StorageCommandDirRewind);
  244. S_API_EPILOGUE;
  245. return S_RETURN_BOOL;
  246. }
  247. /****************** COMMON ******************/
  248. FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) {
  249. S_API_PROLOGUE;
  250. SAData data = {.cstat = {.path = path, .fileinfo = fileinfo}};
  251. S_API_MESSAGE(StorageCommandCommonStat);
  252. S_API_EPILOGUE;
  253. return S_RETURN_ERROR;
  254. }
  255. FS_Error storage_common_remove(Storage* storage, const char* path) {
  256. S_API_PROLOGUE;
  257. S_API_DATA_PATH;
  258. S_API_MESSAGE(StorageCommandCommonRemove);
  259. S_API_EPILOGUE;
  260. return S_RETURN_ERROR;
  261. }
  262. FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) {
  263. FS_Error error = storage_common_copy(storage, old_path, new_path);
  264. if(error == FSE_OK) {
  265. error = storage_common_remove(storage, old_path);
  266. }
  267. return error;
  268. }
  269. FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) {
  270. FS_Error error;
  271. FileInfo fileinfo;
  272. error = storage_common_stat(storage, old_path, &fileinfo);
  273. if(error == FSE_OK) {
  274. if(fileinfo.flags & FSF_DIRECTORY) {
  275. error = storage_common_mkdir(storage, new_path);
  276. } else {
  277. Stream* stream_from = file_stream_alloc(storage);
  278. Stream* stream_to = file_stream_alloc(storage);
  279. do {
  280. if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break;
  281. if(!file_stream_open(stream_to, new_path, FSAM_WRITE, FSOM_CREATE_NEW)) break;
  282. stream_copy_full(stream_from, stream_to);
  283. } while(false);
  284. error = file_stream_get_error(stream_from);
  285. if(error == FSE_OK) {
  286. error = file_stream_get_error(stream_to);
  287. }
  288. stream_free(stream_from);
  289. stream_free(stream_to);
  290. }
  291. }
  292. return error;
  293. }
  294. FS_Error storage_common_mkdir(Storage* storage, const char* path) {
  295. S_API_PROLOGUE;
  296. S_API_DATA_PATH;
  297. S_API_MESSAGE(StorageCommandCommonMkDir);
  298. S_API_EPILOGUE;
  299. return S_RETURN_ERROR;
  300. }
  301. FS_Error storage_common_fs_info(
  302. Storage* storage,
  303. const char* fs_path,
  304. uint64_t* total_space,
  305. uint64_t* free_space) {
  306. S_API_PROLOGUE;
  307. SAData data = {
  308. .cfsinfo = {
  309. .fs_path = fs_path,
  310. .total_space = total_space,
  311. .free_space = free_space,
  312. }};
  313. S_API_MESSAGE(StorageCommandCommonFSInfo);
  314. S_API_EPILOGUE;
  315. return S_RETURN_ERROR;
  316. }
  317. /****************** ERROR ******************/
  318. const char* storage_error_get_desc(FS_Error error_id) {
  319. return filesystem_api_error_get_desc(error_id);
  320. }
  321. FS_Error storage_file_get_error(File* file) {
  322. furi_check(file != NULL);
  323. return file->error_id;
  324. }
  325. int32_t storage_file_get_internal_error(File* file) {
  326. furi_check(file != NULL);
  327. return file->internal_error_id;
  328. }
  329. const char* storage_file_get_error_desc(File* file) {
  330. furi_check(file != NULL);
  331. return filesystem_api_error_get_desc(file->error_id);
  332. }
  333. /****************** Raw SD API ******************/
  334. FS_Error storage_sd_format(Storage* storage) {
  335. S_API_PROLOGUE;
  336. SAData data = {};
  337. S_API_MESSAGE(StorageCommandSDFormat);
  338. S_API_EPILOGUE;
  339. return S_RETURN_ERROR;
  340. }
  341. FS_Error storage_sd_unmount(Storage* storage) {
  342. S_API_PROLOGUE;
  343. SAData data = {};
  344. S_API_MESSAGE(StorageCommandSDUnmount);
  345. S_API_EPILOGUE;
  346. return S_RETURN_ERROR;
  347. }
  348. FS_Error storage_sd_info(Storage* storage, SDInfo* info) {
  349. S_API_PROLOGUE;
  350. SAData data = {
  351. .sdinfo = {
  352. .info = info,
  353. }};
  354. S_API_MESSAGE(StorageCommandSDInfo);
  355. S_API_EPILOGUE;
  356. return S_RETURN_ERROR;
  357. }
  358. FS_Error storage_sd_status(Storage* storage) {
  359. S_API_PROLOGUE;
  360. SAData data = {};
  361. S_API_MESSAGE(StorageCommandSDStatus);
  362. S_API_EPILOGUE;
  363. return S_RETURN_ERROR;
  364. }
  365. File* storage_file_alloc(Storage* storage) {
  366. File* file = malloc(sizeof(File));
  367. file->file_id = FILE_CLOSED;
  368. file->storage = storage;
  369. return file;
  370. }
  371. bool storage_file_is_open(File* file) {
  372. return (file->file_id != FILE_CLOSED);
  373. }
  374. bool storage_file_is_dir(File* file) {
  375. return (file->file_id == FILE_OPENED_DIR);
  376. }
  377. void storage_file_free(File* file) {
  378. if(storage_file_is_open(file)) {
  379. if(storage_file_is_dir(file)) {
  380. storage_dir_close(file);
  381. } else {
  382. storage_file_close(file);
  383. }
  384. }
  385. free(file);
  386. }
  387. FuriPubSub* storage_get_pubsub(Storage* storage) {
  388. return storage->pubsub;
  389. }
  390. bool storage_simply_remove_recursive(Storage* storage, const char* path) {
  391. furi_assert(storage);
  392. furi_assert(path);
  393. FileInfo fileinfo;
  394. bool result = false;
  395. string_t fullname;
  396. string_t cur_dir;
  397. if(storage_simply_remove(storage, path)) {
  398. return true;
  399. }
  400. char* name = malloc(MAX_NAME_LENGTH + 1);
  401. File* dir = storage_file_alloc(storage);
  402. string_init_set_str(cur_dir, path);
  403. bool go_deeper = false;
  404. while(1) {
  405. if(!storage_dir_open(dir, string_get_cstr(cur_dir))) {
  406. storage_dir_close(dir);
  407. break;
  408. }
  409. while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) {
  410. if(fileinfo.flags & FSF_DIRECTORY) {
  411. string_cat_printf(cur_dir, "/%s", name);
  412. go_deeper = true;
  413. break;
  414. }
  415. string_init_printf(fullname, "%s/%s", string_get_cstr(cur_dir), name);
  416. FS_Error error = storage_common_remove(storage, string_get_cstr(fullname));
  417. furi_check(error == FSE_OK);
  418. string_clear(fullname);
  419. }
  420. storage_dir_close(dir);
  421. if(go_deeper) {
  422. go_deeper = false;
  423. continue;
  424. }
  425. FS_Error error = storage_common_remove(storage, string_get_cstr(cur_dir));
  426. furi_check(error == FSE_OK);
  427. if(string_cmp(cur_dir, path)) {
  428. size_t last_char = string_search_rchar(cur_dir, '/');
  429. furi_assert(last_char != STRING_FAILURE);
  430. string_left(cur_dir, last_char);
  431. } else {
  432. result = true;
  433. break;
  434. }
  435. }
  436. storage_file_free(dir);
  437. string_clear(cur_dir);
  438. free(name);
  439. return result;
  440. }
  441. bool storage_simply_remove(Storage* storage, const char* path) {
  442. FS_Error result;
  443. result = storage_common_remove(storage, path);
  444. return result == FSE_OK || result == FSE_NOT_EXIST;
  445. }
  446. bool storage_simply_mkdir(Storage* storage, const char* path) {
  447. FS_Error result;
  448. result = storage_common_mkdir(storage, path);
  449. return result == FSE_OK || result == FSE_EXIST;
  450. }
  451. void storage_get_next_filename(
  452. Storage* storage,
  453. const char* dirname,
  454. const char* filename,
  455. const char* fileextension,
  456. string_t nextfilename,
  457. uint8_t max_len) {
  458. string_t temp_str;
  459. uint16_t num = 0;
  460. string_init_printf(temp_str, "%s/%s%s", dirname, filename, fileextension);
  461. while(storage_common_stat(storage, string_get_cstr(temp_str), NULL) == FSE_OK) {
  462. num++;
  463. string_printf(temp_str, "%s/%s%d%s", dirname, filename, num, fileextension);
  464. }
  465. if(num && (max_len > strlen(filename))) {
  466. string_printf(nextfilename, "%s%d", filename, num);
  467. } else {
  468. string_printf(nextfilename, "%s", filename);
  469. }
  470. string_clear(temp_str);
  471. }