espfs.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. This is a simple read-only implementation of a file system. It uses a block of data coming from the
  3. mkespfsimg tool, and can use that block to do abstracted operations on the files that are in there.
  4. It's written for use with httpd, but doesn't need to be used as such.
  5. */
  6. /*
  7. * ----------------------------------------------------------------------------
  8. * "THE BEER-WARE LICENSE" (Revision 42):
  9. * Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
  10. * this notice you can do whatever you want with this stuff. If we meet some day,
  11. * and you think this stuff is worth it, you can buy me a beer in return.
  12. * ----------------------------------------------------------------------------
  13. */
  14. //These routines can also be tested by comping them in with the espfstest tool. This
  15. //simplifies debugging, but needs some slightly different headers. The #ifdef takes
  16. //care of that.
  17. #ifdef __ets__
  18. //esp build
  19. #include <esp8266.h>
  20. #else
  21. //Test build
  22. #include <stdio.h>
  23. #include <stdint.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #define os_malloc malloc
  27. #define os_free free
  28. #define os_memcpy memcpy
  29. #define os_memset memset
  30. #define os_strncmp strncmp
  31. #define os_strcmp strcmp
  32. #define os_strcpy strcpy
  33. #define os_printf printf
  34. #define ICACHE_FLASH_ATTR
  35. #endif
  36. #include "espfsformat.h"
  37. #include "espfs.h"
  38. EspFsContext espLinkCtxDef;
  39. EspFsContext userPageCtxDef;
  40. EspFsContext * espLinkCtx = &espLinkCtxDef;
  41. EspFsContext * userPageCtx = &userPageCtxDef;
  42. struct EspFsContext
  43. {
  44. char* data;
  45. EspFsSource source;
  46. uint8_t valid;
  47. };
  48. struct EspFsFile {
  49. EspFsContext *ctx;
  50. EspFsHeader *header;
  51. char decompressor;
  52. int32_t posDecomp;
  53. char *posStart;
  54. char *posComp;
  55. void *decompData;
  56. };
  57. /*
  58. Available locations, at least in my flash, with boundaries partially guessed. This
  59. is using 0.9.1/0.9.2 SDK on a not-too-new module.
  60. 0x00000 (0x10000): Code/data (RAM data?)
  61. 0x10000 (0x02000): Gets erased by something?
  62. 0x12000 (0x2E000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL)
  63. 0x40000 (0x20000): Code/data (ROM data?)
  64. 0x60000 (0x1C000): Free
  65. 0x7c000 (0x04000): Param store
  66. 0x80000 - end of flash
  67. Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses
  68. *must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in
  69. a memory exception, crashing the program.
  70. */
  71. //Copies len bytes over from dst to src, but does it using *only*
  72. //aligned 32-bit reads. Yes, it's no too optimized but it's short and sweet and it works.
  73. //ToDo: perhaps os_memcpy also does unaligned accesses?
  74. #ifdef __ets__
  75. void ICACHE_FLASH_ATTR memcpyAligned(char *dst, const char *src, int len) {
  76. int x;
  77. int w, b;
  78. for (x=0; x<len; x++) {
  79. b=((int)src&3);
  80. w=*((int *)(src-b));
  81. if (b==0) *dst=(w>>0);
  82. if (b==1) *dst=(w>>8);
  83. if (b==2) *dst=(w>>16);
  84. if (b==3) *dst=(w>>24);
  85. dst++; src++;
  86. }
  87. }
  88. #else
  89. #define memcpyAligned memcpy
  90. #endif
  91. void ICACHE_FLASH_ATTR memcpyFromFlash(char *dst, const char *src, int len)
  92. {
  93. if( spi_flash_read( (int)src, (void *)dst, len ) != SPI_FLASH_RESULT_OK )
  94. os_memset( dst, 0, len ); // if read was not successful, reply with zeroes
  95. }
  96. // memcpy on MEMORY/FLASH file systems
  97. void espfs_memcpy( EspFsContext * ctx, void * dest, const void * src, int count )
  98. {
  99. if( ctx->source == ESPFS_MEMORY )
  100. os_memcpy( dest, src, count );
  101. else
  102. memcpyFromFlash(dest, src, count);
  103. }
  104. // aligned memcpy on MEMORY/FLASH file systems
  105. void espfs_memcpyAligned( EspFsContext * ctx, void * dest, const void * src, int count )
  106. {
  107. if( ctx->source == ESPFS_MEMORY )
  108. memcpyAligned(dest, src, count);
  109. else
  110. memcpyFromFlash(dest, src, count);
  111. }
  112. // initializes an EspFs context
  113. EspFsInitResult ICACHE_FLASH_ATTR espFsInit(EspFsContext *ctx, void *flashAddress, EspFsSource source) {
  114. ctx->valid = 0;
  115. ctx->source = source;
  116. // base address must be aligned to 4 bytes
  117. if (((int)flashAddress & 3) != 0) {
  118. return ESPFS_INIT_RESULT_BAD_ALIGN;
  119. }
  120. // check if there is valid header at address
  121. EspFsHeader testHeader;
  122. espfs_memcpy(ctx, &testHeader, flashAddress, sizeof(EspFsHeader));
  123. if (testHeader.magic != ESPFS_MAGIC) {
  124. return ESPFS_INIT_RESULT_NO_IMAGE;
  125. }
  126. ctx->data = (char *)flashAddress;
  127. ctx->valid = 1;
  128. return ESPFS_INIT_RESULT_OK;
  129. }
  130. // Returns flags of opened file.
  131. int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) {
  132. if (fh == NULL) {
  133. #ifdef ESPFS_DBG
  134. os_printf("File handle not ready\n");
  135. #endif
  136. return -1;
  137. }
  138. int8_t flags;
  139. espfs_memcpyAligned(fh->ctx, (char*)&flags, (char*)&fh->header->flags, 1);
  140. return (int)flags;
  141. }
  142. // creates and initializes an iterator over the espfs file system
  143. void ICACHE_FLASH_ATTR espFsIteratorInit(EspFsContext *ctx, EspFsIterator *iterator)
  144. {
  145. if( ctx->data == NULL )
  146. {
  147. iterator->ctx = NULL;
  148. return;
  149. }
  150. iterator->ctx = ctx;
  151. iterator->position = NULL;
  152. }
  153. // moves iterator to the next file on espfs
  154. // returns 1 if iterator move was successful, otherwise 0 (last file)
  155. // iterator->header and iterator->name will contain file information
  156. int ICACHE_FLASH_ATTR espFsIteratorNext(EspFsIterator *iterator)
  157. {
  158. if( iterator->ctx == NULL )
  159. return 0;
  160. char * position = iterator->position;
  161. if( position == NULL )
  162. position = iterator->ctx->data; // first node
  163. else
  164. {
  165. // jump the iterator to the next file
  166. position+=sizeof(EspFsHeader) + iterator->header.nameLen+iterator->header.fileLenComp;
  167. if ((int)position&3) position+=4-((int)position&3); //align to next 32bit val
  168. }
  169. iterator->position = position;
  170. EspFsHeader * hdr = &iterator->header;
  171. espfs_memcpy(iterator->ctx, hdr, position, sizeof(EspFsHeader));
  172. if (hdr->magic!=ESPFS_MAGIC) {
  173. #ifdef ESPFS_DBG
  174. os_printf("Magic mismatch. EspFS image broken.\n");
  175. #endif
  176. return 0;
  177. }
  178. if (hdr->flags&FLAG_LASTFILE) {
  179. //os_printf("End of image.\n");
  180. iterator->ctx = NULL; // invalidate the iterator
  181. return 0;
  182. }
  183. position += sizeof(EspFsHeader);
  184. //Grab the name of the file.
  185. espfs_memcpy(iterator->ctx, iterator->name, position, sizeof(iterator->name));
  186. return 1;
  187. }
  188. //Open a file and return a pointer to the file desc struct.
  189. EspFsFile ICACHE_FLASH_ATTR *espFsOpen(EspFsContext *ctx, char *fileName) {
  190. EspFsIterator it;
  191. espFsIteratorInit(ctx, &it);
  192. if (it.ctx == NULL) {
  193. #ifdef ESPFS_DBG
  194. os_printf("Call espFsInit first!\n");
  195. #endif
  196. return NULL;
  197. }
  198. //Strip initial slashes
  199. while(fileName[0]=='/') fileName++;
  200. //Search the file
  201. while( espFsIteratorNext(&it) )
  202. {
  203. if (os_strcmp(it.name, fileName)==0) {
  204. //Yay, this is the file we need!
  205. EspFsFile * r=(EspFsFile *)os_malloc(sizeof(EspFsFile)); //Alloc file desc mem
  206. //os_printf("Alloc %p[%d]\n", r, sizeof(EspFsFile));
  207. if (r==NULL) return NULL;
  208. r->ctx = ctx;
  209. r->header=(EspFsHeader *)it.position;
  210. r->decompressor=it.header.compression;
  211. r->posComp=it.position + it.header.nameLen + sizeof(EspFsHeader);
  212. r->posStart=it.position + it.header.nameLen + sizeof(EspFsHeader);
  213. r->posDecomp=0;
  214. if (it.header.compression==COMPRESS_NONE) {
  215. r->decompData=NULL;
  216. } else {
  217. #ifdef ESPFS_DBG
  218. os_printf("Invalid compression: %d\n", h.compression);
  219. #endif
  220. return NULL;
  221. }
  222. return r;
  223. }
  224. }
  225. return NULL;
  226. }
  227. //Read len bytes from the given file into buff. Returns the actual amount of bytes read.
  228. int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) {
  229. int flen, fdlen;
  230. if (fh==NULL) return 0;
  231. //Cache file length.
  232. espfs_memcpyAligned(fh->ctx, (char*)&flen, (char*)&fh->header->fileLenComp, 4);
  233. espfs_memcpyAligned(fh->ctx, (char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4);
  234. //Do stuff depending on the way the file is compressed.
  235. if (fh->decompressor==COMPRESS_NONE) {
  236. int toRead;
  237. toRead=flen-(fh->posComp-fh->posStart);
  238. if (len>toRead) len=toRead;
  239. // os_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp);
  240. espfs_memcpyAligned(fh->ctx, buff, fh->posComp, len);
  241. fh->posDecomp+=len;
  242. fh->posComp+=len;
  243. // os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp);
  244. return len;
  245. }
  246. return 0;
  247. }
  248. //Close the file.
  249. void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) {
  250. if (fh==NULL) return;
  251. //os_printf("Freed %p\n", fh);
  252. os_free(fh);
  253. }
  254. // checks if the file system is valid (detect if the content is an espfs image or random data)
  255. int ICACHE_FLASH_ATTR espFsIsValid(EspFsContext *ctx) {
  256. return ctx->valid;
  257. }