/
archiver_zip.c
1431 lines (1159 loc) · 42.6 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 const char *zlib_error_string(int rc)
145
146
147
{
switch (rc)
{
148
149
case Z_OK: return NULL; /* not an error. */
case Z_STREAM_END: return NULL; /* not an error. */
150
case Z_ERRNO: return ERR_IO_ERROR;
151
152
153
154
155
156
case Z_NEED_DICT: return ERR_NEED_DICT;
case Z_DATA_ERROR: return ERR_DATA_ERROR;
case Z_MEM_ERROR: return ERR_MEMORY_ERROR;
case Z_BUF_ERROR: return ERR_BUFFER_ERROR;
case Z_VERSION_ERROR: return ERR_VERSION_ERROR;
default: return ERR_UNKNOWN_ERROR;
157
} /* switch */
158
159
} /* zlib_error_string */
160
161
162
163
164
165
166
167
168
/*
* Wrap all zlib calls in this, so the physfs error state is set appropriately.
*/
static int zlib_err(int rc)
{
const char *str = zlib_error_string(rc);
if (str != NULL)
__PHYSFS_setError(str);
169
return rc;
170
171
172
173
174
175
} /* zlib_err */
/*
* Read an unsigned 32-bit int and swap to native byte order.
*/
176
static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
177
178
{
PHYSFS_uint32 v;
179
BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), NULL, 0);
180
*val = PHYSFS_swapULE32(v);
181
return 1;
182
183
184
185
186
187
} /* readui32 */
/*
* Read an unsigned 16-bit int and swap to native byte order.
*/
188
static int readui16(PHYSFS_Io *io, PHYSFS_uint16 *val)
189
190
{
PHYSFS_uint16 v;
191
BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), NULL, 0);
192
*val = PHYSFS_swapULE16(v);
193
return 1;
194
195
196
} /* readui16 */
197
static PHYSFS_sint64 ZIP_read(PHYSFS_Io *_io, void *buf, PHYSFS_uint64 len)
198
{
199
200
ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
PHYSFS_Io *io = finfo->io;
201
202
ZIPentry *entry = finfo->entry;
PHYSFS_sint64 retval = 0;
203
PHYSFS_sint64 maxread = (PHYSFS_sint64) len;
204
205
206
207
PHYSFS_sint64 avail = entry->uncompressed_size -
finfo->uncompressed_position;
if (avail < maxread)
208
maxread = avail;
209
210
BAIL_IF_MACRO(maxread == 0, NULL, 0); /* quick rejection. */
211
212
if (entry->compression_method == COMPMETH_NONE)
213
retval = io->read(io, buf, maxread);
214
215
216
else
{
finfo->stream.next_out = buf;
217
finfo->stream.avail_out = maxread;
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
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;
234
br = io->read(io, finfo->buffer, (PHYSFS_uint64) br);
235
236
237
if (br <= 0)
break;
238
finfo->compressed_position += (PHYSFS_uint32) br;
239
finfo->stream.next_in = finfo->buffer;
240
finfo->stream.avail_in = (PHYSFS_uint32) br;
241
242
} /* if */
} /* if */
243
244
245
rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH));
retval += (finfo->stream.total_out - before);
246
247
248
249
250
251
252
if (rc != Z_OK)
break;
} /* while */
} /* else */
if (retval > 0)
253
finfo->uncompressed_position += (PHYSFS_uint32) retval;
254
255
return retval;
256
257
258
} /* ZIP_read */
259
static PHYSFS_sint64 ZIP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
260
261
262
263
264
{
BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
} /* ZIP_write */
265
static PHYSFS_sint64 ZIP_tell(PHYSFS_Io *io)
266
{
267
return ((ZIPfileinfo *) io->opaque)->uncompressed_position;
268
269
270
} /* ZIP_tell */
271
static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset)
272
{
273
ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
274
ZIPentry *entry = finfo->entry;
275
PHYSFS_Io *io = finfo->io;
276
277
BAIL_IF_MACRO(offset > entry->uncompressed_size, ERR_PAST_EOF, 0);
278
279
if (entry->compression_method == COMPMETH_NONE)
280
{
281
282
const PHYSFS_sint64 newpos = offset + entry->offset;
BAIL_IF_MACRO(!io->seek(io, newpos), NULL, 0);
283
finfo->uncompressed_position = (PHYSFS_uint32) offset;
284
} /* if */
285
286
else
287
{
288
289
290
291
/*
* 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
292
* decode, but we don't rewind first.
293
294
295
296
297
*/
if (offset < finfo->uncompressed_position)
{
/* we do a copy so state is sane if inflateInit2() fails. */
z_stream str;
298
initializeZStream(&str);
299
if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK)
300
return 0;
301
302
if (!io->seek(io, entry->offset))
303
return 0;
304
305
306
307
308
309
310
311
312
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];
313
314
315
PHYSFS_uint32 maxread;
maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position);
316
317
318
if (maxread > sizeof (buf))
maxread = sizeof (buf);
319
if (ZIP_read(_io, buf, maxread) != maxread)
320
return 0;
321
322
323
} /* while */
} /* else */
324
return 1;
325
326
327
} /* ZIP_seek */
328
static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io)
329
{
330
const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
331
return finfo->entry->uncompressed_size;
332
} /* ZIP_length */
333
334
335
336
337
static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry);
static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io)
338
{
339
340
341
342
343
344
345
346
347
348
349
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
384
385
386
387
388
ZIPfileinfo *origfinfo = (ZIPfileinfo *) io->opaque;
PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
ZIPfileinfo *finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
memset(finfo, '\0', sizeof (*finfo));
finfo->entry = origfinfo->entry;
finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry);
GOTO_IF_MACRO(finfo->io == NULL, NULL, ZIP_duplicate_failed);
if (finfo->entry->compression_method != COMPMETH_NONE)
{
finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
GOTO_IF_MACRO(!finfo->buffer, ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
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);
389
390
391
392
393
if (finfo->entry->compression_method != COMPMETH_NONE)
inflateEnd(&finfo->stream);
if (finfo->buffer != NULL)
394
allocator.Free(finfo->buffer);
395
396
allocator.Free(finfo);
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
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
};
414
415
416
static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len)
417
{
418
PHYSFS_uint8 buf[256];
419
PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 };
420
PHYSFS_sint32 i = 0;
421
422
423
PHYSFS_sint64 filelen;
PHYSFS_sint64 filepos;
PHYSFS_sint32 maxread;
424
425
PHYSFS_sint32 totalread = 0;
int found = 0;
426
427
filelen = io->length(io);
428
BAIL_IF_MACRO(filelen == -1, NULL, 0); /* !!! FIXME: unlocalized string */
429
BAIL_IF_MACRO(filelen > 0xFFFFFFFF, "ZIP bigger than 4 gigs?!", 0);
430
431
432
433
434
435
436
437
438
/*
* 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
439
440
* searching for that signature after a little more than 64k at most,
* and call it a corrupted zipfile.
441
442
443
444
445
446
447
448
449
450
*/
if (sizeof (buf) < filelen)
{
filepos = filelen - sizeof (buf);
maxread = sizeof (buf);
} /* if */
else
{
filepos = 0;
451
maxread = (PHYSFS_uint32) filelen;
452
453
} /* else */
454
while ((totalread < filelen) && (totalread < 65557))
455
{
456
BAIL_IF_MACRO(!io->seek(io, filepos), NULL, -1);
457
458
459
/* make sure we catch a signature between buffers. */
if (totalread != 0)
460
{
461
if (!__PHYSFS_readAll(io, buf, maxread - 4))
462
return -1;
463
memcpy(&buf[maxread - 4], &extra, sizeof (extra));
464
totalread += maxread - 4;
465
} /* if */
466
467
else
{
468
if (!__PHYSFS_readAll(io, buf, maxread))
469
return -1;
470
471
472
totalread += maxread;
} /* else */
473
memcpy(&extra, buf, sizeof (extra));
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
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);
491
492
if (filepos < 0)
filepos = 0;
493
494
495
} /* while */
BAIL_IF_MACRO(!found, ERR_NOT_AN_ARCHIVE, -1);
496
497
498
499
if (len != NULL)
*len = filelen;
500
return (filepos + i);
501
} /* zip_find_end_of_central_dir */
502
503
504
static int isZip(PHYSFS_Io *io)
505
{
506
PHYSFS_uint32 sig = 0;
507
int retval = 0;
508
509
510
511
512
/*
* The first thing in a zip file might be the signature of the
* first local file record, so it makes for a quick determination.
*/
513
if (readui32(io, &sig))
514
{
515
516
517
518
519
520
521
522
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...
*/
523
retval = (zip_find_end_of_central_dir(io, NULL) != -1);
524
} /* if */
525
526
} /* if */
527
return retval;
528
} /* isZip */
529
530
531
static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max)
532
{
533
534
PHYSFS_uint32 i;
for (i = 0; i < max; i++)
535
{
536
537
ZIPentry *entry = &entries[i];
if (entry->name != NULL)
538
allocator.Free(entry->name);
539
540
} /* for */
541
allocator.Free(entries);
542
543
544
} /* zip_free_entries */
545
546
547
548
549
/*
* 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.
*/
550
551
static ZIPentry *zip_find_entry(const ZIPinfo *info, const char *path,
int *isDir)
552
553
{
ZIPentry *a = info->entries;
554
555
PHYSFS_sint32 pathlen = strlen(path);
PHYSFS_sint32 lo = 0;
556
557
PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
PHYSFS_sint32 middle;
558
const char *thispath = NULL;
559
560
561
562
int rc;
while (lo <= hi)
{
563
564
565
middle = lo + ((hi - lo) / 2);
thispath = a[middle].name;
rc = strncmp(path, thispath, pathlen);
566
567
if (rc > 0)
568
lo = middle + 1;
569
570
else if (rc < 0)
571
hi = middle - 1;
572
573
574
else /* substring match...might be dir or entry or nothing. */
{
575
576
if (isDir != NULL)
{
577
578
*isDir = (thispath[pathlen] == '/');
if (*isDir)
579
return NULL;
580
581
582
} /* if */
if (thispath[pathlen] == '\0') /* found entry? */
583
return &a[middle];
584
585
586
/* adjust search params, try again. */
else if (thispath[pathlen] > '/')
hi = middle - 1;
587
else
588
lo = middle + 1;
589
590
} /* if */
} /* while */
591
592
593
if (isDir != NULL)
*isDir = 0;
594
595
596
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
} /* zip_find_entry */
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
/* 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 */
613
614
615
static void zip_expand_symlink_path(char *path)
616
{
617
618
char *ptr = path;
char *prevptr = path;
619
620
while (1)
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
651
652
653
654
655
656
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 */
657
658
659
660
661
662
663
664
665
666
667
if (*(ptr + 3) == '\0')
{
/* parent dir at end: move back one, if possible. */
*prevptr = '\0';
} /* if */
} /* if */
} /* if */
else
{
prevptr = ptr;
668
ptr++;
669
670
671
} /* else */
} /* while */
} /* zip_expand_symlink_path */
672
673
/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
674
static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry);
675
676
/*
677
678
679
680
681
* 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.
682
*/
683
static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
684
{
685
686
687
ZIPentry *entry;
zip_expand_symlink_path(path);
688
entry = zip_find_entry(info, path, NULL);
689
690
if (entry != NULL)
{
691
if (!zip_resolve(io, info, entry)) /* recursive! */
692
693
694
695
696
697
698
699
entry = NULL;
else
{
if (entry->symlink != NULL)
entry = entry->symlink;
} /* else */
} /* if */
700
allocator.Free(path);
701
return entry;
702
} /* zip_follow_symlink */
703
704
705
static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
706
{
707
char *path;
708
const PHYSFS_uint32 size = entry->uncompressed_size;
709
710
int rc = 0;
711
712
713
714
715
716
/*
* 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.
*/
717
BAIL_IF_MACRO(!io->seek(io, entry->offset), NULL, 0);
718
719
path = (char *) allocator.Malloc(size + 1);
720
BAIL_IF_MACRO(path == NULL, ERR_OUT_OF_MEMORY, 0);
721
722
if (entry->compression_method == COMPMETH_NONE)
723
rc = __PHYSFS_readAll(io, path, size);
724
725
726
727
else /* symlink target path is compressed... */
{
z_stream stream;
728
const PHYSFS_uint32 complen = entry->compressed_size;
729
PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
730
731
if (compressed != NULL)
{
732
if (__PHYSFS_readAll(io, compressed, complen))
733
{
734
initializeZStream(&stream);
735
stream.next_in = compressed;
736
stream.avail_in = complen;
737
stream.next_out = (unsigned char *) path;
738
739
740
stream.avail_out = size;
if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK)
{
741
rc = zlib_err(inflate(&stream, Z_FINISH));
742
inflateEnd(&stream);
743
744
745
/* both are acceptable outcomes... */
rc = ((rc == Z_OK) || (rc == Z_STREAM_END));
746
747
} /* if */
} /* if */
748
__PHYSFS_smallFree(compressed);
749
750
} /* if */
} /* else */
751
752
if (!rc)
753
allocator.Free(path);
754
755
756
757
else
{
path[entry->uncompressed_size] = '\0'; /* null-terminate it. */
zip_convert_dos_path(entry, path);
758
entry->symlink = zip_follow_symlink(io, info, path);
759
760
} /* else */
761
return (entry->symlink != NULL);
762
763
764
765
766
767
} /* zip_resolve_symlink */
/*
* Parse the local file header of an entry, and update entry->offset.
*/
768
static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
769
770
771
772
773
774
{
PHYSFS_uint32 ui32;
PHYSFS_uint16 ui16;
PHYSFS_uint16 fnamelen;
PHYSFS_uint16 extralen;
775
776
777
778
779
780
781
/*
* 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.
*/
782
783
BAIL_IF_MACRO(!io->seek(io, entry->offset), NULL, 0);
BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
784
BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, ERR_CORRUPTED, 0);
785
BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);
786
BAIL_IF_MACRO(ui16 != entry->version_needed, ERR_CORRUPTED, 0);
787
788
BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0); /* general bits. */
BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);
789
BAIL_IF_MACRO(ui16 != entry->compression_method, ERR_CORRUPTED, 0);
790
791
BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0); /* date/time */
BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
792
BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), ERR_CORRUPTED, 0);
793
BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
794
BAIL_IF_MACRO(ui32 && (ui32 != entry->compressed_size), ERR_CORRUPTED, 0);
795
BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
796
BAIL_IF_MACRO(ui32 && (ui32 != entry->uncompressed_size),ERR_CORRUPTED,0);
797
798
BAIL_IF_MACRO(!readui16(io, &fnamelen), NULL, 0);
BAIL_IF_MACRO(!readui16(io, &extralen), NULL, 0);
799
800
entry->offset += fnamelen + extralen + 30;
801
return 1;
802
803
804
} /* zip_parse_local */
805
static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
{
int retval = 1;
ZipResolveType resolve_type = entry->resolved;
/* Don't bother if we've failed to resolve this entry before. */
BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, ERR_CORRUPTED, 0);
BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, ERR_CORRUPTED, 0);
/* uhoh...infinite symlink loop! */
BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, ERR_SYMLINK_LOOP, 0);
/*
* 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;
828
retval = zip_parse_local(io, entry);
829
830
831
832
833
834
835
836
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)
837
retval = zip_resolve_symlink(io, info, entry);
838
839
840
841
842
843
} /* 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);
844
} /* if */
845
846
return retval;
847
} /* zip_resolve */
848
849
850
static int zip_version_does_symlinks(PHYSFS_uint32 version)
851
852
{
int retval = 0;
853
PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
854
855
856
switch (hosttype)
{
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
/*
* 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. */
874
875
876
877
retval = 1;
break;
} /* switch */
878
return retval;
879
880
881
} /* zip_version_does_symlinks */
882
static int zip_entry_is_symlink(const ZIPentry *entry)
883
{
884
885
886
return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) ||
(entry->resolved == ZIP_BROKEN_SYMLINK) ||
(entry->symlink));
887
} /* zip_entry_is_symlink */
888
889
890
static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr)
891
{
892
PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF);
893
894
895
return ( (zip_version_does_symlinks(entry->version)) &&
(entry->uncompressed_size > 0) &&
((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) );
896
} /* zip_has_symlink_attr */
897
898
899
static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
900
{
901
PHYSFS_uint32 dosdate;
902
903
struct tm unixtime;
memset(&unixtime, '\0', sizeof (unixtime));
904
905
906
dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
dostime &= 0xFFFF;
907
908
909
910
911
912
913
914
915
916
917
918
919
/* 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;
920
921
return ((PHYSFS_sint64) mktime(&unixtime));
922
} /* zip_dos_time_to_physfs_time */
923
924
925
static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixup)
926
927
928
929
930
931
932
933
{
PHYSFS_uint16 fnamelen, extralen, commentlen;
PHYSFS_uint32 external_attr;
PHYSFS_uint16 ui16;
PHYSFS_uint32 ui32;
PHYSFS_sint64 si64;
/* sanity check with central directory signature... */
934
BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
935
936
937
BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, ERR_CORRUPTED, 0);
/* Get the pertinent parts of the record... */
938
939
940
941
942
BAIL_IF_MACRO(!readui16(io, &entry->version), NULL, 0);
BAIL_IF_MACRO(!readui16(io, &entry->version_needed), NULL, 0);
BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0); /* general bits */
BAIL_IF_MACRO(!readui16(io, &entry->compression_method), NULL, 0);
BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
943
entry->last_mod_time = zip_dos_time_to_physfs_time(ui32);
944
945
946
947
948
949
950
951
952
953
BAIL_IF_MACRO(!readui32(io, &entry->crc), NULL, 0);
BAIL_IF_MACRO(!readui32(io, &entry->compressed_size), NULL, 0);
BAIL_IF_MACRO(!readui32(io, &entry->uncompressed_size), NULL, 0);
BAIL_IF_MACRO(!readui16(io, &fnamelen), NULL, 0);
BAIL_IF_MACRO(!readui16(io, &extralen), NULL, 0);
BAIL_IF_MACRO(!readui16(io, &commentlen), NULL, 0);
BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0); /* disk number start */
BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0); /* internal file attribs */
BAIL_IF_MACRO(!readui32(io, &external_attr), NULL, 0);
BAIL_IF_MACRO(!readui32(io, &entry->offset), NULL, 0);
954
entry->offset += ofs_fixup;
955
956
957
958
entry->symlink = NULL; /* will be resolved later, if necessary. */
entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ?
ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE;
959
960
entry->name = (char *) allocator.Malloc(fnamelen + 1);
961
BAIL_IF_MACRO(entry->name == NULL, ERR_OUT_OF_MEMORY, 0);
962
if (!__PHYSFS_readAll(io, entry->name, fnamelen))
963
goto zip_load_entry_puked;
964
965
entry->name[fnamelen] = '\0'; /* null-terminate the filename. */
966
zip_convert_dos_path(entry, entry->name);
967
968
si64 = io->tell(io);
969
if (si64 == -1)
970
goto zip_load_entry_puked;
971
972
/* seek to the start of the next entry in the central directory... */
973
if (!io->seek(io, si64 + extralen + commentlen))
974
goto zip_load_entry_puked;
975
976
return 1; /* success. */
977
978
zip_load_entry_puked:
979
allocator.Free(entry->name);
980
return 0; /* failure. */
981
982
983
} /* zip_load_entry */
984
static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
985
{
986
987
988
if (one != two)
{
const ZIPentry *a = (const ZIPentry *) _a;
989
return strcmp(a[one].name, a[two].name);
990
991
992
} /* if */
return 0;
993
} /* zip_entry_cmp */
994
995
996
static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
997
{
998
999
1000
if (one != two)
{
ZIPentry tmp;