diff options
Diffstat (limited to '')
-rw-r--r-- | contrib/minizip/zip.c | 239 |
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. | ||
128 | typedef 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" | ||
132 | typedef 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 | |||
126 | typedef struct | 139 | typedef 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. | ||
297 | local 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. | ||
306 | local 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. | ||
312 | local 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. | ||
331 | local 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. | ||
357 | local 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. | ||
363 | local 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. | ||
388 | local 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. | ||
405 | local 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. | ||
460 | extern 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 | { |