您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

647 行
16 KiB

  1. //
  2. // main.cpp
  3. // make_spiffs
  4. //
  5. // Created by Ivan Grokhotkov on 13/05/15.
  6. // Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
  7. //
  8. #define TCLAP_SETBASE_ZERO 1
  9. #define VERSION "0.3.6"
  10. #include <iostream>
  11. #include "spiffs/spiffs.h"
  12. #include <vector>
  13. #include <dirent.h>
  14. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #include <unistd.h>
  17. #include <cstring>
  18. #include <string>
  19. #include <time.h>
  20. #include <memory>
  21. #include <cstdlib>
  22. #include "tclap/CmdLine.h"
  23. #include "tclap/UnlabeledValueArg.h"
  24. static std::vector<uint8_t> s_flashmem;
  25. static std::string s_dirName;
  26. static std::string s_imageName;
  27. static int s_imageSize;
  28. static int s_pageSize;
  29. static int s_blockSize;
  30. typedef struct {
  31. time_t mtime;
  32. time_t ctime;
  33. time_t atime;
  34. uint8_t spare[SPIFFS_OBJ_META_LEN - (sizeof(time_t)*3)];
  35. } spiffs_metadata_t;
  36. enum Action { ACTION_NONE, ACTION_PACK, ACTION_UNPACK, ACTION_LIST, ACTION_VISUALIZE };
  37. static Action s_action = ACTION_NONE;
  38. static spiffs s_fs;
  39. static std::vector<uint8_t> s_spiffsWorkBuf;
  40. static std::vector<uint8_t> s_spiffsFds;
  41. static std::vector<uint8_t> s_spiffsCache;
  42. static s32_t api_spiffs_read(u32_t addr, u32_t size, u8_t *dst){
  43. memcpy(dst, &s_flashmem[0] + addr, size);
  44. return SPIFFS_OK;
  45. }
  46. static s32_t api_spiffs_write(u32_t addr, u32_t size, u8_t *src){
  47. memcpy(&s_flashmem[0] + addr, src, size);
  48. return SPIFFS_OK;
  49. }
  50. static s32_t api_spiffs_erase(u32_t addr, u32_t size){
  51. memset(&s_flashmem[0] + addr, 0xff, size);
  52. return SPIFFS_OK;
  53. }
  54. int g_debugLevel = 0;
  55. //implementation
  56. int spiffsTryMount(){
  57. spiffs_config cfg = {0};
  58. cfg.phys_addr = 0x0000;
  59. cfg.phys_size = (u32_t) s_flashmem.size();
  60. cfg.phys_erase_block = s_blockSize;
  61. cfg.log_block_size = s_blockSize;
  62. cfg.log_page_size = s_pageSize;
  63. cfg.hal_read_f = api_spiffs_read;
  64. cfg.hal_write_f = api_spiffs_write;
  65. cfg.hal_erase_f = api_spiffs_erase;
  66. const int maxOpenFiles = 4;
  67. s_spiffsWorkBuf.resize(s_pageSize * 2);
  68. s_spiffsFds.resize(32 * maxOpenFiles);
  69. s_spiffsCache.resize((32 + s_pageSize) * maxOpenFiles);
  70. return SPIFFS_mount(&s_fs, &cfg,
  71. &s_spiffsWorkBuf[0],
  72. &s_spiffsFds[0], s_spiffsFds.size(),
  73. &s_spiffsCache[0], s_spiffsCache.size(),
  74. NULL);
  75. }
  76. bool spiffsMount(){
  77. if(SPIFFS_mounted(&s_fs))
  78. return true;
  79. int res = spiffsTryMount();
  80. return (res == SPIFFS_OK);
  81. }
  82. bool spiffsFormat(){
  83. spiffsMount();
  84. SPIFFS_unmount(&s_fs);
  85. int formated = SPIFFS_format(&s_fs);
  86. if(formated != SPIFFS_OK)
  87. return false;
  88. return (spiffsTryMount() == SPIFFS_OK);
  89. }
  90. void spiffsUnmount(){
  91. if(SPIFFS_mounted(&s_fs))
  92. SPIFFS_unmount(&s_fs);
  93. }
  94. // WHITECAT BEGIN
  95. int addDir(const char* name) {
  96. spiffs_metadata_t meta;
  97. std::string fileName = name;
  98. fileName += "/.";
  99. std::cout << fileName << std::endl;
  100. spiffs_file dst = SPIFFS_open(&s_fs, fileName.c_str(), SPIFFS_CREAT, 0);
  101. if (dst < 0) {
  102. std::cerr << "SPIFFS_write error(" << s_fs.err_code << "): ";
  103. if (s_fs.err_code == SPIFFS_ERR_FULL) {
  104. std::cerr << "File system is full." << std::endl;
  105. } else {
  106. std::cerr << "unknown";
  107. }
  108. std::cerr << std::endl;
  109. SPIFFS_close(&s_fs, dst);
  110. return 1;
  111. }
  112. SPIFFS_close(&s_fs, dst);
  113. if (strlen(name) > 0) {
  114. // Get the system time to file timestamps
  115. meta.atime = time(NULL);
  116. meta.ctime = meta.atime;
  117. meta.mtime = meta.atime;
  118. SPIFFS_update_meta(&s_fs, fileName.c_str(), &meta);
  119. }
  120. return 0;
  121. }
  122. // WHITECAT END
  123. int addFile(char* name, const char* path) {
  124. spiffs_metadata_t meta;
  125. FILE* src = fopen(path, "rb");
  126. if (!src) {
  127. std::cerr << "error: failed to open " << path << " for reading" << std::endl;
  128. return 1;
  129. }
  130. spiffs_file dst = SPIFFS_open(&s_fs, name, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
  131. // read file size
  132. fseek(src, 0, SEEK_END);
  133. size_t size = ftell(src);
  134. fseek(src, 0, SEEK_SET);
  135. if (g_debugLevel > 0) {
  136. std::cout << "file size: " << size << std::endl;
  137. }
  138. size_t left = size;
  139. uint8_t data_byte;
  140. while (left > 0){
  141. if (1 != fread(&data_byte, 1, 1, src)) {
  142. std::cerr << "fread error!" << std::endl;
  143. fclose(src);
  144. SPIFFS_close(&s_fs, dst);
  145. return 1;
  146. }
  147. int res = SPIFFS_write(&s_fs, dst, &data_byte, 1);
  148. if (res < 0) {
  149. std::cerr << "SPIFFS_write error(" << s_fs.err_code << "): ";
  150. if (s_fs.err_code == SPIFFS_ERR_FULL) {
  151. std::cerr << "File system is full." << std::endl;
  152. } else {
  153. std::cerr << "unknown";
  154. }
  155. std::cerr << std::endl;
  156. if (g_debugLevel > 0) {
  157. std::cout << "data left: " << left << std::endl;
  158. }
  159. fclose(src);
  160. SPIFFS_close(&s_fs, dst);
  161. return 1;
  162. }
  163. left -= 1;
  164. }
  165. SPIFFS_close(&s_fs, dst);
  166. // Get the system time to file timestamps
  167. meta.atime = time(NULL);
  168. meta.ctime = meta.atime;
  169. meta.mtime = meta.atime;
  170. SPIFFS_update_meta(&s_fs, name, &meta);
  171. fclose(src);
  172. return 0;
  173. }
  174. int addFiles(const char* dirname, const char* subPath) {
  175. DIR *dir;
  176. struct dirent *ent;
  177. bool error = false;
  178. std::string dirPath = dirname;
  179. dirPath += subPath;
  180. // Open directory
  181. if ((dir = opendir (dirPath.c_str())) != NULL) {
  182. // Read files from directory.
  183. while ((ent = readdir (dir)) != NULL) {
  184. // Ignore dir itself.
  185. if (ent->d_name[0] == '.')
  186. continue;
  187. std::string fullpath = dirPath;
  188. fullpath += ent->d_name;
  189. struct stat path_stat;
  190. stat (fullpath.c_str(), &path_stat);
  191. if (!S_ISREG(path_stat.st_mode)) {
  192. // Check if path is a directory.
  193. if (S_ISDIR(path_stat.st_mode)) {
  194. // Prepare new sub path.
  195. std::string newSubPath = subPath;
  196. newSubPath += ent->d_name;
  197. // WHITECAT BEGIN
  198. addDir(newSubPath.c_str());
  199. // WHITECAT END
  200. newSubPath += "/";
  201. if (addFiles(dirname, newSubPath.c_str()) != 0)
  202. {
  203. std::cerr << "Error for adding content from " << ent->d_name << "!" << std::endl;
  204. }
  205. continue;
  206. }
  207. else
  208. {
  209. std::cerr << "skipping " << ent->d_name << std::endl;
  210. continue;
  211. }
  212. }
  213. // Filepath with dirname as root folder.
  214. std::string filepath = subPath;
  215. filepath += ent->d_name;
  216. std::cout << filepath << std::endl;
  217. // Add File to image.
  218. if (addFile((char*)filepath.c_str(), fullpath.c_str()) != 0) {
  219. std::cerr << "error adding file!" << std::endl;
  220. error = true;
  221. if (g_debugLevel > 0) {
  222. std::cout << std::endl;
  223. }
  224. break;
  225. }
  226. } // end while
  227. closedir (dir);
  228. } else {
  229. std::cerr << "warning: can't read source directory" << std::endl;
  230. return 1;
  231. }
  232. return (error) ? 1 : 0;
  233. }
  234. void listFiles() {
  235. spiffs_DIR dir;
  236. spiffs_dirent ent;
  237. SPIFFS_opendir(&s_fs, 0, &dir);
  238. spiffs_dirent* it;
  239. while (true) {
  240. it = SPIFFS_readdir(&dir, &ent);
  241. if (!it)
  242. break;
  243. std::cout << it->size << '\t' << it->name << std::endl;
  244. }
  245. SPIFFS_closedir(&dir);
  246. }
  247. /**
  248. * @brief Check if directory exists.
  249. * @param path Directory path.
  250. * @return True if exists otherwise false.
  251. *
  252. * @author Pascal Gollor (http://www.pgollor.de/cms/)
  253. */
  254. bool dirExists(const char* path) {
  255. DIR *d = opendir(path);
  256. if (d) {
  257. closedir(d);
  258. return true;
  259. }
  260. return false;
  261. }
  262. /**
  263. * @brief Create directory if it not exists.
  264. * @param path Directory path.
  265. * @return True or false.
  266. *
  267. * @author Pascal Gollor (http://www.pgollor.de/cms/)
  268. */
  269. bool dirCreate(const char* path) {
  270. // Check if directory also exists.
  271. if (dirExists(path)) {
  272. return false;
  273. }
  274. // platform stuff...
  275. #if defined(_WIN32)
  276. if (_mkdir(path) != 0) {
  277. #else
  278. if (mkdir(path, S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH) != 0) {
  279. #endif
  280. std::cerr << "Can not create directory!!!" << std::endl;
  281. return false;
  282. }
  283. return true;
  284. }
  285. /**
  286. * @brief Unpack file from file system.
  287. * @param spiffsFile SPIFFS dir entry pointer.
  288. * @param destPath Destination file path path.
  289. * @return True or false.
  290. *
  291. * @author Pascal Gollor (http://www.pgollor.de/cms/)
  292. */
  293. bool unpackFile(spiffs_dirent *spiffsFile, const char *destPath) {
  294. u8_t buffer[spiffsFile->size];
  295. std::string filename = (const char*)(spiffsFile->name);
  296. // Open file from spiffs file system.
  297. spiffs_file src = SPIFFS_open(&s_fs, (char *)(filename.c_str()), SPIFFS_RDONLY, 0);
  298. // read content into buffer
  299. SPIFFS_read(&s_fs, src, buffer, spiffsFile->size);
  300. // Close spiffs file.
  301. SPIFFS_close(&s_fs, src);
  302. // Open file.
  303. FILE* dst = fopen(destPath, "wb");
  304. // Write content into file.
  305. fwrite(buffer, sizeof(u8_t), sizeof(buffer), dst);
  306. // Close file.
  307. fclose(dst);
  308. return true;
  309. }
  310. /**
  311. * @brief Unpack files from file system.
  312. * @param sDest Directory path as std::string.
  313. * @return True or false.
  314. *
  315. * @author Pascal Gollor (http://www.pgollor.de/cms/)
  316. *
  317. * todo: Do unpack stuff for directories.
  318. */
  319. bool unpackFiles(std::string sDest) {
  320. spiffs_DIR dir;
  321. spiffs_dirent ent;
  322. // Add "./" to path if is not given.
  323. if (sDest.find("./") == std::string::npos && sDest.find("/") == std::string::npos) {
  324. sDest = "./" + sDest;
  325. }
  326. // Check if directory exists. If it does not then try to create it with permissions 755.
  327. if (! dirExists(sDest.c_str())) {
  328. std::cout << "Directory " << sDest << " does not exists. Try to create it." << std::endl;
  329. // Try to create directory.
  330. if (! dirCreate(sDest.c_str())) {
  331. return false;
  332. }
  333. }
  334. // Open directory.
  335. SPIFFS_opendir(&s_fs, 0, &dir);
  336. // Read content from directory.
  337. spiffs_dirent* it = SPIFFS_readdir(&dir, &ent);
  338. while (it) {
  339. // Check if content is a file.
  340. if ((int)(it->type) == 1) {
  341. std::string name = (const char*)(it->name);
  342. std::string sDestFilePath = sDest + name;
  343. size_t pos = name.find_last_of("/");
  344. // If file is in sub directory?
  345. if (pos > 0) {
  346. // Subdir path.
  347. std::string path = sDest;
  348. path += name.substr(0, pos);
  349. // Create subddir if subdir not exists.
  350. if (!dirExists(path.c_str())) {
  351. if (!dirCreate(path.c_str())) {
  352. return false;
  353. }
  354. }
  355. }
  356. // Unpack file to destination directory.
  357. if (! unpackFile(it, sDestFilePath.c_str()) ) {
  358. std::cout << "Can not unpack " << it->name << "!" << std::endl;
  359. return false;
  360. }
  361. // Output stuff.
  362. std::cout
  363. << it->name
  364. << '\t'
  365. << " > " << sDestFilePath
  366. << '\t'
  367. << "size: " << it->size << " Bytes"
  368. << std::endl;
  369. }
  370. // Get next file handle.
  371. it = SPIFFS_readdir(&dir, &ent);
  372. } // end while
  373. // Close directory.
  374. SPIFFS_closedir(&dir);
  375. return true;
  376. }
  377. // Actions
  378. int actionPack() {
  379. s_flashmem.resize(s_imageSize, 0xff);
  380. FILE* fdres = fopen(s_imageName.c_str(), "wb");
  381. if (!fdres) {
  382. std::cerr << "error: failed to open image file" << std::endl;
  383. return 1;
  384. }
  385. spiffsFormat();
  386. // WHITECAT BEGIN
  387. addDir("");
  388. // WHITECAT END
  389. int result = addFiles(s_dirName.c_str(), "/");
  390. spiffsUnmount();
  391. fwrite(&s_flashmem[0], 4, s_flashmem.size()/4, fdres);
  392. fclose(fdres);
  393. return result;
  394. }
  395. /**
  396. * @brief Unpack action.
  397. * @return 0 success, 1 error
  398. *
  399. * @author Pascal Gollor (http://www.pgollor.de/cms/)
  400. */
  401. int actionUnpack(void) {
  402. int ret = 0;
  403. s_flashmem.resize(s_imageSize, 0xff);
  404. // open spiffs image
  405. FILE* fdsrc = fopen(s_imageName.c_str(), "rb");
  406. if (!fdsrc) {
  407. std::cerr << "error: failed to open image file" << std::endl;
  408. return 1;
  409. }
  410. // read content into s_flashmem
  411. ret = fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc);
  412. // close fiel handle
  413. fclose(fdsrc);
  414. // mount file system
  415. spiffsMount();
  416. // unpack files
  417. if (! unpackFiles(s_dirName)) {
  418. ret = 1;
  419. }
  420. // unmount file system
  421. spiffsUnmount();
  422. return ret;
  423. }
  424. int actionList() {
  425. int ret = 0;
  426. s_flashmem.resize(s_imageSize, 0xff);
  427. FILE* fdsrc = fopen(s_imageName.c_str(), "rb");
  428. if (!fdsrc) {
  429. std::cerr << "error: failed to open image file" << std::endl;
  430. return 1;
  431. }
  432. ret = fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc);
  433. fclose(fdsrc);
  434. spiffsMount();
  435. listFiles();
  436. spiffsUnmount();
  437. ret = 0;
  438. return ret;
  439. }
  440. int actionVisualize() {
  441. int ret = 0;
  442. s_flashmem.resize(s_imageSize, 0xff);
  443. FILE* fdsrc = fopen(s_imageName.c_str(), "rb");
  444. if (!fdsrc) {
  445. std::cerr << "error: failed to open image file" << std::endl;
  446. return 1;
  447. }
  448. ret = fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc);
  449. fclose(fdsrc);
  450. spiffsMount();
  451. //SPIFFS_vis(&s_fs);
  452. uint32_t total, used;
  453. SPIFFS_info(&s_fs, &total, &used);
  454. std::cout << "total: " << total << std::endl << "used: " << used << std::endl;
  455. spiffsUnmount();
  456. ret = 0;
  457. return ret;
  458. }
  459. void processArgs(int argc, const char** argv) {
  460. TCLAP::CmdLine cmd("", ' ', VERSION);
  461. TCLAP::ValueArg<std::string> packArg( "c", "create", "create spiffs image from a directory", true, "", "pack_dir");
  462. TCLAP::ValueArg<std::string> unpackArg( "u", "unpack", "unpack spiffs image to a directory", true, "", "dest_dir");
  463. TCLAP::SwitchArg listArg( "l", "list", "list files in spiffs image", false);
  464. TCLAP::SwitchArg visualizeArg( "i", "visualize", "visualize spiffs image", false);
  465. TCLAP::UnlabeledValueArg<std::string> outNameArg( "image_file", "spiffs image file", true, "", "image_file" );
  466. TCLAP::ValueArg<int> imageSizeArg( "s", "size", "fs image size, in bytes", false, 0x10000, "number" );
  467. TCLAP::ValueArg<int> pageSizeArg( "p", "page", "fs page size, in bytes", false, 256, "number" );
  468. TCLAP::ValueArg<int> blockSizeArg( "b", "block", "fs block size, in bytes", false, 4096, "number" );
  469. TCLAP::ValueArg<int> debugArg( "d", "debug", "Debug level. 0 means no debug output.", false, 0, "0-5" );
  470. cmd.add( imageSizeArg );
  471. cmd.add( pageSizeArg );
  472. cmd.add( blockSizeArg );
  473. cmd.add(debugArg);
  474. std::vector<TCLAP::Arg*> args = {&packArg, &unpackArg, &listArg, &visualizeArg};
  475. cmd.xorAdd( args );
  476. cmd.add( outNameArg );
  477. cmd.parse( argc, argv );
  478. if (debugArg.getValue() > 0) {
  479. std::cout << "Debug output enabled" << std::endl;
  480. g_debugLevel = debugArg.getValue();
  481. }
  482. if (packArg.isSet()) {
  483. s_dirName = packArg.getValue();
  484. s_action = ACTION_PACK;
  485. } else if (unpackArg.isSet()) {
  486. s_dirName = unpackArg.getValue();
  487. s_action = ACTION_UNPACK;
  488. } else if (listArg.isSet()) {
  489. s_action = ACTION_LIST;
  490. } else if (visualizeArg.isSet()) {
  491. s_action = ACTION_VISUALIZE;
  492. }
  493. s_imageName = outNameArg.getValue();
  494. s_imageSize = imageSizeArg.getValue();
  495. s_pageSize = pageSizeArg.getValue();
  496. s_blockSize = blockSizeArg.getValue();
  497. }
  498. int main(int argc, const char * argv[]) {
  499. try {
  500. processArgs(argc, argv);
  501. } catch(...) {
  502. std::cerr << "Invalid arguments" << std::endl;
  503. return 1;
  504. }
  505. switch (s_action) {
  506. case ACTION_PACK:
  507. return actionPack();
  508. break;
  509. case ACTION_UNPACK:
  510. return actionUnpack();
  511. break;
  512. case ACTION_LIST:
  513. return actionList();
  514. break;
  515. case ACTION_VISUALIZE:
  516. return actionVisualize();
  517. break;
  518. default:
  519. break;
  520. }
  521. return 1;
  522. }