xref: /openbmc/linux/lib/decompress_inflate.c (revision d2574c33)
1 // SPDX-License-Identifier: GPL-2.0
2 #ifdef STATIC
3 #define PREBOOT
4 /* Pre-boot environment: included */
5 
6 /* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots
7  * errors about console_printk etc... on ARM */
8 #define _LINUX_KERNEL_H
9 
10 #include "zlib_inflate/inftrees.c"
11 #include "zlib_inflate/inffast.c"
12 #include "zlib_inflate/inflate.c"
13 
14 #else /* STATIC */
15 /* initramfs et al: linked */
16 
17 #include <linux/zutil.h>
18 
19 #include "zlib_inflate/inftrees.h"
20 #include "zlib_inflate/inffast.h"
21 #include "zlib_inflate/inflate.h"
22 
23 #include "zlib_inflate/infutil.h"
24 #include <linux/decompress/inflate.h>
25 
26 #endif /* STATIC */
27 
28 #include <linux/decompress/mm.h>
29 
30 #define GZIP_IOBUF_SIZE (16*1024)
31 
32 static long INIT nofill(void *buffer, unsigned long len)
33 {
34 	return -1;
35 }
36 
37 /* Included from initramfs et al code */
38 STATIC int INIT __gunzip(unsigned char *buf, long len,
39 		       long (*fill)(void*, unsigned long),
40 		       long (*flush)(void*, unsigned long),
41 		       unsigned char *out_buf, long out_len,
42 		       long *pos,
43 		       void(*error)(char *x)) {
44 	u8 *zbuf;
45 	struct z_stream_s *strm;
46 	int rc;
47 
48 	rc = -1;
49 	if (flush) {
50 		out_len = 0x8000; /* 32 K */
51 		out_buf = malloc(out_len);
52 	} else {
53 		if (!out_len)
54 			out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
55 	}
56 	if (!out_buf) {
57 		error("Out of memory while allocating output buffer");
58 		goto gunzip_nomem1;
59 	}
60 
61 	if (buf)
62 		zbuf = buf;
63 	else {
64 		zbuf = malloc(GZIP_IOBUF_SIZE);
65 		len = 0;
66 	}
67 	if (!zbuf) {
68 		error("Out of memory while allocating input buffer");
69 		goto gunzip_nomem2;
70 	}
71 
72 	strm = malloc(sizeof(*strm));
73 	if (strm == NULL) {
74 		error("Out of memory while allocating z_stream");
75 		goto gunzip_nomem3;
76 	}
77 
78 	strm->workspace = malloc(flush ? zlib_inflate_workspacesize() :
79 				 sizeof(struct inflate_state));
80 	if (strm->workspace == NULL) {
81 		error("Out of memory while allocating workspace");
82 		goto gunzip_nomem4;
83 	}
84 
85 	if (!fill)
86 		fill = nofill;
87 
88 	if (len == 0)
89 		len = fill(zbuf, GZIP_IOBUF_SIZE);
90 
91 	/* verify the gzip header */
92 	if (len < 10 ||
93 	   zbuf[0] != 0x1f || zbuf[1] != 0x8b || zbuf[2] != 0x08) {
94 		if (pos)
95 			*pos = 0;
96 		error("Not a gzip file");
97 		goto gunzip_5;
98 	}
99 
100 	/* skip over gzip header (1f,8b,08... 10 bytes total +
101 	 * possible asciz filename)
102 	 */
103 	strm->next_in = zbuf + 10;
104 	strm->avail_in = len - 10;
105 	/* skip over asciz filename */
106 	if (zbuf[3] & 0x8) {
107 		do {
108 			/*
109 			 * If the filename doesn't fit into the buffer,
110 			 * the file is very probably corrupt. Don't try
111 			 * to read more data.
112 			 */
113 			if (strm->avail_in == 0) {
114 				error("header error");
115 				goto gunzip_5;
116 			}
117 			--strm->avail_in;
118 		} while (*strm->next_in++);
119 	}
120 
121 	strm->next_out = out_buf;
122 	strm->avail_out = out_len;
123 
124 	rc = zlib_inflateInit2(strm, -MAX_WBITS);
125 
126 	if (!flush) {
127 		WS(strm)->inflate_state.wsize = 0;
128 		WS(strm)->inflate_state.window = NULL;
129 	}
130 
131 	while (rc == Z_OK) {
132 		if (strm->avail_in == 0) {
133 			/* TODO: handle case where both pos and fill are set */
134 			len = fill(zbuf, GZIP_IOBUF_SIZE);
135 			if (len < 0) {
136 				rc = -1;
137 				error("read error");
138 				break;
139 			}
140 			strm->next_in = zbuf;
141 			strm->avail_in = len;
142 		}
143 		rc = zlib_inflate(strm, 0);
144 
145 		/* Write any data generated */
146 		if (flush && strm->next_out > out_buf) {
147 			long l = strm->next_out - out_buf;
148 			if (l != flush(out_buf, l)) {
149 				rc = -1;
150 				error("write error");
151 				break;
152 			}
153 			strm->next_out = out_buf;
154 			strm->avail_out = out_len;
155 		}
156 
157 		/* after Z_FINISH, only Z_STREAM_END is "we unpacked it all" */
158 		if (rc == Z_STREAM_END) {
159 			rc = 0;
160 			break;
161 		} else if (rc != Z_OK) {
162 			error("uncompression error");
163 			rc = -1;
164 		}
165 	}
166 
167 	zlib_inflateEnd(strm);
168 	if (pos)
169 		/* add + 8 to skip over trailer */
170 		*pos = strm->next_in - zbuf+8;
171 
172 gunzip_5:
173 	free(strm->workspace);
174 gunzip_nomem4:
175 	free(strm);
176 gunzip_nomem3:
177 	if (!buf)
178 		free(zbuf);
179 gunzip_nomem2:
180 	if (flush)
181 		free(out_buf);
182 gunzip_nomem1:
183 	return rc; /* returns Z_OK (0) if successful */
184 }
185 
186 #ifndef PREBOOT
187 STATIC int INIT gunzip(unsigned char *buf, long len,
188 		       long (*fill)(void*, unsigned long),
189 		       long (*flush)(void*, unsigned long),
190 		       unsigned char *out_buf,
191 		       long *pos,
192 		       void (*error)(char *x))
193 {
194 	return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error);
195 }
196 #else
197 STATIC int INIT __decompress(unsigned char *buf, long len,
198 			   long (*fill)(void*, unsigned long),
199 			   long (*flush)(void*, unsigned long),
200 			   unsigned char *out_buf, long out_len,
201 			   long *pos,
202 			   void (*error)(char *x))
203 {
204 	return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error);
205 }
206 #endif
207