add a hack for Decap Attack
[picodrive.git] / unzip / unzip.c
CommitLineData
cc68a136 1#include "unzip.h"\r
2\r
3#include <stdlib.h>\r
4#include <string.h>\r
5#include <ctype.h>\r
6#include <assert.h>\r
7\r
325ee167 8#include <zlib.h>\r
cc68a136 9\r
10/* public globals */\r
11//int gUnzipQuiet = 0; /* flag controls error messages */\r
12\r
13#define ERROR_CORRUPT "The zipfile seems to be corrupt, please check it"\r
14#define ERROR_FILESYSTEM "Your filesystem seems to be corrupt, please check it"\r
15#define ERROR_UNSUPPORTED "The format of this zipfile is not supported, please recompress it"\r
16\r
17#define INFLATE_INPUT_BUFFER_MAX 16384\r
18#ifndef MIN\r
19#define MIN(x,y) ((x)<(y)?(x):(y))\r
20#endif\r
21\r
22\r
23// notaz\r
83bd0b76 24#if 1 //def __DEBUG_PRINT\r
25#define logerror printf\r
26#define errormsg(str1,def,fname) printf("%s: " #def ": " str1 "\n", fname);\r
cc68a136 27#else\r
28#define logerror(x...)\r
29#define errormsg(x...)\r
30#endif\r
31\r
32/* Print a error message */\r
33//void errormsg(const char* extmsg, const char* usermsg, const char* zipname) {\r
34 /* Output to the user with no internal detail */\r
35// if (!gUnzipQuiet)\r
36// printf("Error in zipfile %s\n%s\n", zipname, usermsg);\r
37 /* Output to log file with all informations */\r
38// logerror("Error in zipfile %s: %s\n", zipname, extmsg);\r
39// printf("Error in zipfile %s: %s\n", zipname, extmsg);\r
40//}\r
41\r
42/* -------------------------------------------------------------------------\r
43 Unzip support\r
44 ------------------------------------------------------------------------- */\r
45\r
46/* Use these to avoid structure padding and byte-ordering problems */\r
47static UINT16 read_word (char *buf) {\r
48 unsigned char *ubuf = (unsigned char *) buf;\r
49\r
50 return ((UINT16)ubuf[1] << 8) | (UINT16)ubuf[0];\r
51}\r
52\r
53/* Use these to avoid structure padding and byte-ordering problems */\r
54static UINT32 read_dword (char *buf) {\r
55 unsigned char *ubuf = (unsigned char *) buf;\r
56\r
57 return ((UINT32)ubuf[3] << 24) | ((UINT32)ubuf[2] << 16) | ((UINT32)ubuf[1] << 8) | (UINT32)ubuf[0];\r
58}\r
59\r
60/* Locate end-of-central-dir sig in buffer and return offset\r
61 out:\r
62 *offset offset of cent dir start in buffer\r
63 return:\r
64 ==0 not found\r
65 !=0 found, *offset valid\r
66*/\r
67static int ecd_find_sig (char *buffer, int buflen, int *offset)\r
68{\r
69 static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };\r
70 int i;\r
71 for (i=buflen-22; i>=0; i--) {\r
72 if (memcmp(buffer+i, ecdsig, 4) == 0) {\r
73 *offset = i;\r
74 return 1;\r
75 }\r
76 }\r
77 return 0;\r
78}\r
79\r
80/* Read ecd data in zip structure\r
81 in:\r
82 zip->fp, zip->length zip file\r
83 out:\r
84 zip->ecd, zip->ecd_length ecd data\r
85*/\r
86static int ecd_read(ZIP* zip) {\r
87 char* buf;\r
88 int buf_length = 1024; /* initial buffer length */\r
89\r
90 while (1) {\r
91 int offset;\r
92\r
93 if (buf_length > zip->length)\r
94 buf_length = zip->length;\r
95\r
96 if (fseek(zip->fp, zip->length - buf_length, SEEK_SET) != 0) {\r
97 return -1;\r
98 }\r
99\r
100 /* allocate buffer */\r
101 buf = (char*)malloc( buf_length );\r
102 if (!buf) {\r
103 return -1;\r
104 }\r
105\r
106 if (fread( buf, buf_length, 1, zip->fp ) != 1) {\r
107 free(buf);\r
108 return -1;\r
109 }\r
110\r
111 if (ecd_find_sig(buf, buf_length, &offset)) {\r
112 zip->ecd_length = buf_length - offset;\r
113\r
114 zip->ecd = (char*)malloc( zip->ecd_length );\r
115 if (!zip->ecd) {\r
116 free(buf);\r
117 return -1;\r
118 }\r
119\r
120 memcpy(zip->ecd, buf + offset, zip->ecd_length);\r
121\r
122 free(buf);\r
123 return 0;\r
124 }\r
125\r
126 free(buf);\r
127\r
128 if (buf_length < zip->length) {\r
129 /* double buffer */\r
130 buf_length = 2*buf_length;\r
131\r
132 logerror("Retry reading of zip ecd for %d bytes\n",buf_length);\r
133\r
134 } else {\r
135 return -1;\r
136 }\r
137 }\r
138}\r
139\r
140/* offsets in end of central directory structure */\r
141#define ZIPESIG 0x00\r
142#define ZIPEDSK 0x04\r
143#define ZIPECEN 0x06\r
144#define ZIPENUM 0x08\r
145#define ZIPECENN 0x0a\r
146#define ZIPECSZ 0x0c\r
147#define ZIPEOFST 0x10\r
148#define ZIPECOML 0x14\r
149#define ZIPECOM 0x16\r
150\r
151/* offsets in central directory entry structure */\r
152#define ZIPCENSIG 0x0\r
153#define ZIPCVER 0x4\r
154#define ZIPCOS 0x5\r
155#define ZIPCVXT 0x6\r
156#define ZIPCEXOS 0x7\r
157#define ZIPCFLG 0x8\r
158#define ZIPCMTHD 0xa\r
159#define ZIPCTIM 0xc\r
160#define ZIPCDAT 0xe\r
161#define ZIPCCRC 0x10\r
162#define ZIPCSIZ 0x14\r
163#define ZIPCUNC 0x18\r
164#define ZIPCFNL 0x1c\r
165#define ZIPCXTL 0x1e\r
166#define ZIPCCML 0x20\r
167#define ZIPDSK 0x22\r
168#define ZIPINT 0x24\r
169#define ZIPEXT 0x26\r
170#define ZIPOFST 0x2a\r
171#define ZIPCFN 0x2e\r
172\r
173/* offsets in local file header structure */\r
174#define ZIPLOCSIG 0x00\r
175#define ZIPVER 0x04\r
176#define ZIPGENFLG 0x06\r
177#define ZIPMTHD 0x08\r
178#define ZIPTIME 0x0a\r
179#define ZIPDATE 0x0c\r
180#define ZIPCRC 0x0e\r
181#define ZIPSIZE 0x12\r
182#define ZIPUNCMP 0x16\r
183#define ZIPFNLN 0x1a\r
184#define ZIPXTRALN 0x1c\r
185#define ZIPNAME 0x1e\r
186\r
187/* Opens a zip stream for reading\r
188 return:\r
189 !=0 success, zip stream\r
190 ==0 error\r
191*/\r
192ZIP* openzip(const char* zipfile) {\r
193 /* allocate */\r
194 ZIP* zip = (ZIP*)malloc( sizeof(ZIP) );\r
195 if (!zip) {\r
196 return 0;\r
197 }\r
198\r
199 /* open */\r
200 zip->fp = fopen(zipfile, "rb");\r
201 if (!zip->fp) {\r
202 errormsg ("Opening for reading", ERROR_FILESYSTEM, zipfile);\r
203 free(zip);\r
204 return 0;\r
205 }\r
206\r
207 /* go to end */\r
208 if (fseek(zip->fp, 0L, SEEK_END) != 0) {\r
209 errormsg ("Seeking to end", ERROR_FILESYSTEM, zipfile);\r
210 fclose(zip->fp);\r
211 free(zip);\r
212 return 0;\r
213 }\r
214\r
215 /* get length */\r
216 zip->length = ftell(zip->fp);\r
217 if (zip->length < 0) {\r
218 errormsg ("Get file size", ERROR_FILESYSTEM, zipfile);\r
219 fclose(zip->fp);\r
220 free(zip);\r
221 return 0;\r
222 }\r
223 if (zip->length == 0) {\r
224 errormsg ("Empty file", ERROR_CORRUPT, zipfile);\r
225 fclose(zip->fp);\r
226 free(zip);\r
227 return 0;\r
228 }\r
229\r
230 /* read ecd data */\r
231 if (ecd_read(zip)!=0) {\r
232 errormsg ("Reading ECD (end of central directory)", ERROR_CORRUPT, zipfile);\r
233 fclose(zip->fp);\r
234 free(zip);\r
235 return 0;\r
236 }\r
237\r
238 /* compile ecd info */\r
239 zip->end_of_cent_dir_sig = read_dword (zip->ecd+ZIPESIG);\r
240 zip->number_of_this_disk = read_word (zip->ecd+ZIPEDSK);\r
241 zip->number_of_disk_start_cent_dir = read_word (zip->ecd+ZIPECEN);\r
242 zip->total_entries_cent_dir_this_disk = read_word (zip->ecd+ZIPENUM);\r
243 zip->total_entries_cent_dir = read_word (zip->ecd+ZIPECENN);\r
244 zip->size_of_cent_dir = read_dword (zip->ecd+ZIPECSZ);\r
245 zip->offset_to_start_of_cent_dir = read_dword (zip->ecd+ZIPEOFST);\r
246 zip->zipfile_comment_length = read_word (zip->ecd+ZIPECOML);\r
247 zip->zipfile_comment = zip->ecd+ZIPECOM;\r
248\r
249 /* verify that we can work with this zipfile (no disk spanning allowed) */\r
250 if ((zip->number_of_this_disk != zip->number_of_disk_start_cent_dir) ||\r
251 (zip->total_entries_cent_dir_this_disk != zip->total_entries_cent_dir) ||\r
252 (zip->total_entries_cent_dir < 1)) {\r
253 errormsg("Cannot span disks", ERROR_UNSUPPORTED, zipfile);\r
254 free(zip->ecd);\r
255 fclose(zip->fp);\r
256 free(zip);\r
257 return 0;\r
258 }\r
259\r
260 if (fseek(zip->fp, zip->offset_to_start_of_cent_dir, SEEK_SET)!=0) {\r
261 errormsg ("Seeking to central directory", ERROR_CORRUPT, zipfile);\r
262 free(zip->ecd);\r
263 fclose(zip->fp);\r
264 free(zip);\r
265 return 0;\r
266 }\r
267\r
268 /* read from start of central directory */\r
269 zip->cd = (char*)malloc( zip->size_of_cent_dir );\r
270 if (!zip->cd) {\r
271 free(zip->ecd);\r
272 fclose(zip->fp);\r
273 free(zip);\r
274 return 0;\r
275 }\r
276\r
277 if (fread(zip->cd, zip->size_of_cent_dir, 1, zip->fp)!=1) {\r
278 errormsg ("Reading central directory", ERROR_CORRUPT, zipfile);\r
279 free(zip->cd);\r
280 free(zip->ecd);\r
281 fclose(zip->fp);\r
282 free(zip);\r
283 return 0;\r
284 }\r
285\r
286 /* reset ent */\r
287 zip->ent.name = 0;\r
288\r
289 /* rewind */\r
290 zip->cd_pos = 0;\r
291\r
292 /* file name */\r
293 zip->zip = (char*)malloc(strlen(zipfile)+1);\r
294 if (!zip->zip) {\r
295 free(zip->cd);\r
296 free(zip->ecd);\r
297 fclose(zip->fp);\r
298 free(zip);\r
299 return 0;\r
300 }\r
301 strcpy(zip->zip, zipfile);\r
302\r
303 return zip;\r
304}\r
305\r
306/* Reads the current entry from a zip stream\r
307 in:\r
308 zip opened zip\r
309 return:\r
310 !=0 success\r
311 ==0 error\r
312*/\r
313struct zipent* readzip(ZIP* zip) {\r
314\r
315 /* end of directory */\r
316 if (zip->cd_pos >= zip->size_of_cent_dir)\r
317 return 0;\r
318\r
319 /* compile zipent info */\r
320 zip->ent.cent_file_header_sig = read_dword (zip->cd+zip->cd_pos+ZIPCENSIG);\r
321 zip->ent.version_made_by = *(zip->cd+zip->cd_pos+ZIPCVER);\r
322 zip->ent.host_os = *(zip->cd+zip->cd_pos+ZIPCOS);\r
323 zip->ent.version_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCVXT);\r
324 zip->ent.os_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCEXOS);\r
325 zip->ent.general_purpose_bit_flag = read_word (zip->cd+zip->cd_pos+ZIPCFLG);\r
326 zip->ent.compression_method = read_word (zip->cd+zip->cd_pos+ZIPCMTHD);\r
327 zip->ent.last_mod_file_time = read_word (zip->cd+zip->cd_pos+ZIPCTIM);\r
328 zip->ent.last_mod_file_date = read_word (zip->cd+zip->cd_pos+ZIPCDAT);\r
329 zip->ent.crc32 = read_dword (zip->cd+zip->cd_pos+ZIPCCRC);\r
330 zip->ent.compressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCSIZ);\r
331 zip->ent.uncompressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCUNC);\r
332 zip->ent.filename_length = read_word (zip->cd+zip->cd_pos+ZIPCFNL);\r
333 zip->ent.extra_field_length = read_word (zip->cd+zip->cd_pos+ZIPCXTL);\r
334 zip->ent.file_comment_length = read_word (zip->cd+zip->cd_pos+ZIPCCML);\r
335 zip->ent.disk_number_start = read_word (zip->cd+zip->cd_pos+ZIPDSK);\r
336 zip->ent.internal_file_attrib = read_word (zip->cd+zip->cd_pos+ZIPINT);\r
337 zip->ent.external_file_attrib = read_dword (zip->cd+zip->cd_pos+ZIPEXT);\r
338 zip->ent.offset_lcl_hdr_frm_frst_disk = read_dword (zip->cd+zip->cd_pos+ZIPOFST);\r
339\r
340 /* check to see if filename length is illegally long (past the size of this directory\r
341 entry) */\r
342 if (zip->cd_pos + ZIPCFN + zip->ent.filename_length > zip->size_of_cent_dir)\r
343 {\r
344 errormsg("Invalid filename length in directory", ERROR_CORRUPT,zip->zip);\r
345 return 0;\r
346 }\r
347\r
348 /* copy filename */\r
349 free(zip->ent.name);\r
350 zip->ent.name = (char*)malloc(zip->ent.filename_length + 1);\r
351 memcpy(zip->ent.name, zip->cd+zip->cd_pos+ZIPCFN, zip->ent.filename_length);\r
352 zip->ent.name[zip->ent.filename_length] = 0;\r
353\r
354 /* skip to next entry in central dir */\r
355 zip->cd_pos += ZIPCFN + zip->ent.filename_length + zip->ent.extra_field_length + zip->ent.file_comment_length;\r
356\r
357 return &zip->ent;\r
358}\r
359\r
360/* Closes a zip stream */\r
361void closezip(ZIP* zip) {\r
362 /* release all */\r
363 free(zip->ent.name);\r
364 free(zip->cd);\r
365 free(zip->ecd);\r
366 /* only if not suspended */\r
367 if (zip->fp)\r
368 fclose(zip->fp);\r
369 free(zip->zip);\r
370 free(zip);\r
371}\r
372\r
373/* Suspend access to a zip file (release file handler)\r
374 in:\r
375 zip opened zip\r
376 note:\r
377 A suspended zip is automatically reopened at first call of\r
378 readuncompressd() or readcompressed() functions\r
379*/\r
380void suspendzip(ZIP* zip) {\r
381 if (zip->fp) {\r
382 fclose(zip->fp);\r
383 zip->fp = 0;\r
384 }\r
385}\r
386\r
387/* Revive a suspended zip file (reopen file handler)\r
388 in:\r
389 zip suspended zip\r
390 return:\r
391 zip success\r
392 ==0 error (zip must be closed with closezip)\r
393*/\r
394static ZIP* revivezip(ZIP* zip) {\r
395 if (!zip->fp) {\r
396 zip->fp = fopen(zip->zip, "rb");\r
397 if (!zip->fp) {\r
398 return 0;\r
399 }\r
400 }\r
401 return zip;\r
402\r
403}\r
404\r
405/* Reset a zip stream to the first entry\r
406 in:\r
407 zip opened zip\r
408 note:\r
409 ZIP file must be opened and not suspended\r
410*/\r
411void rewindzip(ZIP* zip) {\r
412 zip->cd_pos = 0;\r
413}\r
414\r
415/* Seek zip->fp to compressed data\r
416 return:\r
417 ==0 success\r
418 <0 error\r
419*/\r
420int seekcompresszip(ZIP* zip, struct zipent* ent) {\r
421 char buf[ZIPNAME];\r
422 long offset;\r
423\r
424 if (!zip->fp) {\r
425 if (!revivezip(zip))\r
426 return -1;\r
427 }\r
428\r
429 if (fseek(zip->fp, ent->offset_lcl_hdr_frm_frst_disk, SEEK_SET)!=0) {\r
430 errormsg ("Seeking to header", ERROR_CORRUPT, zip->zip);\r
431 return -1;\r
432 }\r
433\r
434 if (fread(buf, ZIPNAME, 1, zip->fp)!=1) {\r
435 errormsg ("Reading header", ERROR_CORRUPT, zip->zip);\r
436 return -1;\r
437 }\r
438\r
439 {\r
440 UINT16 filename_length = read_word (buf+ZIPFNLN);\r
441 UINT16 extra_field_length = read_word (buf+ZIPXTRALN);\r
442\r
443 /* calculate offset to data and fseek() there */\r
444 offset = ent->offset_lcl_hdr_frm_frst_disk + ZIPNAME + filename_length + extra_field_length;\r
445\r
446 if (fseek(zip->fp, offset, SEEK_SET) != 0) {\r
447 errormsg ("Seeking to compressed data", ERROR_CORRUPT, zip->zip);\r
448 return -1;\r
449 }\r
450\r
451 }\r
452\r
453 return 0;\r
454}\r
455\r
456/* Inflate a file\r
457 in:\r
458 in_file stream to inflate\r
459 in_size size of the compressed data to read\r
460 out_size size of decompressed data\r
461 out:\r
462 out_data buffer for decompressed data\r
463 return:\r
464 ==0 ok\r
465\r
466 990525 rewritten for use with zlib MLR\r
467*/\r
468static int inflate_file(FILE* in_file, unsigned in_size, unsigned char* out_data, unsigned out_size)\r
469{\r
470 int err;\r
471 unsigned char* in_buffer;\r
472 z_stream d_stream; /* decompression stream */\r
473\r
474 d_stream.zalloc = 0;\r
475 d_stream.zfree = 0;\r
476 d_stream.opaque = 0;\r
477\r
478 d_stream.next_in = 0;\r
479 d_stream.avail_in = 0;\r
480 d_stream.next_out = out_data;\r
481 d_stream.avail_out = out_size;\r
482\r
483 err = inflateInit2(&d_stream, -MAX_WBITS);\r
484 /* windowBits is passed < 0 to tell that there is no zlib header.\r
485 * Note that in this case inflate *requires* an extra "dummy" byte\r
486 * after the compressed stream in order to complete decompression and\r
487 * return Z_STREAM_END.\r
488 */\r
489 if (err != Z_OK)\r
490 {\r
491 logerror("inflateInit error: %d\n", err);\r
492 return -1;\r
493 }\r
494\r
495 in_buffer = (unsigned char*)malloc(INFLATE_INPUT_BUFFER_MAX+1);\r
496 if (!in_buffer)\r
497 return -1;\r
498\r
499 for (;;)\r
500 {\r
501 if (in_size <= 0)\r
502 {\r
503 logerror("inflate error: compressed size too small\n");\r
504 free (in_buffer);\r
505 return -1;\r
506 }\r
507 d_stream.next_in = in_buffer;\r
508 d_stream.avail_in = fread (in_buffer, 1, MIN(in_size, INFLATE_INPUT_BUFFER_MAX), in_file);\r
509 in_size -= d_stream.avail_in;\r
510 if (in_size == 0)\r
511 d_stream.avail_in++; /* add dummy byte at end of compressed data */\r
512\r
513 err = inflate(&d_stream, Z_NO_FLUSH);\r
514 if (err == Z_STREAM_END)\r
515 break;\r
516 if (err != Z_OK)\r
517 {\r
518 logerror("inflate error: %d\n", err);\r
519 free (in_buffer);\r
520 return -1;\r
521 }\r
522 }\r
523\r
524 err = inflateEnd(&d_stream);\r
525 if (err != Z_OK)\r
526 {\r
527 logerror("inflateEnd error: %d\n", err);\r
528 free (in_buffer);\r
529 return -1;\r
530 }\r
531\r
532 free (in_buffer);\r
533\r
534 if ((d_stream.avail_out > 0) || (in_size > 0))\r
535 {\r
536 logerror("zip size mismatch. %i\n", in_size);\r
537 return -1;\r
538 }\r
539\r
540 return 0;\r
541}\r
542\r
543/* Read compressed data\r
544 out:\r
545 data compressed data read\r
546 return:\r
547 ==0 success\r
548 <0 error\r
549*/\r
550int readcompresszip(ZIP* zip, struct zipent* ent, char* data) {\r
551 int err = seekcompresszip(zip,ent);\r
552 if (err!=0)\r
553 return err;\r
554\r
555 if (fread(data, ent->compressed_size, 1, zip->fp)!=1) {\r
556 errormsg ("Reading compressed data", ERROR_CORRUPT, zip->zip);\r
557 return -1;\r
558 }\r
559\r
560 return 0;\r
561}\r
562\r
563/* Read UNcompressed data\r
564 out:\r
565 data UNcompressed data\r
566 return:\r
567 ==0 success\r
568 <0 error\r
569*/\r
570int readuncompresszip(ZIP* zip, struct zipent* ent, char* data) {\r
571 if (ent->compression_method == 0x0000) {\r
572 /* file is not compressed, simply stored */\r
573\r
574 /* check if size are equal */\r
575 if (ent->compressed_size != ent->uncompressed_size) {\r
576 errormsg("Wrong uncompressed size in store compression", ERROR_CORRUPT,zip->zip);\r
577 return -3;\r
578 }\r
579\r
580 return readcompresszip(zip,ent,data);\r
581 } else if (ent->compression_method == 0x0008) {\r
582 /* file is compressed using "Deflate" method */\r
583 if (ent->version_needed_to_extract > 0x14) {\r
584 errormsg("Version too new", ERROR_UNSUPPORTED,zip->zip);\r
585 return -2;\r
586 }\r
587\r
588 if (ent->os_needed_to_extract != 0x00) {\r
589 errormsg("OS not supported", ERROR_UNSUPPORTED,zip->zip);\r
590 return -2;\r
591 }\r
592\r
593 if (ent->disk_number_start != zip->number_of_this_disk) {\r
594 errormsg("Cannot span disks", ERROR_UNSUPPORTED,zip->zip);\r
595 return -2;\r
596 }\r
597\r
598 /* read compressed data */\r
599 if (seekcompresszip(zip,ent)!=0) {\r
600 return -1;\r
601 }\r
602\r
603 /* configure inflate */\r
604 if (inflate_file( zip->fp, ent->compressed_size, (unsigned char*)data, ent->uncompressed_size))\r
605 {\r
606 errormsg("Inflating compressed data", ERROR_CORRUPT, zip->zip);\r
607 return -3;\r
608 }\r
609\r
610 return 0;\r
611 } else {\r
612 errormsg("Compression method unsupported", ERROR_UNSUPPORTED, zip->zip);\r
613 return -2;\r
614 }\r
615}\r