#include #include #include #include #include #include #include #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; }