/
archiver_zip.c
1430 lines (1156 loc) · 42.7 KB
1
2
3
/*
* ZIP support routines for PhysicsFS.
*
4
* Please see the file LICENSE.txt in the source's root directory.
5
*
6
7
* This file written by Ryan C. Gordon, with some peeking at "unzip.c"
* by Gilles Vollant.
8
9
*/
10
11
#if (defined PHYSFS_SUPPORTS_ZIP)
12
#include <errno.h>
13
#include <time.h>
14
15
16
17
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
18
19
20
21
22
23
24
#define USE_MINIZ 1
#if USE_MINIZ
#include "physfs_miniz.h"
#else
#include <zlib.h>
#endif
25
/*
26
27
* A buffer of ZIP_READBUFSIZE is allocated for each compressed file opened,
* and is freed when you close the file; compressed data is read into
28
29
30
31
32
33
34
35
36
37
38
* this buffer, and then is decompressed into the buffer passed to
* PHYSFS_read().
*
* Uncompressed entries in a zipfile do not allocate this buffer; they just
* read data directly into the buffer passed to PHYSFS_read().
*
* Depending on your speed and memory requirements, you should tweak this
* value.
*/
#define ZIP_READBUFSIZE (16 * 1024)
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
* Entries are "unresolved" until they are first opened. At that time,
* local file headers parsed/validated, data offsets will be updated to look
* at the actual file data instead of the header, and symlinks will be
* followed and optimized. This means that we don't seek and read around the
* archive until forced to do so, and after the first time, we had to do
* less reading and parsing, which is very CD-ROM friendly.
*/
typedef enum
{
ZIP_UNRESOLVED_FILE,
ZIP_UNRESOLVED_SYMLINK,
ZIP_RESOLVING,
ZIP_RESOLVED,
ZIP_BROKEN_FILE,
55
ZIP_BROKEN_SYMLINK
56
57
58
} ZipResolveType;
59
60
61
/*
* One ZIPentry is kept for each file in an open ZIP archive.
*/
62
typedef struct _ZIPentry
63
{
64
65
66
67
68
69
70
71
72
73
74
char *name; /* Name of file in archive */
struct _ZIPentry *symlink; /* NULL or file we symlink to */
ZipResolveType resolved; /* Have we resolved file/symlink? */
PHYSFS_uint32 offset; /* offset of data in archive */
PHYSFS_uint16 version; /* version made by */
PHYSFS_uint16 version_needed; /* version needed to extract */
PHYSFS_uint16 compression_method; /* compression method */
PHYSFS_uint32 crc; /* crc-32 */
PHYSFS_uint32 compressed_size; /* compressed size */
PHYSFS_uint32 uncompressed_size; /* uncompressed size */
PHYSFS_sint64 last_mod_time; /* last file mod time */
75
76
} ZIPentry;
77
78
79
/*
* One ZIPinfo is kept for each open ZIP archive.
*/
80
81
typedef struct
{
82
PHYSFS_Io *io;
83
84
PHYSFS_uint16 entryCount; /* Number of files in ZIP. */
ZIPentry *entries; /* info on all files in ZIP. */
85
86
} ZIPinfo;
87
88
89
/*
* One ZIPfileinfo is kept for each open file in a ZIP archive.
*/
90
91
typedef struct
{
92
ZIPentry *entry; /* Info on file. */
93
PHYSFS_Io *io; /* physical file handle. */
94
95
96
97
PHYSFS_uint32 compressed_position; /* offset in compressed data. */
PHYSFS_uint32 uncompressed_position; /* tell() position. */
PHYSFS_uint8 *buffer; /* decompression buffer. */
z_stream stream; /* zlib stream state. */
98
99
100
} ZIPfileinfo;
101
102
103
104
105
106
107
108
109
110
/* Magic numbers... */
#define ZIP_LOCAL_FILE_SIG 0x04034b50
#define ZIP_CENTRAL_DIR_SIG 0x02014b50
#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50
/* compression methods... */
#define COMPMETH_NONE 0
/* ...and others... */
111
112
113
114
#define UNIX_FILETYPE_MASK 0170000
#define UNIX_FILETYPE_SYMLINK 0120000
115
116
117
118
119
/*
* Bridge physfs allocation functions to zlib's format...
*/
static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size)
{
120
return ((PHYSFS_Allocator *) opaque)->Malloc(items * size);
121
122
123
124
125
126
127
} /* zlibPhysfsAlloc */
/*
* Bridge physfs allocation functions to zlib's format...
*/
static void zlibPhysfsFree(voidpf opaque, voidpf address)
{
128
((PHYSFS_Allocator *) opaque)->Free(address);
129
130
131
132
133
134
135
136
137
138
139
} /* zlibPhysfsFree */
/*
* Construct a new z_stream to a sane state.
*/
static void initializeZStream(z_stream *pstr)
{
memset(pstr, '\0', sizeof (z_stream));
pstr->zalloc = zlibPhysfsAlloc;
pstr->zfree = zlibPhysfsFree;
140
pstr->opaque = &allocator;
141
142
143
} /* initializeZStream */
144
static PHYSFS_ErrorCode zlib_error_code(int rc)
145
146
147
{
switch (rc)
{
148
149
150
151
152
case Z_OK: return PHYSFS_ERR_OK; /* not an error. */
case Z_STREAM_END: return PHYSFS_ERR_OK; /* not an error. */
case Z_ERRNO: return PHYSFS_ERR_IO;
case Z_MEM_ERROR: return PHYSFS_ERR_OUT_OF_MEMORY;
default: return PHYSFS_ERR_CORRUPT;
153
} /* switch */
154
155
} /* zlib_error_string */
156
157
158
159
/*
* Wrap all zlib calls in this, so the physfs error state is set appropriately.
*/
160
static int zlib_err(const int rc)
161
{
162
__PHYSFS_setError(zlib_error_code(rc));
163
return rc;
164
165
166
167
168
169
} /* zlib_err */
/*
* Read an unsigned 32-bit int and swap to native byte order.
*/
170
static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
171
172
{
PHYSFS_uint32 v;
173
BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
174
*val = PHYSFS_swapULE32(v);
175
return 1;
176
177
178
179
180
181
} /* readui32 */
/*
* Read an unsigned 16-bit int and swap to native byte order.
*/
182
static int readui16(PHYSFS_Io *io, PHYSFS_uint16 *val)
183
184
{
PHYSFS_uint16 v;
185
BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
186
*val = PHYSFS_swapULE16(v);
187
return 1;
188
189
190
} /* readui16 */
191
static PHYSFS_sint64 ZIP_read(PHYSFS_Io *_io, void *buf, PHYSFS_uint64 len)
192
{
193
194
ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
PHYSFS_Io *io = finfo->io;
195
196
ZIPentry *entry = finfo->entry;
PHYSFS_sint64 retval = 0;
197
PHYSFS_sint64 maxread = (PHYSFS_sint64) len;
198
199
200
201
PHYSFS_sint64 avail = entry->uncompressed_size -
finfo->uncompressed_position;
if (avail < maxread)
202
maxread = avail;
203
204
BAIL_IF_MACRO(maxread == 0, ERRPASS, 0); /* quick rejection. */
205
206
if (entry->compression_method == COMPMETH_NONE)
207
retval = io->read(io, buf, maxread);
208
209
210
else
{
finfo->stream.next_out = buf;
211
finfo->stream.avail_out = (uInt) maxread;
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
while (retval < maxread)
{
PHYSFS_uint32 before = finfo->stream.total_out;
int rc;
if (finfo->stream.avail_in == 0)
{
PHYSFS_sint64 br;
br = entry->compressed_size - finfo->compressed_position;
if (br > 0)
{
if (br > ZIP_READBUFSIZE)
br = ZIP_READBUFSIZE;
228
br = io->read(io, finfo->buffer, (PHYSFS_uint64) br);
229
230
231
if (br <= 0)
break;
232
finfo->compressed_position += (PHYSFS_uint32) br;
233
finfo->stream.next_in = finfo->buffer;
234
finfo->stream.avail_in = (PHYSFS_uint32) br;
235
236
} /* if */
} /* if */
237
238
239
rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH));
retval += (finfo->stream.total_out - before);
240
241
242
243
244
245
246
if (rc != Z_OK)
break;
} /* while */
} /* else */
if (retval > 0)
247
finfo->uncompressed_position += (PHYSFS_uint32) retval;
248
249
return retval;
250
251
252
} /* ZIP_read */
253
static PHYSFS_sint64 ZIP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
254
{
255
BAIL_MACRO(PHYSFS_ERR_READ_ONLY, -1);
256
257
258
} /* ZIP_write */
259
static PHYSFS_sint64 ZIP_tell(PHYSFS_Io *io)
260
{
261
return ((ZIPfileinfo *) io->opaque)->uncompressed_position;
262
263
264
} /* ZIP_tell */
265
static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset)
266
{
267
ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
268
ZIPentry *entry = finfo->entry;
269
PHYSFS_Io *io = finfo->io;
270
271
BAIL_IF_MACRO(offset > entry->uncompressed_size, PHYSFS_ERR_PAST_EOF, 0);
272
273
if (entry->compression_method == COMPMETH_NONE)
274
{
275
const PHYSFS_sint64 newpos = offset + entry->offset;
276
BAIL_IF_MACRO(!io->seek(io, newpos), ERRPASS, 0);
277
finfo->uncompressed_position = (PHYSFS_uint32) offset;
278
} /* if */
279
280
else
281
{
282
283
284
285
/*
* If seeking backwards, we need to redecode the file
* from the start and throw away the compressed bits until we hit
* the offset we need. If seeking forward, we still need to
286
* decode, but we don't rewind first.
287
288
289
290
291
*/
if (offset < finfo->uncompressed_position)
{
/* we do a copy so state is sane if inflateInit2() fails. */
z_stream str;
292
initializeZStream(&str);
293
if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK)
294
return 0;
295
296
if (!io->seek(io, entry->offset))
297
return 0;
298
299
300
301
302
303
304
305
306
inflateEnd(&finfo->stream);
memcpy(&finfo->stream, &str, sizeof (z_stream));
finfo->uncompressed_position = finfo->compressed_position = 0;
} /* if */
while (finfo->uncompressed_position != offset)
{
PHYSFS_uint8 buf[512];
307
308
309
PHYSFS_uint32 maxread;
maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position);
310
311
312
if (maxread > sizeof (buf))
maxread = sizeof (buf);
313
if (ZIP_read(_io, buf, maxread) != maxread)
314
return 0;
315
316
317
} /* while */
} /* else */
318
return 1;
319
320
321
} /* ZIP_seek */
322
static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io)
323
{
324
const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
325
return finfo->entry->uncompressed_size;
326
} /* ZIP_length */
327
328
329
330
331
static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry);
static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io)
332
{
333
334
335
ZIPfileinfo *origfinfo = (ZIPfileinfo *) io->opaque;
PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
ZIPfileinfo *finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
336
337
GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
338
339
340
341
memset(finfo, '\0', sizeof (*finfo));
finfo->entry = origfinfo->entry;
finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry);
342
GOTO_IF_MACRO(!finfo->io, ERRPASS, ZIP_duplicate_failed);
343
344
345
346
if (finfo->entry->compression_method != COMPMETH_NONE)
{
finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
347
348
349
if (!finfo->buffer)
GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
else if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
goto ZIP_duplicate_failed;
} /* if */
memcpy(retval, io, sizeof (PHYSFS_Io));
retval->opaque = finfo;
return retval;
ZIP_duplicate_failed:
if (finfo != NULL)
{
if (finfo->io != NULL)
finfo->io->destroy(finfo->io);
if (finfo->buffer != NULL)
{
allocator.Free(finfo->buffer);
inflateEnd(&finfo->stream);
} /* if */
allocator.Free(finfo);
} /* if */
if (retval != NULL)
allocator.Free(retval);
return NULL;
} /* ZIP_duplicate */
static int ZIP_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
static void ZIP_destroy(PHYSFS_Io *io)
{
ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
finfo->io->destroy(finfo->io);
384
385
386
387
388
if (finfo->entry->compression_method != COMPMETH_NONE)
inflateEnd(&finfo->stream);
if (finfo->buffer != NULL)
389
allocator.Free(finfo->buffer);
390
391
allocator.Free(finfo);
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
allocator.Free(io);
} /* ZIP_destroy */
static const PHYSFS_Io ZIP_Io =
{
ZIP_read,
ZIP_write,
ZIP_seek,
ZIP_tell,
ZIP_length,
ZIP_duplicate,
ZIP_flush,
ZIP_destroy,
NULL
};
409
410
411
static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len)
412
{
413
PHYSFS_uint8 buf[256];
414
PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 };
415
PHYSFS_sint32 i = 0;
416
417
418
PHYSFS_sint64 filelen;
PHYSFS_sint64 filepos;
PHYSFS_sint32 maxread;
419
420
PHYSFS_sint32 totalread = 0;
int found = 0;
421
422
filelen = io->length(io);
423
BAIL_IF_MACRO(filelen == -1, ERRPASS, 0);
424
425
426
427
428
429
430
431
432
/*
* Jump to the end of the file and start reading backwards.
* The last thing in the file is the zipfile comment, which is variable
* length, and the field that specifies its size is before it in the
* file (argh!)...this means that we need to scan backwards until we
* hit the end-of-central-dir signature. We can then sanity check that
* the comment was as big as it should be to make sure we're in the
* right place. The comment length field is 16 bits, so we can stop
433
434
* searching for that signature after a little more than 64k at most,
* and call it a corrupted zipfile.
435
436
437
438
439
440
441
442
443
444
*/
if (sizeof (buf) < filelen)
{
filepos = filelen - sizeof (buf);
maxread = sizeof (buf);
} /* if */
else
{
filepos = 0;
445
maxread = (PHYSFS_uint32) filelen;
446
447
} /* else */
448
while ((totalread < filelen) && (totalread < 65557))
449
{
450
BAIL_IF_MACRO(!io->seek(io, filepos), ERRPASS, -1);
451
452
453
/* make sure we catch a signature between buffers. */
if (totalread != 0)
454
{
455
if (!__PHYSFS_readAll(io, buf, maxread - 4))
456
return -1;
457
memcpy(&buf[maxread - 4], &extra, sizeof (extra));
458
totalread += maxread - 4;
459
} /* if */
460
461
else
{
462
if (!__PHYSFS_readAll(io, buf, maxread))
463
return -1;
464
465
466
totalread += maxread;
} /* else */
467
memcpy(&extra, buf, sizeof (extra));
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
for (i = maxread - 4; i > 0; i--)
{
if ((buf[i + 0] == 0x50) &&
(buf[i + 1] == 0x4B) &&
(buf[i + 2] == 0x05) &&
(buf[i + 3] == 0x06) )
{
found = 1; /* that's the signature! */
break;
} /* if */
} /* for */
if (found)
break;
filepos -= (maxread - 4);
485
486
if (filepos < 0)
filepos = 0;
487
488
} /* while */
489
BAIL_IF_MACRO(!found, PHYSFS_ERR_UNSUPPORTED, -1);
490
491
492
493
if (len != NULL)
*len = filelen;
494
return (filepos + i);
495
} /* zip_find_end_of_central_dir */
496
497
498
static int isZip(PHYSFS_Io *io)
499
{
500
PHYSFS_uint32 sig = 0;
501
int retval = 0;
502
503
504
505
506
/*
* The first thing in a zip file might be the signature of the
* first local file record, so it makes for a quick determination.
*/
507
if (readui32(io, &sig))
508
{
509
510
511
512
513
514
515
516
retval = (sig == ZIP_LOCAL_FILE_SIG);
if (!retval)
{
/*
* No sig...might be a ZIP with data at the start
* (a self-extracting executable, etc), so we'll have to do
* it the hard way...
*/
517
retval = (zip_find_end_of_central_dir(io, NULL) != -1);
518
} /* if */
519
520
} /* if */
521
return retval;
522
} /* isZip */
523
524
525
static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max)
526
{
527
528
PHYSFS_uint32 i;
for (i = 0; i < max; i++)
529
{
530
531
ZIPentry *entry = &entries[i];
if (entry->name != NULL)
532
allocator.Free(entry->name);
533
534
} /* for */
535
allocator.Free(entries);
536
537
538
} /* zip_free_entries */
539
540
541
542
543
/*
* This will find the ZIPentry associated with a path in platform-independent
* notation. Directories don't have ZIPentries associated with them, but
* (*isDir) will be set to non-zero if a dir was hit.
*/
544
545
static ZIPentry *zip_find_entry(const ZIPinfo *info, const char *path,
int *isDir)
546
547
{
ZIPentry *a = info->entries;
548
PHYSFS_sint32 pathlen = (PHYSFS_sint32) strlen(path);
549
PHYSFS_sint32 lo = 0;
550
551
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
PHYSFS_sint32 middle;
552
const char *thispath = NULL;
553
554
555
556
int rc;
while (lo <= hi)
{
557
558
559
middle = lo + ((hi - lo) / 2);
thispath = a[middle].name;
rc = strncmp(path, thispath, pathlen);
560
561
if (rc > 0)
562
lo = middle + 1;
563
564
else if (rc < 0)
565
hi = middle - 1;
566
567
568
else /* substring match...might be dir or entry or nothing. */
{
569
570
if (isDir != NULL)
{
571
572
*isDir = (thispath[pathlen] == '/');
if (*isDir)
573
return NULL;
574
575
576
} /* if */
if (thispath[pathlen] == '\0') /* found entry? */
577
return &a[middle];
578
579
580
/* adjust search params, try again. */
else if (thispath[pathlen] > '/')
hi = middle - 1;
581
else
582
lo = middle + 1;
583
584
} /* if */
} /* while */
585
586
587
if (isDir != NULL)
*isDir = 0;
588
589
BAIL_MACRO(PHYSFS_ERR_NO_SUCH_PATH, NULL);
590
} /* zip_find_entry */
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
/* Convert paths from old, buggy DOS zippers... */
static void zip_convert_dos_path(ZIPentry *entry, char *path)
{
PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF);
if (hosttype == 0) /* FS_FAT_ */
{
while (*path)
{
if (*path == '\\')
*path = '/';
path++;
} /* while */
} /* if */
} /* zip_convert_dos_path */
607
608
609
static void zip_expand_symlink_path(char *path)
610
{
611
612
char *ptr = path;
char *prevptr = path;
613
614
while (1)
615
{
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
ptr = strchr(ptr, '/');
if (ptr == NULL)
break;
if (*(ptr + 1) == '.')
{
if (*(ptr + 2) == '/')
{
/* current dir in middle of string: ditch it. */
memmove(ptr, ptr + 2, strlen(ptr + 2) + 1);
} /* else if */
else if (*(ptr + 2) == '\0')
{
/* current dir at end of string: ditch it. */
*ptr = '\0';
} /* else if */
else if (*(ptr + 2) == '.')
{
if (*(ptr + 3) == '/')
{
/* parent dir in middle: move back one, if possible. */
memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1);
ptr = prevptr;
while (prevptr != path)
{
prevptr--;
if (*prevptr == '/')
{
prevptr++;
break;
} /* if */
} /* while */
} /* if */
651
652
653
654
655
656
657
658
659
660
661
if (*(ptr + 3) == '\0')
{
/* parent dir at end: move back one, if possible. */
*prevptr = '\0';
} /* if */
} /* if */
} /* if */
else
{
prevptr = ptr;
662
ptr++;
663
664
665
} /* else */
} /* while */
} /* zip_expand_symlink_path */
666
667
/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
668
static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry);
669
670
/*
671
672
673
674
675
* Look for the entry named by (path). If it exists, resolve it, and return
* a pointer to that entry. If it's another symlink, keep resolving until you
* hit a real file and then return a pointer to the final non-symlink entry.
* If there's a problem, return NULL. (path) is always free()'d by this
* function.
676
*/
677
static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
678
{
679
680
681
ZIPentry *entry;
zip_expand_symlink_path(path);
682
entry = zip_find_entry(info, path, NULL);
683
684
if (entry != NULL)
{
685
if (!zip_resolve(io, info, entry)) /* recursive! */
686
687
688
689
690
691
692
693
entry = NULL;
else
{
if (entry->symlink != NULL)
entry = entry->symlink;
} /* else */
} /* if */
694
allocator.Free(path);
695
return entry;
696
} /* zip_follow_symlink */
697
698
699
static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
700
{
701
char *path;
702
const PHYSFS_uint32 size = entry->uncompressed_size;
703
704
int rc = 0;
705
706
707
708
709
710
/*
* We've already parsed the local file header of the symlink at this
* point. Now we need to read the actual link from the file data and
* follow it.
*/
711
BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
712
713
path = (char *) allocator.Malloc(size + 1);
714
BAIL_IF_MACRO(path == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0);
715
716
if (entry->compression_method == COMPMETH_NONE)
717
rc = __PHYSFS_readAll(io, path, size);
718
719
720
721
else /* symlink target path is compressed... */
{
z_stream stream;
722
const PHYSFS_uint32 complen = entry->compressed_size;
723
PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
724
725
if (compressed != NULL)
{
726
if (__PHYSFS_readAll(io, compressed, complen))
727
{
728
initializeZStream(&stream);
729
stream.next_in = compressed;
730
stream.avail_in = complen;
731
stream.next_out = (unsigned char *) path;
732
733
734
stream.avail_out = size;
if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK)
{
735
rc = zlib_err(inflate(&stream, Z_FINISH));
736
inflateEnd(&stream);
737
738
739
/* both are acceptable outcomes... */
rc = ((rc == Z_OK) || (rc == Z_STREAM_END));
740
741
} /* if */
} /* if */
742
__PHYSFS_smallFree(compressed);
743
744
} /* if */
} /* else */
745
746
if (!rc)
747
allocator.Free(path);
748
749
750
751
else
{
path[entry->uncompressed_size] = '\0'; /* null-terminate it. */
zip_convert_dos_path(entry, path);
752
entry->symlink = zip_follow_symlink(io, info, path);
753
754
} /* else */
755
return (entry->symlink != NULL);
756
757
758
759
760
761
} /* zip_resolve_symlink */
/*
* Parse the local file header of an entry, and update entry->offset.
*/
762
static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
763
764
765
766
767
768
{
PHYSFS_uint32 ui32;
PHYSFS_uint16 ui16;
PHYSFS_uint16 fnamelen;
PHYSFS_uint16 extralen;
769
770
771
772
773
774
775
/*
* crc and (un)compressed_size are always zero if this is a "JAR"
* archive created with Sun's Java tools, apparently. We only
* consider this archive corrupted if those entries don't match and
* aren't zero. That seems to work well.
*/
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0);
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
BAIL_IF_MACRO(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0);
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* general bits. */
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
BAIL_IF_MACRO(ui16 != entry->compression_method, PHYSFS_ERR_CORRUPT, 0);
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); /* date/time */
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), PHYSFS_ERR_CORRUPT, 0);
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
BAIL_IF_MACRO(ui32 && (ui32!=entry->compressed_size),PHYSFS_ERR_CORRUPT,0);
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
BAIL_IF_MACRO(ui32&&(ui32!=entry->uncompressed_size),PHYSFS_ERR_CORRUPT,0);
BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
793
794
entry->offset += fnamelen + extralen + 30;
795
return 1;
796
797
798
} /* zip_parse_local */
799
static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
800
801
802
803
804
{
int retval = 1;
ZipResolveType resolve_type = entry->resolved;
/* Don't bother if we've failed to resolve this entry before. */
805
806
BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, PHYSFS_ERR_CORRUPT, 0);
BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, PHYSFS_ERR_CORRUPT, 0);
807
808
/* uhoh...infinite symlink loop! */
809
BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, PHYSFS_ERR_SYMLINK_LOOP, 0);
810
811
812
813
814
815
816
817
818
819
820
821
/*
* We fix up the offset to point to the actual data on the
* first open, since we don't want to seek across the whole file on
* archive open (can be SLOW on large, CD-stored files), but we
* need to check the local file header...not just for corruption,
* but since it stores offset info the central directory does not.
*/
if (resolve_type != ZIP_RESOLVED)
{
entry->resolved = ZIP_RESOLVING;
822
retval = zip_parse_local(io, entry);
823
824
825
826
827
828
829
830
if (retval)
{
/*
* If it's a symlink, find the original file. This will cause
* resolution of other entries (other symlinks and, eventually,
* the real file) if all goes well.
*/
if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
831
retval = zip_resolve_symlink(io, info, entry);
832
833
834
835
836
837
} /* if */
if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK);
else if (resolve_type == ZIP_UNRESOLVED_FILE)
entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE);
838
} /* if */
839
840
return retval;
841
} /* zip_resolve */
842
843
844
static int zip_version_does_symlinks(PHYSFS_uint32 version)
845
846
{
int retval = 0;
847
PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
848
849
850
switch (hosttype)
{
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
/*
* These are the platforms that can NOT build an archive with
* symlinks, according to the Info-ZIP project.
*/
case 0: /* FS_FAT_ */
case 1: /* AMIGA_ */
case 2: /* VMS_ */
case 4: /* VM_CSM_ */
case 6: /* FS_HPFS_ */
case 11: /* FS_NTFS_ */
case 14: /* FS_VFAT_ */
case 13: /* ACORN_ */
case 15: /* MVS_ */
case 18: /* THEOS_ */
break; /* do nothing. */
default: /* assume the rest to be unix-like. */
868
869
870
871
retval = 1;
break;
} /* switch */
872
return retval;
873
874
875
} /* zip_version_does_symlinks */
876
static int zip_entry_is_symlink(const ZIPentry *entry)
877
{
878
879
880
return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) ||
(entry->resolved == ZIP_BROKEN_SYMLINK) ||
(entry->symlink));
881
} /* zip_entry_is_symlink */
882
883
884
static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr)
885
{
886
PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF);
887
888
889
return ( (zip_version_does_symlinks(entry->version)) &&
(entry->uncompressed_size > 0) &&
((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) );
890
} /* zip_has_symlink_attr */
891
892
893
static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
894
{
895
PHYSFS_uint32 dosdate;
896
897
struct tm unixtime;
memset(&unixtime, '\0', sizeof (unixtime));
898
899
900
dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
dostime &= 0xFFFF;
901
902
903
904
905
906
907
908
909
910
911
912
913
/* dissect date */
unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80;
unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1;
unixtime.tm_mday = ((dosdate ) & 0x1F);
/* dissect time */
unixtime.tm_hour = ((dostime >> 11) & 0x1F);
unixtime.tm_min = ((dostime >> 5) & 0x3F);
unixtime.tm_sec = ((dostime << 1) & 0x3E);
/* let mktime calculate daylight savings time. */
unixtime.tm_isdst = -1;
914
915
return ((PHYSFS_sint64) mktime(&unixtime));
916
} /* zip_dos_time_to_physfs_time */
917
918
919
static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixup)
920
921
922
923
924
925
926
927
{
PHYSFS_uint16 fnamelen, extralen, commentlen;
PHYSFS_uint32 external_attr;
PHYSFS_uint16 ui16;
PHYSFS_uint32 ui32;
PHYSFS_sint64 si64;
/* sanity check with central directory signature... */
928
929
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
930
931
/* Get the pertinent parts of the record... */
932
933
934
935
936
BAIL_IF_MACRO(!readui16(io, &entry->version), ERRPASS, 0);
BAIL_IF_MACRO(!readui16(io, &entry->version_needed), ERRPASS, 0);
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* general bits */
BAIL_IF_MACRO(!readui16(io, &entry->compression_method), ERRPASS, 0);
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
937
entry->last_mod_time = zip_dos_time_to_physfs_time(ui32);
938
939
940
941
942
943
944
945
946
947
BAIL_IF_MACRO(!readui32(io, &entry->crc), ERRPASS, 0);
BAIL_IF_MACRO(!readui32(io, &entry->compressed_size), ERRPASS, 0);
BAIL_IF_MACRO(!readui32(io, &entry->uncompressed_size), ERRPASS, 0);
BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
BAIL_IF_MACRO(!readui16(io, &commentlen), ERRPASS, 0);
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* disk number start */
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* internal file attribs */
BAIL_IF_MACRO(!readui32(io, &external_attr), ERRPASS, 0);
BAIL_IF_MACRO(!readui32(io, &entry->offset), ERRPASS, 0);
948
entry->offset += ofs_fixup;
949
950
951
952
entry->symlink = NULL; /* will be resolved later, if necessary. */
entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ?
ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE;
953
954
entry->name = (char *) allocator.Malloc(fnamelen + 1);
955
BAIL_IF_MACRO(entry->name == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0);
956
if (!__PHYSFS_readAll(io, entry->name, fnamelen))
957
goto zip_load_entry_puked;
958
959
entry->name[fnamelen] = '\0'; /* null-terminate the filename. */
960
zip_convert_dos_path(entry, entry->name);
961
962
si64 = io->tell(io);
963
if (si64 == -1)
964
goto zip_load_entry_puked;
965
966
/* seek to the start of the next entry in the central directory... */
967
if (!io->seek(io, si64 + extralen + commentlen))
968
goto zip_load_entry_puked;
969
970
return 1; /* success. */
971
972
zip_load_entry_puked:
973
allocator.Free(entry->name);
974
return 0; /* failure. */
975
976
977
} /* zip_load_entry */
978
static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
979
{
980
981
982
if (one != two)
{
const ZIPentry *a = (const ZIPentry *) _a;
983
return strcmp(a[one].name, a[two].name);
984
985
986
} /* if */
return 0;
987
} /* zip_entry_cmp */
988
989
990
static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
991
{
992
993
994
995
996
997
998
999
1000
if (one != two)
{
ZIPentry tmp;
ZIPentry *first = &(((ZIPentry *) _a)[one]);
ZIPentry *second = &(((ZIPentry *) _a)[two]);
memcpy(&tmp, first, sizeof (ZIPentry));
memcpy(first, second, sizeof (ZIPentry));
memcpy(second, &tmp, sizeof (ZIPentry));
} /* if */