summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/gunzip.c76
-rw-r--r--test/cmd/unzip.c12
2 files changed, 64 insertions, 24 deletions
diff --git a/lib/gunzip.c b/lib/gunzip.c
index 76f3397fced..20cc14f9688 100644
--- a/lib/gunzip.c
+++ b/lib/gunzip.c
@@ -8,8 +8,10 @@
#include <command.h>
#include <console.h>
#include <div64.h>
+#include <env.h>
#include <gzip.h>
#include <image.h>
+#include <linux/sizes.h>
#include <malloc.h>
#include <memalign.h>
#include <u-boot/crc.h>
@@ -119,7 +121,7 @@ void gzwrite_progress_finish(int returnval,
int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev,
size_t szwritebuf, off_t startoffs, size_t szexpected)
{
- int i, flags;
+ int flags;
z_stream s;
int r = 0;
unsigned char *writebuf;
@@ -127,13 +129,23 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev,
ulong totalfilled = 0;
lbaint_t blksperbuf, outblock;
u32 expected_crc;
- size_t payload_size;
+ size_t i, payload_size;
+ unsigned long blocks_written;
+ lbaint_t writeblocks;
+ int numfilled = 0;
int iteration = 0;
-
- if (len > 0xffffffff) {
- log_err("Input size over 4 GiB in size not supported\n");
- return -1;
- }
+ /*
+ * Allow runtime configuration of decompression chunk on
+ * sandbox to better cover the chunked decompression
+ * functionality without having to use > 4 GiB files.
+ */
+ const ulong minchunk = 0x400;
+ const ulong maxchunk = SZ_4G - minchunk;
+ const ulong chunk =
+ CONFIG_IS_ENABLED(SANDBOX,
+ (clamp(env_get_ulong("gzwrite_chunk", 10, maxchunk),
+ minchunk, maxchunk)),
+ (maxchunk));
if (!szwritebuf ||
(szwritebuf % dev->blksz) ||
@@ -175,7 +187,7 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev,
return -1;
}
- payload_size = len - i - 8;
+ payload_size = len - i;
memcpy(&expected_crc, src + len - 8, sizeof(expected_crc));
expected_crc = le32_to_cpu(expected_crc);
@@ -205,35 +217,44 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev,
return -1;
}
- s.next_in = src + i;
- s.avail_in = payload_size+8;
+ src += i;
+ s.avail_in = 0;
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;
+ if (payload_size == 0) {
+ printf("%s: weird termination with result %d\n",
+ __func__, r);
+ break;
+ }
+
+ s.next_in = src;
+ s.avail_in = (payload_size > chunk) ? chunk : payload_size;
+ src += s.avail_in;
+ payload_size -= s.avail_in;
}
/* 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;
+ if (numfilled) {
+ s.avail_out = szwritebuf - numfilled;
+ s.next_out = writebuf + numfilled;
+ } else {
+ 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;
}
+ crc = crc32(crc, writebuf + numfilled,
+ szwritebuf - s.avail_out - numfilled);
+ totalfilled += szwritebuf - s.avail_out - numfilled;
numfilled = szwritebuf - s.avail_out;
- crc = crc32(crc, writebuf, numfilled);
- totalfilled += numfilled;
if (numfilled < szwritebuf) {
writeblocks = (numfilled+dev->blksz-1)
/ dev->blksz;
@@ -241,14 +262,17 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev,
dev->blksz-(numfilled%dev->blksz));
} else {
writeblocks = blksperbuf;
+ numfilled = 0;
}
gzwrite_progress(iteration++,
totalfilled,
szexpected);
- blocks_written = blk_dwrite(dev, outblock,
+ if (!numfilled) {
+ blocks_written = blk_dwrite(dev, outblock,
writeblocks, writebuf);
- outblock += blocks_written;
+ outblock += blocks_written;
+ }
if (ctrlc()) {
puts("abort\n");
goto out;
@@ -258,6 +282,12 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev,
/* done when inflate() says it's done */
} while (r != Z_STREAM_END);
+ if (numfilled) {
+ blocks_written = blk_dwrite(dev, outblock,
+ writeblocks, writebuf);
+ outblock += blocks_written;
+ }
+
if ((szexpected != totalfilled) ||
(crc != expected_crc))
r = -1;
diff --git a/test/cmd/unzip.c b/test/cmd/unzip.c
index b67c5ba1956..623a2785884 100644
--- a/test/cmd/unzip.c
+++ b/test/cmd/unzip.c
@@ -105,7 +105,7 @@ static int dm_test_cmd_zip_gzwrite(struct unit_test_state *uts)
{
struct udevice *dev;
ofnode root, node;
- int i, ret;
+ int i, j, ret;
/* Enable the mmc9 node for this test */
root = oftree_root(oftree_default());
@@ -119,6 +119,16 @@ static int dm_test_cmd_zip_gzwrite(struct unit_test_state *uts)
return ret;
}
+ /* Test various sizes of decompression chunk sizes */
+ for (j = 0; j < ARRAY_SIZE(sizes); j++) {
+ env_set_ulong("gzwrite_chunk", sizes[j]);
+ for (i = 0; i < ARRAY_SIZE(sizes); i++) {
+ ret = do_test_cmd_zip_unzip(uts, sizes[i], true);
+ if (ret)
+ return ret;
+ }
+ }
+
return 0;
}
DM_TEST(dm_test_cmd_zip_gzwrite, UTF_CONSOLE);