tar_archive.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include "tar_archive.h"
  2. #include <microtar.h>
  3. #include <storage/storage.h>
  4. #include <furi.h>
  5. #include <toolbox/path.h>
  6. #define TAG "TarArch"
  7. #define MAX_NAME_LEN 255
  8. #define FILE_BLOCK_SIZE 512
  9. #define FILE_OPEN_NTRIES 10
  10. #define FILE_OPEN_RETRY_DELAY 25
  11. typedef struct TarArchive {
  12. Storage* storage;
  13. mtar_t tar;
  14. } TarArchive;
  15. /* API WRAPPER */
  16. static int mtar_storage_file_write(void* stream, const void* data, unsigned size) {
  17. uint16_t bytes_written = storage_file_write(stream, data, size);
  18. return (bytes_written == size) ? bytes_written : MTAR_EWRITEFAIL;
  19. }
  20. static int mtar_storage_file_read(void* stream, void* data, unsigned size) {
  21. uint16_t bytes_read = storage_file_read(stream, data, size);
  22. return (bytes_read == size) ? bytes_read : MTAR_EREADFAIL;
  23. }
  24. static int mtar_storage_file_seek(void* stream, unsigned offset) {
  25. bool res = storage_file_seek(stream, offset, true);
  26. return res ? MTAR_ESUCCESS : MTAR_ESEEKFAIL;
  27. }
  28. static int mtar_storage_file_close(void* stream) {
  29. if(stream) {
  30. storage_file_close(stream);
  31. }
  32. return MTAR_ESUCCESS;
  33. }
  34. const struct mtar_ops filesystem_ops = {
  35. .read = mtar_storage_file_read,
  36. .write = mtar_storage_file_write,
  37. .seek = mtar_storage_file_seek,
  38. .close = mtar_storage_file_close,
  39. };
  40. TarArchive* tar_archive_alloc(Storage* storage) {
  41. furi_check(storage);
  42. TarArchive* archive = malloc(sizeof(TarArchive));
  43. archive->storage = storage;
  44. return archive;
  45. }
  46. bool tar_archive_open(TarArchive* archive, const char* path, TarOpenMode mode) {
  47. furi_assert(archive);
  48. FS_AccessMode access_mode;
  49. FS_OpenMode open_mode;
  50. int mtar_access = 0;
  51. switch(mode) {
  52. case TAR_OPEN_MODE_READ:
  53. mtar_access = MTAR_READ;
  54. access_mode = FSAM_READ;
  55. open_mode = FSOM_OPEN_EXISTING;
  56. break;
  57. case TAR_OPEN_MODE_WRITE:
  58. mtar_access = MTAR_WRITE;
  59. access_mode = FSAM_WRITE;
  60. open_mode = FSOM_CREATE_ALWAYS;
  61. break;
  62. default:
  63. return false;
  64. }
  65. File* stream = storage_file_alloc(archive->storage);
  66. if(!storage_file_open(stream, path, access_mode, open_mode)) {
  67. storage_file_free(stream);
  68. return false;
  69. }
  70. mtar_init(&archive->tar, mtar_access, &filesystem_ops, stream);
  71. return true;
  72. }
  73. void tar_archive_free(TarArchive* archive) {
  74. furi_assert(archive);
  75. if(mtar_is_open(&archive->tar)) {
  76. mtar_close(&archive->tar);
  77. }
  78. }
  79. bool tar_archive_dir_add_element(TarArchive* archive, const char* dirpath) {
  80. furi_assert(archive);
  81. return (mtar_write_dir_header(&archive->tar, dirpath) == MTAR_ESUCCESS);
  82. }
  83. bool tar_archive_finalize(TarArchive* archive) {
  84. furi_assert(archive);
  85. return (mtar_finalize(&archive->tar) == MTAR_ESUCCESS);
  86. }
  87. bool tar_archive_store_data(
  88. TarArchive* archive,
  89. const char* path,
  90. const uint8_t* data,
  91. const int32_t data_len) {
  92. furi_assert(archive);
  93. return (
  94. tar_archive_file_add_header(archive, path, data_len) &&
  95. tar_archive_file_add_data_block(archive, data, data_len) &&
  96. tar_archive_file_finalize(archive));
  97. }
  98. bool tar_archive_file_add_header(TarArchive* archive, const char* path, const int32_t data_len) {
  99. furi_assert(archive);
  100. return (mtar_write_file_header(&archive->tar, path, data_len) == MTAR_ESUCCESS);
  101. }
  102. bool tar_archive_file_add_data_block(
  103. TarArchive* archive,
  104. const uint8_t* data_block,
  105. const int32_t block_len) {
  106. furi_assert(archive);
  107. return (mtar_write_data(&archive->tar, data_block, block_len) == block_len);
  108. }
  109. bool tar_archive_file_finalize(TarArchive* archive) {
  110. furi_assert(archive);
  111. return (mtar_end_data(&archive->tar) == MTAR_ESUCCESS);
  112. }
  113. typedef struct {
  114. TarArchive* archive;
  115. const char* work_dir;
  116. } TarArchiveDirectoryOpParams;
  117. static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, void* param) {
  118. TarArchiveDirectoryOpParams* op_params = param;
  119. string_t fname;
  120. if(header->type == MTAR_TDIR) {
  121. string_init(fname);
  122. path_concat(op_params->work_dir, header->name, fname);
  123. bool create_res =
  124. storage_simply_mkdir(op_params->archive->storage, string_get_cstr(fname));
  125. string_clear(fname);
  126. return create_res ? 0 : -1;
  127. }
  128. if(header->type != MTAR_TREG) {
  129. FURI_LOG_W(TAG, "not extracting unsupported type \"%s\"", header->name);
  130. return 0;
  131. }
  132. string_init(fname);
  133. path_concat(op_params->work_dir, header->name, fname);
  134. FURI_LOG_I(TAG, "Extracting %d bytes to '%s'", header->size, header->name);
  135. File* out_file = storage_file_alloc(op_params->archive->storage);
  136. uint8_t* readbuf = malloc(FILE_BLOCK_SIZE);
  137. bool failed = false;
  138. uint8_t n_tries = FILE_OPEN_NTRIES;
  139. do {
  140. while(
  141. (n_tries-- > 0) &&
  142. !storage_file_open(out_file, string_get_cstr(fname), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  143. FURI_LOG_W(TAG, "Failed to open '%s', reties: %d", string_get_cstr(fname), n_tries);
  144. osDelay(FILE_OPEN_RETRY_DELAY);
  145. continue;
  146. }
  147. if(!storage_file_is_open(out_file)) {
  148. failed = true;
  149. break;
  150. }
  151. while(!mtar_eof_data(tar)) {
  152. int32_t readcnt = mtar_read_data(tar, readbuf, FILE_BLOCK_SIZE);
  153. if(!readcnt || !storage_file_write(out_file, readbuf, readcnt)) {
  154. failed = true;
  155. break;
  156. }
  157. }
  158. } while(false);
  159. storage_file_free(out_file);
  160. free(readbuf);
  161. string_clear(fname);
  162. return failed ? -1 : 0;
  163. }
  164. bool tar_archive_unpack_to(TarArchive* archive, const char* destination) {
  165. furi_assert(archive);
  166. TarArchiveDirectoryOpParams param = {
  167. .archive = archive,
  168. .work_dir = destination,
  169. };
  170. FURI_LOG_I(TAG, "Restoring '%s'", destination);
  171. return (mtar_foreach(&archive->tar, archive_extract_foreach_cb, &param) == MTAR_ESUCCESS);
  172. };
  173. bool tar_archive_add_file(
  174. TarArchive* archive,
  175. const char* fs_file_path,
  176. const char* archive_fname,
  177. const int32_t file_size) {
  178. furi_assert(archive);
  179. uint8_t* file_buffer = malloc(FILE_BLOCK_SIZE);
  180. bool success = false;
  181. File* src_file = storage_file_alloc(archive->storage);
  182. uint8_t n_tries = FILE_OPEN_NTRIES;
  183. do {
  184. while((n_tries-- > 0) &&
  185. !storage_file_open(src_file, fs_file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  186. FURI_LOG_W(TAG, "Failed to open '%s', reties: %d", fs_file_path, n_tries);
  187. osDelay(FILE_OPEN_RETRY_DELAY);
  188. continue;
  189. }
  190. if(!storage_file_is_open(src_file) ||
  191. !tar_archive_file_add_header(archive, archive_fname, file_size)) {
  192. break;
  193. }
  194. uint16_t bytes_read = 0;
  195. while((bytes_read = storage_file_read(src_file, file_buffer, FILE_BLOCK_SIZE))) {
  196. success = tar_archive_file_add_data_block(archive, file_buffer, bytes_read);
  197. if(!success) {
  198. break;
  199. }
  200. }
  201. success = success && tar_archive_file_finalize(archive);
  202. } while(false);
  203. storage_file_free(src_file);
  204. free(file_buffer);
  205. return success;
  206. }
  207. bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const char* path_prefix) {
  208. furi_assert(archive);
  209. furi_check(path_prefix);
  210. File* directory = storage_file_alloc(archive->storage);
  211. FileInfo file_info;
  212. FURI_LOG_I(TAG, "Backing up '%s', '%s'", fs_full_path, path_prefix);
  213. char* name = malloc(MAX_NAME_LEN);
  214. bool success = false;
  215. do {
  216. if(!storage_dir_open(directory, fs_full_path)) {
  217. break;
  218. }
  219. while(true) {
  220. if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {
  221. success = true; /* empty dir / no more files */
  222. break;
  223. }
  224. string_t element_name, element_fs_abs_path;
  225. string_init(element_name);
  226. string_init(element_fs_abs_path);
  227. path_concat(fs_full_path, name, element_fs_abs_path);
  228. if(strlen(path_prefix)) {
  229. path_concat(path_prefix, name, element_name);
  230. } else {
  231. string_init_set(element_name, name);
  232. }
  233. if(file_info.flags & FSF_DIRECTORY) {
  234. success = tar_archive_dir_add_element(archive, string_get_cstr(element_name)) &&
  235. tar_archive_add_dir(
  236. archive,
  237. string_get_cstr(element_fs_abs_path),
  238. string_get_cstr(element_name));
  239. } else {
  240. success = tar_archive_add_file(
  241. archive,
  242. string_get_cstr(element_fs_abs_path),
  243. string_get_cstr(element_name),
  244. file_info.size);
  245. }
  246. string_clear(element_name);
  247. string_clear(element_fs_abs_path);
  248. if(!success) {
  249. break;
  250. }
  251. }
  252. } while(false);
  253. free(name);
  254. storage_file_free(directory);
  255. return success;
  256. }