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