From a865b14bece9b6b48514e20a59fce503d773512c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 1 Jun 2012 05:44:50 -0400 Subject: [PATCH] Added Zip64 support to the .zip archiver. Now we can handle .zip files > 4 gigabytes, etc. --- src/archiver_lzma.c | 6 +- src/archiver_unpacked.c | 6 +- src/archiver_zip.c | 392 +++++++++++++++++++++++++++++++++++----- src/physfs.c | 26 +-- src/physfs_internal.h | 6 +- 5 files changed, 365 insertions(+), 71 deletions(-) diff --git a/src/archiver_lzma.c b/src/archiver_lzma.c index 660acda9..52fa2203 100644 --- a/src/archiver_lzma.c +++ b/src/archiver_lzma.c @@ -177,7 +177,7 @@ static int lzma_file_cmp_stdlib(const void *key, const void *object) * Compare two files with each other based on the name * Used for sorting */ -static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +static int lzma_file_cmp(void *_a, size_t one, size_t two) { LZMAfile *files = (LZMAfile *) _a; return strcmp(files[one].item->Name, files[two].item->Name); @@ -187,7 +187,7 @@ static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) /* * Swap two entries in the file array */ -static void lzma_file_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +static void lzma_file_swap(void *_a, size_t one, size_t two) { LZMAfile tmp; LZMAfile *first = &(((LZMAfile *) _a)[one]); @@ -245,7 +245,7 @@ static int lzma_files_init(LZMAarchive *archive) } } /* for */ - __PHYSFS_sort(archive->files, numFiles, lzma_file_cmp, lzma_file_swap); + __PHYSFS_sort(archive->files, (size_t) numFiles, lzma_file_cmp, lzma_file_swap); return 1; } /* lzma_load_files */ diff --git a/src/archiver_unpacked.c b/src/archiver_unpacked.c index fdd58f7b..1a7c104b 100644 --- a/src/archiver_unpacked.c +++ b/src/archiver_unpacked.c @@ -145,7 +145,7 @@ static const PHYSFS_Io UNPK_Io = }; -static int entryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +static int entryCmp(void *_a, size_t one, size_t two) { if (one != two) { @@ -157,7 +157,7 @@ static int entryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) } /* entryCmp */ -static void entrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +static void entrySwap(void *_a, size_t one, size_t two) { if (one != two) { @@ -458,7 +458,7 @@ PHYSFS_Dir *UNPK_openArchive(PHYSFS_Io *io, UNPKentry *e, BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL); } /* if */ - __PHYSFS_sort(e, num, entryCmp, entrySwap); + __PHYSFS_sort(e, (size_t) num, entryCmp, entrySwap); info->io = io; info->entryCount = num; info->entries = e; diff --git a/src/archiver_zip.c b/src/archiver_zip.c index 0746d060..2843fb98 100644 --- a/src/archiver_zip.c +++ b/src/archiver_zip.c @@ -64,13 +64,13 @@ typedef struct _ZIPentry 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_uint64 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_uint64 compressed_size; /* compressed size */ + PHYSFS_uint64 uncompressed_size; /* uncompressed size */ PHYSFS_sint64 last_mod_time; /* last file mod time */ } ZIPentry; @@ -80,8 +80,9 @@ typedef struct _ZIPentry typedef struct { PHYSFS_Io *io; - PHYSFS_uint16 entryCount; /* Number of files in ZIP. */ - ZIPentry *entries; /* info on all files in ZIP. */ + int zip64; /* non-zero if this is a Zip64 archive. */ + PHYSFS_uint64 entryCount; /* Number of files in ZIP. */ + ZIPentry *entries; /* info on all files in ZIP. */ } ZIPinfo; /* @@ -99,9 +100,12 @@ typedef struct /* Magic numbers... */ -#define ZIP_LOCAL_FILE_SIG 0x04034b50 -#define ZIP_CENTRAL_DIR_SIG 0x02014b50 -#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50 +#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 /* compression methods... */ #define COMPMETH_NONE 0 @@ -164,6 +168,17 @@ static int zlib_err(const int rc) } /* zlib_err */ +/* + * 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 */ + /* * Read an unsigned 32-bit int and swap to native byte order. */ @@ -322,7 +337,7 @@ static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset) static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io) { const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque; - return finfo->entry->uncompressed_size; + return (PHYSFS_sint64) finfo->entry->uncompressed_size; } /* ZIP_length */ @@ -521,9 +536,9 @@ static int isZip(PHYSFS_Io *io) } /* isZip */ -static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max) +static void zip_free_entries(ZIPentry *entries, PHYSFS_uint64 max) { - PHYSFS_uint32 i; + PHYSFS_uint64 i; for (i = 0; i < max; i++) { ZIPentry *entry = &entries[i]; @@ -545,9 +560,9 @@ static ZIPentry *zip_find_entry(const ZIPinfo *info, const char *path, { ZIPentry *a = info->entries; PHYSFS_sint32 pathlen = (PHYSFS_sint32) strlen(path); - PHYSFS_sint32 lo = 0; - PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); - PHYSFS_sint32 middle; + PHYSFS_sint64 lo = 0; + PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1); + PHYSFS_sint64 middle; const char *thispath = NULL; int rc; @@ -695,7 +710,7 @@ static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path) static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry) { - const PHYSFS_uint32 size = entry->uncompressed_size; + const PHYSFS_uint64 size = entry->uncompressed_size; char *path = NULL; int rc = 0; @@ -716,7 +731,7 @@ static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry) else /* symlink target path is compressed... */ { z_stream stream; - const PHYSFS_uint32 complen = entry->compressed_size; + const PHYSFS_uint64 complen = entry->compressed_size; PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen); if (compressed != NULL) { @@ -768,6 +783,8 @@ static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry) * 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. + * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's + * possible that's a Zip64 thing. */ BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0); @@ -781,10 +798,15 @@ static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry) BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); /* date/time */ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), PHYSFS_ERR_CORRUPT, 0); + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); - BAIL_IF_MACRO(ui32 && (ui32!=entry->compressed_size),PHYSFS_ERR_CORRUPT,0); + BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) && + (ui32 != entry->compressed_size), PHYSFS_ERR_CORRUPT, 0); + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); - BAIL_IF_MACRO(ui32&&(ui32!=entry->uncompressed_size),PHYSFS_ERR_CORRUPT,0); + BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) && + (ui32 != entry->uncompressed_size), PHYSFS_ERR_CORRUPT, 0); + BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0); BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0); @@ -913,10 +935,13 @@ static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime) } /* zip_dos_time_to_physfs_time */ -static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixup) +static int zip_load_entry(PHYSFS_Io *io, const int zip64, ZIPentry *entry, + PHYSFS_uint64 ofs_fixup) { PHYSFS_uint16 fnamelen, extralen, commentlen; PHYSFS_uint32 external_attr; + PHYSFS_uint32 starting_disk; + PHYSFS_uint64 offset; PHYSFS_uint16 ui16; PHYSFS_uint32 ui32; PHYSFS_sint64 si64; @@ -933,16 +958,19 @@ static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixu BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); entry->last_mod_time = zip_dos_time_to_physfs_time(ui32); BAIL_IF_MACRO(!readui32(io, &entry->crc), ERRPASS, 0); - BAIL_IF_MACRO(!readui32(io, &entry->compressed_size), ERRPASS, 0); - BAIL_IF_MACRO(!readui32(io, &entry->uncompressed_size), ERRPASS, 0); + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + entry->compressed_size = (PHYSFS_uint64) ui32; + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + entry->uncompressed_size = (PHYSFS_uint64) ui32; BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0); BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0); BAIL_IF_MACRO(!readui16(io, &commentlen), ERRPASS, 0); - BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* disk number start */ + BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); + starting_disk = (PHYSFS_uint32) ui16; BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* internal file attribs */ BAIL_IF_MACRO(!readui32(io, &external_attr), ERRPASS, 0); - BAIL_IF_MACRO(!readui32(io, &entry->offset), ERRPASS, 0); - entry->offset += ofs_fixup; + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + offset = (PHYSFS_uint64) ui32; entry->symlink = NULL; /* will be resolved later, if necessary. */ entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ? @@ -960,7 +988,80 @@ static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixu if (si64 == -1) goto zip_load_entry_puked; - /* seek to the start of the next entry in the central directory... */ + /* + * The actual sizes didn't fit in 32-bits; look for the Zip64 + * extended information extra field... + */ + if ( (zip64) && + ((offset == 0xFFFFFFFF) || + (starting_disk == 0xFFFFFFFF) || + (entry->compressed_size == 0xFFFFFFFF) || + (entry->uncompressed_size == 0xFFFFFFFF)) ) + { + int found = 0; + PHYSFS_uint16 sig, len; + while (extralen > 4) + { + if (!readui16(io, &sig)) + goto zip_load_entry_puked; + else if (!readui16(io, &len)) + goto zip_load_entry_puked; + + si64 += 4 + len; + extralen -= 4 + len; + if (sig != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG) + { + if (!io->seek(io, si64)) + goto zip_load_entry_puked; + continue; + } /* if */ + + found = 1; + break; + } /* while */ + + GOTO_IF_MACRO(!found, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); + + if (entry->uncompressed_size == 0xFFFFFFFF) + { + GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); + if (!readui64(io, &entry->uncompressed_size)) + goto zip_load_entry_puked; + len -= 8; + } /* if */ + + if (entry->compressed_size == 0xFFFFFFFF) + { + GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); + if (!readui64(io, &entry->compressed_size)) + goto zip_load_entry_puked; + len -= 8; + } /* if */ + + if (offset == 0xFFFFFFFF) + { + GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); + if (!readui64(io, &offset)) + goto zip_load_entry_puked; + len -= 8; + } /* if */ + + if (starting_disk == 0xFFFFFFFF) + { + GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); + if (!readui32(io, &starting_disk)) + goto zip_load_entry_puked; + len -= 4; + } /* if */ + + GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); + } /* if */ + + GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); + + entry->offset = offset + ofs_fixup; + + /* seek to the start of the next entry in the central directory... */ if (!io->seek(io, si64 + extralen + commentlen)) goto zip_load_entry_puked; @@ -972,7 +1073,7 @@ static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixu } /* zip_load_entry */ -static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +static int zip_entry_cmp(void *_a, size_t one, size_t two) { if (one != two) { @@ -984,7 +1085,7 @@ static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) } /* zip_entry_cmp */ -static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +static void zip_entry_swap(void *_a, size_t one, size_t two) { if (one != two) { @@ -999,10 +1100,12 @@ static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) static int zip_load_entries(PHYSFS_Io *io, ZIPinfo *info, - PHYSFS_uint32 data_ofs, PHYSFS_uint32 central_ofs) + const PHYSFS_uint64 data_ofs, + const PHYSFS_uint64 central_ofs) { - PHYSFS_uint32 max = info->entryCount; - PHYSFS_uint32 i; + const PHYSFS_uint64 max = info->entryCount; + const int zip64 = info->zip64; + PHYSFS_uint64 i; BAIL_IF_MACRO(!io->seek(io, central_ofs), ERRPASS, 0); @@ -1011,26 +1114,203 @@ static int zip_load_entries(PHYSFS_Io *io, ZIPinfo *info, for (i = 0; i < max; i++) { - if (!zip_load_entry(io, &info->entries[i], data_ofs)) + if (!zip_load_entry(io, zip64, &info->entries[i], data_ofs)) { zip_free_entries(info->entries, i); return 0; } /* if */ } /* for */ - __PHYSFS_sort(info->entries, max, zip_entry_cmp, zip_entry_swap); + __PHYSFS_sort(info->entries, (size_t) max, zip_entry_cmp, zip_entry_swap); return 1; } /* zip_load_entries */ +static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io, + PHYSFS_sint64 _pos, + PHYSFS_uint64 offset) +{ + /* + * Naturally, the offset is useless to us; it is the offset from the + * start of file, which is meaningless if we've appended this .zip to + * a self-extracting .exe. We need to find this on our own. It should + * be directly before the locator record, but the record in question, + * like the original end-of-central-directory record, ends with a + * variable-length field. Unlike the original, which has to store the + * size of that variable-length field in a 16-bit int and thus has to be + * within 64k, the new one gets 64-bits. + * + * Fortunately, the only currently-specified record for that variable + * length block is some weird proprietary thing that deals with EBCDIC + * and tape backups or something. So we don't seek far. + */ + + PHYSFS_uint32 ui32; + const PHYSFS_uint64 pos = (PHYSFS_uint64) _pos; + + assert(_pos > 0); + + /* Try offset specified in the Zip64 end of central directory locator. */ + /* This works if the entire PHYSFS_Io is the zip file. */ + BAIL_IF_MACRO(!io->seek(io, offset), ERRPASS, -1); + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1); + if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG) + return offset; + + /* Try 56 bytes before the Zip64 end of central directory locator. */ + /* This works if the record isn't variable length and is version 1. */ + if (pos > 56) + { + BAIL_IF_MACRO(!io->seek(io, pos-56), ERRPASS, -1); + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1); + if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG) + return pos-56; + } /* if */ + + /* Try 84 bytes before the Zip64 end of central directory locator. */ + /* This works if the record isn't variable length and is version 2. */ + if (pos > 84) + { + BAIL_IF_MACRO(!io->seek(io, pos-84), ERRPASS, -1); + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1); + if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG) + return pos-84; + } /* if */ + + /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */ + /* Just try moving back at most 256k. Oh well. */ + if ((offset < pos) && (pos > 4)) + { + /* we assume you can eat this stack if you handle Zip64 files. */ + PHYSFS_uint8 buf[256 * 1024]; + PHYSFS_uint64 len = pos - offset; + PHYSFS_uint32 i; + + if (len > sizeof (buf)) + len = sizeof (buf); + + BAIL_IF_MACRO(!io->seek(io, pos - len), ERRPASS, -1); + BAIL_IF_MACRO(!__PHYSFS_readAll(io, buf, len), ERRPASS, -1); + for (i = len - 4; i >= 0; i--) + { + if (buf[i] != 0x50) + continue; + if ( (buf[i+1] == 0x4b) && + (buf[i+2] == 0x06) && + (buf[i+3] == 0x06) ) + return pos - (len - i); + } /* for */ + } /* if */ + + BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */ +} /* zip64_find_end_of_central_dir */ + + +static int zip64_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info, + PHYSFS_uint64 *data_start, + PHYSFS_uint64 *dir_ofs, + PHYSFS_sint64 pos) +{ + PHYSFS_uint64 ui64; + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + //PHYSFS_sint64 len; + + /* We should be positioned right past the locator signature. */ + + if ((pos < 0) || (!io->seek(io, pos))) + return 0; + + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + if (ui32 != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG) + return -1; /* it's not a Zip64 archive. Not an error, though! */ + + info->zip64 = 1; + + /* number of the disk with the start of the central directory. */ + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0); + + /* offset of Zip64 end of central directory record. */ + BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0); + + /* total number of disks */ + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + BAIL_IF_MACRO(ui32 != 1, PHYSFS_ERR_CORRUPT, 0); + + pos = zip64_find_end_of_central_dir(io, pos, ui64); + if (pos < 0) + return 0; /* oh well. */ + + /* + * For self-extracting archives, etc, there's crapola in the file + * before the zipfile records; we calculate how much data there is + * prepended by determining how far the zip64-end-of-central-directory + * offset is from where it is supposed to be...the difference in bytes + * is how much arbitrary data is at the start of the physical file. + */ + assert(((PHYSFS_uint64) pos) >= ui64); + *data_start = ((PHYSFS_uint64) pos) - ui64; + + BAIL_IF_MACRO(!io->seek(io, pos), ERRPASS, 0); + + /* check signature again, just in case. */ + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + BAIL_IF_MACRO(ui32 != ZIP64_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); + + /* size of Zip64 end of central directory record. */ + BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0); + + /* version made by. */ + BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); + + /* version needed to extract. */ + BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); + + /* number of this disk. */ + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0); + + /* number of disk with start of central directory record. */ + BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); + BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0); + + /* total number of entries in the central dir on this disk */ + BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0); + + /* total number of entries in the central dir */ + BAIL_IF_MACRO(!readui64(io, &info->entryCount), ERRPASS, 0); + BAIL_IF_MACRO(ui64 != info->entryCount, PHYSFS_ERR_CORRUPT, 0); + + /* size of the central directory */ + BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0); + + /* offset of central directory */ + BAIL_IF_MACRO(!readui64(io, dir_ofs), ERRPASS, 0); + + /* Since we know the difference, fix up the central dir offset... */ + *dir_ofs += *data_start; + + /* + * There are more fields here, for encryption and feature-specific things, + * but we don't care about any of them at the moment. + */ + + return 1; /* made it. */ +} /* zip64_parse_end_of_central_dir */ + + static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info, - PHYSFS_uint32 *data_start, - PHYSFS_uint32 *central_dir_ofs) + PHYSFS_uint64 *data_start, + PHYSFS_uint64 *dir_ofs) { + PHYSFS_uint16 entryCount16; + PHYSFS_uint32 offset32; PHYSFS_uint32 ui32; PHYSFS_uint16 ui16; PHYSFS_sint64 len; PHYSFS_sint64 pos; + int rc; /* find the end-of-central-dir record, and seek to it. */ pos = zip_find_end_of_central_dir(io, &len); @@ -1041,6 +1321,18 @@ static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info, BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); + /* Seek back to see if "Zip64 end of central directory locator" exists. */ + /* this record is 20 bytes before end-of-central-dir */ + rc = zip64_parse_end_of_central_dir(io, info, data_start, dir_ofs, pos-20); + BAIL_IF_MACRO(rc == 0, ERRPASS, 0); + if (rc == 1) + return 1; /* we're done here. */ + + assert(rc == -1); /* no error, just not a Zip64 archive. */ + + /* Not Zip64? Seek back to where we were and keep processing. */ + BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0); + /* number of this disk */ BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); BAIL_IF_MACRO(ui16 != 0, PHYSFS_ERR_CORRUPT, 0); @@ -1053,15 +1345,16 @@ static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info, BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* total number of entries in the central dir */ - BAIL_IF_MACRO(!readui16(io, &info->entryCount), ERRPASS, 0); - BAIL_IF_MACRO(ui16 != info->entryCount, PHYSFS_ERR_CORRUPT, 0); + BAIL_IF_MACRO(!readui16(io, &entryCount16), ERRPASS, 0); + BAIL_IF_MACRO(ui16 != entryCount16, PHYSFS_ERR_CORRUPT, 0); /* size of the central directory */ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); /* offset of central directory */ - BAIL_IF_MACRO(!readui32(io, central_dir_ofs), ERRPASS, 0); - BAIL_IF_MACRO(pos < *central_dir_ofs + ui32, PHYSFS_ERR_CORRUPT, 0); + BAIL_IF_MACRO(!readui32(io, &offset32), ERRPASS, 0); + *dir_ofs = (PHYSFS_uint64) offset32; + BAIL_IF_MACRO(pos < (*dir_ofs + ui32), PHYSFS_ERR_CORRUPT, 0); /* * For self-extracting archives, etc, there's crapola in the file @@ -1071,10 +1364,10 @@ static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info, * sizeof central dir)...the difference in bytes is how much arbitrary * data is at the start of the physical file. */ - *data_start = (PHYSFS_uint32) (pos - (*central_dir_ofs + ui32)); + *data_start = (PHYSFS_uint64) (pos - (*dir_ofs + ui32)); /* Now that we know the difference, fix up the central dir offset... */ - *central_dir_ofs += *data_start; + *dir_ofs += *data_start; /* zipfile comment length */ BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); @@ -1093,8 +1386,8 @@ static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info, static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting) { ZIPinfo *info = NULL; - PHYSFS_uint32 data_start; - PHYSFS_uint32 cent_dir_ofs; + PHYSFS_uint64 data_start; + PHYSFS_uint64 cent_dir_ofs; assert(io != NULL); /* shouldn't ever happen. */ @@ -1122,14 +1415,14 @@ static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting) } /* ZIP_openArchive */ -static PHYSFS_sint32 zip_find_start_of_dir(ZIPinfo *info, const char *path, +static PHYSFS_sint64 zip_find_start_of_dir(ZIPinfo *info, const char *path, int stop_on_first_find) { - PHYSFS_sint32 lo = 0; - PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint64 lo = 0; + PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1); PHYSFS_sint32 middle; PHYSFS_uint32 dlen = (PHYSFS_uint32) strlen(path); - PHYSFS_sint32 retval = -1; + PHYSFS_sint64 retval = -1; const char *name; int rc; @@ -1198,7 +1491,8 @@ static void ZIP_enumerateFiles(PHYSFS_Dir *opaque, const char *dname, const char *origdir, void *callbackdata) { ZIPinfo *info = ((ZIPinfo *) opaque); - PHYSFS_sint32 dlen, dlen_inc, max, i; + PHYSFS_sint32 dlen, dlen_inc; + PHYSFS_sint64 i, max; i = zip_find_start_of_dir(info, dname, 0); if (i == -1) /* no such directory. */ @@ -1209,7 +1503,7 @@ static void ZIP_enumerateFiles(PHYSFS_Dir *opaque, const char *dname, dlen--; dlen_inc = ((dlen > 0) ? 1 : 0) + dlen; - max = (PHYSFS_sint32) info->entryCount; + max = (PHYSFS_sint64) info->entryCount; while (i < max) { char *e = info->entries[i].name; @@ -1384,7 +1678,7 @@ static int ZIP_stat(PHYSFS_Dir *opaque, const char *filename, int *exists, else { - stat->filesize = entry->uncompressed_size; + stat->filesize = (PHYSFS_sint64) entry->uncompressed_size; stat->filetype = PHYSFS_FILETYPE_REGULAR; } /* else */ diff --git a/src/physfs.c b/src/physfs.c index 1c210f75..9498a9b2 100644 --- a/src/physfs.c +++ b/src/physfs.c @@ -589,11 +589,11 @@ static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *)) } /* doEnumStringList */ -static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi, - int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), - void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +static void __PHYSFS_bubble_sort(void *a, size_t lo, size_t hi, + int (*cmpfn)(void *, size_t, size_t), + void (*swapfn)(void *, size_t, size_t)) { - PHYSFS_uint32 i; + size_t i; int sorted; do @@ -611,13 +611,13 @@ static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi, } /* __PHYSFS_bubble_sort */ -static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi, - int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), - void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +static void __PHYSFS_quick_sort(void *a, size_t lo, size_t hi, + int (*cmpfn)(void *, size_t, size_t), + void (*swapfn)(void *, size_t, size_t)) { - PHYSFS_uint32 i; - PHYSFS_uint32 j; - PHYSFS_uint32 v; + size_t i; + size_t j; + size_t v; if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD) __PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn); @@ -649,9 +649,9 @@ static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi, } /* __PHYSFS_quick_sort */ -void __PHYSFS_sort(void *entries, PHYSFS_uint32 max, - int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), - void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +void __PHYSFS_sort(void *entries, size_t max, + int (*cmpfn)(void *, size_t, size_t), + void (*swapfn)(void *, size_t, size_t)) { /* * Quicksort w/ Bubblesort fallback algorithm inspired by code from here: diff --git a/src/physfs_internal.h b/src/physfs_internal.h index 98560fe8..1dec63fa 100644 --- a/src/physfs_internal.h +++ b/src/physfs_internal.h @@ -299,9 +299,9 @@ void __PHYSFS_setError(const PHYSFS_ErrorCode err); * * See zip.c for an example. */ -void __PHYSFS_sort(void *entries, PHYSFS_uint32 max, - int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), - void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)); +void __PHYSFS_sort(void *entries, size_t max, + int (*cmpfn)(void *, size_t, size_t), + void (*swapfn)(void *, size_t, size_t)); /* * This isn't a formal error code, it's just for BAIL_MACRO.