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.
319 lines
8.8 KiB
319 lines
8.8 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.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "espfsformat.h"
|
|
#include "espfs.h"
|
|
|
|
#include "heatshrink_decoder.h"
|
|
#include "logging.h"
|
|
|
|
// forward declaration for use in the stand-alone espfs tool
|
|
int httpdPlatEspfsRead(void *dest, uint32_t offset, size_t len);
|
|
|
|
struct EspFsFile {
|
|
uint32_t header;
|
|
char decompressor;
|
|
uint32_t posDecomp;
|
|
uint32_t posStart;
|
|
uint32_t posComp;
|
|
heatshrink_decoder *decompData;
|
|
};
|
|
|
|
|
|
EspFsInitResult espFsInit()
|
|
{
|
|
// check if there is valid header at address
|
|
EspFsHeader testHeader;
|
|
int rv;
|
|
rv = httpdPlatEspfsRead(&testHeader, 0, sizeof(EspFsHeader));
|
|
if (rv != 0 || testHeader.magic != ESPFS_MAGIC) {
|
|
espfs_error("[EspFS] Invalid magic on first file header");
|
|
return ESPFS_INIT_RESULT_NO_IMAGE;
|
|
}
|
|
return ESPFS_INIT_RESULT_OK;
|
|
}
|
|
|
|
// Returns flags of opened file.
|
|
int espFsFlags(EspFsFile *fh)
|
|
{
|
|
if (fh == NULL) {
|
|
espfs_error("[EspFS] File handle not ready");
|
|
return -1;
|
|
}
|
|
|
|
int8_t flags;
|
|
httpdPlatEspfsRead(&flags, fh->header + offsetof(EspFsHeader, flags), 1);
|
|
return (int) flags;
|
|
}
|
|
|
|
void espFsWalkInit(EspFsWalk *walk)
|
|
{
|
|
if (!walk) { return; }
|
|
walk->hpos = 0;
|
|
}
|
|
|
|
bool espFsWalkNext(EspFsWalk *walk, EspFsHeader *header, char *namebuf, size_t namebuf_cap, uint32_t *filepos)
|
|
{
|
|
int rv;
|
|
|
|
if (!header || !namebuf) {
|
|
espfs_error("[EspFS] espFsWalkNext NULL header or namebuf arg");
|
|
return false;
|
|
}
|
|
|
|
uint32_t p = walk->hpos;
|
|
|
|
//Grab the next file header.
|
|
rv = httpdPlatEspfsRead(header, p, sizeof(EspFsHeader));
|
|
if (rv != 0) {
|
|
return false;
|
|
}
|
|
|
|
if (header->magic != ESPFS_MAGIC) {
|
|
espfs_error("[EspFS] Magic mismatch. EspFS image broken.");
|
|
return false;
|
|
}
|
|
|
|
if (header->flags & FLAG_LASTFILE) {
|
|
espfs_dbg("[EspFS] End of image.");
|
|
return false;
|
|
}
|
|
|
|
if (header->nameLen > namebuf_cap) {
|
|
espfs_dbg("[EspFS] Name too long for buffer");
|
|
return false;
|
|
}
|
|
|
|
//Grab the name of the file.
|
|
p += sizeof(EspFsHeader);
|
|
httpdPlatEspfsRead(namebuf, p, header->nameLen);
|
|
namebuf[header->nameLen] = 0; // ensure it's terminated
|
|
|
|
if (filepos) {
|
|
*filepos = walk->hpos;
|
|
}
|
|
|
|
walk->hpos += sizeof(EspFsHeader) + header->nameLen + header->fileLenComp;
|
|
// Align
|
|
while(walk->hpos & 3) {
|
|
walk->hpos++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//Open a file and return a pointer to the file desc struct.
|
|
EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos)
|
|
{
|
|
int rv;
|
|
if (h->magic != ESPFS_MAGIC) {
|
|
espfs_error("[EspFS] Magic mismatch. EspFS image broken.");
|
|
return NULL;
|
|
}
|
|
if (h->flags & FLAG_LASTFILE) {
|
|
espfs_dbg("[EspFS] End of image.");
|
|
return NULL;
|
|
}
|
|
|
|
hpos += sizeof(EspFsHeader);
|
|
hpos += h->nameLen; // Skip to content
|
|
|
|
EspFsFile *r = (EspFsFile *) httpdPlatMalloc(sizeof(EspFsFile)); //Alloc file desc mem
|
|
if (r == NULL) { return NULL; }
|
|
r->header = hpos;
|
|
r->decompressor = h->compression;
|
|
r->posComp = hpos;
|
|
r->posStart = hpos;
|
|
r->posDecomp = 0;
|
|
|
|
espfs_dbg("[EspFS] Found file @ hpos %d", hpos);
|
|
|
|
if (h->compression == COMPRESS_NONE) {
|
|
r->decompData = NULL;
|
|
} else if (h->compression == COMPRESS_HEATSHRINK) {
|
|
//File is compressed with Heatshrink.
|
|
char parm;
|
|
//Decoder params are stored in 1st byte.
|
|
rv = httpdPlatEspfsRead(&parm, r->posComp, 1);
|
|
if (rv != 0) {
|
|
return NULL;
|
|
}
|
|
r->posComp++;
|
|
espfs_dbg("[EspFS] Heatshrink compressed file; decode parms = %x", parm);
|
|
heatshrink_decoder *dec = heatshrink_decoder_alloc(16, (parm >> 4) & 0xf, parm & 0xf);
|
|
r->decompData = dec;
|
|
return r;
|
|
} else {
|
|
espfs_error("[EspFS] Invalid compression: %d", h->compression);
|
|
httpdPlatFree(r);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//Open a file and return a pointer to the file desc struct.
|
|
EspFsFile *espFsOpenAt(uint32_t hpos)
|
|
{
|
|
EspFsHeader h;
|
|
int rv = httpdPlatEspfsRead(&h, hpos, sizeof(EspFsHeader));
|
|
if (rv != 0) {
|
|
return NULL;
|
|
}
|
|
return espFsOpenFromHeader(&h, hpos);
|
|
}
|
|
|
|
|
|
//Open a file and return a pointer to the file desc struct.
|
|
EspFsFile *espFsOpen(const char *fileName)
|
|
{
|
|
int rv;
|
|
uint32_t p = 0;
|
|
uint32_t hpos;
|
|
char namebuf[256];
|
|
EspFsHeader h;
|
|
EspFsFile *r;
|
|
//Strip initial slashes
|
|
while (fileName[0] == '/') { fileName++; }
|
|
|
|
espfs_dbg("[EspFS] Open file: %s", fileName);
|
|
|
|
//Go find that file!
|
|
while (1) {
|
|
hpos = p;
|
|
//Grab the next file header.
|
|
rv = httpdPlatEspfsRead(&h, p, sizeof(EspFsHeader));
|
|
if (rv != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (h.magic != ESPFS_MAGIC) {
|
|
espfs_error("[EspFS] Magic mismatch. EspFS image broken.");
|
|
return NULL;
|
|
}
|
|
if (h.flags & FLAG_LASTFILE) {
|
|
espfs_dbg("[EspFS] End of image.");
|
|
return NULL;
|
|
}
|
|
|
|
//Grab the name of the file.
|
|
p += sizeof(EspFsHeader);
|
|
rv = httpdPlatEspfsRead(&namebuf, p, h.nameLen);
|
|
if (rv != 0) {
|
|
return NULL;
|
|
}
|
|
namebuf[h.nameLen] = 0; // ensure it's terminated
|
|
|
|
if (strcmp(namebuf, fileName) == 0) {
|
|
//Yay, this is the file we need!
|
|
return espFsOpenFromHeader(&h, hpos);
|
|
}
|
|
//We don't need this file. Skip name and file
|
|
p += h.nameLen + h.fileLenComp;
|
|
while(p & 3) {
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Read len bytes from the given file into buff. Returns the actual amount of bytes read.
|
|
int espFsRead(EspFsFile *fh, char *buff, size_t len)
|
|
{
|
|
int rv;
|
|
int flen;
|
|
int fdlen;
|
|
if (fh == NULL) { return 0; }
|
|
|
|
rv = httpdPlatEspfsRead(&flen, fh->header + offsetof(EspFsHeader, fileLenComp), 4);
|
|
if (rv != 0) {
|
|
return 0;
|
|
}
|
|
|
|
//Cache file length.
|
|
//Do stuff depending on the way the file is compressed.
|
|
if (fh->decompressor == COMPRESS_NONE) {
|
|
int toRead = flen - (int) (fh->posComp - fh->posStart);
|
|
if (toRead < 0) { toRead = 0; }
|
|
if (len > toRead) { len = toRead; }
|
|
rv = httpdPlatEspfsRead(buff, fh->posComp, len);
|
|
if (rv != 0) {
|
|
return 0;
|
|
}
|
|
|
|
fh->posDecomp += len;
|
|
fh->posComp += len;
|
|
return (int) len;
|
|
} else if (fh->decompressor == COMPRESS_HEATSHRINK) {
|
|
rv = httpdPlatEspfsRead(&fdlen, fh->header + offsetof(EspFsHeader, fileLenDecomp), 4);
|
|
if (rv != 0) {
|
|
return 0;
|
|
}
|
|
|
|
size_t decoded = 0;
|
|
size_t elen, rlen;
|
|
char ebuff[16];
|
|
heatshrink_decoder *dec = fh->decompData;
|
|
if (fh->posDecomp == fdlen) {
|
|
return 0;
|
|
}
|
|
|
|
// We must ensure that whole file is decompressed and written to output buffer.
|
|
// This means even when there is no input data (elen==0) try to poll decoder until
|
|
// posDecomp equals decompressed file length
|
|
|
|
while (decoded < len) {
|
|
//Feed data into the decompressor
|
|
//ToDo: Check ret val of heatshrink fns for errors
|
|
elen = flen - (fh->posComp - fh->posStart);
|
|
if (elen > 0) {
|
|
rv = httpdPlatEspfsRead(ebuff, fh->posComp, 16);
|
|
if (rv != 0) {
|
|
return 0;
|
|
}
|
|
|
|
heatshrink_decoder_sink(dec, (uint8_t *) ebuff, (elen > 16) ? 16 : elen, &rlen);
|
|
fh->posComp += rlen;
|
|
}
|
|
//Grab decompressed data and put into buff
|
|
heatshrink_decoder_poll(dec, (uint8_t *) buff, len - decoded, &rlen);
|
|
fh->posDecomp += rlen;
|
|
buff += rlen;
|
|
decoded += rlen;
|
|
|
|
if (elen == 0) {
|
|
if (fh->posDecomp == fdlen) {
|
|
heatshrink_decoder_finish(dec);
|
|
}
|
|
return (int) decoded;
|
|
}
|
|
}
|
|
return (int) len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//Close the file.
|
|
void espFsClose(EspFsFile *fh)
|
|
{
|
|
if (fh == NULL) { return; }
|
|
if (fh->decompressor == COMPRESS_HEATSHRINK) {
|
|
heatshrink_decoder *dec = (heatshrink_decoder *) fh->decompData;
|
|
heatshrink_decoder_free(dec);
|
|
}
|
|
httpdPlatFree(fh);
|
|
}
|
|
|