/
archiver_zip.c
1730 lines (1398 loc) · 53 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
12
13
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_ZIP
14
15
#include <errno.h>
16
#include <time.h>
17
18
19
#include "physfs_miniz.h"
20
/*
21
22
* 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
23
24
25
26
27
28
29
30
31
32
33
* 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)
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
* 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,
49
ZIP_DIRECTORY,
50
ZIP_BROKEN_FILE,
51
ZIP_BROKEN_SYMLINK
52
53
54
} ZipResolveType;
55
56
57
/*
* One ZIPentry is kept for each file in an open ZIP archive.
*/
58
typedef struct _ZIPentry
59
{
60
61
62
char *name; /* Name of file in archive */
struct _ZIPentry *symlink; /* NULL or file we symlink to */
ZipResolveType resolved; /* Have we resolved file/symlink? */
63
PHYSFS_uint64 offset; /* offset of data in archive */
64
65
66
67
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 */
68
69
PHYSFS_uint64 compressed_size; /* compressed size */
PHYSFS_uint64 uncompressed_size; /* uncompressed size */
70
PHYSFS_sint64 last_mod_time; /* last file mod time */
71
72
73
struct _ZIPentry *hashnext; /* next item in this hash bucket */
struct _ZIPentry *children; /* linked list of kids, if dir */
struct _ZIPentry *sibling; /* next item in same dir */
74
75
} ZIPentry;
76
77
78
/*
* One ZIPinfo is kept for each open ZIP archive.
*/
79
80
typedef struct
{
81
PHYSFS_Io *io; /* the i/o interface for this archive. */
82
83
84
ZIPentry root; /* root of directory tree. */
ZIPentry **hash; /* all entries hashed for fast lookup. */
size_t hashBuckets; /* number of buckets in hash. */
85
int zip64; /* non-zero if this is a Zip64 archive. */
86
87
} ZIPinfo;
88
89
90
/*
* One ZIPfileinfo is kept for each open file in a ZIP archive.
*/
91
92
typedef struct
{
93
ZIPentry *entry; /* Info on file. */
94
PHYSFS_Io *io; /* physical file handle. */
95
96
97
98
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. */
99
100
101
} ZIPfileinfo;
102
/* Magic numbers... */
103
104
105
106
107
108
#define ZIP_LOCAL_FILE_SIG 0x04034b50
#define ZIP_CENTRAL_DIR_SIG 0x02014b50
#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50
#define ZIP64_END_OF_CENTRAL_DIR_SIG 0x06064b50
#define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50
#define ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG 0x0001
109
110
111
112
113
114
/* compression methods... */
#define COMPMETH_NONE 0
/* ...and others... */
115
116
117
118
#define UNIX_FILETYPE_MASK 0170000
#define UNIX_FILETYPE_SYMLINK 0120000
119
120
121
122
123
/*
* Bridge physfs allocation functions to zlib's format...
*/
static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size)
{
124
return ((PHYSFS_Allocator *) opaque)->Malloc(items * size);
125
126
127
128
129
130
131
} /* zlibPhysfsAlloc */
/*
* Bridge physfs allocation functions to zlib's format...
*/
static void zlibPhysfsFree(voidpf opaque, voidpf address)
{
132
((PHYSFS_Allocator *) opaque)->Free(address);
133
134
135
136
137
138
139
140
141
142
143
} /* 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;
144
pstr->opaque = &allocator;
145
146
147
} /* initializeZStream */
148
static PHYSFS_ErrorCode zlib_error_code(int rc)
149
150
151
{
switch (rc)
{
152
153
154
155
156
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;
157
} /* switch */
158
159
} /* zlib_error_string */
160
161
162
163
/*
* Wrap all zlib calls in this, so the physfs error state is set appropriately.
*/
164
static int zlib_err(const int rc)
165
{
166
PHYSFS_setErrorCode(zlib_error_code(rc));
167
return rc;
168
169
} /* zlib_err */
170
171
172
173
174
175
176
/*
* Hash a string for lookup an a ZIPinfo hashtable.
*/
static inline PHYSFS_uint32 zip_hash_string(const ZIPinfo *info, const char *s)
{
return __PHYSFS_hashString(s, strlen(s)) % info->hashBuckets;
} /* zip_hash_string */
177
178
179
180
181
182
183
184
185
186
187
188
/*
* Read an unsigned 64-bit int and swap to native byte order.
*/
static int readui64(PHYSFS_Io *io, PHYSFS_uint64 *val)
{
PHYSFS_uint64 v;
BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
*val = PHYSFS_swapULE64(v);
return 1;
} /* readui64 */
189
190
191
/*
* Read an unsigned 32-bit int and swap to native byte order.
*/
192
static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
193
194
{
PHYSFS_uint32 v;
195
BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
196
*val = PHYSFS_swapULE32(v);
197
return 1;
198
199
200
201
202
203
} /* readui32 */
/*
* Read an unsigned 16-bit int and swap to native byte order.
*/
204
static int readui16(PHYSFS_Io *io, PHYSFS_uint16 *val)
205
206
{
PHYSFS_uint16 v;
207
BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
208
*val = PHYSFS_swapULE16(v);
209
return 1;
210
211
212
} /* readui16 */
213
static PHYSFS_sint64 ZIP_read(PHYSFS_Io *_io, void *buf, PHYSFS_uint64 len)
214
{
215
216
ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
PHYSFS_Io *io = finfo->io;
217
218
ZIPentry *entry = finfo->entry;
PHYSFS_sint64 retval = 0;
219
PHYSFS_sint64 maxread = (PHYSFS_sint64) len;
220
221
222
223
PHYSFS_sint64 avail = entry->uncompressed_size -
finfo->uncompressed_position;
if (avail < maxread)
224
maxread = avail;
225
226
BAIL_IF_MACRO(maxread == 0, ERRPASS, 0); /* quick rejection. */
227
228
if (entry->compression_method == COMPMETH_NONE)
229
retval = io->read(io, buf, maxread);
230
231
232
else
{
finfo->stream.next_out = buf;
233
finfo->stream.avail_out = (uInt) maxread;
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
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;
250
br = io->read(io, finfo->buffer, (PHYSFS_uint64) br);
251
252
253
if (br <= 0)
break;
254
finfo->compressed_position += (PHYSFS_uint32) br;
255
finfo->stream.next_in = finfo->buffer;
256
finfo->stream.avail_in = (PHYSFS_uint32) br;
257
258
} /* if */
} /* if */
259
260
261
rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH));
retval += (finfo->stream.total_out - before);
262
263
264
265
266
267
268
if (rc != Z_OK)
break;
} /* while */
} /* else */
if (retval > 0)
269
finfo->uncompressed_position += (PHYSFS_uint32) retval;
270
271
return retval;
272
273
274
} /* ZIP_read */
275
static PHYSFS_sint64 ZIP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
276
{
277
BAIL_MACRO(PHYSFS_ERR_READ_ONLY, -1);
278
279
280
} /* ZIP_write */
281
static PHYSFS_sint64 ZIP_tell(PHYSFS_Io *io)
282
{
283
return ((ZIPfileinfo *) io->opaque)->uncompressed_position;
284
285
286
} /* ZIP_tell */
287
static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset)
288
{
289
ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
290
ZIPentry *entry = finfo->entry;
291
PHYSFS_Io *io = finfo->io;
292
293
BAIL_IF_MACRO(offset > entry->uncompressed_size, PHYSFS_ERR_PAST_EOF, 0);
294
295
if (entry->compression_method == COMPMETH_NONE)
296
{
297
const PHYSFS_sint64 newpos = offset + entry->offset;
298
BAIL_IF_MACRO(!io->seek(io, newpos), ERRPASS, 0);
299
finfo->uncompressed_position = (PHYSFS_uint32) offset;
300
} /* if */
301
302
else
303
{
304
305
306
307
/*
* 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
308
* decode, but we don't rewind first.
309
310
311
312
313
*/
if (offset < finfo->uncompressed_position)
{
/* we do a copy so state is sane if inflateInit2() fails. */
z_stream str;
314
initializeZStream(&str);
315
if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK)
316
return 0;
317
318
if (!io->seek(io, entry->offset))
319
return 0;
320
321
322
323
324
325
326
327
328
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];
329
330
331
PHYSFS_uint32 maxread;
maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position);
332
333
334
if (maxread > sizeof (buf))
maxread = sizeof (buf);
335
if (ZIP_read(_io, buf, maxread) != maxread)
336
return 0;
337
338
339
} /* while */
} /* else */
340
return 1;
341
342
343
} /* ZIP_seek */
344
static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io)
345
{
346
const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
347
return (PHYSFS_sint64) finfo->entry->uncompressed_size;
348
} /* ZIP_length */
349
350
351
352
353
static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry);
static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io)
354
{
355
356
357
ZIPfileinfo *origfinfo = (ZIPfileinfo *) io->opaque;
PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
ZIPfileinfo *finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
358
359
GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, failed);
GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, failed);
360
361
362
363
memset(finfo, '\0', sizeof (*finfo));
finfo->entry = origfinfo->entry;
finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry);
364
GOTO_IF_MACRO(!finfo->io, ERRPASS, failed);
365
366
367
368
if (finfo->entry->compression_method != COMPMETH_NONE)
{
finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
369
370
371
GOTO_IF_MACRO(!finfo->buffer, PHYSFS_ERR_OUT_OF_MEMORY, failed);
if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
goto failed;
372
373
374
375
376
377
} /* if */
memcpy(retval, io, sizeof (PHYSFS_Io));
retval->opaque = finfo;
return retval;
378
failed:
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
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);
405
406
407
408
409
if (finfo->entry->compression_method != COMPMETH_NONE)
inflateEnd(&finfo->stream);
if (finfo->buffer != NULL)
410
allocator.Free(finfo->buffer);
411
412
allocator.Free(finfo);
413
414
415
416
417
418
allocator.Free(io);
} /* ZIP_destroy */
static const PHYSFS_Io ZIP_Io =
{
419
CURRENT_PHYSFS_IO_API_VERSION, NULL,
420
421
422
423
424
425
426
ZIP_read,
ZIP_write,
ZIP_seek,
ZIP_tell,
ZIP_length,
ZIP_duplicate,
ZIP_flush,
427
ZIP_destroy
428
429
};
430
431
432
static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len)
433
{
434
PHYSFS_uint8 buf[256];
435
PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 };
436
PHYSFS_sint32 i = 0;
437
438
439
PHYSFS_sint64 filelen;
PHYSFS_sint64 filepos;
PHYSFS_sint32 maxread;
440
441
PHYSFS_sint32 totalread = 0;
int found = 0;
442
443
filelen = io->length(io);
444
BAIL_IF_MACRO(filelen == -1, ERRPASS, -1);
445
446
447
448
449
450
451
452
453
/*
* 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
454
455
* searching for that signature after a little more than 64k at most,
* and call it a corrupted zipfile.
456
457
458
459
460
461
462
463
464
465
*/
if (sizeof (buf) < filelen)
{
filepos = filelen - sizeof (buf);
maxread = sizeof (buf);
} /* if */
else
{
filepos = 0;
466
maxread = (PHYSFS_uint32) filelen;
467
468
} /* else */
469
while ((totalread < filelen) && (totalread < 65557))
470
{
471
BAIL_IF_MACRO(!io->seek(io, filepos), ERRPASS, -1);
472
473
474
/* make sure we catch a signature between buffers. */
if (totalread != 0)
475
{
476
if (!__PHYSFS_readAll(io, buf, maxread - 4))
477
return -1;
478
memcpy(&buf[maxread - 4], &extra, sizeof (extra));
479
totalread += maxread - 4;
480
} /* if */
481
482
else
{
483
if (!__PHYSFS_readAll(io, buf, maxread))
484
return -1;
485
486
487
totalread += maxread;
} /* else */
488
memcpy(&extra, buf, sizeof (extra));
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
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);
506
507
if (filepos < 0)
filepos = 0;
508
509
} /* while */
510
BAIL_IF_MACRO(!found, PHYSFS_ERR_UNSUPPORTED, -1);
511
512
513
514
if (len != NULL)
*len = filelen;
515
return (filepos + i);
516
} /* zip_find_end_of_central_dir */
517
518
519
static int isZip(PHYSFS_Io *io)
520
{
521
PHYSFS_uint32 sig = 0;
522
int retval = 0;
523
524
525
526
527
/*
* The first thing in a zip file might be the signature of the
* first local file record, so it makes for a quick determination.
*/
528
if (readui32(io, &sig))
529
{
530
531
532
533
534
535
536
537
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...
*/
538
retval = (zip_find_end_of_central_dir(io, NULL) != -1);
539
} /* if */
540
541
} /* if */
542
return retval;
543
} /* isZip */
544
545
546
547
/* Find the ZIPentry for a path in platform-independent notation. */
static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path)
548
{
549
550
551
PHYSFS_uint32 hashval;
ZIPentry *prev = NULL;
ZIPentry *retval;
552
553
554
if (*path == '\0')
return &info->root;
555
556
557
hashval = zip_hash_string(info, path);
for (retval = info->hash[hashval]; retval; retval = retval->hashnext)
558
{
559
if (strcmp(retval->name, path) == 0)
560
{
561
if (prev != NULL) /* move this to the front of the list */
562
{
563
564
565
prev->hashnext = retval->hashnext;
retval->hashnext = info->hash[hashval];
info->hash[hashval] = retval;
566
567
} /* if */
568
return retval;
569
} /* if */
570
571
572
prev = retval;
} /* for */
573
574
BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, NULL);
575
} /* zip_find_entry */
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
/* 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 */
592
593
594
static void zip_expand_symlink_path(char *path)
595
{
596
597
char *ptr = path;
char *prevptr = path;
598
599
while (1)
600
{
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
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 */
636
637
638
639
640
641
642
643
644
645
646
if (*(ptr + 3) == '\0')
{
/* parent dir at end: move back one, if possible. */
*prevptr = '\0';
} /* if */
} /* if */
} /* if */
else
{
prevptr = ptr;
647
ptr++;
648
649
650
} /* else */
} /* while */
} /* zip_expand_symlink_path */
651
652
/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
653
static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry);
654
655
/*
656
657
658
* 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.
659
* If there's a problem, return NULL.
660
*/
661
static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
662
{
663
664
665
ZIPentry *entry;
zip_expand_symlink_path(path);
666
entry = zip_find_entry(info, path);
667
668
if (entry != NULL)
{
669
if (!zip_resolve(io, info, entry)) /* recursive! */
670
671
672
673
674
675
676
677
entry = NULL;
else
{
if (entry->symlink != NULL)
entry = entry->symlink;
} /* else */
} /* if */
678
return entry;
679
} /* zip_follow_symlink */
680
681
682
static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
683
{
684
const PHYSFS_uint64 size = entry->uncompressed_size;
685
char *path = NULL;
686
687
int rc = 0;
688
689
690
691
692
693
/*
* 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.
*/
694
BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
695
696
697
path = (char *) __PHYSFS_smallAlloc(size + 1);
BAIL_IF_MACRO(!path, PHYSFS_ERR_OUT_OF_MEMORY, 0);
698
699
if (entry->compression_method == COMPMETH_NONE)
700
rc = __PHYSFS_readAll(io, path, size);
701
702
703
704
else /* symlink target path is compressed... */
{
z_stream stream;
705
const PHYSFS_uint64 complen = entry->compressed_size;
706
PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
707
708
if (compressed != NULL)
{
709
if (__PHYSFS_readAll(io, compressed, complen))
710
{
711
initializeZStream(&stream);
712
stream.next_in = compressed;
713
stream.avail_in = complen;
714
stream.next_out = (unsigned char *) path;
715
716
717
stream.avail_out = size;
if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK)
{
718
rc = zlib_err(inflate(&stream, Z_FINISH));
719
inflateEnd(&stream);
720
721
722
/* both are acceptable outcomes... */
rc = ((rc == Z_OK) || (rc == Z_STREAM_END));
723
724
} /* if */
} /* if */
725
__PHYSFS_smallFree(compressed);
726
727
} /* if */
} /* else */
728
729
if (rc)
730
731
732
{
path[entry->uncompressed_size] = '\0'; /* null-terminate it. */
zip_convert_dos_path(entry, path);
733
entry->symlink = zip_follow_symlink(io, info, path);
734
735
} /* else */
736
737
__PHYSFS_smallFree(path);
738
return (entry->symlink != NULL);
739
740
741
742
743
744
} /* zip_resolve_symlink */
/*
* Parse the local file header of an entry, and update entry->offset.
*/
745
static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
746
747
748
749
750
751
{
PHYSFS_uint32 ui32;
PHYSFS_uint16 ui16;
PHYSFS_uint16 fnamelen;
PHYSFS_uint16 extralen;
752
753
754
755
756
/*
* 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.
757
758
* We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's
* possible that's a Zip64 thing.
759
760
*/
761
762
763
764
765
766
767
768
769
770
771
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);
772
773
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
774
775
776
BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
(ui32 != entry->compressed_size), PHYSFS_ERR_CORRUPT, 0);
777
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
778
779
780
BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
(ui32 != entry->uncompressed_size), PHYSFS_ERR_CORRUPT, 0);
781
782
BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
783
784
entry->offset += fnamelen + extralen + 30;
785
return 1;
786
787
788
} /* zip_parse_local */
789
static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
790
791
{
int retval = 1;
792
793
794
795
const ZipResolveType resolve_type = entry->resolved;
if (resolve_type == ZIP_DIRECTORY)
return 1; /* we're good. */
796
797
/* Don't bother if we've failed to resolve this entry before. */
798
799
BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, PHYSFS_ERR_CORRUPT, 0);
BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, PHYSFS_ERR_CORRUPT, 0);
800
801
/* uhoh...infinite symlink loop! */
802
BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, PHYSFS_ERR_SYMLINK_LOOP, 0);
803
804
805
806
807
808
809
810
811
812
813
814
/*
* 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;
815
retval = zip_parse_local(io, entry);
816
817
818
819
820
821
822
823
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)
824
retval = zip_resolve_symlink(io, info, entry);
825
826
827
828
829
830
} /* 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);
831
} /* if */
832
833
return retval;
834
} /* zip_resolve */
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
static int zip_hash_entry(ZIPinfo *info, ZIPentry *entry);
/* Fill in missing parent directories. */
static ZIPentry *zip_hash_ancestors(ZIPinfo *info, char *name)
{
ZIPentry *retval = &info->root;
char *sep = strrchr(name, '/');
if (sep)
{
const size_t namelen = (sep - name) + 1;
ZIPentry *parent;
*sep = '\0'; /* chop off last piece. */
retval = zip_find_entry(info, name);
*sep = '/';
if (retval != NULL)
{
if (retval->resolved != ZIP_DIRECTORY)
BAIL_MACRO(PHYSFS_ERR_CORRUPT, NULL);
return retval; /* already hashed. */
} /* if */
/* okay, this is a new dir. Build and hash us. */
retval = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) + namelen);
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
memset(retval, '\0', sizeof (*retval));
retval->name = ((char *) retval) + sizeof (ZIPentry);
memcpy(retval->name, name, namelen);
retval->name[namelen] = '\0';
retval->resolved = ZIP_DIRECTORY;
if (!zip_hash_entry(info, retval))
{
allocator.Free(retval);
return NULL;
} /* if */
} /* else */
return retval;
} /* zip_hash_ancestors */
static int zip_hash_entry(ZIPinfo *info, ZIPentry *entry)
{
PHYSFS_uint32 hashval;
ZIPentry *parent;
assert(!zip_find_entry(info, entry->name)); /* checked elsewhere */
parent = zip_hash_ancestors(info, entry->name);
if (!parent)
return 0;
hashval = zip_hash_string(info, entry->name);
entry->hashnext = info->hash[hashval];
info->hash[hashval] = entry;
entry->sibling = parent->children;
parent->children = entry;
return 1;
} /* zip_hash_entry */
static int zip_entry_is_symlink(const ZIPentry *entry)
{
return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) ||
(entry->resolved == ZIP_BROKEN_SYMLINK) ||
(entry->symlink));
} /* zip_entry_is_symlink */
909
static int zip_version_does_symlinks(PHYSFS_uint32 version)
910
911
{
int retval = 0;
912
PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
913
914
915
switch (hosttype)
{
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
/*
* 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. */
933
934
935
936
retval = 1;
break;
} /* switch */
937
return retval;
938
939
940
941
} /* zip_version_does_symlinks */
static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr)
942
{
943
PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF);
944
945
946
return ( (zip_version_does_symlinks(entry->version)) &&
(entry->uncompressed_size > 0) &&
((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) );
947
} /* zip_has_symlink_attr */
948
949
950
static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
951
{
952
PHYSFS_uint32 dosdate;
953
954
struct tm unixtime;
memset(&unixtime, '\0', sizeof (unixtime));
955
956
957
dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
dostime &= 0xFFFF;
958
959
960
961
962
963
964
965
966
967
968
969
970
/* 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;
971
972
return ((PHYSFS_sint64) mktime(&unixtime));
973
} /* zip_dos_time_to_physfs_time */
974
975
976
977
static ZIPentry *zip_load_entry(PHYSFS_Io *io, const int zip64,
const PHYSFS_uint64 ofs_fixup)
978
{
979
980
ZIPentry entry;
ZIPentry *retval = NULL;
981
982
PHYSFS_uint16 fnamelen, extralen, commentlen;
PHYSFS_uint32 external_attr;
983
984
PHYSFS_uint32 starting_disk;
PHYSFS_uint64 offset;
985
986
987
988
PHYSFS_uint16 ui16;
PHYSFS_uint32 ui32;
PHYSFS_sint64 si64;
989
990
memset(&entry, '\0', sizeof (entry));
991
/* sanity check with central directory signature... */
992
993
if (!readui32(io, &ui32)) return NULL;
BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, NULL);
994
995
/* Get the pertinent parts of the record... */
996
997
998
999
1000
if (!readui16(io, &entry.version)) return NULL;
if (!readui16(io, &entry.version_needed)) return NULL;
if (!readui16(io, &ui16)) return NULL; /* general bits */
if (!readui16(io, &entry.compression_method)) return NULL;
if (!readui32(io, &ui32)) return NULL;