mirror of https://github.com/jeelabs/esp-link.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
8.1 KiB
293 lines
8.1 KiB
/*
|
|
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;
|
|
}
|
|
|
|
|