1/*
2 * ZIP support routines for PhysicsFS.
3 *
4 * Please see the file LICENSE.txt in the source's root directory.
5 *
6 * This file written by Ryan C. Gordon, with some peeking at "unzip.c"
7 * by Gilles Vollant.
8 */
9
10#define __PHYSICSFS_INTERNAL__
11#include "physfs_internal.h"
12
13#if PHYSFS_SUPPORTS_ZIP
14
15#include <errno.h>
16#include <time.h>
17
18#if (PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN)
19#define MINIZ_LITTLE_ENDIAN 1
20#else
21#define MINIZ_LITTLE_ENDIAN 0
22#endif
23#include "physfs_miniz.h"
24
25/*
26 * A buffer of ZIP_READBUFSIZE is allocated for each compressed file opened,
27 * and is freed when you close the file; compressed data is read into
28 * this buffer, and then is decompressed into the buffer passed to
29 * PHYSFS_read().
30 *
31 * Uncompressed entries in a zipfile do not allocate this buffer; they just
32 * read data directly into the buffer passed to PHYSFS_read().
33 *
34 * Depending on your speed and memory requirements, you should tweak this
35 * value.
36 */
37#define ZIP_READBUFSIZE (16 * 1024)
38
39
40/*
41 * Entries are "unresolved" until they are first opened. At that time,
42 * local file headers parsed/validated, data offsets will be updated to look
43 * at the actual file data instead of the header, and symlinks will be
44 * followed and optimized. This means that we don't seek and read around the
45 * archive until forced to do so, and after the first time, we had to do
46 * less reading and parsing, which is very CD-ROM friendly.
47 */
48typedef enum
49{
50 ZIP_UNRESOLVED_FILE,
51 ZIP_UNRESOLVED_SYMLINK,
52 ZIP_RESOLVING,
53 ZIP_RESOLVED,
54 ZIP_DIRECTORY,
55 ZIP_BROKEN_FILE,
56 ZIP_BROKEN_SYMLINK
57} ZipResolveType;
58
59
60/*
61 * One ZIPentry is kept for each file in an open ZIP archive.
62 */
63typedef struct _ZIPentry
64{
65 __PHYSFS_DirTreeEntry tree; /* manages directory tree */
66 struct _ZIPentry *symlink; /* NULL or file we symlink to */
67 ZipResolveType resolved; /* Have we resolved file/symlink? */
68 PHYSFS_uint64 offset; /* offset of data in archive */
69 PHYSFS_uint16 version; /* version made by */
70 PHYSFS_uint16 version_needed; /* version needed to extract */
71 PHYSFS_uint16 general_bits; /* general purpose bits */
72 PHYSFS_uint16 compression_method; /* compression method */
73 PHYSFS_uint32 crc; /* crc-32 */
74 PHYSFS_uint64 compressed_size; /* compressed size */
75 PHYSFS_uint64 uncompressed_size; /* uncompressed size */
76 PHYSFS_sint64 last_mod_time; /* last file mod time */
77 PHYSFS_uint32 dos_mod_time; /* original MS-DOS style mod time */
78} ZIPentry;
79
80/*
81 * One ZIPinfo is kept for each open ZIP archive.
82 */
83typedef struct
84{
85 __PHYSFS_DirTree tree; /* manages directory tree. */
86 PHYSFS_Io *io; /* the i/o interface for this archive. */
87 int zip64; /* non-zero if this is a Zip64 archive. */
88 int has_crypto; /* non-zero if any entry uses encryption. */
89} ZIPinfo;
90
91/*
92 * One ZIPfileinfo is kept for each open file in a ZIP archive.
93 */
94typedef struct
95{
96 ZIPentry *entry; /* Info on file. */
97 PHYSFS_Io *io; /* physical file handle. */
98 PHYSFS_uint32 compressed_position; /* offset in compressed data. */
99 PHYSFS_uint32 uncompressed_position; /* tell() position. */
100 PHYSFS_uint8 *buffer; /* decompression buffer. */
101 PHYSFS_uint32 crypto_keys[3]; /* for "traditional" crypto. */
102 PHYSFS_uint32 initial_crypto_keys[3]; /* for "traditional" crypto. */
103 z_stream stream; /* zlib stream state. */
104} ZIPfileinfo;
105
106
107/* Magic numbers... */
108#define ZIP_LOCAL_FILE_SIG 0x04034b50
109#define ZIP_CENTRAL_DIR_SIG 0x02014b50
110#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50
111#define ZIP64_END_OF_CENTRAL_DIR_SIG 0x06064b50
112#define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50
113#define ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG 0x0001
114
115/* compression methods... */
116#define COMPMETH_NONE 0
117/* ...and others... */
118
119
120#define UNIX_FILETYPE_MASK 0170000
121#define UNIX_FILETYPE_SYMLINK 0120000
122
123#define ZIP_GENERAL_BITS_TRADITIONAL_CRYPTO (1 << 0)
124#define ZIP_GENERAL_BITS_IGNORE_LOCAL_HEADER (1 << 3)
125
126/* support for "traditional" PKWARE encryption. */
127static int zip_entry_is_tradional_crypto(const ZIPentry *entry)
128{
129 return (entry->general_bits & ZIP_GENERAL_BITS_TRADITIONAL_CRYPTO) != 0;
130} /* zip_entry_is_traditional_crypto */
131
132static int zip_entry_ignore_local_header(const ZIPentry *entry)
133{
134 return (entry->general_bits & ZIP_GENERAL_BITS_IGNORE_LOCAL_HEADER) != 0;
135} /* zip_entry_is_traditional_crypto */
136
137static PHYSFS_uint32 zip_crypto_crc32(const PHYSFS_uint32 crc, const PHYSFS_uint8 val)
138{
139 int i;
140 PHYSFS_uint32 xorval = (crc ^ ((PHYSFS_uint32) val)) & 0xFF;
141 for (i = 0; i < 8; i++)
142 xorval = ((xorval & 1) ? (0xEDB88320 ^ (xorval >> 1)) : (xorval >> 1));
143 return xorval ^ (crc >> 8);
144} /* zip_crc32 */
145
146static void zip_update_crypto_keys(PHYSFS_uint32 *keys, const PHYSFS_uint8 val)
147{
148 keys[0] = zip_crypto_crc32(keys[0], val);
149 keys[1] = keys[1] + (keys[0] & 0x000000FF);
150 keys[1] = (keys[1] * 134775813) + 1;
151 keys[2] = zip_crypto_crc32(keys[2], (PHYSFS_uint8) ((keys[1] >> 24) & 0xFF));
152} /* zip_update_crypto_keys */
153
154static PHYSFS_uint8 zip_decrypt_byte(const PHYSFS_uint32 *keys)
155{
156 const PHYSFS_uint16 tmp = keys[2] | 2;
157 return (PHYSFS_uint8) ((tmp * (tmp ^ 1)) >> 8);
158} /* zip_decrypt_byte */
159
160static PHYSFS_sint64 zip_read_decrypt(ZIPfileinfo *finfo, void *buf, PHYSFS_uint64 len)
161{
162 PHYSFS_Io *io = finfo->io;
163 const PHYSFS_sint64 br = io->read(io, buf, len);
164
165 /* Decompression the new data if necessary. */
166 if (zip_entry_is_tradional_crypto(finfo->entry) && (br > 0))
167 {
168 PHYSFS_uint32 *keys = finfo->crypto_keys;
169 PHYSFS_uint8 *ptr = (PHYSFS_uint8 *) buf;
170 PHYSFS_sint64 i;
171 for (i = 0; i < br; i++, ptr++)
172 {
173 const PHYSFS_uint8 ch = *ptr ^ zip_decrypt_byte(keys);
174 zip_update_crypto_keys(keys, ch);
175 *ptr = ch;
176 } /* for */
177 } /* if */
178
179 return br;
180} /* zip_read_decrypt */
181
182static int zip_prep_crypto_keys(ZIPfileinfo *finfo, const PHYSFS_uint8 *crypto_header, const PHYSFS_uint8 *password)
183{
184 /* It doesn't appear to be documented in PKWare's APPNOTE.TXT, but you
185 need to use a different byte in the header to verify the password
186 if general purpose bit 3 is set. Discovered this from Info-Zip.
187 That's what the (verifier) value is doing, below. */
188
189 PHYSFS_uint32 *keys = finfo->crypto_keys;
190 const ZIPentry *entry = finfo->entry;
191 const int usedate = zip_entry_ignore_local_header(entry);
192 const PHYSFS_uint8 verifier = (PHYSFS_uint8) ((usedate ? (entry->dos_mod_time >> 8) : (entry->crc >> 24)) & 0xFF);
193 PHYSFS_uint8 finalbyte = 0;
194 int i = 0;
195
196 /* initialize vector with defaults, then password, then header. */
197 keys[0] = 305419896;
198 keys[1] = 591751049;
199 keys[2] = 878082192;
200
201 while (*password)
202 zip_update_crypto_keys(keys, *(password++));
203
204 for (i = 0; i < 12; i++)
205 {
206 const PHYSFS_uint8 c = crypto_header[i] ^ zip_decrypt_byte(keys);
207 zip_update_crypto_keys(keys, c);
208 finalbyte = c;
209 } /* for */
210
211 /* you have a 1/256 chance of passing this test incorrectly. :/ */
212 if (finalbyte != verifier)
213 BAIL(PHYSFS_ERR_BAD_PASSWORD, 0);
214
215 /* save the initial vector for seeking purposes. Not secure!! */
216 memcpy(finfo->initial_crypto_keys, finfo->crypto_keys, 12);
217 return 1;
218} /* zip_prep_crypto_keys */
219
220
221/*
222 * Bridge physfs allocation functions to zlib's format...
223 */
224static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size)
225{
226 return ((PHYSFS_Allocator *) opaque)->Malloc(items * size);
227} /* zlibPhysfsAlloc */
228
229/*
230 * Bridge physfs allocation functions to zlib's format...
231 */
232static void zlibPhysfsFree(voidpf opaque, voidpf address)
233{
234 ((PHYSFS_Allocator *) opaque)->Free(address);
235} /* zlibPhysfsFree */
236
237
238/*
239 * Construct a new z_stream to a sane state.
240 */
241static void initializeZStream(z_stream *pstr)
242{
243 memset(pstr, '\0', sizeof (z_stream));
244 pstr->zalloc = zlibPhysfsAlloc;
245 pstr->zfree = zlibPhysfsFree;
246 pstr->opaque = &allocator;
247} /* initializeZStream */
248
249
250static PHYSFS_ErrorCode zlib_error_code(int rc)
251{
252 switch (rc)
253 {
254 case Z_OK: return PHYSFS_ERR_OK; /* not an error. */
255 case Z_STREAM_END: return PHYSFS_ERR_OK; /* not an error. */
256 case Z_ERRNO: return PHYSFS_ERR_IO;
257 case Z_MEM_ERROR: return PHYSFS_ERR_OUT_OF_MEMORY;
258 default: return PHYSFS_ERR_CORRUPT;
259 } /* switch */
260} /* zlib_error_string */
261
262
263/*
264 * Wrap all zlib calls in this, so the physfs error state is set appropriately.
265 */
266static int zlib_err(const int rc)
267{
268 PHYSFS_setErrorCode(zlib_error_code(rc));
269 return rc;
270} /* zlib_err */
271
272/*
273 * Read an unsigned 64-bit int and swap to native byte order.
274 */
275static int readui64(PHYSFS_Io *io, PHYSFS_uint64 *val)
276{
277 PHYSFS_uint64 v;
278 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
279 *val = PHYSFS_swapULE64(v);
280 return 1;
281} /* readui64 */
282
283/*
284 * Read an unsigned 32-bit int and swap to native byte order.
285 */
286static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
287{
288 PHYSFS_uint32 v;
289 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
290 *val = PHYSFS_swapULE32(v);
291 return 1;
292} /* readui32 */
293
294
295/*
296 * Read an unsigned 16-bit int and swap to native byte order.
297 */
298static int readui16(PHYSFS_Io *io, PHYSFS_uint16 *val)
299{
300 PHYSFS_uint16 v;
301 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
302 *val = PHYSFS_swapULE16(v);
303 return 1;
304} /* readui16 */
305
306
307static PHYSFS_sint64 ZIP_read(PHYSFS_Io *_io, void *buf, PHYSFS_uint64 len)
308{
309 ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
310 ZIPentry *entry = finfo->entry;
311 PHYSFS_sint64 retval = 0;
312 PHYSFS_sint64 maxread = (PHYSFS_sint64) len;
313 PHYSFS_sint64 avail = entry->uncompressed_size -
314 finfo->uncompressed_position;
315
316 if (avail < maxread)
317 maxread = avail;
318
319 BAIL_IF_ERRPASS(maxread == 0, 0); /* quick rejection. */
320
321 if (entry->compression_method == COMPMETH_NONE)
322 retval = zip_read_decrypt(finfo, buf, maxread);
323 else
324 {
325 finfo->stream.next_out = buf;
326 finfo->stream.avail_out = (uInt) maxread;
327
328 while (retval < maxread)
329 {
330 const PHYSFS_uint32 before = (PHYSFS_uint32) finfo->stream.total_out;
331 int rc;
332
333 if (finfo->stream.avail_in == 0)
334 {
335 PHYSFS_sint64 br;
336
337 br = entry->compressed_size - finfo->compressed_position;
338 if (br > 0)
339 {
340 if (br > ZIP_READBUFSIZE)
341 br = ZIP_READBUFSIZE;
342
343 br = zip_read_decrypt(finfo, finfo->buffer, (PHYSFS_uint64) br);
344 if (br <= 0)
345 break;
346
347 finfo->compressed_position += (PHYSFS_uint32) br;
348 finfo->stream.next_in = finfo->buffer;
349 finfo->stream.avail_in = (unsigned int) br;
350 } /* if */
351 } /* if */
352
353 rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH));
354 retval += (finfo->stream.total_out - before);
355
356 if (rc != Z_OK)
357 break;
358 } /* while */
359 } /* else */
360
361 if (retval > 0)
362 finfo->uncompressed_position += (PHYSFS_uint32) retval;
363
364 return retval;
365} /* ZIP_read */
366
367
368static PHYSFS_sint64 ZIP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
369{
370 BAIL(PHYSFS_ERR_READ_ONLY, -1);
371} /* ZIP_write */
372
373
374static PHYSFS_sint64 ZIP_tell(PHYSFS_Io *io)
375{
376 return ((ZIPfileinfo *) io->opaque)->uncompressed_position;
377} /* ZIP_tell */
378
379
380static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset)
381{
382 ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
383 ZIPentry *entry = finfo->entry;
384 PHYSFS_Io *io = finfo->io;
385 const int encrypted = zip_entry_is_tradional_crypto(entry);
386
387 BAIL_IF(offset > entry->uncompressed_size, PHYSFS_ERR_PAST_EOF, 0);
388
389 if (!encrypted && (entry->compression_method == COMPMETH_NONE))
390 {
391 PHYSFS_sint64 newpos = offset + entry->offset;
392 BAIL_IF_ERRPASS(!io->seek(io, newpos), 0);
393 finfo->uncompressed_position = (PHYSFS_uint32) offset;
394 } /* if */
395
396 else
397 {
398 /*
399 * If seeking backwards, we need to redecode the file
400 * from the start and throw away the compressed bits until we hit
401 * the offset we need. If seeking forward, we still need to
402 * decode, but we don't rewind first.
403 */
404 if (offset < finfo->uncompressed_position)
405 {
406 /* we do a copy so state is sane if inflateInit2() fails. */
407 z_stream str;
408 initializeZStream(&str);
409 if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK)
410 return 0;
411
412 if (!io->seek(io, entry->offset + (encrypted ? 12 : 0)))
413 return 0;
414
415 inflateEnd(&finfo->stream);
416 memcpy(&finfo->stream, &str, sizeof (z_stream));
417 finfo->uncompressed_position = finfo->compressed_position = 0;
418
419 if (encrypted)
420 memcpy(finfo->crypto_keys, finfo->initial_crypto_keys, 12);
421 } /* if */
422
423 while (finfo->uncompressed_position != offset)
424 {
425 PHYSFS_uint8 buf[512];
426 PHYSFS_uint32 maxread;
427
428 maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position);
429 if (maxread > sizeof (buf))
430 maxread = sizeof (buf);
431
432 if (ZIP_read(_io, buf, maxread) != maxread)
433 return 0;
434 } /* while */
435 } /* else */
436
437 return 1;
438} /* ZIP_seek */
439
440
441static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io)
442{
443 const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
444 return (PHYSFS_sint64) finfo->entry->uncompressed_size;
445} /* ZIP_length */
446
447
448static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry);
449
450static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io)
451{
452 ZIPfileinfo *origfinfo = (ZIPfileinfo *) io->opaque;
453 PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
454 ZIPfileinfo *finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
455 GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, failed);
456 GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, failed);
457 memset(finfo, '\0', sizeof (*finfo));
458
459 finfo->entry = origfinfo->entry;
460 finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry);
461 GOTO_IF_ERRPASS(!finfo->io, failed);
462
463 initializeZStream(&finfo->stream);
464 if (finfo->entry->compression_method != COMPMETH_NONE)
465 {
466 finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
467 GOTO_IF(!finfo->buffer, PHYSFS_ERR_OUT_OF_MEMORY, failed);
468 if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
469 goto failed;
470 } /* if */
471
472 memcpy(retval, io, sizeof (PHYSFS_Io));
473 retval->opaque = finfo;
474 return retval;
475
476failed:
477 if (finfo != NULL)
478 {
479 if (finfo->io != NULL)
480 finfo->io->destroy(finfo->io);
481
482 if (finfo->buffer != NULL)
483 {
484 allocator.Free(finfo->buffer);
485 inflateEnd(&finfo->stream);
486 } /* if */
487
488 allocator.Free(finfo);
489 } /* if */
490
491 if (retval != NULL)
492 allocator.Free(retval);
493
494 return NULL;
495} /* ZIP_duplicate */
496
497static int ZIP_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
498
499static void ZIP_destroy(PHYSFS_Io *io)
500{
501 ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
502 finfo->io->destroy(finfo->io);
503
504 if (finfo->entry->compression_method != COMPMETH_NONE)
505 inflateEnd(&finfo->stream);
506
507 if (finfo->buffer != NULL)
508 allocator.Free(finfo->buffer);
509
510 allocator.Free(finfo);
511 allocator.Free(io);
512} /* ZIP_destroy */
513
514
515static const PHYSFS_Io ZIP_Io =
516{
517 CURRENT_PHYSFS_IO_API_VERSION, NULL,
518 ZIP_read,
519 ZIP_write,
520 ZIP_seek,
521 ZIP_tell,
522 ZIP_length,
523 ZIP_duplicate,
524 ZIP_flush,
525 ZIP_destroy
526};
527
528
529
530static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len)
531{
532 PHYSFS_uint8 buf[256];
533 PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 };
534 PHYSFS_sint32 i = 0;
535 PHYSFS_sint64 filelen;
536 PHYSFS_sint64 filepos;
537 PHYSFS_sint32 maxread;
538 PHYSFS_sint32 totalread = 0;
539 int found = 0;
540
541 filelen = io->length(io);
542 BAIL_IF_ERRPASS(filelen == -1, -1);
543
544 /*
545 * Jump to the end of the file and start reading backwards.
546 * The last thing in the file is the zipfile comment, which is variable
547 * length, and the field that specifies its size is before it in the
548 * file (argh!)...this means that we need to scan backwards until we
549 * hit the end-of-central-dir signature. We can then sanity check that
550 * the comment was as big as it should be to make sure we're in the
551 * right place. The comment length field is 16 bits, so we can stop
552 * searching for that signature after a little more than 64k at most,
553 * and call it a corrupted zipfile.
554 */
555
556 if (sizeof (buf) < filelen)
557 {
558 filepos = filelen - sizeof (buf);
559 maxread = sizeof (buf);
560 } /* if */
561 else
562 {
563 filepos = 0;
564 maxread = (PHYSFS_uint32) filelen;
565 } /* else */
566
567 while ((totalread < filelen) && (totalread < 65557))
568 {
569 BAIL_IF_ERRPASS(!io->seek(io, filepos), -1);
570
571 /* make sure we catch a signature between buffers. */
572 if (totalread != 0)
573 {
574 if (!__PHYSFS_readAll(io, buf, maxread - 4))
575 return -1;
576 memcpy(&buf[maxread - 4], extra, sizeof (extra));
577 totalread += maxread - 4;
578 } /* if */
579 else
580 {
581 if (!__PHYSFS_readAll(io, buf, maxread))
582 return -1;
583 totalread += maxread;
584 } /* else */
585
586 memcpy(extra, buf, sizeof (extra));
587
588 for (i = maxread - 4; i > 0; i--)
589 {
590 if ((buf[i + 0] == 0x50) &&
591 (buf[i + 1] == 0x4B) &&
592 (buf[i + 2] == 0x05) &&
593 (buf[i + 3] == 0x06) )
594 {
595 found = 1; /* that's the signature! */
596 break;
597 } /* if */
598 } /* for */
599
600 if (found)
601 break;
602
603 filepos -= (maxread - 4);
604 if (filepos < 0)
605 filepos = 0;
606 } /* while */
607
608 BAIL_IF(!found, PHYSFS_ERR_UNSUPPORTED, -1);
609
610 if (len != NULL)
611 *len = filelen;
612
613 return (filepos + i);
614} /* zip_find_end_of_central_dir */
615
616
617static int isZip(PHYSFS_Io *io)
618{
619 PHYSFS_uint32 sig = 0;
620 int retval = 0;
621
622 /*
623 * The first thing in a zip file might be the signature of the
624 * first local file record, so it makes for a quick determination.
625 */
626 if (readui32(io, &sig))
627 {
628 retval = (sig == ZIP_LOCAL_FILE_SIG);
629 if (!retval)
630 {
631 /*
632 * No sig...might be a ZIP with data at the start
633 * (a self-extracting executable, etc), so we'll have to do
634 * it the hard way...
635 */
636 retval = (zip_find_end_of_central_dir(io, NULL) != -1);
637 } /* if */
638 } /* if */
639
640 return retval;
641} /* isZip */
642
643
644/* Convert paths from old, buggy DOS zippers... */
645static void zip_convert_dos_path(const PHYSFS_uint16 entryversion, char *path)
646{
647 const PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entryversion >> 8) & 0xFF);
648 if (hosttype == 0) /* FS_FAT_ */
649 {
650 while (*path)
651 {
652 if (*path == '\\')
653 *path = '/';
654 path++;
655 } /* while */
656 } /* if */
657} /* zip_convert_dos_path */
658
659
660static void zip_expand_symlink_path(char *path)
661{
662 char *ptr = path;
663 char *prevptr = path;
664
665 while (1)
666 {
667 ptr = strchr(ptr, '/');
668 if (ptr == NULL)
669 break;
670
671 if (*(ptr + 1) == '.')
672 {
673 if (*(ptr + 2) == '/')
674 {
675 /* current dir in middle of string: ditch it. */
676 memmove(ptr, ptr + 2, strlen(ptr + 2) + 1);
677 } /* else if */
678
679 else if (*(ptr + 2) == '\0')
680 {
681 /* current dir at end of string: ditch it. */
682 *ptr = '\0';
683 } /* else if */
684
685 else if (*(ptr + 2) == '.')
686 {
687 if (*(ptr + 3) == '/')
688 {
689 /* parent dir in middle: move back one, if possible. */
690 memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1);
691 ptr = prevptr;
692 while (prevptr != path)
693 {
694 prevptr--;
695 if (*prevptr == '/')
696 {
697 prevptr++;
698 break;
699 } /* if */
700 } /* while */
701 } /* if */
702
703 if (*(ptr + 3) == '\0')
704 {
705 /* parent dir at end: move back one, if possible. */
706 *prevptr = '\0';
707 } /* if */
708 } /* if */
709 } /* if */
710 else
711 {
712 prevptr = ptr;
713 ptr++;
714 } /* else */
715 } /* while */
716} /* zip_expand_symlink_path */
717
718
719static inline ZIPentry *zip_find_entry(ZIPinfo *info, const char *path)
720{
721 return (ZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
722} /* zip_find_entry */
723
724/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
725static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry);
726
727/*
728 * Look for the entry named by (path). If it exists, resolve it, and return
729 * a pointer to that entry. If it's another symlink, keep resolving until you
730 * hit a real file and then return a pointer to the final non-symlink entry.
731 * If there's a problem, return NULL.
732 */
733static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
734{
735 ZIPentry *entry;
736
737 zip_expand_symlink_path(path);
738 entry = zip_find_entry(info, path);
739 if (entry != NULL)
740 {
741 if (!zip_resolve(io, info, entry)) /* recursive! */
742 entry = NULL;
743 else
744 {
745 if (entry->symlink != NULL)
746 entry = entry->symlink;
747 } /* else */
748 } /* if */
749
750 return entry;
751} /* zip_follow_symlink */
752
753
754static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
755{
756 const size_t size = (size_t) entry->uncompressed_size;
757 char *path = NULL;
758 int rc = 0;
759
760 /*
761 * We've already parsed the local file header of the symlink at this
762 * point. Now we need to read the actual link from the file data and
763 * follow it.
764 */
765
766 BAIL_IF_ERRPASS(!io->seek(io, entry->offset), 0);
767
768 path = (char *) __PHYSFS_smallAlloc(size + 1);
769 BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, 0);
770
771 if (entry->compression_method == COMPMETH_NONE)
772 rc = __PHYSFS_readAll(io, path, size);
773
774 else /* symlink target path is compressed... */
775 {
776 z_stream stream;
777 const size_t complen = (size_t) entry->compressed_size;
778 PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
779 if (compressed != NULL)
780 {
781 if (__PHYSFS_readAll(io, compressed, complen))
782 {
783 initializeZStream(&stream);
784 stream.next_in = compressed;
785 stream.avail_in = (unsigned int) complen;
786 stream.next_out = (unsigned char *) path;
787 stream.avail_out = (unsigned int) size;
788 if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK)
789 {
790 rc = zlib_err(inflate(&stream, Z_FINISH));
791 inflateEnd(&stream);
792
793 /* both are acceptable outcomes... */
794 rc = ((rc == Z_OK) || (rc == Z_STREAM_END));
795 } /* if */
796 } /* if */
797 __PHYSFS_smallFree(compressed);
798 } /* if */
799 } /* else */
800
801 if (rc)
802 {
803 path[entry->uncompressed_size] = '\0'; /* null-terminate it. */
804 zip_convert_dos_path(entry->version, path);
805 entry->symlink = zip_follow_symlink(io, info, path);
806 } /* else */
807
808 __PHYSFS_smallFree(path);
809
810 return (entry->symlink != NULL);
811} /* zip_resolve_symlink */
812
813
814/*
815 * Parse the local file header of an entry, and update entry->offset.
816 */
817static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
818{
819 PHYSFS_uint32 ui32;
820 PHYSFS_uint16 ui16;
821 PHYSFS_uint16 fnamelen;
822 PHYSFS_uint16 extralen;
823
824 /*
825 * crc and (un)compressed_size are always zero if this is a "JAR"
826 * archive created with Sun's Java tools, apparently. We only
827 * consider this archive corrupted if those entries don't match and
828 * aren't zero. That seems to work well.
829 * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's
830 * possible that's a Zip64 thing.
831 */
832
833 /* !!! FIXME: apparently these are zero if general purpose bit 3 is set,
834 !!! FIXME: which is probably true for Jar files, fwiw, but we don't
835 !!! FIXME: care about these values anyhow. */
836
837 BAIL_IF_ERRPASS(!io->seek(io, entry->offset), 0);
838 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
839 BAIL_IF(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0);
840 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0);
841 /* Windows Explorer might rewrite the entire central directory, setting
842 this field to 2.0/MS-DOS for all files, so favor the local version,
843 which it leaves intact if it didn't alter that specific file. */
844 entry->version_needed = ui16;
845 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); /* general bits. */
846 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0);
847 BAIL_IF(ui16 != entry->compression_method, PHYSFS_ERR_CORRUPT, 0);
848 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); /* date/time */
849 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
850 BAIL_IF(ui32 && (ui32 != entry->crc), PHYSFS_ERR_CORRUPT, 0);
851
852 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
853 BAIL_IF(ui32 && (ui32 != 0xFFFFFFFF) &&
854 (ui32 != entry->compressed_size), PHYSFS_ERR_CORRUPT, 0);
855
856 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
857 BAIL_IF(ui32 && (ui32 != 0xFFFFFFFF) &&
858 (ui32 != entry->uncompressed_size), PHYSFS_ERR_CORRUPT, 0);
859
860 BAIL_IF_ERRPASS(!readui16(io, &fnamelen), 0);
861 BAIL_IF_ERRPASS(!readui16(io, &extralen), 0);
862
863 entry->offset += fnamelen + extralen + 30;
864 return 1;
865} /* zip_parse_local */
866
867
868static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
869{
870 int retval = 1;
871 const ZipResolveType resolve_type = entry->resolved;
872
873 if (resolve_type == ZIP_DIRECTORY)
874 return 1; /* we're good. */
875
876 /* Don't bother if we've failed to resolve this entry before. */
877 BAIL_IF(resolve_type == ZIP_BROKEN_FILE, PHYSFS_ERR_CORRUPT, 0);
878 BAIL_IF(resolve_type == ZIP_BROKEN_SYMLINK, PHYSFS_ERR_CORRUPT, 0);
879
880 /* uhoh...infinite symlink loop! */
881 BAIL_IF(resolve_type == ZIP_RESOLVING, PHYSFS_ERR_SYMLINK_LOOP, 0);
882
883 /*
884 * We fix up the offset to point to the actual data on the
885 * first open, since we don't want to seek across the whole file on
886 * archive open (can be SLOW on large, CD-stored files), but we
887 * need to check the local file header...not just for corruption,
888 * but since it stores offset info the central directory does not.
889 */
890 if (resolve_type != ZIP_RESOLVED)
891 {
892 if (entry->tree.isdir) /* an ancestor dir that DirTree filled in? */
893 {
894 entry->resolved = ZIP_DIRECTORY;
895 return 1;
896 } /* if */
897
898 retval = zip_parse_local(io, entry);
899 if (retval)
900 {
901 /*
902 * If it's a symlink, find the original file. This will cause
903 * resolution of other entries (other symlinks and, eventually,
904 * the real file) if all goes well.
905 */
906 if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
907 retval = zip_resolve_symlink(io, info, entry);
908 } /* if */
909
910 if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
911 entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK);
912 else if (resolve_type == ZIP_UNRESOLVED_FILE)
913 entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE);
914 } /* if */
915
916 return retval;
917} /* zip_resolve */
918
919
920static int zip_entry_is_symlink(const ZIPentry *entry)
921{
922 return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) ||
923 (entry->resolved == ZIP_BROKEN_SYMLINK) ||
924 (entry->symlink));
925} /* zip_entry_is_symlink */
926
927
928static int zip_version_does_symlinks(PHYSFS_uint32 version)
929{
930 int retval = 0;
931 PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
932
933 switch (hosttype)
934 {
935 /*
936 * These are the platforms that can NOT build an archive with
937 * symlinks, according to the Info-ZIP project.
938 */
939 case 0: /* FS_FAT_ */
940 case 1: /* AMIGA_ */
941 case 2: /* VMS_ */
942 case 4: /* VM_CSM_ */
943 case 6: /* FS_HPFS_ */
944 case 11: /* FS_NTFS_ */
945 case 14: /* FS_VFAT_ */
946 case 13: /* ACORN_ */
947 case 15: /* MVS_ */
948 case 18: /* THEOS_ */
949 break; /* do nothing. */
950
951 default: /* assume the rest to be unix-like. */
952 retval = 1;
953 break;
954 } /* switch */
955
956 return retval;
957} /* zip_version_does_symlinks */
958
959
960static inline int zip_has_symlink_attr(const ZIPentry *entry,
961 const PHYSFS_uint32 extern_attr)
962{
963 PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF);
964 return ( (zip_version_does_symlinks(entry->version)) &&
965 (entry->uncompressed_size > 0) &&
966 ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) );
967} /* zip_has_symlink_attr */
968
969
970static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
971{
972 PHYSFS_uint32 dosdate;
973 struct tm unixtime;
974 memset(&unixtime, '\0', sizeof (unixtime));
975
976 dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
977 dostime &= 0xFFFF;
978
979 /* dissect date */
980 unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80;
981 unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1;
982 unixtime.tm_mday = ((dosdate ) & 0x1F);
983
984 /* dissect time */
985 unixtime.tm_hour = ((dostime >> 11) & 0x1F);
986 unixtime.tm_min = ((dostime >> 5) & 0x3F);
987 unixtime.tm_sec = ((dostime << 1) & 0x3E);
988
989 /* let mktime calculate daylight savings time. */
990 unixtime.tm_isdst = -1;
991
992 return ((PHYSFS_sint64) mktime(&unixtime));
993} /* zip_dos_time_to_physfs_time */
994
995
996static ZIPentry *zip_load_entry(ZIPinfo *info, const int zip64,
997 const PHYSFS_uint64 ofs_fixup)
998{
999 PHYSFS_Io *io = info->io;
1000 ZIPentry entry;
1001 ZIPentry *retval = NULL;
1002 PHYSFS_uint16 fnamelen, extralen, commentlen;
1003 PHYSFS_uint32 external_attr;
1004 PHYSFS_uint32 starting_disk;
1005 PHYSFS_uint64 offset;
1006 PHYSFS_uint16 ui16;
1007 PHYSFS_uint32 ui32;
1008 PHYSFS_sint64 si64;
1009 char *name = NULL;
1010 int isdir = 0;
1011
1012 /* sanity check with central directory signature... */
1013 BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL);
1014 BAIL_IF(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, NULL);
1015
1016 memset(&entry, '\0', sizeof (entry));
1017
1018 /* Get the pertinent parts of the record... */
1019 BAIL_IF_ERRPASS(!readui16(io, &entry.version), NULL);
1020 BAIL_IF_ERRPASS(!readui16(io, &entry.version_needed), NULL);
1021 BAIL_IF_ERRPASS(!readui16(io, &entry.general_bits), NULL); /* general bits */
1022 BAIL_IF_ERRPASS(!readui16(io, &entry.compression_method), NULL);
1023 BAIL_IF_ERRPASS(!readui32(io, &entry.dos_mod_time), NULL);
1024 entry.last_mod_time = zip_dos_time_to_physfs_time(entry.dos_mod_time);
1025 BAIL_IF_ERRPASS(!readui32(io, &entry.crc), NULL);
1026 BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL);
1027 entry.compressed_size = (PHYSFS_uint64) ui32;
1028 BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL);
1029 entry.uncompressed_size = (PHYSFS_uint64) ui32;
1030 BAIL_IF_ERRPASS(!readui16(io, &fnamelen), NULL);
1031 BAIL_IF_ERRPASS(!readui16(io, &extralen), NULL);
1032 BAIL_IF_ERRPASS(!readui16(io, &commentlen), NULL);
1033 BAIL_IF_ERRPASS(!readui16(io, &ui16), NULL);
1034 starting_disk = (PHYSFS_uint32) ui16;
1035 BAIL_IF_ERRPASS(!readui16(io, &ui16), NULL); /* internal file attribs */
1036 BAIL_IF_ERRPASS(!readui32(io, &external_attr), NULL);
1037 BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL);
1038 offset = (PHYSFS_uint64) ui32;
1039
1040 name = (char *) __PHYSFS_smallAlloc(fnamelen + 1);
1041 BAIL_IF(!name, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
1042 if (!__PHYSFS_readAll(io, name, fnamelen))
1043 {
1044 __PHYSFS_smallFree(name);
1045 return NULL;
1046 } /* if */
1047
1048 if (name[fnamelen - 1] == '/')
1049 {
1050 name[fnamelen - 1] = '\0';
1051 isdir = 1;
1052 } /* if */
1053 name[fnamelen] = '\0'; /* null-terminate the filename. */
1054
1055 zip_convert_dos_path(entry.version, name);
1056
1057 retval = (ZIPentry *) __PHYSFS_DirTreeAdd(&info->tree, name, isdir);
1058 __PHYSFS_smallFree(name);
1059
1060 BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
1061
1062 /* It's okay to BAIL without freeing retval, because it's stored in the
1063 __PHYSFS_DirTree and will be freed later anyhow. */
1064 BAIL_IF(retval->last_mod_time != 0, PHYSFS_ERR_CORRUPT, NULL); /* dupe? */
1065
1066 /* Move the data we already read into place in the official object. */
1067 memcpy(((PHYSFS_uint8 *) retval) + sizeof (__PHYSFS_DirTreeEntry),
1068 ((PHYSFS_uint8 *) &entry) + sizeof (__PHYSFS_DirTreeEntry),
1069 sizeof (*retval) - sizeof (__PHYSFS_DirTreeEntry));
1070
1071 retval->symlink = NULL; /* will be resolved later, if necessary. */
1072
1073 if (isdir)
1074 retval->resolved = ZIP_DIRECTORY;
1075 else
1076 {
1077 retval->resolved = (zip_has_symlink_attr(retval, external_attr)) ?
1078 ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE;
1079 } /* else */
1080
1081 si64 = io->tell(io);
1082 BAIL_IF_ERRPASS(si64 == -1, NULL);
1083
1084 /* If the actual sizes didn't fit in 32-bits, look for the Zip64
1085 extended information extra field... */
1086 if ( (zip64) &&
1087 ((offset == 0xFFFFFFFF) ||
1088 (starting_disk == 0xFFFFFFFF) ||
1089 (retval->compressed_size == 0xFFFFFFFF) ||
1090 (retval->uncompressed_size == 0xFFFFFFFF)) )
1091 {
1092 int found = 0;
1093 PHYSFS_uint16 sig = 0;
1094 PHYSFS_uint16 len = 0;
1095 while (extralen > 4)
1096 {
1097 BAIL_IF_ERRPASS(!readui16(io, &sig), NULL);
1098 BAIL_IF_ERRPASS(!readui16(io, &len), NULL);
1099
1100 si64 += 4 + len;
1101 extralen -= 4 + len;
1102 if (sig != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG)
1103 {
1104 BAIL_IF_ERRPASS(!io->seek(io, si64), NULL);
1105 continue;
1106 } /* if */
1107
1108 found = 1;
1109 break;
1110 } /* while */
1111
1112 BAIL_IF(!found, PHYSFS_ERR_CORRUPT, NULL);
1113
1114 if (retval->uncompressed_size == 0xFFFFFFFF)
1115 {
1116 BAIL_IF(len < 8, PHYSFS_ERR_CORRUPT, NULL);
1117 BAIL_IF_ERRPASS(!readui64(io, &retval->uncompressed_size), NULL);
1118 len -= 8;
1119 } /* if */
1120
1121 if (retval->compressed_size == 0xFFFFFFFF)
1122 {
1123 BAIL_IF(len < 8, PHYSFS_ERR_CORRUPT, NULL);
1124 BAIL_IF_ERRPASS(!readui64(io, &retval->compressed_size), NULL);
1125 len -= 8;
1126 } /* if */
1127
1128 if (offset == 0xFFFFFFFF)
1129 {
1130 BAIL_IF(len < 8, PHYSFS_ERR_CORRUPT, NULL);
1131 BAIL_IF_ERRPASS(!readui64(io, &offset), NULL);
1132 len -= 8;
1133 } /* if */
1134
1135 if (starting_disk == 0xFFFFFFFF)
1136 {
1137 BAIL_IF(len < 8, PHYSFS_ERR_CORRUPT, NULL);
1138 BAIL_IF_ERRPASS(!readui32(io, &starting_disk), NULL);
1139 len -= 4;
1140 } /* if */
1141
1142 BAIL_IF(len != 0, PHYSFS_ERR_CORRUPT, NULL);
1143 } /* if */
1144
1145 BAIL_IF(starting_disk != 0, PHYSFS_ERR_CORRUPT, NULL);
1146
1147 retval->offset = offset + ofs_fixup;
1148
1149 /* seek to the start of the next entry in the central directory... */
1150 BAIL_IF_ERRPASS(!io->seek(io, si64 + extralen + commentlen), NULL);
1151
1152 return retval; /* success. */
1153} /* zip_load_entry */
1154
1155
1156/* This leaves things allocated on error; the caller will clean up the mess. */
1157static int zip_load_entries(ZIPinfo *info,
1158 const PHYSFS_uint64 data_ofs,
1159 const PHYSFS_uint64 central_ofs,
1160 const PHYSFS_uint64 entry_count)
1161{
1162 PHYSFS_Io *io = info->io;
1163 const int zip64 = info->zip64;
1164 PHYSFS_uint64 i;
1165
1166 BAIL_IF_ERRPASS(!io->seek(io, central_ofs), 0);
1167
1168 for (i = 0; i < entry_count; i++)
1169 {
1170 ZIPentry *entry = zip_load_entry(info, zip64, data_ofs);
1171 BAIL_IF_ERRPASS(!entry, 0);
1172 if (zip_entry_is_tradional_crypto(entry))
1173 info->has_crypto = 1;
1174 } /* for */
1175
1176 return 1;
1177} /* zip_load_entries */
1178
1179
1180static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io,
1181 PHYSFS_sint64 _pos,
1182 PHYSFS_uint64 offset)
1183{
1184 /*
1185 * Naturally, the offset is useless to us; it is the offset from the
1186 * start of file, which is meaningless if we've appended this .zip to
1187 * a self-extracting .exe. We need to find this on our own. It should
1188 * be directly before the locator record, but the record in question,
1189 * like the original end-of-central-directory record, ends with a
1190 * variable-length field. Unlike the original, which has to store the
1191 * size of that variable-length field in a 16-bit int and thus has to be
1192 * within 64k, the new one gets 64-bits.
1193 *
1194 * Fortunately, the only currently-specified record for that variable
1195 * length block is some weird proprietary thing that deals with EBCDIC
1196 * and tape backups or something. So we don't seek far.
1197 */
1198
1199 PHYSFS_uint32 ui32;
1200 const PHYSFS_uint64 pos = (PHYSFS_uint64) _pos;
1201
1202 assert(_pos > 0);
1203
1204 /* Try offset specified in the Zip64 end of central directory locator. */
1205 /* This works if the entire PHYSFS_Io is the zip file. */
1206 BAIL_IF_ERRPASS(!io->seek(io, offset), -1);
1207 BAIL_IF_ERRPASS(!readui32(io, &ui32), -1);
1208 if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
1209 return offset;
1210
1211 /* Try 56 bytes before the Zip64 end of central directory locator. */
1212 /* This works if the record isn't variable length and is version 1. */
1213 if (pos > 56)
1214 {
1215 BAIL_IF_ERRPASS(!io->seek(io, pos-56), -1);
1216 BAIL_IF_ERRPASS(!readui32(io, &ui32), -1);
1217 if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
1218 return pos-56;
1219 } /* if */
1220
1221 /* Try 84 bytes before the Zip64 end of central directory locator. */
1222 /* This works if the record isn't variable length and is version 2. */
1223 if (pos > 84)
1224 {
1225 BAIL_IF_ERRPASS(!io->seek(io, pos-84), -1);
1226 BAIL_IF_ERRPASS(!readui32(io, &ui32), -1);
1227 if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
1228 return pos-84;
1229 } /* if */
1230
1231 /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */
1232 /* Just try moving back at most 256k. Oh well. */
1233 if ((offset < pos) && (pos > 4))
1234 {
1235 const size_t maxbuflen = 256 * 1024;
1236 size_t len = (size_t) (pos - offset);
1237 PHYSFS_uint8 *buf = NULL;
1238 PHYSFS_sint32 i;
1239
1240 if (len > maxbuflen)
1241 len = maxbuflen;
1242
1243 buf = (PHYSFS_uint8 *) __PHYSFS_smallAlloc(len);
1244 BAIL_IF(!buf, PHYSFS_ERR_OUT_OF_MEMORY, -1);
1245
1246 if (!io->seek(io, pos - len) || !__PHYSFS_readAll(io, buf, len))
1247 {
1248 __PHYSFS_smallFree(buf);
1249 return -1; /* error was set elsewhere. */
1250 } /* if */
1251
1252 for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--)
1253 {
1254 if ( (buf[i] == 0x50) && (buf[i+1] == 0x4b) &&
1255 (buf[i+2] == 0x06) && (buf[i+3] == 0x06) )
1256 {
1257 __PHYSFS_smallFree(buf);
1258 return pos - ((PHYSFS_sint64) (len - i));
1259 } /* if */
1260 } /* for */
1261
1262 __PHYSFS_smallFree(buf);
1263 } /* if */
1264
1265 BAIL(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */
1266} /* zip64_find_end_of_central_dir */
1267
1268
1269static int zip64_parse_end_of_central_dir(ZIPinfo *info,
1270 PHYSFS_uint64 *data_start,
1271 PHYSFS_uint64 *dir_ofs,
1272 PHYSFS_uint64 *entry_count,
1273 PHYSFS_sint64 pos)
1274{
1275 PHYSFS_Io *io = info->io;
1276 PHYSFS_uint64 ui64;
1277 PHYSFS_uint32 ui32;
1278 PHYSFS_uint16 ui16;
1279
1280 /* We should be positioned right past the locator signature. */
1281
1282 if ((pos < 0) || (!io->seek(io, pos)))
1283 return 0;
1284
1285 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
1286 if (ui32 != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG)
1287 return -1; /* it's not a Zip64 archive. Not an error, though! */
1288
1289 info->zip64 = 1;
1290
1291 /* number of the disk with the start of the central directory. */
1292 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
1293 BAIL_IF(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
1294
1295 /* offset of Zip64 end of central directory record. */
1296 BAIL_IF_ERRPASS(!readui64(io, &ui64), 0);
1297
1298 /* total number of disks */
1299 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
1300 BAIL_IF(ui32 != 1, PHYSFS_ERR_CORRUPT, 0);
1301
1302 pos = zip64_find_end_of_central_dir(io, pos, ui64);
1303 if (pos < 0)
1304 return 0; /* oh well. */
1305
1306 /*
1307 * For self-extracting archives, etc, there's crapola in the file
1308 * before the zipfile records; we calculate how much data there is
1309 * prepended by determining how far the zip64-end-of-central-directory
1310 * offset is from where it is supposed to be...the difference in bytes
1311 * is how much arbitrary data is at the start of the physical file.
1312 */
1313 assert(((PHYSFS_uint64) pos) >= ui64);
1314 *data_start = ((PHYSFS_uint64) pos) - ui64;
1315
1316 BAIL_IF_ERRPASS(!io->seek(io, pos), 0);
1317
1318 /* check signature again, just in case. */
1319 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
1320 BAIL_IF(ui32 != ZIP64_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
1321
1322 /* size of Zip64 end of central directory record. */
1323 BAIL_IF_ERRPASS(!readui64(io, &ui64), 0);
1324
1325 /* version made by. */
1326 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0);
1327
1328 /* version needed to extract. */
1329 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0);
1330
1331 /* number of this disk. */
1332 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
1333 BAIL_IF(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
1334
1335 /* number of disk with start of central directory record. */
1336 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
1337 BAIL_IF(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
1338
1339 /* total number of entries in the central dir on this disk */
1340 BAIL_IF_ERRPASS(!readui64(io, &ui64), 0);
1341
1342 /* total number of entries in the central dir */
1343 BAIL_IF_ERRPASS(!readui64(io, entry_count), 0);
1344 BAIL_IF(ui64 != *entry_count, PHYSFS_ERR_CORRUPT, 0);
1345
1346 /* size of the central directory */
1347 BAIL_IF_ERRPASS(!readui64(io, &ui64), 0);
1348
1349 /* offset of central directory */
1350 BAIL_IF_ERRPASS(!readui64(io, dir_ofs), 0);
1351
1352 /* Since we know the difference, fix up the central dir offset... */
1353 *dir_ofs += *data_start;
1354
1355 /*
1356 * There are more fields here, for encryption and feature-specific things,
1357 * but we don't care about any of them at the moment.
1358 */
1359
1360 return 1; /* made it. */
1361} /* zip64_parse_end_of_central_dir */
1362
1363
1364static int zip_parse_end_of_central_dir(ZIPinfo *info,
1365 PHYSFS_uint64 *data_start,
1366 PHYSFS_uint64 *dir_ofs,
1367 PHYSFS_uint64 *entry_count)
1368{
1369 PHYSFS_Io *io = info->io;
1370 PHYSFS_uint16 entryCount16;
1371 PHYSFS_uint32 offset32;
1372 PHYSFS_uint32 ui32;
1373 PHYSFS_uint16 ui16;
1374 PHYSFS_sint64 len;
1375 PHYSFS_sint64 pos;
1376 int rc;
1377
1378 /* find the end-of-central-dir record, and seek to it. */
1379 pos = zip_find_end_of_central_dir(io, &len);
1380 BAIL_IF_ERRPASS(pos == -1, 0);
1381 BAIL_IF_ERRPASS(!io->seek(io, pos), 0);
1382
1383 /* check signature again, just in case. */
1384 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
1385 BAIL_IF(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
1386
1387 /* Seek back to see if "Zip64 end of central directory locator" exists. */
1388 /* this record is 20 bytes before end-of-central-dir */
1389 rc = zip64_parse_end_of_central_dir(info, data_start, dir_ofs,
1390 entry_count, pos - 20);
1391
1392 /* Error or success? Bounce out of here. Keep going if not zip64. */
1393 if ((rc == 0) || (rc == 1))
1394 return rc;
1395
1396 assert(rc == -1); /* no error, just not a Zip64 archive. */
1397
1398 /* Not Zip64? Seek back to where we were and keep processing. */
1399 BAIL_IF_ERRPASS(!io->seek(io, pos + 4), 0);
1400
1401 /* number of this disk */
1402 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0);
1403 BAIL_IF(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
1404
1405 /* number of the disk with the start of the central directory */
1406 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0);
1407 BAIL_IF(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
1408
1409 /* total number of entries in the central dir on this disk */
1410 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0);
1411
1412 /* total number of entries in the central dir */
1413 BAIL_IF_ERRPASS(!readui16(io, &entryCount16), 0);
1414 BAIL_IF(ui16 != entryCount16, PHYSFS_ERR_CORRUPT, 0);
1415
1416 *entry_count = entryCount16;
1417
1418 /* size of the central directory */
1419 BAIL_IF_ERRPASS(!readui32(io, &ui32), 0);
1420
1421 /* offset of central directory */
1422 BAIL_IF_ERRPASS(!readui32(io, &offset32), 0);
1423 *dir_ofs = (PHYSFS_uint64) offset32;
1424 BAIL_IF(((PHYSFS_uint64) pos) < (*dir_ofs + ui32), PHYSFS_ERR_CORRUPT, 0);
1425
1426 /*
1427 * For self-extracting archives, etc, there's crapola in the file
1428 * before the zipfile records; we calculate how much data there is
1429 * prepended by determining how far the central directory offset is
1430 * from where it is supposed to be (start of end-of-central-dir minus
1431 * sizeof central dir)...the difference in bytes is how much arbitrary
1432 * data is at the start of the physical file.
1433 */
1434 *data_start = (PHYSFS_uint64) (pos - (*dir_ofs + ui32));
1435
1436 /* Now that we know the difference, fix up the central dir offset... */
1437 *dir_ofs += *data_start;
1438
1439 /* zipfile comment length */
1440 BAIL_IF_ERRPASS(!readui16(io, &ui16), 0);
1441
1442 /*
1443 * Make sure that the comment length matches to the end of file...
1444 * If it doesn't, we're either in the wrong part of the file, or the
1445 * file is corrupted, but we give up either way.
1446 */
1447 BAIL_IF((pos + 22 + ui16) != len, PHYSFS_ERR_CORRUPT, 0);
1448
1449 return 1; /* made it. */
1450} /* zip_parse_end_of_central_dir */
1451
1452
1453static void ZIP_closeArchive(void *opaque)
1454{
1455 ZIPinfo *info = (ZIPinfo *) (opaque);
1456
1457 if (!info)
1458 return;
1459
1460 if (info->io)
1461 info->io->destroy(info->io);
1462
1463 __PHYSFS_DirTreeDeinit(&info->tree);
1464
1465 allocator.Free(info);
1466} /* ZIP_closeArchive */
1467
1468
1469static void *ZIP_openArchive(PHYSFS_Io *io, const char *name,
1470 int forWriting, int *claimed)
1471{
1472 ZIPinfo *info = NULL;
1473 ZIPentry *root = NULL;
1474 PHYSFS_uint64 dstart = 0; /* data start */
1475 PHYSFS_uint64 cdir_ofs; /* central dir offset */
1476 PHYSFS_uint64 count;
1477
1478 assert(io != NULL); /* shouldn't ever happen. */
1479
1480 BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
1481 BAIL_IF_ERRPASS(!isZip(io), NULL);
1482
1483 *claimed = 1;
1484
1485 info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo));
1486 BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
1487 memset(info, '\0', sizeof (ZIPinfo));
1488
1489 info->io = io;
1490
1491 if (!zip_parse_end_of_central_dir(info, &dstart, &cdir_ofs, &count))
1492 goto ZIP_openarchive_failed;
1493 else if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (ZIPentry), 1, 0))
1494 goto ZIP_openarchive_failed;
1495
1496 root = (ZIPentry *) info->tree.root;
1497 root->resolved = ZIP_DIRECTORY;
1498
1499 if (!zip_load_entries(info, dstart, cdir_ofs, count))
1500 goto ZIP_openarchive_failed;
1501
1502 assert(info->tree.root->sibling == NULL);
1503 return info;
1504
1505ZIP_openarchive_failed:
1506 info->io = NULL; /* don't let ZIP_closeArchive destroy (io). */
1507 ZIP_closeArchive(info);
1508 return NULL;
1509} /* ZIP_openArchive */
1510
1511
1512static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry)
1513{
1514 int success;
1515 PHYSFS_Io *retval = io->duplicate(io);
1516 BAIL_IF_ERRPASS(!retval, NULL);
1517
1518 assert(!entry->tree.isdir); /* should have been checked before calling. */
1519
1520 /* (inf) can be NULL if we already resolved. */
1521 success = (inf == NULL) || zip_resolve(retval, inf, entry);
1522 if (success)
1523 {
1524 PHYSFS_sint64 offset;
1525 offset = ((entry->symlink) ? entry->symlink->offset : entry->offset);
1526 success = retval->seek(retval, offset);
1527 } /* if */
1528
1529 if (!success)
1530 {
1531 retval->destroy(retval);
1532 retval = NULL;
1533 } /* if */
1534
1535 return retval;
1536} /* zip_get_io */
1537
1538
1539static PHYSFS_Io *ZIP_openRead(void *opaque, const char *filename)
1540{
1541 PHYSFS_Io *retval = NULL;
1542 ZIPinfo *info = (ZIPinfo *) opaque;
1543 ZIPentry *entry = zip_find_entry(info, filename);
1544 ZIPfileinfo *finfo = NULL;
1545 PHYSFS_Io *io = NULL;
1546 PHYSFS_uint8 *password = NULL;
1547
1548 /* if not found, see if maybe "$PASSWORD" is appended. */
1549 if ((!entry) && (info->has_crypto))
1550 {
1551 const char *ptr = strrchr(filename, '$');
1552 if (ptr != NULL)
1553 {
1554 const size_t len = (size_t) (ptr - filename);
1555 char *str = (char *) __PHYSFS_smallAlloc(len + 1);
1556 BAIL_IF(!str, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
1557 memcpy(str, filename, len);
1558 str[len] = '\0';
1559 entry = zip_find_entry(info, str);
1560 __PHYSFS_smallFree(str);
1561 password = (PHYSFS_uint8 *) (ptr + 1);
1562 } /* if */
1563 } /* if */
1564
1565 BAIL_IF_ERRPASS(!entry, NULL);
1566
1567 BAIL_IF_ERRPASS(!zip_resolve(info->io, info, entry), NULL);
1568
1569 BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
1570
1571 retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
1572 GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
1573
1574 finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
1575 GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
1576 memset(finfo, '\0', sizeof (ZIPfileinfo));
1577
1578 io = zip_get_io(info->io, info, entry);
1579 GOTO_IF_ERRPASS(!io, ZIP_openRead_failed);
1580 finfo->io = io;
1581 finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry);
1582 initializeZStream(&finfo->stream);
1583
1584 if (finfo->entry->compression_method != COMPMETH_NONE)
1585 {
1586 finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
1587 if (!finfo->buffer)
1588 GOTO(PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
1589 else if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
1590 goto ZIP_openRead_failed;
1591 } /* if */
1592
1593 if (!zip_entry_is_tradional_crypto(entry))
1594 GOTO_IF(password != NULL, PHYSFS_ERR_BAD_PASSWORD, ZIP_openRead_failed);
1595 else
1596 {
1597 PHYSFS_uint8 crypto_header[12];
1598 GOTO_IF(password == NULL, PHYSFS_ERR_BAD_PASSWORD, ZIP_openRead_failed);
1599 if (io->read(io, crypto_header, 12) != 12)
1600 goto ZIP_openRead_failed;
1601 else if (!zip_prep_crypto_keys(finfo, crypto_header, password))
1602 goto ZIP_openRead_failed;
1603 } /* if */
1604
1605 memcpy(retval, &ZIP_Io, sizeof (PHYSFS_Io));
1606 retval->opaque = finfo;
1607
1608 return retval;
1609
1610ZIP_openRead_failed:
1611 if (finfo != NULL)
1612 {
1613 if (finfo->io != NULL)
1614 finfo->io->destroy(finfo->io);
1615
1616 if (finfo->buffer != NULL)
1617 {
1618 allocator.Free(finfo->buffer);
1619 inflateEnd(&finfo->stream);
1620 } /* if */
1621
1622 allocator.Free(finfo);
1623 } /* if */
1624
1625 if (retval != NULL)
1626 allocator.Free(retval);
1627
1628 return NULL;
1629} /* ZIP_openRead */
1630
1631
1632static PHYSFS_Io *ZIP_openWrite(void *opaque, const char *filename)
1633{
1634 BAIL(PHYSFS_ERR_READ_ONLY, NULL);
1635} /* ZIP_openWrite */
1636
1637
1638static PHYSFS_Io *ZIP_openAppend(void *opaque, const char *filename)
1639{
1640 BAIL(PHYSFS_ERR_READ_ONLY, NULL);
1641} /* ZIP_openAppend */
1642
1643
1644static int ZIP_remove(void *opaque, const char *name)
1645{
1646 BAIL(PHYSFS_ERR_READ_ONLY, 0);
1647} /* ZIP_remove */
1648
1649
1650static int ZIP_mkdir(void *opaque, const char *name)
1651{
1652 BAIL(PHYSFS_ERR_READ_ONLY, 0);
1653} /* ZIP_mkdir */
1654
1655
1656static int ZIP_stat(void *opaque, const char *filename, PHYSFS_Stat *stat)
1657{
1658 ZIPinfo *info = (ZIPinfo *) opaque;
1659 ZIPentry *entry = zip_find_entry(info, filename);
1660
1661 if (entry == NULL)
1662 return 0;
1663
1664 else if (!zip_resolve(info->io, info, entry))
1665 return 0;
1666
1667 else if (entry->resolved == ZIP_DIRECTORY)
1668 {
1669 stat->filesize = 0;
1670 stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
1671 } /* if */
1672
1673 else if (zip_entry_is_symlink(entry))
1674 {
1675 stat->filesize = 0;
1676 stat->filetype = PHYSFS_FILETYPE_SYMLINK;
1677 } /* else if */
1678
1679 else
1680 {
1681 stat->filesize = (PHYSFS_sint64) entry->uncompressed_size;
1682 stat->filetype = PHYSFS_FILETYPE_REGULAR;
1683 } /* else */
1684
1685 stat->modtime = ((entry) ? entry->last_mod_time : 0);
1686 stat->createtime = stat->modtime;
1687 stat->accesstime = -1;
1688 stat->readonly = 1; /* .zip files are always read only */
1689
1690 return 1;
1691} /* ZIP_stat */
1692
1693
1694const PHYSFS_Archiver __PHYSFS_Archiver_ZIP =
1695{
1696 CURRENT_PHYSFS_ARCHIVER_API_VERSION,
1697 {
1698 "ZIP",
1699 "PkZip/WinZip/Info-Zip compatible",
1700 "Ryan C. Gordon <icculus@icculus.org>",
1701 "https://icculus.org/physfs/",
1702 1, /* supportsSymlinks */
1703 },
1704 ZIP_openArchive,
1705 __PHYSFS_DirTreeEnumerate,
1706 ZIP_openRead,
1707 ZIP_openWrite,
1708 ZIP_openAppend,
1709 ZIP_remove,
1710 ZIP_mkdir,
1711 ZIP_stat,
1712 ZIP_closeArchive
1713};
1714
1715#endif /* defined PHYSFS_SUPPORTS_ZIP */
1716
1717/* end of physfs_archiver_zip.c ... */
1718
1719