dmi: check both the AC and ID flags at the same time
[syslinux.git] / memdisk / unzip.c
blob18a1df42150801617b75408eb318396fd8c13d93
1 /*
2 * unzip.c
4 * This is a collection of several routines from gzip-1.0.3
5 * adapted for Linux.
7 * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
8 * puts by Nick Holloway 1993, better puts by Martin Mares 1995
9 * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
11 * Adapted for MEMDISK by H. Peter Anvin, April 2003
14 #include <stdint.h>
15 #include "memdisk.h"
16 #include "conio.h"
18 #undef DEBUG /* Means something different for this file */
21 * gzip declarations
24 #define OF(args) args
25 #define STATIC static
27 #define memzero(s, n) memset ((s), 0, (n))
29 typedef uint8_t uch;
30 typedef uint16_t ush;
31 typedef uint32_t ulg;
33 #define WSIZE 0x8000 /* Window size must be at least 32k, */
34 /* and a power of two */
36 static uch *inbuf; /* input pointer */
37 static uch window[WSIZE]; /* sliding output window buffer */
39 static unsigned insize; /* total input bytes read */
40 static unsigned inbytes; /* valid bytes in inbuf */
41 static unsigned outcnt; /* bytes in output buffer */
43 /* gzip flag byte */
44 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
45 #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
46 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
47 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
48 #define COMMENT 0x10 /* bit 4 set: file comment present */
49 #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
50 #define RESERVED 0xC0 /* bit 6,7: reserved */
52 /* Diagnostic functions */
53 #ifdef DEBUG
54 # define Assert(cond,msg) {if(!(cond)) error(msg);}
55 # define Trace(x) fprintf x
56 # define Tracev(x) {if (verbose) fprintf x ;}
57 # define Tracevv(x) {if (verbose>1) fprintf x ;}
58 # define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
59 # define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
60 #else
61 # define Assert(cond,msg)
62 # define Trace(x)
63 # define Tracev(x)
64 # define Tracevv(x)
65 # define Tracec(c,x)
66 # define Tracecv(c,x)
67 #endif
69 static int fill_inbuf(void);
70 static void flush_window(void);
71 static void error(char *m);
72 static void gzip_mark(void **);
73 static void gzip_release(void **);
75 static ulg crc_32_tab[256];
77 /* Get byte from input buffer */
78 static inline uch get_byte(void)
80 if (inbytes) {
81 uch b = *inbuf++;
82 inbytes--;
83 return b;
84 } else {
85 return fill_inbuf(); /* Input buffer underrun */
89 /* Unget byte from input buffer */
90 static inline void unget_byte(void)
92 inbytes++;
93 inbuf--;
96 static ulg bytes_out = 0; /* Number of bytes output */
97 static uch *output_data; /* Output data pointer */
98 static ulg output_size; /* Number of output bytes expected */
100 static void *malloc(int size);
101 static void free(void *where);
103 static ulg free_mem_ptr, free_mem_end_ptr;
105 #include "inflate.c"
107 static void *malloc(int size)
109 void *p;
111 if (size < 0)
112 error("malloc error");
114 free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
116 p = (void *)free_mem_ptr;
117 free_mem_ptr += size;
119 if (free_mem_ptr >= free_mem_end_ptr)
120 error("out of memory");
122 return p;
125 static void free(void *where)
127 /* Don't care */
128 (void)where;
131 static void gzip_mark(void **ptr)
133 *ptr = (void *)free_mem_ptr;
136 static void gzip_release(void **ptr)
138 free_mem_ptr = (long)*ptr;
141 /* ===========================================================================
142 * Fill the input buffer. This is called only when the buffer is empty
143 * and at least one byte is really needed.
145 static int fill_inbuf(void)
147 /* This should never happen. We have already pointed the algorithm
148 to all the data we have. */
149 die("failed\nDecompression error: ran out of input data\n");
152 /* ===========================================================================
153 * Write the output window window[0..outcnt-1] and update crc and bytes_out.
154 * (Used for the decompressed data only.)
156 static void flush_window(void)
158 ulg c = crc; /* temporary variable */
159 unsigned n;
160 uch *in, *out, ch;
162 if (bytes_out + outcnt > output_size)
163 error("output buffer overrun");
165 in = window;
166 out = output_data;
167 for (n = 0; n < outcnt; n++) {
168 ch = *out++ = *in++;
169 c = crc_32_tab[(c ^ ch) & 0xff] ^ (c >> 8);
171 crc = c;
172 output_data = out;
173 bytes_out += (ulg) outcnt;
174 outcnt = 0;
177 static void error(char *x)
179 die("failed\nDecompression error: %s\n", x);
182 /* GZIP header */
183 struct gzip_header {
184 uint16_t magic;
185 uint8_t method;
186 uint8_t flags;
187 uint32_t timestamp;
188 uint8_t extra_flags;
189 uint8_t os_type;
190 } __attribute__ ((packed));
191 /* (followed by optional and variable length "extra", "original name",
192 and "comment" fields) */
194 struct gzip_trailer {
195 uint32_t crc;
196 uint32_t dbytes;
197 } __attribute__ ((packed));
199 /* PKZIP header. See
200 * <http://www.pkware.com/products/enterprise/white_papers/appnote.html>.
202 struct pkzip_header {
203 uint32_t magic;
204 uint16_t version;
205 uint16_t flags;
206 uint16_t method;
207 uint16_t modified_time;
208 uint16_t modified_date;
209 uint32_t crc;
210 uint32_t zbytes;
211 uint32_t dbytes;
212 uint16_t filename_len;
213 uint16_t extra_len;
214 } __attribute__ ((packed));
215 /* (followed by optional and variable length "filename" and "extra"
216 fields) */
218 /* gzip flag byte */
219 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
220 #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
221 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
222 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
223 #define COMMENT 0x10 /* bit 4 set: file comment present */
224 #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
225 #define RESERVED 0xC0 /* bit 6,7: reserved */
227 /* pkzip flag byte */
228 #define PK_ENCRYPTED 0x01 /* bit 0 set: file is encrypted */
229 #define PK_DATADESC 0x08 /* bit 3 set: file has trailing "data
230 descriptor" */
231 #define PK_UNSUPPORTED 0xFFF0 /* All other bits must be zero */
233 /* Return 0 if (indata, size) points to a ZIP file, and fill in
234 compressed data size, uncompressed data size, CRC, and offset of
235 data.
237 If indata is not a ZIP file, return -1. */
238 int check_zip(void *indata, uint32_t size, uint32_t * zbytes_p,
239 uint32_t * dbytes_p, uint32_t * orig_crc, uint32_t * offset_p)
241 struct gzip_header *gzh = (struct gzip_header *)indata;
242 struct pkzip_header *pkzh = (struct pkzip_header *)indata;
243 uint32_t offset;
245 if (gzh->magic == 0x8b1f) {
246 struct gzip_trailer *gzt = indata + size - sizeof(struct gzip_trailer);
247 /* We only support method #8, DEFLATED */
248 if (gzh->method != 8) {
249 error("gzip file uses invalid method");
250 return -1;
252 if (gzh->flags & ENCRYPTED) {
253 error("gzip file is encrypted; not supported");
254 return -1;
256 if (gzh->flags & CONTINUATION) {
257 error("gzip file is a continuation file; not supported");
258 return -1;
260 if (gzh->flags & RESERVED) {
261 error("gzip file has unsupported flags");
262 return -1;
264 offset = sizeof(*gzh);
265 if (gzh->flags & EXTRA_FIELD) {
266 /* Skip extra field */
267 unsigned len = *(unsigned *)(indata + offset);
268 offset += 2 + len;
270 if (gzh->flags & ORIG_NAME) {
271 /* Discard the old name */
272 uint8_t *p = indata;
273 while (p[offset] != 0 && offset < size) {
274 offset++;
276 offset++;
279 if (gzh->flags & COMMENT) {
280 /* Discard the comment */
281 uint8_t *p = indata;
282 while (p[offset] != 0 && offset < size) {
283 offset++;
285 offset++;
288 if (offset > size) {
289 error("gzip file corrupt");
290 return -1;
292 *zbytes_p = size - offset - sizeof(struct gzip_trailer);
293 *dbytes_p = gzt->dbytes;
294 *orig_crc = gzt->crc;
295 *offset_p = offset;
296 return 0;
297 } else if (pkzh->magic == 0x04034b50UL) {
298 /* Magic number matches pkzip file. */
300 offset = sizeof(*pkzh);
301 if (pkzh->flags & PK_ENCRYPTED) {
302 error("pkzip file is encrypted; not supported");
303 return -1;
305 if (pkzh->flags & PK_DATADESC) {
306 error("pkzip file uses data_descriptor field; not supported");
307 return -1;
309 if (pkzh->flags & PK_UNSUPPORTED) {
310 error("pkzip file has unsupported flags");
311 return -1;
314 /* We only support method #8, DEFLATED */
315 if (pkzh->method != 8) {
316 error("pkzip file uses invalid method");
317 return -1;
319 /* skip header */
320 offset = sizeof(*pkzh);
321 /* skip filename */
322 offset += pkzh->filename_len;
323 /* skip extra field */
324 offset += pkzh->extra_len;
326 if (offset + pkzh->zbytes > size) {
327 error("pkzip file corrupt");
328 return -1;
331 *zbytes_p = pkzh->zbytes;
332 *dbytes_p = pkzh->dbytes;
333 *orig_crc = pkzh->crc;
334 *offset_p = offset;
335 return 0;
336 } else {
337 /* Magic number does not match. */
338 return -1;
341 error("Internal error in check_zip");
342 return -1;
346 * Decompress the image, trying to flush the end of it as close
347 * to end_mem as possible. Return a pointer to the data block,
348 * and change datalen.
350 extern void _end;
352 static char heap[65536];
354 void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes,
355 uint32_t orig_crc, void *target)
357 /* Set up the heap; it is simply a chunk of bss memory */
358 free_mem_ptr = (size_t)heap;
359 free_mem_end_ptr = (size_t)heap + sizeof heap;
361 /* Set up input buffer */
362 inbuf = indata;
363 /* Sometimes inflate() looks beyond the end of the compressed data,
364 but it always backs up before it is done. So we give it 4 bytes
365 of slack. */
366 insize = inbytes = zbytes + 4;
368 /* Set up output buffer */
369 outcnt = 0;
370 output_data = target;
371 output_size = dbytes;
372 bytes_out = 0;
374 makecrc();
375 gunzip();
377 /* Verify that gunzip() consumed the entire input. */
378 if (inbytes != 4)
379 error("compressed data length error");
381 /* Check the uncompressed data length and CRC. */
382 if (bytes_out != dbytes)
383 error("uncompressed data length error");
385 if (orig_crc != CRC_VALUE)
386 error("crc error");
388 puts("ok\n");
390 return target;