zip-reader.c: Fix memory leak
[pspp] / src / libpspp / zip-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2011 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <errno.h>
26 #include <xalloc.h>
27 #include <libpspp/assertion.h>
28
29 #include <byteswap.h>
30 #include <crc.h>
31
32 #include "inflate.h"
33
34 #include "str.h"
35
36 #include "zip-reader.h"
37 #include "zip-private.h"
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41 #define N_(msgid) (msgid)
42
43
44 static bool find_eocd (FILE *fp, off_t *off);
45
46 static int
47 stored_read (struct zip_member *zm, void *buf, size_t n)
48 {
49   return fread (buf, 1, n, zm->fp);
50 }
51
52 static bool
53 stored_init (struct zip_member *zm UNUSED)
54 {
55   return true;
56 }
57
58 static void
59 stored_finish (struct zip_member *zm UNUSED)
60 {
61   /* Nothing required */
62 }
63
64
65 static struct decompressor decompressors[n_COMPRESSION] = 
66   {
67     {stored_init, stored_read, stored_finish},
68 #if HAVE_ZLIB_H
69     {inflate_init, inflate_read, inflate_finish}
70 #endif
71   };
72
73 static enum compression
74 comp_code (struct zip_member *zm, uint16_t c)
75 {
76   enum compression which;
77   switch (c)
78     {
79     case 0:
80       which = COMPRESSION_STORED;
81       break;
82 #if HAVE_ZLIB_H
83     case 8:
84       which = COMPRESSION_INFLATE;
85       break;
86 #endif
87     default:
88       ds_put_format (zm->errs, _("Unsupported compression type (%d)"), c);
89       which = n_COMPRESSION;
90       break;
91     }
92   return which;
93 }
94
95
96 struct zip_reader
97 {
98   char *filename;                  /* The name of the file from which the data is read */
99   FILE *fr;                        /* The stream from which the meta data is read */
100   uint16_t n_members;              /* The number of members in this archive */
101   struct zip_member **members;     /* The members (may be null pointers until the headers have been read */
102   int nm;
103   struct string *errs;
104 };
105
106 void
107 zip_member_finish (struct zip_member *zm)
108 {
109   ds_clear (zm->errs);
110   /*  Probably not useful, because we would have to read right to the end of the member
111   if (zm->expected_crc != zm->crc)
112     {
113       ds_put_cstr (zm->errs, _("CRC error reading zip"));
114     }
115   */
116   zip_member_unref (zm);
117 }
118
119
120
121 /* Destroy the zip reader */
122 void
123 zip_reader_destroy (struct zip_reader *zr)
124 {
125   int i;
126   if (zr == NULL) 
127     return;
128
129   fclose (zr->fr);
130   free (zr->filename);
131
132   for (i = 0; i < zr->n_members; ++i)
133     {
134       zip_member_unref (zr->members[i]);
135     }
136   free (zr->members);
137   free (zr);
138 }
139
140
141 void
142 zm_dump (const struct zip_member *zm)
143 {
144   printf ("%d\t%08x\t %s\n", zm->ucomp_size, zm->expected_crc, zm->name);
145 }
146
147
148 /* Skip N bytes in F */
149 static void
150 skip_bytes (FILE *f, size_t n)
151 {
152   fseeko (f, n, SEEK_CUR);
153 }
154
155 /* Read N bytes from F, storing the result in X */
156 static void
157 get_bytes (FILE *f, void *x, size_t n)
158 {
159   fread (x, 1, n, f);
160 }
161
162 /* Read a 32 bit value from F */
163 static void
164 get_u32 (FILE *f, uint32_t *v)
165 {
166   uint32_t x;
167   get_bytes (f, &x, sizeof x);
168 #ifdef WORDS_BIGENDIAN
169   *v = bswap_32 (x);
170 #else
171   *v = x;
172 #endif
173 }
174
175 /* Read a 16 bit value from F */
176 static void
177 get_u16 (FILE *f, uint16_t *v)
178 {
179   uint16_t x;
180   get_bytes (f, &x, sizeof x);
181 #ifdef WORDS_BIGENDIAN
182   *v = bswap_16 (x);
183 #else
184   *v = x;
185 #endif
186 }
187
188
189 /* Read 32 bit integer and compare it with EXPECTED.
190    place an error string in ERR if necessary. */
191 static bool
192 check_magic (FILE *f, uint32_t expected, struct string *err)
193 {
194   uint32_t magic;
195
196   get_u32 (f, &magic);
197
198   if ((expected != magic))
199     {
200       ds_put_format (err,
201                      _("Corrupt file at 0x%llx: Expected %"PRIx32"; got %"PRIx32), 
202                      (long long int) ftello (f) - sizeof (uint32_t), expected, magic);
203
204       return false;
205     }
206   return true;
207 }
208
209
210 /* Reads upto BYTES bytes from ZM and puts them in BUF.
211    Returns the number of bytes read, or -1 on error */
212 int
213 zip_member_read (struct zip_member *zm, void *buf, size_t bytes)
214 {
215   int bytes_read = 0;
216
217   ds_clear (zm->errs);
218
219   if ( bytes > zm->bytes_unread)
220     bytes = zm->bytes_unread;
221
222   bytes_read  = decompressors[zm->compression].read (zm, buf, bytes);
223   if ( bytes_read < 0)
224     return bytes_read;
225
226   zm->crc = crc32_update (zm->crc, buf, bytes_read);
227
228   zm->bytes_unread -= bytes_read;
229
230   return bytes_read;
231 }
232
233
234 /*
235   Read a local file header from ZR and add it to ZR's internal array.
236   Returns a pointer to the member read.  This pointer belongs to ZR.
237   If the caller wishes to control it, she should ref it with 
238   zip_member_ref.
239 */
240 static struct zip_member *
241 zip_header_read_next (struct zip_reader *zr)
242 {
243   struct zip_member *zm = xzalloc (sizeof *zm);
244
245   uint16_t v, nlen, extralen;
246   uint16_t gp, time, date;
247   
248   uint16_t clen, diskstart, iattr;
249   uint32_t eattr;
250   uint16_t comp_type;
251
252   ds_clear (zr->errs);
253
254   if ( ! check_magic (zr->fr, MAGIC_SOCD, zr->errs))
255     return NULL;
256
257   get_u16 (zr->fr, &v);
258
259   get_u16 (zr->fr, &v);
260   get_u16 (zr->fr, &gp);
261   get_u16 (zr->fr, &comp_type);
262
263   zm->compression = comp_code (zm, comp_type);
264
265   get_u16 (zr->fr, &time);
266   get_u16 (zr->fr, &date);
267   get_u32 (zr->fr, &zm->expected_crc);
268   get_u32 (zr->fr, &zm->comp_size);
269   get_u32 (zr->fr, &zm->ucomp_size);
270   get_u16 (zr->fr, &nlen);
271   get_u16 (zr->fr, &extralen);
272   get_u16 (zr->fr, &clen);
273   get_u16 (zr->fr, &diskstart);
274   get_u16 (zr->fr, &iattr);
275   get_u32 (zr->fr, &eattr);
276   get_u32 (zr->fr, &zm->offset);
277
278   zm->name = xzalloc (nlen + 1);
279   get_bytes (zr->fr, zm->name, nlen);
280
281   skip_bytes (zr->fr, extralen);
282   
283   zr->members[zr->nm++] = zm;
284
285   zm->fp = fopen (zr->filename, "r");
286   zm->ref_cnt = 1;
287   zm->errs = zr->errs;
288
289   return zm;
290 }
291
292
293 /* Create a reader from the zip called FILENAME */
294 struct zip_reader *
295 zip_reader_create (const char *filename, struct string *errs)
296 {
297   uint16_t disknum, total_members;
298   off_t offset = 0;
299   uint32_t central_dir_start, central_dir_length;
300
301   struct zip_reader *zr = xzalloc (sizeof *zr);
302   zr->errs = errs;
303   if ( zr->errs)
304     ds_init_empty (zr->errs);
305
306   zr->nm = 0;
307
308   zr->fr = fopen (filename, "r");
309   if (NULL == zr->fr)
310     {
311       ds_put_cstr (zr->errs, strerror (errno));
312       free (zr);
313       return NULL;
314     }
315
316   if ( ! check_magic (zr->fr, MAGIC_LHDR, zr->errs))
317     {
318       fclose (zr->fr);
319       free (zr);
320       return NULL;
321     }
322
323   if ( ! find_eocd (zr->fr, &offset))
324     {
325       ds_put_format (zr->errs, _("Cannot find central directory"));
326       fclose (zr->fr);
327       free (zr);
328       return NULL;
329     }
330
331   if ( 0 != fseeko (zr->fr, offset, SEEK_SET))
332     {
333       const char *mm = strerror (errno);
334       ds_put_format (zr->errs, _("Failed to seek to end of central directory record: %s"), mm);
335       fclose (zr->fr);
336       free (zr);
337       return NULL;
338     }
339
340
341   if ( ! check_magic (zr->fr, MAGIC_EOCD, zr->errs))
342     {
343       fclose (zr->fr);
344       free (zr);
345       return NULL;
346     }
347   
348   get_u16 (zr->fr, &disknum);
349   get_u16 (zr->fr, &disknum);
350
351   get_u16 (zr->fr, &zr->n_members);
352   get_u16 (zr->fr, &total_members);
353
354   get_u32 (zr->fr, &central_dir_length);
355   get_u32 (zr->fr, &central_dir_start);
356
357   if ( 0 != fseeko (zr->fr, central_dir_start, SEEK_SET))
358     {
359       const char *mm = strerror (errno);
360       ds_put_format (zr->errs, _("Failed to seek to central directory: %s"), mm);
361       fclose (zr->fr);
362       free (zr);
363       return NULL;
364     }
365
366   zr->members = xcalloc (zr->n_members, sizeof (*zr->members));
367   memset (zr->members, 0, zr->n_members * sizeof (*zr->members));
368
369   zr->filename = strdup (filename);
370
371   return zr;
372 }
373
374
375
376 /* Return the member called MEMBER from the reader ZR  */
377 struct zip_member *
378 zip_member_open (struct zip_reader *zr, const char *member)
379 {
380   uint16_t v, nlen, extra_len;
381   uint16_t gp, comp_type, time, date;
382   uint32_t ucomp_size, comp_size;
383   
384   uint32_t crc;
385   bool new_member = false;
386   char *name = NULL;
387
388   int i;
389   struct zip_member *zm = NULL;
390
391   if ( zr == NULL)
392     return NULL;
393
394   for (i = 0; i < zr->n_members; ++i)
395   {
396     zm = zr->members[i];
397
398     if (zm == NULL)
399       {
400         zm = zr->members[i] = zip_header_read_next (zr);
401         new_member = true;
402       }
403     if (zm && 0 == strcmp (zm->name, member))
404       break;
405     else
406       zm = NULL;
407   }
408   
409   if ( zm == NULL)
410     return NULL;
411
412   if ( 0 != fseeko (zm->fp, zm->offset, SEEK_SET))
413     {
414       const char *mm = strerror (errno);
415       ds_put_format (zm->errs, _("Failed to seek to start of member `%s': %s"), zm->name, mm);
416       return NULL;
417     }
418
419   if ( ! check_magic (zm->fp, MAGIC_LHDR, zr->errs))
420     {
421       return NULL;
422     }
423
424   get_u16 (zm->fp, &v);
425   get_u16 (zm->fp, &gp);
426   get_u16 (zm->fp, &comp_type);
427   zm->compression = comp_code (zm, comp_type);
428   get_u16 (zm->fp, &time);
429   get_u16 (zm->fp, &date);
430   get_u32 (zm->fp, &crc);
431   get_u32 (zm->fp, &comp_size);
432
433   get_u32 (zm->fp, &ucomp_size);
434   get_u16 (zm->fp, &nlen);
435   get_u16 (zm->fp, &extra_len);
436
437   name = xzalloc (nlen + 1);
438
439   get_bytes (zm->fp, name, nlen);
440
441   skip_bytes (zm->fp, extra_len);
442
443   if (strcmp (name, zm->name) != 0)
444     {
445       ds_put_format (zm->errs,
446                      _("Name mismatch in zip archive. Central directory says `%s'; local file header says `%s'"),
447                      zm->name, name);
448       free (name);
449       free (zm);
450       return NULL;
451     }
452
453   free (name);
454
455   zm->bytes_unread = zm->ucomp_size;
456   
457   if ( !new_member)
458     decompressors[zm->compression].finish (zm);
459
460   if (!decompressors[zm->compression].init (zm) )
461     return NULL;
462
463   return zm;
464 }
465
466 void
467 zip_member_ref (struct zip_member *zm)
468 {
469   zm->ref_cnt++;
470 }
471
472
473
474
475 void
476 zip_member_unref (struct zip_member *zm)
477 {
478   if ( zm == NULL)
479     return;
480
481   if (--zm->ref_cnt == 0)
482     {
483       decompressors[zm->compression].finish (zm);
484       if (zm->fp)
485         fclose (zm->fp);
486       free (zm->name);
487       free (zm);
488     }
489 }
490
491
492 \f
493
494 static bool probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off);
495
496
497 /* Search for something that looks like the End Of Central Directory in FP.
498    If found, the offset of the record will be placed in OFF.
499    Returns true if found false otherwise.
500 */
501 static bool
502 find_eocd (FILE *fp, off_t *off)
503 {
504   off_t start, stop;
505   const uint32_t magic = MAGIC_EOCD;
506   bool found = false;
507
508   /* The magic cannot be more than 22 bytes from the end of the file, 
509      because that is the minimum length of the EndOfCentralDirectory
510      record.
511    */
512   if ( 0 > fseeko (fp, -22, SEEK_END))
513     {
514       return false;
515     }
516   start = ftello (fp);
517   stop = start + sizeof (magic);
518   do 
519     {
520       found = probe_magic (fp, magic, start, stop, off);
521       /* FIXME: For extra confidence lookup the directory start record here*/
522       if ( start == 0)
523         break;
524       stop = start + sizeof (magic);
525       start >>= 1;
526     }
527   while (!found );
528
529   return found;
530 }
531
532
533 /*
534   Search FP for MAGIC starting at START and reaching until STOP.
535   Returns true iff MAGIC is found.  False otherwise.
536   OFF receives the location of the magic.
537 */
538 static bool
539 probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off)
540 {
541   int i;
542   int state = 0;
543   unsigned char seq[4];
544   unsigned char byte;
545
546   if ( 0 > fseeko (fp, start, SEEK_SET))
547     {
548       return -1;
549     }
550
551   for (i = 0; i < 4 ; ++i)
552     {
553       seq[i] = (magic >> i * 8) & 0xFF;
554     }
555
556   do
557     {
558       fread (&byte, 1, 1, fp);
559
560       if ( byte == seq[state])
561         state++;
562       else
563         state = 0;
564       
565       if ( state == 4)
566         {
567           *off = ftello (fp) - 4;
568           return true;
569         }
570       start++;
571       if ( start >= stop)
572         break;
573     }
574   while (!feof (fp));
575
576   return false;
577 }
578