aboutsummaryrefslogtreecommitdiff
path: root/contrib/minizip/zip.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--contrib/minizip/zip.c239
1 files changed, 238 insertions, 1 deletions
diff --git a/contrib/minizip/zip.c b/contrib/minizip/zip.c
index a6329ae..d16e9ae 100644
--- a/contrib/minizip/zip.c
+++ b/contrib/minizip/zip.c
@@ -123,6 +123,19 @@ typedef struct linkedlist_data_s
123} linkedlist_data; 123} linkedlist_data;
124 124
125 125
126// zipAlreadyThere() set functions for a set of zero-terminated strings, and
127// a block_t type for reading the central directory datablocks.
128typedef char const *set_key_t;
129#define set_cmp(a, b) strcmp(a, b)
130#define set_drop(s, k) set_free(s, (void *)(intptr_t)(k))
131#include "skipset.h"
132typedef struct {
133 unsigned char *next; // next byte in datablock data
134 size_t left; // number of bytes left in data (at least)
135 linkedlist_datablock_internal *node; // current datablock
136} block_t;
137
138
126typedef struct 139typedef struct
127{ 140{
128 z_stream stream; /* zLib stream structure for inflate */ 141 z_stream stream; /* zLib stream structure for inflate */
@@ -174,6 +187,10 @@ typedef struct
174 char *globalcomment; 187 char *globalcomment;
175#endif 188#endif
176 189
190 // Support for zipAlreadyThere().
191 set_t set; // set for detecting name collisions
192 block_t block; // block for reading the central directory
193
177} zip64_internal; 194} zip64_internal;
178 195
179 196
@@ -264,6 +281,223 @@ local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len)
264 return ZIP_OK; 281 return ZIP_OK;
265} 282}
266 283
284// zipAlreadyThere() operations. "set" in the zip internal structure keeps the
285// set of names that are in the under-construction central directory so far. A
286// skipset provides ~O(log n) time insertion and searching. Central directory
287// records, stored in a linked list of allocated memory datablocks, is read
288// through "block" in the zip internal structure.
289
290// The block_*() functions support extracting the central directory file names
291// from the datablocks. They are designed to support a growing directory by
292// automatically continuing once more data has been appended to the linked
293// datablocks.
294
295// Initialize *block to the head of list. This should only be called once the
296// list has at least some data in it, i.e. list->first_block is not NULL.
297local void block_init(block_t *block, linkedlist_data *list) {
298 block->node = list->first_block;
299 block->next = block->node->data;
300 block->left = block->node->filled_in_this_block;
301}
302
303// Mark *block as bad, with all subsequent reads returning end, even if more
304// data is added to the datablocks. This is invoked if the central directory is
305// invalid, so there is no longer any point in attempting to interpret it.
306local void block_stop(block_t *block) {
307 block->left = 0;
308 block->next = NULL;
309}
310
311// Return true if *block has reached the end of the data in the datablocks.
312local int block_end(block_t *block) {
313 linkedlist_datablock_internal *node = block->node;
314 if (node == NULL)
315 // This block was previously terminated with extreme prejudice.
316 return 1;
317 if (block->next < node->data + node->filled_in_this_block)
318 // There are more bytes to read in the current datablock.
319 return 0;
320 while (node->next_datablock != NULL) {
321 if (node->filled_in_this_block != 0)
322 // There are some bytes in a later datablock.
323 return 0;
324 node = node->next_datablock;
325 }
326 // Reached the end of the list of datablocks. There's nothing.
327 return 1;
328}
329
330// Return one byte from *block, or -1 if the end is reached.
331local int block_get(block_t *block) {
332 while (block->left == 0) {
333 if (block->node == NULL)
334 // We've been marked bad. Return end.
335 return -1;
336 // Update left in case more was filled in since we were last here.
337 block->left = block->node->filled_in_this_block -
338 (block->next - block->node->data);
339 if (block->left != 0)
340 // There was indeed more data appended in the current datablock.
341 break;
342 if (block->node->next_datablock == NULL)
343 // No more data here, and there is no next datablock. At the end.
344 return -1;
345 // Try the next datablock for more data.
346 block->node = block->node->next_datablock;
347 block->next = block->node->data;
348 block->left = block->node->filled_in_this_block;
349 }
350 // We have a byte to return.
351 block->left--;
352 return *block->next++;
353}
354
355// Return a 16-bit unsigned little-endian value from block, or a negative value
356// if the end is reached.
357local long block_get2(block_t *block) {
358 long got = block_get(block);
359 return got | ((long)block_get(block) << 8);
360}
361
362// Read up to len bytes from block into buf. Return the number of bytes read.
363local size_t block_read(block_t *block, unsigned char *buf, size_t len) {
364 size_t need = len;
365 while (need) {
366 if (block->left == 0) {
367 // Get a byte to update and step through the linked list as needed.
368 int got = block_get(block);
369 if (got == -1)
370 // Reached the end.
371 break;
372 *buf++ = (unsigned char)got;
373 need--;
374 continue;
375 }
376 size_t take = need > block->left ? block->left : need;
377 memcpy(buf, block->next, take);
378 block->next += take;
379 block->left -= take;
380 buf += take;
381 need -= take;
382 }
383 return len - need; // return the number of bytes copied
384}
385
386// Skip n bytes in block. Return 0 on success or -1 if there are less than n
387// bytes to the end.
388local int block_skip(block_t *block, size_t n) {
389 while (n > block->left) {
390 n -= block->left;
391 block->next += block->left;
392 block->left = 0;
393 if (block_get(block) == -1)
394 return -1;
395 n--;
396 }
397 block->next += n;
398 block->left -= n;
399 return 0;
400}
401
402// Process the next central directory record at *block. Return the allocated,
403// zero-terminated file name, or NULL for end of input or invalid data. If
404// invalid, *block is marked bad. This uses *set for the allocation of memory.
405local char *block_central_name(block_t *block, set_t *set) {
406 char *name = NULL;
407 for (;;) {
408 if (block_end(block))
409 // At the end of the central directory (so far).
410 return NULL;
411
412 // Check for a central directory record signature.
413 if (block_get2(block) != (CENTRALHEADERMAGIC & 0xffff) ||
414 block_get2(block) != (CENTRALHEADERMAGIC >> 16))
415 // Incorrect signature.
416 break;
417
418 // Go through the remaining fixed-length portion of the record,
419 // extracting the lengths of the three variable-length fields.
420 block_skip(block, 24);
421 unsigned flen = block_get2(block); // file name length
422 unsigned xlen = block_get2(block); // extra field length
423 unsigned clen = block_get2(block); // comment field length
424 if (block_skip(block, 12) == -1)
425 // Premature end of the record.
426 break;
427
428 // Extract the name and skip over the extra and comment fields.
429 name = set_alloc(set, NULL, flen + 1);
430 if (block_read(block, (unsigned char *)name, flen) < flen ||
431 block_skip(block, xlen + clen) == -1)
432 // Premature end of the record.
433 break;
434
435 // Check for embedded nuls in the name.
436 if (memchr(name, 0, flen) != NULL) {
437 // This name can never match the zero-terminated name provided to
438 // zipAlreadyThere(), so we discard it and go back to get another
439 // name. (Who the heck is putting nuls inside their zip file entry
440 // names anyway?)
441 set_free(set, name);
442 continue;
443 }
444
445 // All good. Return the zero-terminated file name.
446 name[flen] = 0;
447 return name;
448 }
449
450 // Invalid signature or premature end of the central directory record.
451 // Abandon trying to process the central directory.
452 set_free(set, name);
453 block_stop(block);
454 return NULL;
455}
456
457// Return 0 if name is not in the central directory so far, 1 if it is, -1 if
458// the central directory is invalid, -2 if out of memory, or ZIP_PARAMERROR if
459// file is NULL.
460extern int ZEXPORT zipAlreadyThere(zipFile file, char const *name) {
461 zip64_internal *zip = file;
462 if (zip == NULL)
463 return ZIP_PARAMERROR;
464 if (zip->central_dir.first_block == NULL)
465 // No central directory yet, so no, name isn't there.
466 return 0;
467 if (setjmp(zip->set.env)) {
468 // Memory allocation failure.
469 set_end(&zip->set);
470 return -2;
471 }
472 if (!set_ok(&zip->set)) {
473 // This is the first time here with some central directory content. We
474 // construct this set of names only on demand. Prepare set and block.
475 set_start(&zip->set);
476 block_init(&zip->block, &zip->central_dir);
477 }
478
479 // Update the set of names from the current central directory contents.
480 // This reads any new central directory records since the last time we were
481 // here.
482 for (;;) {
483 char *there = block_central_name(&zip->block, &zip->set);
484 if (there == NULL) {
485 if (zip->block.next == NULL)
486 // The central directory is invalid.
487 return -1;
488 break;
489 }
490
491 // Add there to the set.
492 if (set_insert(&zip->set, there))
493 // There's already a duplicate in the central directory! We'll just
494 // let this be and carry on.
495 set_free(&zip->set, there);
496 }
497
498 // Return true if name is in the central directory.
499 return set_found(&zip->set, name);
500}
267 501
268 502
269/****************************************************************************/ 503/****************************************************************************/
@@ -551,7 +785,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib
551 785
552 for (i=(int)uReadSize-3; (i--)>0;) 786 for (i=(int)uReadSize-3; (i--)>0;)
553 { 787 {
554 // Signature "0x07064b50" Zip64 end of central directory locater 788 // Signature "0x07064b50" Zip64 end of central directory locator
555 if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) 789 if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07))
556 { 790 {
557 uPosFound = uReadPos+(unsigned)i; 791 uPosFound = uReadPos+(unsigned)i;
@@ -843,6 +1077,7 @@ extern zipFile ZEXPORT zipOpen3(const void *pathname, int append, zipcharpc* glo
843 ziinit.number_entry = 0; 1077 ziinit.number_entry = 0;
844 ziinit.add_position_when_writing_offset = 0; 1078 ziinit.add_position_when_writing_offset = 0;
845 init_linkedlist(&(ziinit.central_dir)); 1079 init_linkedlist(&(ziinit.central_dir));
1080 memset(&ziinit.set, 0, sizeof(set_t)); // make sure set appears dormant
846 1081
847 1082
848 1083
@@ -1870,6 +2105,8 @@ extern int ZEXPORT zipClose(zipFile file, const char* global_comment) {
1870 } 2105 }
1871 free_linkedlist(&(zi->central_dir)); 2106 free_linkedlist(&(zi->central_dir));
1872 2107
2108 set_end(&zi->set); // set was zeroed, so this is safe
2109
1873 pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; 2110 pos = centraldir_pos_inzip - zi->add_position_when_writing_offset;
1874 if(pos >= 0xffffffff || zi->number_entry >= 0xFFFF) 2111 if(pos >= 0xffffffff || zi->number_entry >= 0xFFFF)
1875 { 2112 {