X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=lib%2Fgunzip.c;h=bee3b9261f3449c438c43fac77bac45cde127238;hb=ebdd07f5b2e4b00eee7f8630b3b026a65c3f51c4;hp=35abfb38e176ff64b621fbe9d91b3542ce8150cd;hpb=47f75cf2e1d8648e3438630f3a4bddf9b5caa25d;p=oweals%2Fu-boot.git diff --git a/lib/gunzip.c b/lib/gunzip.c index 35abfb38e1..bee3b9261f 100644 --- a/lib/gunzip.c +++ b/lib/gunzip.c @@ -1,17 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2000-2006 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ */ #include -#include +#include #include +#include +#include +#include #include #include +#include +#include +#include #include +#define HEADER0 '\x1f' +#define HEADER1 '\x8b' #define ZALLOC_ALIGNMENT 16 #define HEAD_CRC 2 #define EXTRA_FIELD 4 @@ -37,7 +44,7 @@ void gzfree(void *x, void *addr, unsigned nb) free (addr); } -int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp) +int gzip_parse_header(const unsigned char *src, unsigned long len) { int i, flags; @@ -58,13 +65,212 @@ int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp) ; if ((flags & HEAD_CRC) != 0) i += 2; - if (i >= *lenp) { + if (i >= len) { puts ("Error: gunzip out of data in header\n"); return (-1); } + return i; +} + +int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp) +{ + int offset = gzip_parse_header(src, *lenp); + + if (offset < 0) + return offset; + + return zunzip(dst, dstlen, src, lenp, 1, offset); +} + +#ifdef CONFIG_CMD_UNZIP +__weak +void gzwrite_progress_init(u64 expectedsize) +{ + putc('\n'); +} + +__weak +void gzwrite_progress(int iteration, + u64 bytes_written, + u64 total_bytes) +{ + if (0 == (iteration & 3)) + printf("%llu/%llu\r", bytes_written, total_bytes); +} + +__weak +void gzwrite_progress_finish(int returnval, + u64 bytes_written, + u64 total_bytes, + u32 expected_crc, + u32 calculated_crc) +{ + if (0 == returnval) { + printf("\n\t%llu bytes, crc 0x%08x\n", + total_bytes, calculated_crc); + } else { + printf("\n\tuncompressed %llu of %llu\n" + "\tcrcs == 0x%08x/0x%08x\n", + bytes_written, total_bytes, + expected_crc, calculated_crc); + } +} + +int gzwrite(unsigned char *src, int len, + struct blk_desc *dev, + unsigned long szwritebuf, + u64 startoffs, + u64 szexpected) +{ + int i, flags; + z_stream s; + int r = 0; + unsigned char *writebuf; + unsigned crc = 0; + u64 totalfilled = 0; + lbaint_t blksperbuf, outblock; + u32 expected_crc; + u32 payload_size; + int iteration = 0; + + if (!szwritebuf || + (szwritebuf % dev->blksz) || + (szwritebuf < dev->blksz)) { + printf("%s: size %lu not a multiple of %lu\n", + __func__, szwritebuf, dev->blksz); + return -1; + } + + if (startoffs & (dev->blksz-1)) { + printf("%s: start offset %llu not a multiple of %lu\n", + __func__, startoffs, dev->blksz); + return -1; + } + + blksperbuf = szwritebuf / dev->blksz; + outblock = lldiv(startoffs, dev->blksz); + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + puts("Error: Bad gzipped data\n"); + return -1; + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + + if (i >= len-8) { + puts("Error: gunzip out of data in header"); + return -1; + } + + payload_size = len - i - 8; + + memcpy(&expected_crc, src + len - 8, sizeof(expected_crc)); + expected_crc = le32_to_cpu(expected_crc); + u32 szuncompressed; + memcpy(&szuncompressed, src + len - 4, sizeof(szuncompressed)); + if (szexpected == 0) { + szexpected = le32_to_cpu(szuncompressed); + } else if (szuncompressed != (u32)szexpected) { + printf("size of %llx doesn't match trailer low bits %x\n", + szexpected, szuncompressed); + return -1; + } + if (lldiv(szexpected, dev->blksz) > (dev->lba - outblock)) { + printf("%s: uncompressed size %llu exceeds device size\n", + __func__, szexpected); + return -1; + } + + gzwrite_progress_init(szexpected); + + s.zalloc = gzalloc; + s.zfree = gzfree; + + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("Error: inflateInit2() returned %d\n", r); + return -1; + } + + s.next_in = src + i; + s.avail_in = payload_size+8; + writebuf = (unsigned char *)malloc_cache_aligned(szwritebuf); + + /* decompress until deflate stream ends or end of file */ + do { + if (s.avail_in == 0) { + printf("%s: weird termination with result %d\n", + __func__, r); + break; + } + + /* run inflate() on input until output buffer not full */ + do { + unsigned long blocks_written; + int numfilled; + lbaint_t writeblocks; + + s.avail_out = szwritebuf; + s.next_out = writebuf; + r = inflate(&s, Z_SYNC_FLUSH); + if ((r != Z_OK) && + (r != Z_STREAM_END)) { + printf("Error: inflate() returned %d\n", r); + goto out; + } + numfilled = szwritebuf - s.avail_out; + crc = crc32(crc, writebuf, numfilled); + totalfilled += numfilled; + if (numfilled < szwritebuf) { + writeblocks = (numfilled+dev->blksz-1) + / dev->blksz; + memset(writebuf+numfilled, 0, + dev->blksz-(numfilled%dev->blksz)); + } else { + writeblocks = blksperbuf; + } + + gzwrite_progress(iteration++, + totalfilled, + szexpected); + blocks_written = blk_dwrite(dev, outblock, + writeblocks, writebuf); + outblock += blocks_written; + if (ctrlc()) { + puts("abort\n"); + goto out; + } + WATCHDOG_RESET(); + } while (s.avail_out == 0); + /* done when inflate() says it's done */ + } while (r != Z_STREAM_END); + + if ((szexpected != totalfilled) || + (crc != expected_crc)) + r = -1; + else + r = 0; + +out: + gzwrite_progress_finish(r, totalfilled, szexpected, + expected_crc, crc); + free(writebuf); + inflateEnd(&s); - return zunzip(dst, dstlen, src, lenp, 1, i); + return r; } +#endif /* * Uncompress blocks compressed with zlib without headers @@ -73,6 +279,7 @@ int zunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp, int stoponerr, int offset) { z_stream s; + int err = 0; int r; s.zalloc = gzalloc; @@ -80,7 +287,7 @@ int zunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp, r = inflateInit2(&s, -MAX_WBITS); if (r != Z_OK) { - printf ("Error: inflateInit2() returned %d\n", r); + printf("Error: inflateInit2() returned %d\n", r); return -1; } s.next_in = src + offset; @@ -90,15 +297,14 @@ int zunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp, do { r = inflate(&s, Z_FINISH); if (stoponerr == 1 && r != Z_STREAM_END && - (s.avail_out == 0 || r != Z_BUF_ERROR)) { + (s.avail_in == 0 || s.avail_out == 0 || r != Z_BUF_ERROR)) { printf("Error: inflate() returned %d\n", r); - inflateEnd(&s); - return -1; + err = -1; + break; } - s.avail_in = *lenp - offset - (int)(s.next_out - (unsigned char*)dst); } while (r == Z_BUF_ERROR); *lenp = s.next_out - (unsigned char *) dst; inflateEnd(&s); - return 0; + return err; }