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.
189 lines
5.2 KiB
189 lines
5.2 KiB
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <zlib.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include "parsing.h"
|
|
#include "espfs.h"
|
|
#include "httpd-logging.h"
|
|
|
|
static off_t espfs_parse_filesize = -1;
|
|
static int espfs_parse_fd = -1;
|
|
|
|
size_t decompressGzip(const uint8_t *in, size_t insize, int outfd)
|
|
{
|
|
#define OUTBUF_LEN 10240
|
|
uint8_t outbuf[OUTBUF_LEN];
|
|
#define ERRORBUF_LEN 256
|
|
char errorbuf[ERRORBUF_LEN];
|
|
|
|
z_stream stream;
|
|
int zresult;
|
|
|
|
stream.zalloc = Z_NULL;
|
|
stream.zfree = Z_NULL;
|
|
stream.opaque = Z_NULL;
|
|
stream.next_in = in;
|
|
stream.avail_in = (uInt) insize;
|
|
stream.next_out = outbuf;
|
|
stream.avail_out = OUTBUF_LEN;
|
|
// 31 -> 15 window bits + 16 for gzip
|
|
zresult = inflateInit2(&stream, 31);
|
|
if (zresult != Z_OK) {
|
|
espfs_error("InflateInit2 failed with code %d", zresult);
|
|
exit(1);
|
|
}
|
|
|
|
while (1) {
|
|
stream.avail_out = OUTBUF_LEN;
|
|
stream.next_out = outbuf;
|
|
|
|
espfs_dbg("inflate chunk\n");
|
|
zresult = inflate(&stream, Z_FINISH);
|
|
if (zresult == Z_BUF_ERROR || zresult == Z_OK || zresult == Z_STREAM_END) {
|
|
size_t have = OUTBUF_LEN - stream.avail_out;
|
|
espfs_dbg("inflated: %d\n", (int) have);
|
|
errno = 0;
|
|
if ((ssize_t)have != write(outfd, outbuf, have)) {
|
|
snprintf(errorbuf, ERRORBUF_LEN, "Write output: %s", strerror(errno));
|
|
espfs_error("%s", errorbuf);
|
|
exit(1);
|
|
}
|
|
if (zresult == Z_STREAM_END) {
|
|
espfs_dbg("Z_STREAM_END\n");
|
|
break;
|
|
}
|
|
} else {
|
|
espfs_error("gzip error: %d\n", zresult);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
zresult = inflateEnd(&stream);
|
|
if (zresult != Z_OK) {
|
|
espfs_error("InflateEnd failed with code %d", zresult);
|
|
exit(1);
|
|
}
|
|
|
|
espfs_dbg("Total decoded = %d\n", (int) stream.total_out);
|
|
return stream.total_out;
|
|
}
|
|
|
|
/**
|
|
* Parse an image file and show the files contained.
|
|
* This is a simple sanity test.
|
|
*
|
|
* @param[in] imagefile - image file to parse
|
|
* @param[in] extractfile - name of the file to extract
|
|
* @param outfd - output FD when extracting
|
|
*/
|
|
void parseEspfsImage(const char *imagefile, const char *extractfile, int outfd)
|
|
{
|
|
int rv;
|
|
espfs_dbg("Parsing: %s", imagefile);
|
|
#define ERRORBUF_LEN 256
|
|
char errorbuf[ERRORBUF_LEN];
|
|
|
|
errno = 0;
|
|
FILE *f = fopen(imagefile, "r");
|
|
if (!f) {
|
|
snprintf(errorbuf, ERRORBUF_LEN, "Open %s for writing: %s", imagefile, strerror(errno));
|
|
espfs_error("%s", errorbuf);
|
|
exit(1);
|
|
}
|
|
int fd = fileno(f);
|
|
|
|
espfs_parse_filesize = lseek(fd, 0, SEEK_END);
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
espfs_parse_fd = fd;
|
|
|
|
rv = (int) espFsInit();
|
|
if (rv != 0) {
|
|
espfs_error("Fail to init espfs: %d", rv);
|
|
exit(1);
|
|
}
|
|
|
|
if (extractfile) {
|
|
EspFsFile *efile = espFsOpen(extractfile);
|
|
EspFsHeader hdr;
|
|
|
|
if (!efile) {
|
|
espfs_error("Fail to open file %s from image", extractfile);
|
|
exit(1);
|
|
}
|
|
|
|
rv = espFsFileReadHeader(efile, &hdr);
|
|
if (rv != 0) {
|
|
espfs_error("Fail to read file header");
|
|
exit(1);
|
|
}
|
|
|
|
bool isGzip = hdr.flags & EFS_FLAG_GZIP;
|
|
|
|
size_t expected_readlen = isGzip ? hdr.fileLenComp : hdr.fileLenDecomp;
|
|
|
|
uint8_t *buff = malloc(expected_readlen);
|
|
size_t lenRead = espFsRead(efile, buff, expected_readlen);
|
|
if (lenRead != expected_readlen) {
|
|
espfs_error("Fail to read raw file from espfs image - read len %d", (int) lenRead);
|
|
exit(1);
|
|
}
|
|
|
|
if (isGzip) {
|
|
espfs_dbg("File is gzipped!");
|
|
decompressGzip(buff, lenRead, outfd);
|
|
} else {
|
|
write(outfd, buff, lenRead);
|
|
}
|
|
|
|
fsync(outfd);
|
|
exit(0);
|
|
}
|
|
|
|
/* Walk the image */
|
|
|
|
EspFsWalk walk;
|
|
espFsWalkInit(&walk);
|
|
|
|
EspFsHeader header;
|
|
uint32_t offset;
|
|
char namebuf[1024];
|
|
|
|
while (espFsWalkNext(&walk, &header, namebuf, 1024, &offset)) {
|
|
// to stdout
|
|
printf("File @ 0x%06x: \"%s\", flags=0x%02x, comp=%d, ", offset, namebuf, header.flags, header.compression);
|
|
|
|
if (header.compression == 1) {
|
|
printf("heathshrink (cold %d -> unpacked %d bytes)\n",
|
|
header.fileLenComp, header.fileLenDecomp);
|
|
} else if (header.flags & EFS_FLAG_GZIP) {
|
|
printf("gzip (cold %d -> unpacked %d bytes)\n",
|
|
header.fileLenComp, header.fileLenDecomp);
|
|
} else {
|
|
if (header.fileLenComp != header.fileLenDecomp) {
|
|
espfs_error("Uncompressed, but fileLenComp != fileLenDecomp (%d, %d)", header.fileLenComp, header.fileLenDecomp);
|
|
}
|
|
|
|
printf("plain (%d bytes)\n", header.fileLenDecomp);
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
}
|
|
|
|
|
|
int httpdPlatEspfsRead(void *dest, size_t offset, size_t len)
|
|
{
|
|
espfs_dbg("FS read @ %d, len %d\n", offset, (int) len);
|
|
if ((off_t) (offset + len) > espfs_parse_filesize) {
|
|
espfs_error("Read out fo range!");
|
|
return -1;
|
|
}
|
|
lseek(espfs_parse_fd, (off_t) offset, SEEK_SET);
|
|
read(espfs_parse_fd, dest, len);
|
|
return 0;
|
|
}
|
|
|