3 * NOTE: this is part of libzzipmmapped (i.e. it is not libzzip).
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.
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.
17 * Guido Draheim <guidod@gmx.de>
19 * Copyright (c) Guido Draheim, use under copyleft (LGPL,MPL)
21 #define _ZZIP_DISK_FILE_STRUCT 1
23 #include <zzip/types.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>
40 static const char *error[] = {
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",
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 */
64 static zzip__new__ ZZIP_MEM_ENTRY *
65 zzip_mem_entry_new(ZZIP_DISK * disk, ZZIP_DISK_ENTRY * entry);
67 zzip_mem_entry_free(ZZIP_MEM_ENTRY * _zzip_restrict item);
69 zzip__new__ ZZIP_MEM_DISK *
70 zzip_mem_disk_new(void)
72 return calloc(1, sizeof(ZZIP_MEM_DISK));
75 /** create new diskdir handle.
76 * wraps underlying zzip_disk_open. */
77 zzip__new__ ZZIP_MEM_DISK *
78 zzip_mem_disk_open(char *filename)
80 ZZIP_DISK *disk = zzip_disk_open(filename);
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);
89 /** create new diskdir handle.
90 * wraps underlying zzip_disk_open. */
91 zzip__new__ ZZIP_MEM_DISK *
92 zzip_mem_disk_fdopen(int fd)
94 ZZIP_DISK *disk = zzip_disk_mmap(fd);
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);
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)
108 ZZIP_DISK *disk = zzip_disk_buffer(buffer, buflen);
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);
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)
122 zzip_mem_disk_load(ZZIP_MEM_DISK * dir, ZZIP_DISK * disk)
125 { errno=EINVAL; return -1; }
127 zzip_mem_disk_unload(dir);
129 ___ struct zzip_disk_entry *entry = zzip_disk_findfirst(disk);
130 for (; entry; entry = zzip_disk_findnext(disk, entry))
132 ZZIP_MEM_ENTRY *item = zzip_mem_entry_new(disk, entry);
137 dir->last->zz_next = item; /* chain last */
142 dir->last = item; /* to earlier */
150 zzip_mem_disk_unload(dir);
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)
161 zzip__new__ ZZIP_MEM_ENTRY *
162 zzip_mem_entry_new(ZZIP_DISK * disk, ZZIP_DISK_ENTRY * entry)
164 if (! disk || ! entry)
165 { errno=EINVAL; return 0; }
166 ___ ZZIP_MEM_ENTRY *item = calloc(1, sizeof(*item));
168 return 0; /* errno=ENOMEM; */
169 ___ struct zzip_file_header *header =
170 zzip_disk_entry_to_file_header(disk, entry);
174 return 0; /* errno=EBADMSG; */
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.
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);
192 if (! item->zz_comment || ! item->zz_name)
194 goto error; /* errno=ENOMEM */
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);
203 if (ext1_ptr + ext1_len >= disk->endbuf ||
204 ext2_ptr + ext2_len >= disk->endbuf)
206 errno = EBADMSG; /* format error CVE-2017-5978 */
207 goto error; /* zzip_mem_entry_free(item); return 0; */
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);
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);
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);
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);
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.
246 zzip_mem_entry_free(item);
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.
256 zzip_mem_entry_extra_block(ZZIP_MEM_ENTRY * entry, short datatype)
261 char* ext = (char*)( entry->zz_ext[i] );
262 char* ext_end = ext + entry->zz_extlen[i];
265 while (ext + zzip_extra_block_headerlength <= ext_end)
267 if (datatype == zzip_extra_block_get_datatype(ext))
269 return ((ZZIP_EXTRA_BLOCK*) ext);
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;
285 zzip_mem_entry_free(ZZIP_MEM_ENTRY * _zzip_restrict item)
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);
301 zzip_mem_disk_unload(ZZIP_MEM_DISK * dir)
303 ZZIP_MEM_ENTRY *item = dir->list;
306 ZZIP_MEM_ENTRY *next = item->zz_next;
307 zzip_mem_entry_free(item);
310 dir->list = dir->last = 0;
311 zzip_disk_close(dir->disk);
316 zzip_mem_disk_close(ZZIP_MEM_DISK * _zzip_restrict dir)
320 zzip_mem_disk_unload(dir);
321 zzip_disk_close(dir->disk);
328 foo(short zz_datatype)
330 /* Header IDs of 0 thru 31 are reserved for use by PKWARE.(APPNOTE.TXT) */
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 */
379 zzip_mem_disk_findfile(ZZIP_MEM_DISK * dir,
380 char *filename, ZZIP_MEM_ENTRY * after,
381 zzip_strcmp_fn_t compare)
383 ZZIP_MEM_ENTRY *entry = (! after ? dir->list : after->zz_next);
385 compare = (zzip_strcmp_fn_t) (strcmp);
386 for (; entry; entry = entry->zz_next)
388 if (! compare(filename, entry->zz_name))
397 zzip_mem_disk_findmatch(ZZIP_MEM_DISK * dir,
398 char *filespec, ZZIP_MEM_ENTRY * after,
399 zzip_fnmatch_fn_t compare, int flags)
401 ZZIP_MEM_ENTRY *entry = (! after ? dir->list : after->zz_next);
403 compare = (zzip_fnmatch_fn_t) _zzip_fnmatch;
404 for (; entry; entry = entry->zz_next)
406 if (! compare(filespec, entry->zz_name, flags))
414 zzip__new__ ZZIP_MEM_DISK_FILE *
415 zzip_mem_entry_fopen(ZZIP_MEM_DISK * dir, ZZIP_MEM_ENTRY * entry)
417 /* keep this in sync with zzip_disk_entry_fopen */
418 ZZIP_DISK_FILE *file = malloc(sizeof(ZZIP_MEM_DISK_FILE));
421 file->buffer = dir->disk->buffer;
422 file->endbuf = dir->disk->endbuf;
423 file->avail = zzip_mem_entry_usize(entry);
425 if (! file->avail || zzip_mem_entry_data_stored(entry))
426 { file->stored = zzip_mem_entry_to_data (entry); return file; }
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);
435 if (! zzip_mem_entry_data_deflated(entry) ||
436 inflateInit2(&file->zlib, -MAX_WBITS) != Z_OK)
437 { free (file); return 0; }
442 zzip__new__ ZZIP_MEM_DISK_FILE *
443 zzip_mem_disk_fopen(ZZIP_MEM_DISK * dir, char *filename)
445 ZZIP_MEM_ENTRY *entry = zzip_mem_disk_findfile(dir, filename, 0, 0);
449 return zzip_mem_entry_fopen(dir, entry);
453 zzip_mem_disk_fread(void *ptr, _zzip_size_t size, _zzip_size_t nmemb,
454 ZZIP_MEM_DISK_FILE * file)
456 return zzip_disk_fread(ptr, size, nmemb, file);
460 zzip_mem_disk_fclose(ZZIP_MEM_DISK_FILE * file)
462 return zzip_disk_fclose(file);
466 zzip_mem_disk_feof(ZZIP_MEM_DISK_FILE * file)
468 return zzip_disk_feof(file);
471 /* convert dostime of entry to unix time_t */
473 zzip_disk_entry_get_mktime(ZZIP_DISK_ENTRY * entry)
475 uint16_t dostime = ZZIP_GET16(entry->z_dostime.time);
476 uint16_t dosdate = ZZIP_GET16(entry->z_dostime.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... */