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