Loading libziparchive/include/ziparchive/zip_archive.h +1 −1 Original line number Diff line number Diff line Loading @@ -145,7 +145,7 @@ struct ZipArchiveInfo { /** The size in bytes of the archive itself. Used by zipinfo. */ off64_t archive_size; /** The number of entries in the archive. */ size_t entry_count; uint64_t entry_count; }; /** Loading libziparchive/zip_archive.cc +116 −57 Original line number Diff line number Diff line Loading @@ -65,6 +65,10 @@ static const bool kCrcChecksEnabled = false; // The maximum number of bytes to scan backwards for the EOCD start. static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord); // Set a reasonable cap (256 GiB) for the zip file size. So the data is always valid when // we parse the fields in cd or local headers as 64 bits signed integers. static constexpr uint64_t kMaxFileLength = 256 * static_cast<uint64_t>(1u << 30u); /* * A Read-only Zip archive. * Loading Loading @@ -125,12 +129,27 @@ ZipArchive::~ZipArchive() { } } static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive, struct CentralDirectoryInfo { uint64_t num_records; // The size of the central directory (in bytes). uint64_t cd_size; // The offset of the start of the central directory, relative // to the start of the file. uint64_t cd_start_offset; }; static ZipError FindCentralDirectoryInfoForZip64(CentralDirectoryInfo* /* cdInfo */) { ALOGW("Zip: Parsing zip64 EOCD isn't supported yet."); return kInvalidFile; } static ZipError FindCentralDirectoryInfo(const char* debug_file_name, ZipArchive* archive, off64_t file_length, uint32_t read_amount, uint8_t* scan_buffer) { CentralDirectoryInfo* cdInfo) { std::vector<uint8_t> scan_buffer(read_amount); const off64_t search_start = file_length - read_amount; if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) { if (!archive->mapped_zip.ReadAtOffset(scan_buffer.data(), read_amount, search_start)) { ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start)); return kIoError; Loading Loading @@ -159,7 +178,7 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc } const off64_t eocd_offset = search_start + i; const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i); auto eocd = reinterpret_cast<const EocdRecord*>(scan_buffer.data() + i); /* * Verify that there's no trailing space at the end of the central directory * and its comment. Loading @@ -171,6 +190,13 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc return kInvalidFile; } // One of the field is 0xFFFFFFFF, look for the zip64 EOCD instead. if (eocd->cd_size == UINT32_MAX || eocd->cd_start_offset == UINT32_MAX) { ALOGV("Looking for the zip64 EOCD, cd_size: %" PRIu32 "cd_start_offset: %" PRId32, eocd->cd_size, eocd->cd_start_offset); return FindCentralDirectoryInfoForZip64(cdInfo); } /* * Grab the CD offset and size, and the number of entries in the * archive and verify that they look reasonable. Loading @@ -180,47 +206,29 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset)); return kInvalidOffset; } if (eocd->num_records == 0) { #if defined(__ANDROID__) ALOGW("Zip: empty archive?"); #endif return kEmptyArchive; } ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records, eocd->cd_size, eocd->cd_start_offset); // It all looks good. Create a mapping for the CD, and set the fields // in archive. if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset), static_cast<size_t>(eocd->cd_size))) { return kMmapFailed; } archive->num_entries = eocd->num_records; archive->directory_offset = eocd->cd_start_offset; return 0; *cdInfo = {.num_records = eocd->num_records, .cd_size = eocd->cd_size, .cd_start_offset = eocd->cd_start_offset}; return kSuccess; } /* * Find the zip Central Directory and memory-map it. * * On success, returns 0 after populating fields from the EOCD area: * On success, returns kSuccess after populating fields from the EOCD area: * directory_offset * directory_ptr * num_entries */ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) { // Test file length. We use lseek64 to make sure the file // is small enough to be a zip file (Its size must be less than // 0xffffffff bytes). static ZipError MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) { // Test file length. We use lseek64 to make sure the file is small enough to be a zip file. off64_t file_length = archive->mapped_zip.GetFileLength(); if (file_length == -1) { return kInvalidFile; } if (file_length > static_cast<off64_t>(0xffffffff)) { if (file_length > kMaxFileLength) { ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length)); return kInvalidFile; } Loading @@ -247,12 +255,41 @@ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* arch read_amount = static_cast<uint32_t>(file_length); } std::vector<uint8_t> scan_buffer(read_amount); int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data()); CentralDirectoryInfo cdInfo = {}; if (auto result = FindCentralDirectoryInfo(debug_file_name, archive, file_length, read_amount, &cdInfo); result != kSuccess) { return result; } if (cdInfo.num_records == 0) { #if defined(__ANDROID__) ALOGW("Zip: empty archive?"); #endif return kEmptyArchive; } if (cdInfo.cd_size >= SIZE_MAX) { ALOGW("Zip: The size of central directory doesn't fit in range of size_t: %" PRIu64, cdInfo.cd_size); return kInvalidFile; } ALOGV("+++ num_entries=%" PRIu64 " dir_size=%" PRIu64 " dir_offset=%" PRIu64, cdInfo.num_records, cdInfo.cd_size, cdInfo.cd_start_offset); // It all looks good. Create a mapping for the CD, and set the fields in archive. if (!archive->InitializeCentralDirectory(static_cast<off64_t>(cdInfo.cd_start_offset), static_cast<size_t>(cdInfo.cd_size))) { return kMmapFailed; } archive->num_entries = cdInfo.num_records; archive->directory_offset = cdInfo.cd_start_offset; return kSuccess; } /* * Parses the Zip archive's Central Directory. Allocates and populates the * hash table. Loading @@ -262,13 +299,12 @@ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* arch static int32_t ParseZipArchive(ZipArchive* archive) { const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr(); const size_t cd_length = archive->central_directory.GetMapLength(); const uint16_t num_entries = archive->num_entries; const uint64_t num_entries = archive->num_entries; // TODO(xunchang) parse the zip64 Eocd if (num_entries > UINT16_MAX) { archive->cd_entry_map = CdEntryMapZip64::Create(); if (num_entries <= UINT16_MAX) { archive->cd_entry_map = CdEntryMapZip32::Create(static_cast<uint16_t>(num_entries)); } else { archive->cd_entry_map = CdEntryMapZip32::Create(num_entries); archive->cd_entry_map = CdEntryMapZip64::Create(); } if (archive->cd_entry_map == nullptr) { return kAllocationFailed; Loading @@ -280,9 +316,9 @@ static int32_t ParseZipArchive(ZipArchive* archive) { */ const uint8_t* const cd_end = cd_ptr + cd_length; const uint8_t* ptr = cd_ptr; for (uint16_t i = 0; i < num_entries; i++) { for (uint64_t i = 0; i < num_entries; i++) { if (ptr > cd_end - sizeof(CentralDirectoryRecord)) { ALOGW("Zip: ran off the end (item #%" PRIu16 ", %zu bytes of central directory)", i, ALOGW("Zip: ran off the end (item #%" PRIu64 ", %zu bytes of central directory)", i, cd_length); #if defined(__ANDROID__) android_errorWriteLog(0x534e4554, "36392138"); Loading @@ -292,14 +328,7 @@ static int32_t ParseZipArchive(ZipArchive* archive) { const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr); if (cdr->record_signature != CentralDirectoryRecord::kSignature) { ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i); return kInvalidFile; } const off64_t local_header_offset = cdr->local_file_header_offset; if (local_header_offset >= archive->directory_offset) { ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16, static_cast<int64_t>(local_header_offset), i); ALOGW("Zip: missed a central dir sig (at %" PRIu64 ")", i); return kInvalidFile; } Loading @@ -308,15 +337,37 @@ static int32_t ParseZipArchive(ZipArchive* archive) { const uint16_t comment_length = cdr->comment_length; const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord); if (file_name + file_name_length > cd_end) { ALOGW("Zip: file name for entry %" PRIu16 if (file_name_length >= cd_length || file_name > cd_end - file_name_length) { ALOGW("Zip: file name for entry %" PRIu64 " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu", i, file_name_length, cd_length); return kInvalidEntryName; } const uint8_t* extra_field = file_name + file_name_length; if (extra_length >= cd_length || extra_field > cd_end - extra_length) { ALOGW("Zip: extra field for entry %" PRIu64 " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu", i, extra_length, cd_length); return kInvalidFile; } off64_t local_header_offset = cdr->local_file_header_offset; if (local_header_offset == UINT32_MAX) { // TODO(xunchang) parse the zip64 eocd ALOGW("Zip: Parsing zip64 cd entry isn't supported yet"); return kInvalidFile; } if (local_header_offset >= archive->directory_offset) { ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu64, static_cast<int64_t>(local_header_offset), i); return kInvalidFile; } // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters. if (!IsValidEntryName(file_name, file_name_length)) { ALOGW("Zip: invalid file name at entry %" PRIu16, i); ALOGW("Zip: invalid file name at entry %" PRIu64, i); return kInvalidEntryName; } Loading @@ -331,7 +382,7 @@ static int32_t ParseZipArchive(ZipArchive* archive) { ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length; if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) { ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i); ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu64, ptr - cd_ptr, cd_length, i); return kInvalidFile; } } Loading @@ -351,14 +402,14 @@ static int32_t ParseZipArchive(ZipArchive* archive) { return kInvalidFile; } ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries); ALOGV("+++ zip good scan %" PRIu64 " entries", num_entries); return 0; } static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) { int32_t result = MapCentralDirectory(debug_file_name, archive); return result != 0 ? result : ParseZipArchive(archive); return result != kSuccess ? result : ParseZipArchive(archive); } int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle, Loading Loading @@ -489,7 +540,15 @@ static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName, // Figure out the local header offset from the central directory. The // actual file data will begin after the local header and the name / // extra comments. const off64_t local_header_offset = cdr->local_file_header_offset; off64_t local_header_offset = cdr->local_file_header_offset; // One of the info field is UINT32_MAX, try to parse the real value in the zip64 extended info in // the extra field. if (cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX || cdr->local_file_header_offset == UINT32_MAX) { ALOGW("Zip: Parsing zip64 local file header isn't supported yet"); return kInvalidFile; } if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) { ALOGW("Zip: bad local hdr offset in zip"); return kInvalidOffset; Loading libziparchive/zip_archive_common.h +85 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <inttypes.h> #include <optional> // The "end of central directory" (EOCD) record. Each archive // contains exactly once such record which appears at the end of // the archive. It contains archive wide information like the Loading Loading @@ -173,6 +175,89 @@ struct DataDescriptor { DISALLOW_COPY_AND_ASSIGN(DataDescriptor); } __attribute__((packed)); // The zip64 end of central directory locator helps to find the zip64 EOCD. struct Zip64EocdLocator { static constexpr uint32_t kSignature = 0x07064b50; // The signature of zip64 eocd locator, must be |kSignature| uint32_t locator_signature; // The start disk of the zip64 eocd. This implementation assumes that each // archive spans a single disk only. uint32_t eocd_start_disk; // The offset offset of the zip64 end of central directory record. uint64_t zip64_eocd_offset; // The total number of disks. This implementation assumes that each archive // spans a single disk only. uint32_t num_of_disks; private: Zip64EocdLocator() = default; DISALLOW_COPY_AND_ASSIGN(Zip64EocdLocator); } __attribute__((packed)); // The optional zip64 EOCD. If one of the fields in the end of central directory // record is too small to hold required data, the field SHOULD be set to -1 // (0xFFFF or 0xFFFFFFFF) and the ZIP64 format record SHOULD be created. struct Zip64EocdRecord { static constexpr uint32_t kSignature = 0x06064b50; // The signature of zip64 eocd record, must be |kSignature| uint32_t record_signature; // Size of zip64 end of central directory record. It SHOULD be the size of the // remaining record and SHOULD NOT include the leading 12 bytes. uint64_t record_size; // The version of the tool that make this archive. uint16_t version_made_by; // Tool version needed to extract this archive. uint16_t version_needed; // Number of this disk. uint32_t disk_num; // Number of the disk with the start of the central directory. uint32_t cd_start_disk; // Total number of entries in the central directory on this disk. // This implementation assumes that each archive spans a single // disk only. i.e, that num_records_on_disk == num_records. uint64_t num_records_on_disk; // The total number of central directory records. uint64_t num_records; // The size of the central directory in bytes. uint64_t cd_size; // The offset of the start of the central directory, relative to the start of // the file. uint64_t cd_start_offset; private: Zip64EocdRecord() = default; DISALLOW_COPY_AND_ASSIGN(Zip64EocdRecord); } __attribute__((packed)); // The possible contents of the Zip64 Extended Information Extra Field. It may appear in // the 'extra' field of a central directory record or local file header. The order of // the fields in the zip64 extended information record is fixed, but the fields MUST // only appear if the corresponding local or central directory record field is set to // 0xFFFF or 0xFFFFFFFF. And this entry in the Local header MUST include BOTH original // and compressed file size fields. struct Zip64ExtendedInfo { static constexpr uint16_t kHeaderId = 0x0001; // The header tag for this 'extra' block, should be |kHeaderId|. uint16_t header_id; // The size in bytes of the remaining data (excluding the top 4 bytes). uint16_t data_size; // Size in bytes of the uncompressed file. std::optional<uint64_t> uncompressed_file_size; // Size in bytes of the compressed file. std::optional<uint64_t> compressed_file_size; // Local file header offset relative to the start of the zip file. std::optional<uint64_t> local_header_offset; // This implementation assumes that each archive spans a single disk only. So // the disk_number is not used. // uint32_t disk_num; private: Zip64ExtendedInfo() = default; DISALLOW_COPY_AND_ASSIGN(Zip64ExtendedInfo); }; // mask value that signifies that the entry has a DD static const uint32_t kGPBDDFlagMask = 0x0008; Loading libziparchive/zip_archive_private.h +1 −1 Original line number Diff line number Diff line Loading @@ -95,7 +95,7 @@ struct ZipArchive { std::unique_ptr<android::base::MappedFile> directory_map; // number of entries in the Zip archive uint16_t num_entries; uint64_t num_entries; std::unique_ptr<CdEntryMapInterface> cd_entry_map; ZipArchive(MappedZipFile&& map, bool assume_ownership); Loading libziparchive/ziptool.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -133,8 +133,8 @@ static void MaybeShowHeader(ZipArchiveHandle zah) { if (!flag_1 && includes.empty() && excludes.empty()) { ZipArchiveInfo info{GetArchiveInfo(zah)}; printf("Archive: %s\n", archive_name); printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size, info.entry_count); printf("Zip file size: %" PRId64 " bytes, number of entries: %" PRIu64 "\n", info.archive_size, info.entry_count); } } } Loading Loading
libziparchive/include/ziparchive/zip_archive.h +1 −1 Original line number Diff line number Diff line Loading @@ -145,7 +145,7 @@ struct ZipArchiveInfo { /** The size in bytes of the archive itself. Used by zipinfo. */ off64_t archive_size; /** The number of entries in the archive. */ size_t entry_count; uint64_t entry_count; }; /** Loading
libziparchive/zip_archive.cc +116 −57 Original line number Diff line number Diff line Loading @@ -65,6 +65,10 @@ static const bool kCrcChecksEnabled = false; // The maximum number of bytes to scan backwards for the EOCD start. static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord); // Set a reasonable cap (256 GiB) for the zip file size. So the data is always valid when // we parse the fields in cd or local headers as 64 bits signed integers. static constexpr uint64_t kMaxFileLength = 256 * static_cast<uint64_t>(1u << 30u); /* * A Read-only Zip archive. * Loading Loading @@ -125,12 +129,27 @@ ZipArchive::~ZipArchive() { } } static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive, struct CentralDirectoryInfo { uint64_t num_records; // The size of the central directory (in bytes). uint64_t cd_size; // The offset of the start of the central directory, relative // to the start of the file. uint64_t cd_start_offset; }; static ZipError FindCentralDirectoryInfoForZip64(CentralDirectoryInfo* /* cdInfo */) { ALOGW("Zip: Parsing zip64 EOCD isn't supported yet."); return kInvalidFile; } static ZipError FindCentralDirectoryInfo(const char* debug_file_name, ZipArchive* archive, off64_t file_length, uint32_t read_amount, uint8_t* scan_buffer) { CentralDirectoryInfo* cdInfo) { std::vector<uint8_t> scan_buffer(read_amount); const off64_t search_start = file_length - read_amount; if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) { if (!archive->mapped_zip.ReadAtOffset(scan_buffer.data(), read_amount, search_start)) { ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start)); return kIoError; Loading Loading @@ -159,7 +178,7 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc } const off64_t eocd_offset = search_start + i; const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i); auto eocd = reinterpret_cast<const EocdRecord*>(scan_buffer.data() + i); /* * Verify that there's no trailing space at the end of the central directory * and its comment. Loading @@ -171,6 +190,13 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc return kInvalidFile; } // One of the field is 0xFFFFFFFF, look for the zip64 EOCD instead. if (eocd->cd_size == UINT32_MAX || eocd->cd_start_offset == UINT32_MAX) { ALOGV("Looking for the zip64 EOCD, cd_size: %" PRIu32 "cd_start_offset: %" PRId32, eocd->cd_size, eocd->cd_start_offset); return FindCentralDirectoryInfoForZip64(cdInfo); } /* * Grab the CD offset and size, and the number of entries in the * archive and verify that they look reasonable. Loading @@ -180,47 +206,29 @@ static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* arc eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset)); return kInvalidOffset; } if (eocd->num_records == 0) { #if defined(__ANDROID__) ALOGW("Zip: empty archive?"); #endif return kEmptyArchive; } ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records, eocd->cd_size, eocd->cd_start_offset); // It all looks good. Create a mapping for the CD, and set the fields // in archive. if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset), static_cast<size_t>(eocd->cd_size))) { return kMmapFailed; } archive->num_entries = eocd->num_records; archive->directory_offset = eocd->cd_start_offset; return 0; *cdInfo = {.num_records = eocd->num_records, .cd_size = eocd->cd_size, .cd_start_offset = eocd->cd_start_offset}; return kSuccess; } /* * Find the zip Central Directory and memory-map it. * * On success, returns 0 after populating fields from the EOCD area: * On success, returns kSuccess after populating fields from the EOCD area: * directory_offset * directory_ptr * num_entries */ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) { // Test file length. We use lseek64 to make sure the file // is small enough to be a zip file (Its size must be less than // 0xffffffff bytes). static ZipError MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) { // Test file length. We use lseek64 to make sure the file is small enough to be a zip file. off64_t file_length = archive->mapped_zip.GetFileLength(); if (file_length == -1) { return kInvalidFile; } if (file_length > static_cast<off64_t>(0xffffffff)) { if (file_length > kMaxFileLength) { ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length)); return kInvalidFile; } Loading @@ -247,12 +255,41 @@ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* arch read_amount = static_cast<uint32_t>(file_length); } std::vector<uint8_t> scan_buffer(read_amount); int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data()); CentralDirectoryInfo cdInfo = {}; if (auto result = FindCentralDirectoryInfo(debug_file_name, archive, file_length, read_amount, &cdInfo); result != kSuccess) { return result; } if (cdInfo.num_records == 0) { #if defined(__ANDROID__) ALOGW("Zip: empty archive?"); #endif return kEmptyArchive; } if (cdInfo.cd_size >= SIZE_MAX) { ALOGW("Zip: The size of central directory doesn't fit in range of size_t: %" PRIu64, cdInfo.cd_size); return kInvalidFile; } ALOGV("+++ num_entries=%" PRIu64 " dir_size=%" PRIu64 " dir_offset=%" PRIu64, cdInfo.num_records, cdInfo.cd_size, cdInfo.cd_start_offset); // It all looks good. Create a mapping for the CD, and set the fields in archive. if (!archive->InitializeCentralDirectory(static_cast<off64_t>(cdInfo.cd_start_offset), static_cast<size_t>(cdInfo.cd_size))) { return kMmapFailed; } archive->num_entries = cdInfo.num_records; archive->directory_offset = cdInfo.cd_start_offset; return kSuccess; } /* * Parses the Zip archive's Central Directory. Allocates and populates the * hash table. Loading @@ -262,13 +299,12 @@ static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* arch static int32_t ParseZipArchive(ZipArchive* archive) { const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr(); const size_t cd_length = archive->central_directory.GetMapLength(); const uint16_t num_entries = archive->num_entries; const uint64_t num_entries = archive->num_entries; // TODO(xunchang) parse the zip64 Eocd if (num_entries > UINT16_MAX) { archive->cd_entry_map = CdEntryMapZip64::Create(); if (num_entries <= UINT16_MAX) { archive->cd_entry_map = CdEntryMapZip32::Create(static_cast<uint16_t>(num_entries)); } else { archive->cd_entry_map = CdEntryMapZip32::Create(num_entries); archive->cd_entry_map = CdEntryMapZip64::Create(); } if (archive->cd_entry_map == nullptr) { return kAllocationFailed; Loading @@ -280,9 +316,9 @@ static int32_t ParseZipArchive(ZipArchive* archive) { */ const uint8_t* const cd_end = cd_ptr + cd_length; const uint8_t* ptr = cd_ptr; for (uint16_t i = 0; i < num_entries; i++) { for (uint64_t i = 0; i < num_entries; i++) { if (ptr > cd_end - sizeof(CentralDirectoryRecord)) { ALOGW("Zip: ran off the end (item #%" PRIu16 ", %zu bytes of central directory)", i, ALOGW("Zip: ran off the end (item #%" PRIu64 ", %zu bytes of central directory)", i, cd_length); #if defined(__ANDROID__) android_errorWriteLog(0x534e4554, "36392138"); Loading @@ -292,14 +328,7 @@ static int32_t ParseZipArchive(ZipArchive* archive) { const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr); if (cdr->record_signature != CentralDirectoryRecord::kSignature) { ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i); return kInvalidFile; } const off64_t local_header_offset = cdr->local_file_header_offset; if (local_header_offset >= archive->directory_offset) { ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16, static_cast<int64_t>(local_header_offset), i); ALOGW("Zip: missed a central dir sig (at %" PRIu64 ")", i); return kInvalidFile; } Loading @@ -308,15 +337,37 @@ static int32_t ParseZipArchive(ZipArchive* archive) { const uint16_t comment_length = cdr->comment_length; const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord); if (file_name + file_name_length > cd_end) { ALOGW("Zip: file name for entry %" PRIu16 if (file_name_length >= cd_length || file_name > cd_end - file_name_length) { ALOGW("Zip: file name for entry %" PRIu64 " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu", i, file_name_length, cd_length); return kInvalidEntryName; } const uint8_t* extra_field = file_name + file_name_length; if (extra_length >= cd_length || extra_field > cd_end - extra_length) { ALOGW("Zip: extra field for entry %" PRIu64 " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu", i, extra_length, cd_length); return kInvalidFile; } off64_t local_header_offset = cdr->local_file_header_offset; if (local_header_offset == UINT32_MAX) { // TODO(xunchang) parse the zip64 eocd ALOGW("Zip: Parsing zip64 cd entry isn't supported yet"); return kInvalidFile; } if (local_header_offset >= archive->directory_offset) { ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu64, static_cast<int64_t>(local_header_offset), i); return kInvalidFile; } // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters. if (!IsValidEntryName(file_name, file_name_length)) { ALOGW("Zip: invalid file name at entry %" PRIu16, i); ALOGW("Zip: invalid file name at entry %" PRIu64, i); return kInvalidEntryName; } Loading @@ -331,7 +382,7 @@ static int32_t ParseZipArchive(ZipArchive* archive) { ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length; if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) { ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i); ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu64, ptr - cd_ptr, cd_length, i); return kInvalidFile; } } Loading @@ -351,14 +402,14 @@ static int32_t ParseZipArchive(ZipArchive* archive) { return kInvalidFile; } ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries); ALOGV("+++ zip good scan %" PRIu64 " entries", num_entries); return 0; } static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) { int32_t result = MapCentralDirectory(debug_file_name, archive); return result != 0 ? result : ParseZipArchive(archive); return result != kSuccess ? result : ParseZipArchive(archive); } int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle, Loading Loading @@ -489,7 +540,15 @@ static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName, // Figure out the local header offset from the central directory. The // actual file data will begin after the local header and the name / // extra comments. const off64_t local_header_offset = cdr->local_file_header_offset; off64_t local_header_offset = cdr->local_file_header_offset; // One of the info field is UINT32_MAX, try to parse the real value in the zip64 extended info in // the extra field. if (cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX || cdr->local_file_header_offset == UINT32_MAX) { ALOGW("Zip: Parsing zip64 local file header isn't supported yet"); return kInvalidFile; } if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) { ALOGW("Zip: bad local hdr offset in zip"); return kInvalidOffset; Loading
libziparchive/zip_archive_common.h +85 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <inttypes.h> #include <optional> // The "end of central directory" (EOCD) record. Each archive // contains exactly once such record which appears at the end of // the archive. It contains archive wide information like the Loading Loading @@ -173,6 +175,89 @@ struct DataDescriptor { DISALLOW_COPY_AND_ASSIGN(DataDescriptor); } __attribute__((packed)); // The zip64 end of central directory locator helps to find the zip64 EOCD. struct Zip64EocdLocator { static constexpr uint32_t kSignature = 0x07064b50; // The signature of zip64 eocd locator, must be |kSignature| uint32_t locator_signature; // The start disk of the zip64 eocd. This implementation assumes that each // archive spans a single disk only. uint32_t eocd_start_disk; // The offset offset of the zip64 end of central directory record. uint64_t zip64_eocd_offset; // The total number of disks. This implementation assumes that each archive // spans a single disk only. uint32_t num_of_disks; private: Zip64EocdLocator() = default; DISALLOW_COPY_AND_ASSIGN(Zip64EocdLocator); } __attribute__((packed)); // The optional zip64 EOCD. If one of the fields in the end of central directory // record is too small to hold required data, the field SHOULD be set to -1 // (0xFFFF or 0xFFFFFFFF) and the ZIP64 format record SHOULD be created. struct Zip64EocdRecord { static constexpr uint32_t kSignature = 0x06064b50; // The signature of zip64 eocd record, must be |kSignature| uint32_t record_signature; // Size of zip64 end of central directory record. It SHOULD be the size of the // remaining record and SHOULD NOT include the leading 12 bytes. uint64_t record_size; // The version of the tool that make this archive. uint16_t version_made_by; // Tool version needed to extract this archive. uint16_t version_needed; // Number of this disk. uint32_t disk_num; // Number of the disk with the start of the central directory. uint32_t cd_start_disk; // Total number of entries in the central directory on this disk. // This implementation assumes that each archive spans a single // disk only. i.e, that num_records_on_disk == num_records. uint64_t num_records_on_disk; // The total number of central directory records. uint64_t num_records; // The size of the central directory in bytes. uint64_t cd_size; // The offset of the start of the central directory, relative to the start of // the file. uint64_t cd_start_offset; private: Zip64EocdRecord() = default; DISALLOW_COPY_AND_ASSIGN(Zip64EocdRecord); } __attribute__((packed)); // The possible contents of the Zip64 Extended Information Extra Field. It may appear in // the 'extra' field of a central directory record or local file header. The order of // the fields in the zip64 extended information record is fixed, but the fields MUST // only appear if the corresponding local or central directory record field is set to // 0xFFFF or 0xFFFFFFFF. And this entry in the Local header MUST include BOTH original // and compressed file size fields. struct Zip64ExtendedInfo { static constexpr uint16_t kHeaderId = 0x0001; // The header tag for this 'extra' block, should be |kHeaderId|. uint16_t header_id; // The size in bytes of the remaining data (excluding the top 4 bytes). uint16_t data_size; // Size in bytes of the uncompressed file. std::optional<uint64_t> uncompressed_file_size; // Size in bytes of the compressed file. std::optional<uint64_t> compressed_file_size; // Local file header offset relative to the start of the zip file. std::optional<uint64_t> local_header_offset; // This implementation assumes that each archive spans a single disk only. So // the disk_number is not used. // uint32_t disk_num; private: Zip64ExtendedInfo() = default; DISALLOW_COPY_AND_ASSIGN(Zip64ExtendedInfo); }; // mask value that signifies that the entry has a DD static const uint32_t kGPBDDFlagMask = 0x0008; Loading
libziparchive/zip_archive_private.h +1 −1 Original line number Diff line number Diff line Loading @@ -95,7 +95,7 @@ struct ZipArchive { std::unique_ptr<android::base::MappedFile> directory_map; // number of entries in the Zip archive uint16_t num_entries; uint64_t num_entries; std::unique_ptr<CdEntryMapInterface> cd_entry_map; ZipArchive(MappedZipFile&& map, bool assume_ownership); Loading
libziparchive/ziptool.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -133,8 +133,8 @@ static void MaybeShowHeader(ZipArchiveHandle zah) { if (!flag_1 && includes.empty() && excludes.empty()) { ZipArchiveInfo info{GetArchiveInfo(zah)}; printf("Archive: %s\n", archive_name); printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size, info.entry_count); printf("Zip file size: %" PRId64 " bytes, number of entries: %" PRIu64 "\n", info.archive_size, info.entry_count); } } } Loading