123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- /*
- This is a simple read-only implementation of a file system. It uses a block of data coming from the
- mkespfsimg tool, and can use that block to do abstracted operations on the files that are in there.
- It's written for use with httpd, but doesn't need to be used as such.
- */
- /*
- * ----------------------------------------------------------------------------
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
- * this notice you can do whatever you want with this stuff. If we meet some day,
- * and you think this stuff is worth it, you can buy me a beer in return.
- * ----------------------------------------------------------------------------
- */
- //These routines can also be tested by comping them in with the espfstest tool. This
- //simplifies debugging, but needs some slightly different headers. The #ifdef takes
- //care of that.
- #ifdef __ets__
- //esp build
- #include <esp8266.h>
- #else
- //Test build
- #include <stdio.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #define os_malloc malloc
- #define os_free free
- #define os_memcpy memcpy
- #define os_memset memset
- #define os_strncmp strncmp
- #define os_strcmp strcmp
- #define os_strcpy strcpy
- #define os_printf printf
- #define ICACHE_FLASH_ATTR
- #endif
- #include "espfsformat.h"
- #include "espfs.h"
- EspFsContext espLinkCtxDef;
- EspFsContext userPageCtxDef;
- EspFsContext * espLinkCtx = &espLinkCtxDef;
- EspFsContext * userPageCtx = &userPageCtxDef;
- struct EspFsContext
- {
- char* data;
- EspFsSource source;
- uint8_t valid;
- };
- struct EspFsFile {
- EspFsContext *ctx;
- EspFsHeader *header;
- char decompressor;
- int32_t posDecomp;
- char *posStart;
- char *posComp;
- void *decompData;
- };
- /*
- Available locations, at least in my flash, with boundaries partially guessed. This
- is using 0.9.1/0.9.2 SDK on a not-too-new module.
- 0x00000 (0x10000): Code/data (RAM data?)
- 0x10000 (0x02000): Gets erased by something?
- 0x12000 (0x2E000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL)
- 0x40000 (0x20000): Code/data (ROM data?)
- 0x60000 (0x1C000): Free
- 0x7c000 (0x04000): Param store
- 0x80000 - end of flash
- Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses
- *must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in
- a memory exception, crashing the program.
- */
- //Copies len bytes over from dst to src, but does it using *only*
- //aligned 32-bit reads. Yes, it's no too optimized but it's short and sweet and it works.
- //ToDo: perhaps os_memcpy also does unaligned accesses?
- #ifdef __ets__
- void ICACHE_FLASH_ATTR memcpyAligned(char *dst, const char *src, int len) {
- int x;
- int w, b;
- for (x=0; x<len; x++) {
- b=((int)src&3);
- w=*((int *)(src-b));
- if (b==0) *dst=(w>>0);
- if (b==1) *dst=(w>>8);
- if (b==2) *dst=(w>>16);
- if (b==3) *dst=(w>>24);
- dst++; src++;
- }
- }
- #else
- #define memcpyAligned memcpy
- #endif
- void ICACHE_FLASH_ATTR memcpyFromFlash(char *dst, const char *src, int len)
- {
- if( spi_flash_read( (int)src, (void *)dst, len ) != SPI_FLASH_RESULT_OK )
- os_memset( dst, 0, len ); // if read was not successful, reply with zeroes
- }
- // memcpy on MEMORY/FLASH file systems
- void espfs_memcpy( EspFsContext * ctx, void * dest, const void * src, int count )
- {
- if( ctx->source == ESPFS_MEMORY )
- os_memcpy( dest, src, count );
- else
- memcpyFromFlash(dest, src, count);
- }
- // aligned memcpy on MEMORY/FLASH file systems
- void espfs_memcpyAligned( EspFsContext * ctx, void * dest, const void * src, int count )
- {
- if( ctx->source == ESPFS_MEMORY )
- memcpyAligned(dest, src, count);
- else
- memcpyFromFlash(dest, src, count);
- }
- // initializes an EspFs context
- EspFsInitResult ICACHE_FLASH_ATTR espFsInit(EspFsContext *ctx, void *flashAddress, EspFsSource source) {
- ctx->valid = 0;
- ctx->source = source;
- // base address must be aligned to 4 bytes
- if (((int)flashAddress & 3) != 0) {
- return ESPFS_INIT_RESULT_BAD_ALIGN;
- }
- // check if there is valid header at address
- EspFsHeader testHeader;
- espfs_memcpy(ctx, &testHeader, flashAddress, sizeof(EspFsHeader));
- if (testHeader.magic != ESPFS_MAGIC) {
- return ESPFS_INIT_RESULT_NO_IMAGE;
- }
- ctx->data = (char *)flashAddress;
- ctx->valid = 1;
- return ESPFS_INIT_RESULT_OK;
- }
- // Returns flags of opened file.
- int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) {
- if (fh == NULL) {
- #ifdef ESPFS_DBG
- os_printf("File handle not ready\n");
- #endif
- return -1;
- }
- int8_t flags;
- espfs_memcpyAligned(fh->ctx, (char*)&flags, (char*)&fh->header->flags, 1);
- return (int)flags;
- }
- // creates and initializes an iterator over the espfs file system
- void ICACHE_FLASH_ATTR espFsIteratorInit(EspFsContext *ctx, EspFsIterator *iterator)
- {
- if( ctx->data == NULL )
- {
- iterator->ctx = NULL;
- return;
- }
- iterator->ctx = ctx;
- iterator->position = NULL;
- }
- // moves iterator to the next file on espfs
- // returns 1 if iterator move was successful, otherwise 0 (last file)
- // iterator->header and iterator->name will contain file information
- int ICACHE_FLASH_ATTR espFsIteratorNext(EspFsIterator *iterator)
- {
- if( iterator->ctx == NULL )
- return 0;
-
- char * position = iterator->position;
- if( position == NULL )
- position = iterator->ctx->data; // first node
- else
- {
- // jump the iterator to the next file
-
- position+=sizeof(EspFsHeader) + iterator->header.nameLen+iterator->header.fileLenComp;
- if ((int)position&3) position+=4-((int)position&3); //align to next 32bit val
- }
-
- iterator->position = position;
- EspFsHeader * hdr = &iterator->header;
- espfs_memcpy(iterator->ctx, hdr, position, sizeof(EspFsHeader));
-
- if (hdr->magic!=ESPFS_MAGIC) {
- #ifdef ESPFS_DBG
- os_printf("Magic mismatch. EspFS image broken.\n");
- #endif
- return 0;
- }
- if (hdr->flags&FLAG_LASTFILE) {
- //os_printf("End of image.\n");
- iterator->ctx = NULL; // invalidate the iterator
- return 0;
- }
-
- position += sizeof(EspFsHeader);
-
- //Grab the name of the file.
- espfs_memcpy(iterator->ctx, iterator->name, position, sizeof(iterator->name));
-
- return 1;
- }
- //Open a file and return a pointer to the file desc struct.
- EspFsFile ICACHE_FLASH_ATTR *espFsOpen(EspFsContext *ctx, char *fileName) {
- EspFsIterator it;
- espFsIteratorInit(ctx, &it);
- if (it.ctx == NULL) {
- #ifdef ESPFS_DBG
- os_printf("Call espFsInit first!\n");
- #endif
- return NULL;
- }
- //Strip initial slashes
- while(fileName[0]=='/') fileName++;
-
- //Search the file
- while( espFsIteratorNext(&it) )
- {
- if (os_strcmp(it.name, fileName)==0) {
- //Yay, this is the file we need!
- EspFsFile * r=(EspFsFile *)os_malloc(sizeof(EspFsFile)); //Alloc file desc mem
- //os_printf("Alloc %p[%d]\n", r, sizeof(EspFsFile));
- if (r==NULL) return NULL;
- r->ctx = ctx;
- r->header=(EspFsHeader *)it.position;
- r->decompressor=it.header.compression;
- r->posComp=it.position + it.header.nameLen + sizeof(EspFsHeader);
- r->posStart=it.position + it.header.nameLen + sizeof(EspFsHeader);
- r->posDecomp=0;
- if (it.header.compression==COMPRESS_NONE) {
- r->decompData=NULL;
- } else {
- #ifdef ESPFS_DBG
- os_printf("Invalid compression: %d\n", h.compression);
- #endif
- return NULL;
- }
- return r;
- }
- }
- return NULL;
- }
- //Read len bytes from the given file into buff. Returns the actual amount of bytes read.
- int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) {
- int flen, fdlen;
- if (fh==NULL) return 0;
- //Cache file length.
- espfs_memcpyAligned(fh->ctx, (char*)&flen, (char*)&fh->header->fileLenComp, 4);
- espfs_memcpyAligned(fh->ctx, (char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4);
- //Do stuff depending on the way the file is compressed.
- if (fh->decompressor==COMPRESS_NONE) {
- int toRead;
- toRead=flen-(fh->posComp-fh->posStart);
- if (len>toRead) len=toRead;
- // os_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp);
- espfs_memcpyAligned(fh->ctx, buff, fh->posComp, len);
- fh->posDecomp+=len;
- fh->posComp+=len;
- // os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp);
- return len;
- }
- return 0;
- }
- //Close the file.
- void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) {
- if (fh==NULL) return;
- //os_printf("Freed %p\n", fh);
- os_free(fh);
- }
- // checks if the file system is valid (detect if the content is an espfs image or random data)
- int ICACHE_FLASH_ATTR espFsIsValid(EspFsContext *ctx) {
- return ctx->valid;
- }
|