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