xref: /openbmc/linux/lib/decompress_inflate.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2bc22c17eSAlain Knaff #ifdef STATIC
32d3862d2SYinghai Lu #define PREBOOT
4bc22c17eSAlain Knaff /* Pre-boot environment: included */
5bc22c17eSAlain Knaff 
6bc22c17eSAlain Knaff /* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots
7bc22c17eSAlain Knaff  * errors about console_printk etc... on ARM */
8bc22c17eSAlain Knaff #define _LINUX_KERNEL_H
9bc22c17eSAlain Knaff 
10bc22c17eSAlain Knaff #include "zlib_inflate/inftrees.c"
11bc22c17eSAlain Knaff #include "zlib_inflate/inffast.c"
12bc22c17eSAlain Knaff #include "zlib_inflate/inflate.c"
1312619610SMikhail Zaslonko #ifdef CONFIG_ZLIB_DFLTCC
1412619610SMikhail Zaslonko #include "zlib_dfltcc/dfltcc.c"
1512619610SMikhail Zaslonko #include "zlib_dfltcc/dfltcc_inflate.c"
1612619610SMikhail Zaslonko #endif
17bc22c17eSAlain Knaff 
18bc22c17eSAlain Knaff #else /* STATIC */
19bc22c17eSAlain Knaff /* initramfs et al: linked */
20bc22c17eSAlain Knaff 
21bc22c17eSAlain Knaff #include <linux/zutil.h>
22bc22c17eSAlain Knaff 
23bc22c17eSAlain Knaff #include "zlib_inflate/inftrees.h"
24bc22c17eSAlain Knaff #include "zlib_inflate/inffast.h"
25bc22c17eSAlain Knaff #include "zlib_inflate/inflate.h"
26bc22c17eSAlain Knaff 
27bc22c17eSAlain Knaff #include "zlib_inflate/infutil.h"
28af661e83SRashika Kheria #include <linux/decompress/inflate.h>
29bc22c17eSAlain Knaff 
30bc22c17eSAlain Knaff #endif /* STATIC */
31bc22c17eSAlain Knaff 
32bc22c17eSAlain Knaff #include <linux/decompress/mm.h>
33bc22c17eSAlain Knaff 
34daeb6b6fSPhillip Lougher #define GZIP_IOBUF_SIZE (16*1024)
35bc22c17eSAlain Knaff 
nofill(void * buffer,unsigned long len)36d97b07c5SYinghai Lu static long INIT nofill(void *buffer, unsigned long len)
376a881162SPhillip Lougher {
386a881162SPhillip Lougher 	return -1;
396a881162SPhillip Lougher }
406a881162SPhillip Lougher 
41bc22c17eSAlain Knaff /* Included from initramfs et al code */
__gunzip(unsigned char * buf,long len,long (* fill)(void *,unsigned long),long (* flush)(void *,unsigned long),unsigned char * out_buf,long out_len,long * pos,void (* error)(char * x))42*00444448SArnd Bergmann static int INIT __gunzip(unsigned char *buf, long len,
43d97b07c5SYinghai Lu 		       long (*fill)(void*, unsigned long),
44d97b07c5SYinghai Lu 		       long (*flush)(void*, unsigned long),
452d3862d2SYinghai Lu 		       unsigned char *out_buf, long out_len,
46d97b07c5SYinghai Lu 		       long *pos,
4793685ad2SLasse Collin 		       void(*error)(char *x)) {
48bc22c17eSAlain Knaff 	u8 *zbuf;
49bc22c17eSAlain Knaff 	struct z_stream_s *strm;
50bc22c17eSAlain Knaff 	int rc;
51bc22c17eSAlain Knaff 
52bc22c17eSAlain Knaff 	rc = -1;
53bc22c17eSAlain Knaff 	if (flush) {
545619448fSH. Peter Anvin 		out_len = 0x8000; /* 32 K */
55bc22c17eSAlain Knaff 		out_buf = malloc(out_len);
56bc22c17eSAlain Knaff 	} else {
572d3862d2SYinghai Lu 		if (!out_len)
581431574aSAlexandre Courbot 			out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
59bc22c17eSAlain Knaff 	}
60bc22c17eSAlain Knaff 	if (!out_buf) {
61bc22c17eSAlain Knaff 		error("Out of memory while allocating output buffer");
62bc22c17eSAlain Knaff 		goto gunzip_nomem1;
63bc22c17eSAlain Knaff 	}
64bc22c17eSAlain Knaff 
65bc22c17eSAlain Knaff 	if (buf)
66bc22c17eSAlain Knaff 		zbuf = buf;
67bc22c17eSAlain Knaff 	else {
68daeb6b6fSPhillip Lougher 		zbuf = malloc(GZIP_IOBUF_SIZE);
69bc22c17eSAlain Knaff 		len = 0;
70bc22c17eSAlain Knaff 	}
71bc22c17eSAlain Knaff 	if (!zbuf) {
72bc22c17eSAlain Knaff 		error("Out of memory while allocating input buffer");
73bc22c17eSAlain Knaff 		goto gunzip_nomem2;
74bc22c17eSAlain Knaff 	}
75bc22c17eSAlain Knaff 
76bc22c17eSAlain Knaff 	strm = malloc(sizeof(*strm));
77bc22c17eSAlain Knaff 	if (strm == NULL) {
78bc22c17eSAlain Knaff 		error("Out of memory while allocating z_stream");
79bc22c17eSAlain Knaff 		goto gunzip_nomem3;
80bc22c17eSAlain Knaff 	}
81bc22c17eSAlain Knaff 
82bc22c17eSAlain Knaff 	strm->workspace = malloc(flush ? zlib_inflate_workspacesize() :
8312619610SMikhail Zaslonko #ifdef CONFIG_ZLIB_DFLTCC
8412619610SMikhail Zaslonko 	/* Always allocate the full workspace for DFLTCC */
8512619610SMikhail Zaslonko 				 zlib_inflate_workspacesize());
8612619610SMikhail Zaslonko #else
87bc22c17eSAlain Knaff 				 sizeof(struct inflate_state));
8812619610SMikhail Zaslonko #endif
89bc22c17eSAlain Knaff 	if (strm->workspace == NULL) {
90bc22c17eSAlain Knaff 		error("Out of memory while allocating workspace");
91bc22c17eSAlain Knaff 		goto gunzip_nomem4;
92bc22c17eSAlain Knaff 	}
93bc22c17eSAlain Knaff 
946a881162SPhillip Lougher 	if (!fill)
956a881162SPhillip Lougher 		fill = nofill;
966a881162SPhillip Lougher 
97bc22c17eSAlain Knaff 	if (len == 0)
98daeb6b6fSPhillip Lougher 		len = fill(zbuf, GZIP_IOBUF_SIZE);
99bc22c17eSAlain Knaff 
100bc22c17eSAlain Knaff 	/* verify the gzip header */
101bc22c17eSAlain Knaff 	if (len < 10 ||
102bc22c17eSAlain Knaff 	   zbuf[0] != 0x1f || zbuf[1] != 0x8b || zbuf[2] != 0x08) {
103bc22c17eSAlain Knaff 		if (pos)
104bc22c17eSAlain Knaff 			*pos = 0;
105bc22c17eSAlain Knaff 		error("Not a gzip file");
106bc22c17eSAlain Knaff 		goto gunzip_5;
107bc22c17eSAlain Knaff 	}
108bc22c17eSAlain Knaff 
109bc22c17eSAlain Knaff 	/* skip over gzip header (1f,8b,08... 10 bytes total +
110bc22c17eSAlain Knaff 	 * possible asciz filename)
111bc22c17eSAlain Knaff 	 */
112bc22c17eSAlain Knaff 	strm->next_in = zbuf + 10;
1131da914e0SLasse Collin 	strm->avail_in = len - 10;
114bc22c17eSAlain Knaff 	/* skip over asciz filename */
115bc22c17eSAlain Knaff 	if (zbuf[3] & 0x8) {
1161da914e0SLasse Collin 		do {
1171da914e0SLasse Collin 			/*
1181da914e0SLasse Collin 			 * If the filename doesn't fit into the buffer,
1191da914e0SLasse Collin 			 * the file is very probably corrupt. Don't try
1201da914e0SLasse Collin 			 * to read more data.
1211da914e0SLasse Collin 			 */
1221da914e0SLasse Collin 			if (strm->avail_in == 0) {
1231da914e0SLasse Collin 				error("header error");
1241da914e0SLasse Collin 				goto gunzip_5;
125bc22c17eSAlain Knaff 			}
1261da914e0SLasse Collin 			--strm->avail_in;
1271da914e0SLasse Collin 		} while (*strm->next_in++);
1281da914e0SLasse Collin 	}
129bc22c17eSAlain Knaff 
130bc22c17eSAlain Knaff 	strm->next_out = out_buf;
131bc22c17eSAlain Knaff 	strm->avail_out = out_len;
132bc22c17eSAlain Knaff 
133bc22c17eSAlain Knaff 	rc = zlib_inflateInit2(strm, -MAX_WBITS);
134bc22c17eSAlain Knaff 
13512619610SMikhail Zaslonko #ifdef CONFIG_ZLIB_DFLTCC
13612619610SMikhail Zaslonko 	/* Always keep the window for DFLTCC */
13712619610SMikhail Zaslonko #else
138bc22c17eSAlain Knaff 	if (!flush) {
139bc22c17eSAlain Knaff 		WS(strm)->inflate_state.wsize = 0;
140bc22c17eSAlain Knaff 		WS(strm)->inflate_state.window = NULL;
141bc22c17eSAlain Knaff 	}
14212619610SMikhail Zaslonko #endif
143bc22c17eSAlain Knaff 
144bc22c17eSAlain Knaff 	while (rc == Z_OK) {
145bc22c17eSAlain Knaff 		if (strm->avail_in == 0) {
146bc22c17eSAlain Knaff 			/* TODO: handle case where both pos and fill are set */
147daeb6b6fSPhillip Lougher 			len = fill(zbuf, GZIP_IOBUF_SIZE);
148bc22c17eSAlain Knaff 			if (len < 0) {
149bc22c17eSAlain Knaff 				rc = -1;
150bc22c17eSAlain Knaff 				error("read error");
151bc22c17eSAlain Knaff 				break;
152bc22c17eSAlain Knaff 			}
153bc22c17eSAlain Knaff 			strm->next_in = zbuf;
154bc22c17eSAlain Knaff 			strm->avail_in = len;
155bc22c17eSAlain Knaff 		}
156bc22c17eSAlain Knaff 		rc = zlib_inflate(strm, 0);
157bc22c17eSAlain Knaff 
158bc22c17eSAlain Knaff 		/* Write any data generated */
159bc22c17eSAlain Knaff 		if (flush && strm->next_out > out_buf) {
160d97b07c5SYinghai Lu 			long l = strm->next_out - out_buf;
161bc22c17eSAlain Knaff 			if (l != flush(out_buf, l)) {
162bc22c17eSAlain Knaff 				rc = -1;
163bc22c17eSAlain Knaff 				error("write error");
164bc22c17eSAlain Knaff 				break;
165bc22c17eSAlain Knaff 			}
166bc22c17eSAlain Knaff 			strm->next_out = out_buf;
167bc22c17eSAlain Knaff 			strm->avail_out = out_len;
168bc22c17eSAlain Knaff 		}
169bc22c17eSAlain Knaff 
170bc22c17eSAlain Knaff 		/* after Z_FINISH, only Z_STREAM_END is "we unpacked it all" */
171bc22c17eSAlain Knaff 		if (rc == Z_STREAM_END) {
172bc22c17eSAlain Knaff 			rc = 0;
173bc22c17eSAlain Knaff 			break;
174bc22c17eSAlain Knaff 		} else if (rc != Z_OK) {
175bc22c17eSAlain Knaff 			error("uncompression error");
176bc22c17eSAlain Knaff 			rc = -1;
177bc22c17eSAlain Knaff 		}
178bc22c17eSAlain Knaff 	}
179bc22c17eSAlain Knaff 
180bc22c17eSAlain Knaff 	zlib_inflateEnd(strm);
181bc22c17eSAlain Knaff 	if (pos)
182bc22c17eSAlain Knaff 		/* add + 8 to skip over trailer */
183bc22c17eSAlain Knaff 		*pos = strm->next_in - zbuf+8;
184bc22c17eSAlain Knaff 
185bc22c17eSAlain Knaff gunzip_5:
186bc22c17eSAlain Knaff 	free(strm->workspace);
187bc22c17eSAlain Knaff gunzip_nomem4:
188bc22c17eSAlain Knaff 	free(strm);
189bc22c17eSAlain Knaff gunzip_nomem3:
190bc22c17eSAlain Knaff 	if (!buf)
191bc22c17eSAlain Knaff 		free(zbuf);
192bc22c17eSAlain Knaff gunzip_nomem2:
193bc22c17eSAlain Knaff 	if (flush)
194bc22c17eSAlain Knaff 		free(out_buf);
195bc22c17eSAlain Knaff gunzip_nomem1:
196bc22c17eSAlain Knaff 	return rc; /* returns Z_OK (0) if successful */
197bc22c17eSAlain Knaff }
198bc22c17eSAlain Knaff 
1992d3862d2SYinghai Lu #ifndef PREBOOT
gunzip(unsigned char * buf,long len,long (* fill)(void *,unsigned long),long (* flush)(void *,unsigned long),unsigned char * out_buf,long * pos,void (* error)(char * x))2002d3862d2SYinghai Lu STATIC int INIT gunzip(unsigned char *buf, long len,
2012d3862d2SYinghai Lu 		       long (*fill)(void*, unsigned long),
2022d3862d2SYinghai Lu 		       long (*flush)(void*, unsigned long),
2032d3862d2SYinghai Lu 		       unsigned char *out_buf,
2042d3862d2SYinghai Lu 		       long *pos,
2052d3862d2SYinghai Lu 		       void (*error)(char *x))
2062d3862d2SYinghai Lu {
2072d3862d2SYinghai Lu 	return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error);
2082d3862d2SYinghai Lu }
2092d3862d2SYinghai Lu #else
__decompress(unsigned char * buf,long len,long (* fill)(void *,unsigned long),long (* flush)(void *,unsigned long),unsigned char * out_buf,long out_len,long * pos,void (* error)(char * x))2102d3862d2SYinghai Lu STATIC int INIT __decompress(unsigned char *buf, long len,
2112d3862d2SYinghai Lu 			   long (*fill)(void*, unsigned long),
2122d3862d2SYinghai Lu 			   long (*flush)(void*, unsigned long),
2132d3862d2SYinghai Lu 			   unsigned char *out_buf, long out_len,
2142d3862d2SYinghai Lu 			   long *pos,
2152d3862d2SYinghai Lu 			   void (*error)(char *x))
2162d3862d2SYinghai Lu {
2172d3862d2SYinghai Lu 	return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error);
2182d3862d2SYinghai Lu }
2192d3862d2SYinghai Lu #endif
220