]> granicus.if.org Git - zziplib/blob - zzip/memdisk.c
~0.13.64~ on github releases
[zziplib] / zzip / memdisk.c
1
2 /*
3  * NOTE: this is part of libzzipmmapped (i.e. it is not libzzip).
4  *                                            ==================
5  *
6  * The mem_disk cache will parse the central information of a zip archive
7  * and store it internally. One the one hand it allows to find files
8  * faster - no disk access is required and endian conversion is not
9  * needed. If zzip is compiled with zip extensions then it is about
10  * the only way to build maintainable code around the zip format.
11  *
12  * Note that 64bit support is almost entirely living in extension
13  * blocks as well as different character encodings and file access
14  * control bits that are mostly platform specific.
15  *
16  * Author:
17  *    Guido Draheim <guidod@gmx.de>
18  *
19  * Copyright (c) Guido Draheim, use under copyleft (LGPL,MPL)
20  */
21 #define _ZZIP_DISK_FILE_STRUCT 1
22
23 #include <zzip/types.h>
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <time.h>
29
30 #include <zlib.h>
31 #include <zzip/format.h>
32 #include <zzip/fetch.h>
33 #include <zzip/mmapped.h>
34 #include <zzip/memdisk.h>
35 #include <zzip/__fnmatch.h>
36
37 #define ___ {
38 #define ____ }
39
40 static const char *error[] = {
41     "Ok",
42 #   define _zzip_mem_disk_open_fail 1
43     "zzip_mem_disk_open: zzip_disk_open did fail",
44 #   define _zzip_mem_disk_fdopen_fail 2
45     "zzip_mem_disk_fdopen: zzip_disk_mmap did fail"
46 #   define _zzip_mem_disk_buffer_fail 3
47     "zzip_mem_disk_buffer: zzip_disk_buffer did fail",
48     0
49 };
50
51 #define ZZIP_EXTRA_zip64 0x0001
52 typedef struct _zzip_extra_zip64
53 {                               /* ZIP64 extended information extra field */
54     zzip_byte_t z_datatype[2];  /* Tag for this "extra" block type */
55     zzip_byte_t z_datasize[2];  /* Size of this "extra" block */
56     zzip_byte_t z_usize[8];     /* Original uncompressed file size */
57     zzip_byte_t z_csize[8];     /* Size of compressed data */
58     zzip_byte_t z_offset[8];    /* Offset of local header record */
59     zzip_byte_t z_diskstart[4]; /* Number of the disk for file start */
60 } zzip_extra_zip64;
61
62 /*forward*/
63
64 static zzip__new__ ZZIP_MEM_ENTRY *
65 zzip_mem_entry_new(ZZIP_DISK * disk, ZZIP_DISK_ENTRY * entry);
66 static void
67 zzip_mem_entry_free(ZZIP_MEM_ENTRY * _zzip_restrict item);
68
69 zzip__new__ ZZIP_MEM_DISK *
70 zzip_mem_disk_new(void)
71 {
72     return calloc(1, sizeof(ZZIP_MEM_DISK));
73 }
74
75 /** create new diskdir handle.
76  *  wraps underlying zzip_disk_open. */
77 zzip__new__ ZZIP_MEM_DISK *
78 zzip_mem_disk_open(char *filename)
79 {
80     ZZIP_DISK *disk = zzip_disk_open(filename);
81     if (! disk)
82         { perror(error[_zzip_mem_disk_open_fail]); return 0; }
83     ___ ZZIP_MEM_DISK *dir = zzip_mem_disk_new();
84     zzip_mem_disk_load(dir, disk);
85     return dir;
86     ____;
87 }
88
89 /** create new diskdir handle.
90  *  wraps underlying zzip_disk_open. */
91 zzip__new__ ZZIP_MEM_DISK *
92 zzip_mem_disk_fdopen(int fd)
93 {
94     ZZIP_DISK *disk = zzip_disk_mmap(fd);
95     if (! disk)
96         { perror(error[_zzip_mem_disk_fdopen_fail]); return 0; }
97     ___ ZZIP_MEM_DISK *dir = zzip_mem_disk_new();
98     zzip_mem_disk_load(dir, disk);
99     return dir;
100     ____;
101 }
102
103 /** create new diskdir handle.
104  *  wraps underlying zzip_disk_buffer. */
105 zzip__new__ ZZIP_MEM_DISK *
106 zzip_mem_disk_buffer(char *buffer, size_t buflen)
107 {
108     ZZIP_DISK *disk = zzip_disk_buffer(buffer, buflen);
109     if (! disk)
110         { perror(error[_zzip_mem_disk_buffer_fail]); return 0; }
111     ___ ZZIP_MEM_DISK *dir = zzip_mem_disk_new();
112     zzip_mem_disk_load(dir, disk);
113     return dir;
114     ____;
115 }
116
117 /** parse central dir.
118  *  creates an internal copy of each entry converted to the local platform.
119  *  returns: number of entries, or -1 on error (setting errno)
120  */
121 long
122 zzip_mem_disk_load(ZZIP_MEM_DISK * dir, ZZIP_DISK * disk)
123 {
124     if (! dir || ! disk)
125         { errno=EINVAL; return -1; }
126     if (dir->list)
127         zzip_mem_disk_unload(dir);
128     ___ long count = 0;
129     ___ struct zzip_disk_entry *entry = zzip_disk_findfirst(disk);
130     for (; entry; entry = zzip_disk_findnext(disk, entry))
131     {
132         ZZIP_MEM_ENTRY *item = zzip_mem_entry_new(disk, entry);
133         if (! item)
134             goto error;
135         if (dir->last)
136         {
137             dir->last->zz_next = item;  /* chain last */
138         } else
139         {
140             dir->list = item;
141         }
142         dir->last = item;       /* to earlier */
143         count++;
144     }
145     ____;
146     dir->disk = disk;
147     return count;
148     ____;
149   error:
150     zzip_mem_disk_unload(dir);
151     return -1;
152 }
153
154 /** convert a zip disk entry to internal format.
155  * creates a new item parsing the information out of the various places
156  * in the zip archive. This is a good place to extend functionality if
157  * you have a project with extra requirements as you can push more bits
158  * right into the diskdir_entry for later usage in higher layers.
159  * returns: new item, or null on error (setting errno =  ENOMEM|EBADMSG)
160  */
161 zzip__new__ ZZIP_MEM_ENTRY *
162 zzip_mem_entry_new(ZZIP_DISK * disk, ZZIP_DISK_ENTRY * entry)
163 {
164     if (! disk || ! entry)
165         { errno=EINVAL; return 0; }
166     ___ ZZIP_MEM_ENTRY *item = calloc(1, sizeof(*item));
167     if (! item)
168         return 0;               /* errno=ENOMEM; */
169     ___ struct zzip_file_header *header =
170         zzip_disk_entry_to_file_header(disk, entry);
171     if (! header) 
172     {
173         free (item);
174         return 0; /* errno=EBADMSG; */
175     }
176     /*  there is a number of duplicated information in the file header
177      *  or the disk entry block. Theoretically some part may be missing
178      *  that exists in the other, ... but we will prefer the disk entry.
179      */
180     item->zz_comment = zzip_disk_entry_strdup_comment(disk, entry);
181     item->zz_name = zzip_disk_entry_strdup_name(disk, entry);
182     item->zz_data = zzip_file_header_to_data(header);
183     item->zz_flags = zzip_disk_entry_get_flags(entry);
184     item->zz_compr = zzip_disk_entry_get_compr(entry);
185     item->zz_mktime = zzip_disk_entry_get_mktime(entry);
186     item->zz_crc32 = zzip_disk_entry_get_crc32(entry);
187     item->zz_csize = zzip_disk_entry_get_csize(entry);
188     item->zz_usize = zzip_disk_entry_get_usize(entry);
189     item->zz_diskstart = zzip_disk_entry_get_diskstart(entry);
190     item->zz_filetype = zzip_disk_entry_get_filetype(entry);
191     
192     if (! item->zz_comment || ! item->zz_name)
193     {
194         goto error; /* errno=ENOMEM */
195     }
196
197     {   /* copy the extra blocks to memory as well (maximum 64K each) */
198         zzip_size_t /*    */ ext1_len = zzip_disk_entry_get_extras(entry);
199         char *_zzip_restrict ext1_ptr = zzip_disk_entry_to_extras(entry);
200         zzip_size_t /*    */ ext2_len = zzip_file_header_get_extras(header);
201         char *_zzip_restrict ext2_ptr = zzip_file_header_to_extras(header);
202
203         if (ext1_ptr + ext1_len >= disk->endbuf ||
204             ext2_ptr + ext2_len >= disk->endbuf)
205         {
206             errno = EBADMSG; /* format error CVE-2017-5978 */
207             goto error; /* zzip_mem_entry_free(item); return 0; */
208         }
209
210         if (ext1_len)
211         {
212             void *mem = malloc(ext1_len);
213             if (! mem) goto error; /* errno = ENOMEM */
214             item->zz_ext[1] = mem;
215             item->zz_extlen[1] = ext1_len;
216             memcpy(mem, ext1_ptr, ext1_len);
217         }
218         if (ext2_len)
219         {
220             void *mem = malloc(ext2_len);
221             if (! mem) goto error; /* errno = ENOMEM */
222             item->zz_ext[2] = mem;
223             item->zz_extlen[2] = ext2_len;
224             memcpy(mem, ext2_ptr, ext2_len);
225         }
226     }
227     {
228         /* override sizes/offsets with zip64 values for largefile support */
229         zzip_extra_zip64 *block = (zzip_extra_zip64 *)
230             zzip_mem_entry_extra_block(item, ZZIP_EXTRA_zip64);
231         if (block)
232         {
233             item->zz_usize = ZZIP_GET64(block->z_usize);
234             item->zz_csize = ZZIP_GET64(block->z_csize);
235             item->zz_offset = ZZIP_GET64(block->z_offset);
236             item->zz_diskstart = ZZIP_GET32(block->z_diskstart);
237         }
238     }
239     /* NOTE:
240      * All information from the central directory entry is now in memory.
241      * Effectivly that allows us to modify it and write it back to disk.
242      */
243     return item;
244     ____;
245 error:
246     zzip_mem_entry_free(item);
247     return 0;
248     ____;
249 }
250
251 /* find an extra block for the given datatype code.
252  * The returned EXTRA_BLOCK is still in disk-encoding but
253  * already a pointer into an allocated heap space block.
254  */
255 ZZIP_EXTRA_BLOCK *
256 zzip_mem_entry_extra_block(ZZIP_MEM_ENTRY * entry, short datatype)
257 {
258     int i = 2;
259     while (1)
260     {
261         char* ext = (char*)( entry->zz_ext[i] );
262         char* ext_end = ext + entry->zz_extlen[i];
263         if (ext)
264         {
265             while (ext + zzip_extra_block_headerlength <= ext_end)
266             {
267                 if (datatype == zzip_extra_block_get_datatype(ext))
268                 {
269                     return ((ZZIP_EXTRA_BLOCK*) ext);
270                 }
271                 /* skip to start of next extra_block */
272                 ___ zzip_size_t datasize = zzip_extra_block_get_datasize(ext);
273                 ext += zzip_extra_block_headerlength;
274                 ext += datasize;
275                 ____;
276             }
277         }
278         if (! i)
279             return 0;
280         i--;
281     }
282 }
283
284 void
285 zzip_mem_entry_free(ZZIP_MEM_ENTRY * _zzip_restrict item)
286 {
287     if (item)
288     {
289         /* *INDENT-OFF* */
290         if (item->zz_ext[0]) free (item->zz_ext[0]);
291         if (item->zz_ext[1]) free (item->zz_ext[1]);
292         if (item->zz_ext[2]) free (item->zz_ext[2]);
293         if (item->zz_comment) free (item->zz_comment);
294         if (item->zz_name) free (item->zz_name);
295         free (item);
296         /* *INDENT-ON* */
297     }
298 }
299
300 void
301 zzip_mem_disk_unload(ZZIP_MEM_DISK * dir)
302 {
303     ZZIP_MEM_ENTRY *item = dir->list;
304     while (item)
305     {
306         ZZIP_MEM_ENTRY *next = item->zz_next;
307         zzip_mem_entry_free(item);
308         item = next;
309     }
310     dir->list = dir->last = 0;
311     zzip_disk_close(dir->disk);
312     dir->disk = 0;
313 }
314
315 void
316 zzip_mem_disk_close(ZZIP_MEM_DISK * _zzip_restrict dir)
317 {
318     if (dir)
319     {
320         zzip_mem_disk_unload(dir);
321         zzip_disk_close(dir->disk);
322         free(dir);
323     }
324 }
325
326 #if 0
327 static void
328 foo(short zz_datatype)
329 {
330     /* Header IDs of 0 thru 31 are reserved for use by PKWARE.(APPNOTE.TXT) */
331     switch (zz_datatype)
332     {
333     /* *INDENT-OFF* */
334     case 0x0001: /* ZIP64 extended information extra field */
335     case 0x0007: /* AV Info */
336     case 0x0008: /* Reserved for future Unicode file name data (PFS) */
337     case 0x0009: /* OS/2 */
338     case 0x000a: /* NTFS */
339     case 0x000c: /* OpenVMS */
340     case 0x000d: /* Unix */
341     case 0x000e: /* Reserved for file stream and fork descriptors */
342     case 0x000f: /* Patch Descriptor */
343     case 0x0014: /* PKCS#7 Store for X.509 Certificates */
344     case 0x0015: /* X.509 Certificate ID and Signature for file */
345     case 0x0016: /* X.509 Certificate ID for Central Directory */
346     case 0x0017: /* Strong Encryption Header */
347     case 0x0018: /* Record Management Controls */
348     case 0x0019: /* PKCS#7 Encryption Recipient Certificate List */
349     /* ......................................................... */
350     case 0x0065: /* IBM S/390, AS/400 attributes - uncompressed */
351     case 0x0066: /* Reserved for IBM S/390, AS/400 attr - compressed */
352     case 0x07c8: /* Macintosh */
353     case 0x2605: /* ZipIt Macintosh */
354     case 0x2705: /* ZipIt Macintosh 1.3.5+ */
355     case 0x2805: /* ZipIt Macintosh 1.3.5+ */
356     case 0x334d: /* Info-ZIP Macintosh */
357     case 0x4341: /* Acorn/SparkFS  */
358     case 0x4453: /* Windows NT security descriptor (binary ACL) */
359     case 0x4704: /* VM/CMS */
360     case 0x470f: /* MVS */
361     case 0x4b46: /* FWKCS MD5 (see below) */
362     case 0x4c41: /* OS/2 access control list (text ACL) */
363     case 0x4d49: /* Info-ZIP OpenVMS */
364     case 0x4f4c: /* Xceed original location extra field */
365     case 0x5356: /* AOS/VS (ACL) */
366     case 0x5455: /* extended timestamp */
367     case 0x554e: /* Xceed unicode extra field */
368     case 0x5855: /* Info-ZIP Unix (original, also OS/2, NT, etc) */
369     case 0x6542: /* BeOS/BeBox */
370     case 0x756e: /* ASi Unix */
371     case 0x7855: /* Info-ZIP Unix (new) */
372     case 0xfd4a: /* SMS/QDOS */
373     /* *INDENT-ON* */
374     }
375 }
376 #endif
377
378 ZZIP_MEM_ENTRY *
379 zzip_mem_disk_findfile(ZZIP_MEM_DISK * dir,
380                        char *filename, ZZIP_MEM_ENTRY * after,
381                        zzip_strcmp_fn_t compare)
382 {
383     ZZIP_MEM_ENTRY *entry = (! after ? dir->list : after->zz_next);
384     if (! compare)
385         compare = (zzip_strcmp_fn_t) (strcmp);
386     for (; entry; entry = entry->zz_next)
387     {
388         if (! compare(filename, entry->zz_name))
389         {
390             return entry;
391         }
392     }
393     return 0;
394 }
395
396 ZZIP_MEM_ENTRY *
397 zzip_mem_disk_findmatch(ZZIP_MEM_DISK * dir,
398                         char *filespec, ZZIP_MEM_ENTRY * after,
399                         zzip_fnmatch_fn_t compare, int flags)
400 {
401     ZZIP_MEM_ENTRY *entry = (! after ? dir->list : after->zz_next);
402     if (! compare)
403         compare = (zzip_fnmatch_fn_t) _zzip_fnmatch;
404     for (; entry; entry = entry->zz_next)
405     {
406         if (! compare(filespec, entry->zz_name, flags))
407         {
408             return entry;
409         }
410     }
411     return 0;
412 }
413
414 zzip__new__ ZZIP_MEM_DISK_FILE *
415 zzip_mem_entry_fopen(ZZIP_MEM_DISK * dir, ZZIP_MEM_ENTRY * entry)
416 {
417     /* keep this in sync with zzip_disk_entry_fopen */
418     ZZIP_DISK_FILE *file = malloc(sizeof(ZZIP_MEM_DISK_FILE));
419     if (! file)
420         return file;
421     file->buffer = dir->disk->buffer;
422     file->endbuf = dir->disk->endbuf;
423     file->avail = zzip_mem_entry_usize(entry);
424
425     if (! file->avail || zzip_mem_entry_data_stored(entry))
426         { file->stored = zzip_mem_entry_to_data (entry); return file; }
427
428     file->stored = 0;
429     file->zlib.opaque = 0;
430     file->zlib.zalloc = Z_NULL;
431     file->zlib.zfree = Z_NULL;
432     file->zlib.avail_in = zzip_mem_entry_csize(entry);
433     file->zlib.next_in = zzip_mem_entry_to_data(entry);
434
435     if (! zzip_mem_entry_data_deflated(entry) ||
436         inflateInit2(&file->zlib, -MAX_WBITS) != Z_OK)
437         { free (file); return 0; }
438
439     return file;
440 }
441
442 zzip__new__ ZZIP_MEM_DISK_FILE *
443 zzip_mem_disk_fopen(ZZIP_MEM_DISK * dir, char *filename)
444 {
445     ZZIP_MEM_ENTRY *entry = zzip_mem_disk_findfile(dir, filename, 0, 0);
446     if (! entry)
447         return 0;
448     else
449         return zzip_mem_entry_fopen(dir, entry);
450 }
451
452 _zzip_size_t
453 zzip_mem_disk_fread(void *ptr, _zzip_size_t size, _zzip_size_t nmemb,
454                     ZZIP_MEM_DISK_FILE * file)
455 {
456     return zzip_disk_fread(ptr, size, nmemb, file);
457 }
458
459 int
460 zzip_mem_disk_fclose(ZZIP_MEM_DISK_FILE * file)
461 {
462     return zzip_disk_fclose(file);
463 }
464
465 int
466 zzip_mem_disk_feof(ZZIP_MEM_DISK_FILE * file)
467 {
468     return zzip_disk_feof(file);
469 }
470
471 /* convert dostime of entry to unix time_t */
472 long
473 zzip_disk_entry_get_mktime(ZZIP_DISK_ENTRY * entry)
474 {
475     uint16_t dostime = ZZIP_GET16(entry->z_dostime.time);
476     uint16_t dosdate = ZZIP_GET16(entry->z_dostime.date);
477     struct tm date;
478     date.tm_sec = (dostime) & 0x1F;     /* bits 0..4 */
479     date.tm_min = (dostime >> 5) & 0x3F;        /* bits 5..10 */
480     date.tm_hour = (dostime >> 11);     /* bits 11..15 */
481     date.tm_mday = (dosdate) & 0x1F;    /* bits 16..20 */
482     date.tm_mon = (dosdate >> 5) & 0xF; /* bits 21..24 */
483     date.tm_year = (dosdate >> 9) + 80; /* bits 25..31 */
484     return mktime(&date);       /* well, unix has that function... */
485 }