diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h index 4697bb752..3d51de9c0 100644 --- a/libziparchive/include/ziparchive/zip_archive.h +++ b/libziparchive/include/ziparchive/zip_archive.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -36,10 +37,10 @@ enum { kCompressDeflated = 8, // standard deflate }; -/* - * Represents information about a zip entry in a zip file. - */ -struct ZipEntry { +// This struct holds the common information of a zip entry other than the +// the entry size. The compressed and uncompressed length will be handled +// separately in the derived class. +struct ZipEntryCommon { // Compression method. One of kCompressStored or kCompressDeflated. // See also `gpbf` for deflate subtypes. uint16_t method; @@ -67,16 +68,6 @@ struct ZipEntry { // Data descriptor footer at the end of the file entry. uint32_t crc32; - // Compressed length of this ZipEntry. Might be present - // either in the local file header or in the data descriptor - // footer. - uint32_t compressed_length; - - // Uncompressed length of this ZipEntry. Might be present - // either in the local file header or in the data descriptor - // footer. - uint32_t uncompressed_length; - // If the value of uncompressed length and compressed length are stored in // the zip64 extended info of the extra field. bool zip64_format_size{false}; @@ -97,6 +88,52 @@ struct ZipEntry { bool is_text; }; +struct ZipEntry64; +// Many users of the library assume the entry size is capped at UNIT32_MAX. So we keep +// the interface for the old ZipEntry here; and we could switch them over to the new +// ZipEntry64 later. +struct ZipEntry : public ZipEntryCommon { + // Compressed length of this ZipEntry. The maximum value is UNIT32_MAX. + // Might be present either in the local file header or in the data + // descriptor footer. + uint32_t compressed_length{0}; + + // Uncompressed length of this ZipEntry. The maximum value is UNIT32_MAX. + // Might be present either in the local file header or in the data + // descriptor footer. + uint32_t uncompressed_length{0}; + + // Copies the contents of a ZipEntry64 object to a 32 bits ZipEntry. Returns 0 if the + // size of the entry fits into uint32_t, returns a negative error code + // (kUnsupportedEntrySize) otherwise. + static int32_t CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src); + + private: + ZipEntry& operator=(const ZipEntryCommon& other) { + ZipEntryCommon::operator=(other); + return *this; + } +}; + +// Represents information about a zip entry in a zip file. +struct ZipEntry64 : public ZipEntryCommon { + // Compressed length of this ZipEntry. The maximum value is UNIT64_MAX. + // Might be present either in the local file header, the zip64 extended field, + // or in the data descriptor footer. + uint64_t compressed_length{0}; + + // Uncompressed length of this ZipEntry. The maximum value is UNIT64_MAX. + // Might be present either in the local file header, the zip64 extended field, + // or in the data descriptor footer. + uint64_t uncompressed_length{0}; + + explicit ZipEntry64() = default; + explicit ZipEntry64(const ZipEntry& zip_entry) : ZipEntryCommon(zip_entry) { + compressed_length = zip_entry.compressed_length; + uncompressed_length = zip_entry.uncompressed_length; + } +}; + struct ZipArchive; typedef ZipArchive* ZipArchiveHandle; @@ -172,7 +209,8 @@ ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive); * On non-Windows platforms this method does not modify internal state and * can be called concurrently. */ -int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data); +int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, + ZipEntry64* data); /* * Start iterating over all entries of a zip file. The order of iteration @@ -206,8 +244,8 @@ int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr, * Returns 0 on success, -1 if there are no more elements in this * archive and lower negative values on failure. */ -int32_t Next(void* cookie, ZipEntry* data, std::string* name); -int32_t Next(void* cookie, ZipEntry* data, std::string_view* name); +int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name); +int32_t Next(void* cookie, ZipEntry64* data, std::string* name); /* * End iteration over all entries of a zip file and frees the memory allocated @@ -224,7 +262,7 @@ void EndIteration(void* cookie); * * Returns 0 on success and negative values on failure. */ -int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd); +int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd); /** * Uncompress a given zip entry to the memory region at |begin| and of @@ -234,7 +272,8 @@ int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd); * * Returns 0 on success and negative values on failure. */ -int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size); +int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin, + size_t size); int GetFileDescriptor(const ZipArchiveHandle archive); @@ -246,6 +285,16 @@ off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive); const char* ErrorCodeString(int32_t error_code); +// Many users of libziparchive assume the entry size to be 32 bits long. So we keep these +// interfaces that use 32 bit ZipEntry to make old code work. TODO(xunchang) Remove the 32 bit +// wrapper functions once we switch all users to recognize ZipEntry64. +int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data); +int32_t Next(void* cookie, ZipEntry* data, std::string* name); +int32_t Next(void* cookie, ZipEntry* data, std::string_view* name); +int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd); +int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin, + size_t size); + #if !defined(_WIN32) typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, void* cookie); @@ -253,7 +302,9 @@ typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, voi * Stream the uncompressed data through the supplied function, * passing cookie to it each time it gets called. */ -int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry, +int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry, + ProcessZipEntryFunction func, void* cookie); +int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry, ProcessZipEntryFunction func, void* cookie); #endif @@ -274,7 +325,7 @@ class Writer { class Reader { public: - virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const = 0; + virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const = 0; virtual ~Reader(); protected: @@ -296,6 +347,6 @@ class Reader { * If |crc_out| is not nullptr, it is set to the crc32 checksum of the * uncompressed data. */ -int32_t Inflate(const Reader& reader, const uint32_t compressed_length, - const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out); +int32_t Inflate(const Reader& reader, const uint64_t compressed_length, + const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out); } // namespace zip_archive diff --git a/libziparchive/test_ziparchive_large.py b/libziparchive/test_ziparchive_large.py index 6b82f6385..46d02aa3a 100644 --- a/libziparchive/test_ziparchive_large.py +++ b/libziparchive/test_ziparchive_large.py @@ -83,7 +83,7 @@ class Zip64Test(unittest.TestCase): self._ExtractEntries(zip_path.name) - def test_largeCompressedEntries(self): + def test_largeCompressedEntriesSmallerThan4G(self): zip_path = tempfile.NamedTemporaryFile(suffix='.zip') with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as output_zip: @@ -99,8 +99,7 @@ class Zip64Test(unittest.TestCase): def test_forceDataDescriptor(self): file_path = tempfile.NamedTemporaryFile(suffix='.txt') - # TODO create the entry > 4GiB. - self._WriteFile(file_path.name, 1024) + self._WriteFile(file_path.name, 5000 * 1024) zip_path = tempfile.NamedTemporaryFile(suffix='.zip') with zipfile.ZipFile(zip_path, 'w', allowZip64=True) as output_zip: @@ -113,6 +112,35 @@ class Zip64Test(unittest.TestCase): self.assertEquals([file_path.name[1:]], read_names) self._ExtractEntries(zip_path.name) + + def test_largeUncompressedEntriesLargerThan4G(self): + zip_path = tempfile.NamedTemporaryFile(suffix='.zip') + with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_STORED, + allowZip64=True) as output_zip: + # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed + # sizes in the extra field. Test if our ziptool should be able to parse it. + entry_dict = {'g.txt': 5000 * 1024, 'h.txt': 6000 * 1024} + self._AddEntriesToZip(output_zip, entry_dict) + + read_names = self._getEntryNames(zip_path.name) + self.assertEquals(sorted(entry_dict.keys()), sorted(read_names)) + self._ExtractEntries(zip_path.name) + + + def test_largeCompressedEntriesLargerThan4G(self): + zip_path = tempfile.NamedTemporaryFile(suffix='.zip') + with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED, + allowZip64=True) as output_zip: + # Add entries close to 4GiB in size. Somehow the python library will put the (un)compressed + # sizes in the extra field. Test if our ziptool should be able to parse it. + entry_dict = {'i.txt': 4096 * 1024, 'j.txt': 7000 * 1024} + self._AddEntriesToZip(output_zip, entry_dict) + + read_names = self._getEntryNames(zip_path.name) + self.assertEquals(sorted(entry_dict.keys()), sorted(read_names)) + self._ExtractEntries(zip_path.name) + + if __name__ == '__main__': testsuite = unittest.TestLoader().discover( os.path.dirname(os.path.realpath(__file__))) diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc index 031d43a4c..8f9774f19 100644 --- a/libziparchive/zip_archive.cc +++ b/libziparchive/zip_archive.cc @@ -406,15 +406,6 @@ static ZipError ParseZip64ExtendedInfoInExtraField( return kInvalidFile; } - // TODO(xunchang) Support handling file large than UINT32_MAX. It's theoretically possible - // for libz to (de)compressing file larger than UINT32_MAX. But we should use our own - // bytes counter to replace stream.total_out. - if ((uncompressedFileSize.has_value() && uncompressedFileSize.value() > UINT32_MAX) || - (compressedFileSize.has_value() && compressedFileSize.value() > UINT32_MAX)) { - ALOGW("Zip: File size larger than UINT32_MAX isn't supported yet"); - return kInvalidFile; - } - zip64Info->uncompressed_file_size = uncompressedFileSize; zip64Info->compressed_file_size = compressedFileSize; zip64Info->local_header_offset = localHeaderOffset; @@ -613,7 +604,7 @@ void CloseArchive(ZipArchiveHandle archive) { delete archive; } -static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) { +static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, const ZipEntry64* entry) { // Maximum possible size for data descriptor: 2 * 4 + 2 * 8 = 24 bytes uint8_t ddBuf[24]; off64_t offset = entry->offset; @@ -644,7 +635,7 @@ static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry if (entry->compressed_length != descriptor.compressed_size || entry->uncompressed_length != descriptor.uncompressed_size || entry->crc32 != descriptor.crc32) { - ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32 + ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}", entry->compressed_length, entry->uncompressed_length, entry->crc32, descriptor.compressed_size, descriptor.uncompressed_size, descriptor.crc32); @@ -655,7 +646,7 @@ static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry } static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName, - const uint64_t nameOffset, ZipEntry* data) { + const uint64_t nameOffset, ZipEntry64* data) { // Recover the start of the central directory entry from the filename // pointer. The filename is the first entry past the fixed-size data, // so we can just subtract back from that. @@ -704,11 +695,8 @@ static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName, return status; } - // TODO(xunchang) remove the size limit and support entry length > UINT32_MAX. - data->uncompressed_length = - static_cast(zip64_info.uncompressed_file_size.value_or(cdr->uncompressed_size)); - data->compressed_length = - static_cast(zip64_info.compressed_file_size.value_or(cdr->compressed_size)); + data->uncompressed_length = zip64_info.uncompressed_file_size.value_or(cdr->uncompressed_size); + data->compressed_length = zip64_info.compressed_file_size.value_or(cdr->compressed_size); local_header_offset = zip64_info.local_header_offset.value_or(local_header_offset); data->zip64_format_size = cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX; @@ -822,7 +810,7 @@ static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName, data->has_data_descriptor = 0; if (data->compressed_length != lfh_compressed_size || data->uncompressed_length != lfh_uncompressed_size || data->crc32 != lfh->crc32) { - ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32 + ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}", data->compressed_length, data->uncompressed_length, data->crc32, lfh_compressed_size, lfh_uncompressed_size, lfh->crc32); @@ -855,16 +843,15 @@ static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName, return kInvalidOffset; } - if (static_cast(data_offset + data->compressed_length) > cd_offset) { - ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", + if (data->compressed_length > cd_offset - data_offset) { + ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")", static_cast(data_offset), data->compressed_length, static_cast(cd_offset)); return kInvalidOffset; } - if (data->method == kCompressStored && - static_cast(data_offset + data->uncompressed_length) > cd_offset) { - ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", + if (data->method == kCompressStored && data->uncompressed_length > cd_offset - data_offset) { + ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")", static_cast(data_offset), data->uncompressed_length, static_cast(cd_offset)); return kInvalidOffset; @@ -918,8 +905,33 @@ void EndIteration(void* cookie) { delete reinterpret_cast(cookie); } +int32_t ZipEntry::CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src) { + if (src->compressed_length > UINT32_MAX || src->uncompressed_length > UINT32_MAX) { + ALOGW( + "Zip: the entry size is too large to fit into the 32 bits ZipEntry, uncompressed " + "length %" PRIu64 ", compressed length %" PRIu64, + src->uncompressed_length, src->compressed_length); + return kUnsupportedEntrySize; + } + + *dst = *src; + dst->uncompressed_length = static_cast(src->uncompressed_length); + dst->compressed_length = static_cast(src->compressed_length); + return kSuccess; +} + int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data) { + ZipEntry64 entry64; + if (auto status = FindEntry(archive, entryName, &entry64); status != kSuccess) { + return status; + } + + return ZipEntry::CopyFromZipEntry64(data, &entry64); +} + +int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, + ZipEntry64* data) { if (entryName.empty() || entryName.size() > static_cast(UINT16_MAX)) { ALOGW("Zip: Invalid filename of length %zu", entryName.size()); return kInvalidEntryName; @@ -936,6 +948,24 @@ int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryNa } int32_t Next(void* cookie, ZipEntry* data, std::string* name) { + ZipEntry64 entry64; + if (auto status = Next(cookie, &entry64, name); status != kSuccess) { + return status; + } + + return ZipEntry::CopyFromZipEntry64(data, &entry64); +} + +int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) { + ZipEntry64 entry64; + if (auto status = Next(cookie, &entry64, name); status != kSuccess) { + return status; + } + + return ZipEntry::CopyFromZipEntry64(data, &entry64); +} + +int32_t Next(void* cookie, ZipEntry64* data, std::string* name) { std::string_view sv; int32_t result = Next(cookie, data, &sv); if (result == 0 && name) { @@ -944,7 +974,7 @@ int32_t Next(void* cookie, ZipEntry* data, std::string* name) { return result; } -int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) { +int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name) { IterationHandle* handle = reinterpret_cast(cookie); if (handle == nullptr) { ALOGW("Zip: Null ZipArchiveHandle"); @@ -979,10 +1009,21 @@ int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) { // the data appended to it. class MemoryWriter : public zip_archive::Writer { public: - MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {} + static MemoryWriter Create(uint8_t* buf, size_t size, const ZipEntry64* entry) { + const uint64_t declared_length = entry->uncompressed_length; + if (declared_length > size) { + ALOGW("Zip: file size %" PRIu64 " is larger than the buffer size %zu.", declared_length, + size); + return MemoryWriter{nullptr, 0}; + } + + return MemoryWriter(buf, size); + } + + bool IsValid() const { return buf_ != nullptr; } virtual bool Append(uint8_t* buf, size_t buf_size) override { - if (bytes_written_ + buf_size > size_) { + if (size_ < buf_size || bytes_written_ > size_ - buf_size) { ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_, bytes_written_ + buf_size); return false; @@ -994,7 +1035,9 @@ class MemoryWriter : public zip_archive::Writer { } private: - uint8_t* const buf_; + MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {} + + uint8_t* const buf_{nullptr}; const size_t size_; size_t bytes_written_; }; @@ -1010,14 +1053,19 @@ class FileWriter : public zip_archive::Writer { // block device). // // Returns a valid FileWriter on success, |nullptr| if an error occurred. - static FileWriter Create(int fd, const ZipEntry* entry) { - const uint32_t declared_length = entry->uncompressed_length; + static FileWriter Create(int fd, const ZipEntry64* entry) { + const uint64_t declared_length = entry->uncompressed_length; const off64_t current_offset = lseek64(fd, 0, SEEK_CUR); if (current_offset == -1) { ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno)); return FileWriter{}; } + if (declared_length > SIZE_MAX || declared_length > INT64_MAX) { + ALOGW("Zip: file size %" PRIu64 " is too large to extract.", declared_length); + return FileWriter{}; + } + #if defined(__linux__) if (declared_length > 0) { // Make sure we have enough space on the volume to extract the compressed @@ -1031,9 +1079,8 @@ class FileWriter : public zip_archive::Writer { // disk does not have enough space. long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length)); if (result == -1 && errno == ENOSPC) { - ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s", - static_cast(declared_length), static_cast(current_offset), - strerror(errno)); + ALOGW("Zip: unable to allocate %" PRIu64 " bytes at offset %" PRId64 ": %s", + declared_length, static_cast(current_offset), strerror(errno)); return FileWriter{}; } } @@ -1068,8 +1115,8 @@ class FileWriter : public zip_archive::Writer { bool IsValid() const { return fd_ != -1; } virtual bool Append(uint8_t* buf, size_t buf_size) override { - if (total_bytes_written_ + buf_size > declared_length_) { - ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_, + if (declared_length_ < buf_size || total_bytes_written_ > declared_length_ - buf_size) { + ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_, total_bytes_written_ + buf_size); return false; } @@ -1085,8 +1132,13 @@ class FileWriter : public zip_archive::Writer { } private: - explicit FileWriter(const int fd = -1, const size_t declared_length = 0) - : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {} + explicit FileWriter(const int fd = -1, const uint64_t declared_length = 0) + : Writer(), + fd_(fd), + declared_length_(static_cast(declared_length)), + total_bytes_written_(0) { + CHECK_LE(declared_length, SIZE_MAX); + } int fd_; const size_t declared_length_; @@ -1095,10 +1147,10 @@ class FileWriter : public zip_archive::Writer { class EntryReader : public zip_archive::Reader { public: - EntryReader(const MappedZipFile& zip_file, const ZipEntry* entry) + EntryReader(const MappedZipFile& zip_file, const ZipEntry64* entry) : Reader(), zip_file_(zip_file), entry_(entry) {} - virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const { + virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset); } @@ -1106,7 +1158,7 @@ class EntryReader : public zip_archive::Reader { private: const MappedZipFile& zip_file_; - const ZipEntry* entry_; + const ZipEntry64* entry_; }; // This method is using libz macros with old-style-casts @@ -1123,8 +1175,8 @@ namespace zip_archive { Reader::~Reader() {} Writer::~Writer() {} -int32_t Inflate(const Reader& reader, const uint32_t compressed_length, - const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out) { +int32_t Inflate(const Reader& reader, const uint64_t compressed_length, + const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out) { const size_t kBufSize = 32768; std::vector read_buf(kBufSize); std::vector write_buf(kBufSize); @@ -1167,12 +1219,14 @@ int32_t Inflate(const Reader& reader, const uint32_t compressed_length, const bool compute_crc = (crc_out != nullptr); uLong crc = 0; - uint32_t remaining_bytes = compressed_length; + uint64_t remaining_bytes = compressed_length; + uint64_t total_output = 0; do { /* read as much as we can */ if (zstream.avail_in == 0) { - const uint32_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes; - const uint32_t offset = (compressed_length - remaining_bytes); + const uint32_t read_size = + (remaining_bytes > kBufSize) ? kBufSize : static_cast(remaining_bytes); + const off64_t offset = (compressed_length - remaining_bytes); // Make sure to read at offset to ensure concurrent access to the fd. if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) { ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno)); @@ -1203,6 +1257,7 @@ int32_t Inflate(const Reader& reader, const uint32_t compressed_length, crc = crc32(crc, &write_buf[0], static_cast(write_size)); } + total_output += kBufSize - zstream.avail_out; zstream.next_out = &write_buf[0]; zstream.avail_out = kBufSize; } @@ -1219,9 +1274,8 @@ int32_t Inflate(const Reader& reader, const uint32_t compressed_length, if (compute_crc) { *crc_out = crc; } - - if (zstream.total_out != uncompressed_length || remaining_bytes != 0) { - ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out, + if (total_output != uncompressed_length || remaining_bytes != 0) { + ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu64 ")", zstream.total_out, uncompressed_length); return kInconsistentInformation; } @@ -1230,7 +1284,7 @@ int32_t Inflate(const Reader& reader, const uint32_t compressed_length, } } // namespace zip_archive -static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, +static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry, zip_archive::Writer* writer, uint64_t* crc_out) { const EntryReader reader(mapped_zip, entry); @@ -1238,20 +1292,21 @@ static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* e crc_out); } -static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, +static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry, zip_archive::Writer* writer, uint64_t* crc_out) { static const uint32_t kBufSize = 32768; std::vector buf(kBufSize); - const uint32_t length = entry->uncompressed_length; - uint32_t count = 0; + const uint64_t length = entry->uncompressed_length; + uint64_t count = 0; uLong crc = 0; while (count < length) { - uint32_t remaining = length - count; + uint64_t remaining = length - count; off64_t offset = entry->offset + count; // Safe conversion because kBufSize is narrow enough for a 32 bit signed value. - const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining; + const uint32_t block_size = + (remaining > kBufSize) ? kBufSize : static_cast(remaining); // Make sure to read at offset to ensure concurrent access to the fd. if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) { @@ -1272,20 +1327,21 @@ static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entr return 0; } -int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive::Writer* writer) { +int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry, + zip_archive::Writer* writer) { const uint16_t method = entry->method; // this should default to kUnknownCompressionMethod. int32_t return_value = -1; uint64_t crc = 0; if (method == kCompressStored) { - return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc); + return_value = CopyEntryToWriter(handle->mapped_zip, entry, writer, &crc); } else if (method == kCompressDeflated) { - return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc); + return_value = InflateEntryToWriter(handle->mapped_zip, entry, writer, &crc); } if (!return_value && entry->has_data_descriptor) { - return_value = ValidateDataDescriptor(archive->mapped_zip, entry); + return_value = ValidateDataDescriptor(handle->mapped_zip, entry); if (return_value) { return return_value; } @@ -1300,12 +1356,28 @@ int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive:: return return_value; } -int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size) { - MemoryWriter writer(begin, size); +int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin, + size_t size) { + ZipEntry64 entry64(*entry); + return ExtractToMemory(archive, &entry64, begin, size); +} + +int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin, + size_t size) { + auto writer = MemoryWriter::Create(begin, size, entry); + if (!writer.IsValid()) { + return kIoError; + } + return ExtractToWriter(archive, entry, &writer); } -int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd) { +int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd) { + ZipEntry64 entry64(*entry); + return ExtractEntryToFile(archive, &entry64, fd); +} + +int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd) { auto writer = FileWriter::Create(fd, entry); if (!writer.IsValid()) { return kIoError; @@ -1337,7 +1409,13 @@ class ProcessWriter : public zip_archive::Writer { void* cookie_; }; -int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry, +int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry, + ProcessZipEntryFunction func, void* cookie) { + ZipEntry64 entry64(*entry); + return ProcessZipEntryContents(archive, &entry64, func, cookie); +} + +int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry, ProcessZipEntryFunction func, void* cookie) { ProcessWriter writer(func, cookie); return ExtractToWriter(archive, entry, &writer); @@ -1466,7 +1544,7 @@ bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_s return true; } -tm ZipEntry::GetModificationTime() const { +tm ZipEntryCommon::GetModificationTime() const { tm t = {}; t.tm_hour = (mod_time >> 11) & 0x1f; diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h index 4ed07aac3..4b43cba67 100644 --- a/libziparchive/zip_archive_private.h +++ b/libziparchive/zip_archive_private.h @@ -106,7 +106,8 @@ struct ZipArchive { bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size); }; -int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer); +int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry, + zip_archive::Writer* writer); // Reads the unaligned data of type |T| and auto increment the offset. template diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc index 356334058..3d4e58074 100644 --- a/libziparchive/zip_archive_test.cc +++ b/libziparchive/zip_archive_test.cc @@ -217,7 +217,7 @@ TEST(ziparchive, Iteration_std_string_view) { void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie)); - ZipEntry data; + ZipEntry64 data; std::vector names; std::string_view name; while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name); @@ -232,12 +232,12 @@ TEST(ziparchive, Iteration_std_string_view) { static void AssertIterationNames(void* iteration_cookie, const std::vector& expected_names_sorted) { - ZipEntry data; + ZipEntry64 data; std::vector names; - std::string name; + std::string_view name; for (size_t i = 0; i < expected_names_sorted.size(); ++i) { ASSERT_EQ(0, Next(iteration_cookie, &data, &name)); - names.push_back(name); + names.push_back(std::string(name)); } // End of iteration. ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); @@ -325,8 +325,8 @@ TEST(ziparchive, IterationWithBadPrefixAndSuffix) { void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y")); - ZipEntry data; - std::string name; + ZipEntry64 data; + std::string_view name; // End of iteration. ASSERT_EQ(-1, Next(iteration_cookie, &data, &name)); @@ -338,7 +338,7 @@ TEST(ziparchive, FindEntry) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); - ZipEntry data; + ZipEntry64 data; ASSERT_EQ(0, FindEntry(handle, "a.txt", &data)); // Known facts about a.txt, from zipinfo -v. @@ -359,7 +359,7 @@ TEST(ziparchive, FindEntry_empty) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); - ZipEntry data; + ZipEntry64 data; ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data)); CloseArchive(handle); @@ -370,7 +370,7 @@ TEST(ziparchive, FindEntry_too_long) { ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); std::string very_long_name(65536, 'x'); - ZipEntry data; + ZipEntry64 data; ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data)); CloseArchive(handle); @@ -383,8 +383,8 @@ TEST(ziparchive, TestInvalidDeclaredLength) { void* iteration_cookie; ASSERT_EQ(0, StartIteration(handle, &iteration_cookie)); - std::string name; - ZipEntry data; + std::string_view name; + ZipEntry64 data; ASSERT_EQ(Next(iteration_cookie, &data, &name), 0); ASSERT_EQ(Next(iteration_cookie, &data, &name), 0); @@ -415,9 +415,9 @@ TEST(ziparchive, OpenArchiveFdRange) { static_cast(leading_garbage.size()))); // An entry that's deflated. - ZipEntry data; + ZipEntry64 data; ASSERT_EQ(0, FindEntry(handle, "a.txt", &data)); - const uint32_t a_size = data.uncompressed_length; + const auto a_size = static_cast(data.uncompressed_length); ASSERT_EQ(a_size, kATxtContents.size()); auto buffer = std::unique_ptr(new uint8_t[a_size]); ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size)); @@ -425,7 +425,7 @@ TEST(ziparchive, OpenArchiveFdRange) { // An entry that's stored. ASSERT_EQ(0, FindEntry(handle, "b.txt", &data)); - const uint32_t b_size = data.uncompressed_length; + const auto b_size = static_cast(data.uncompressed_length); ASSERT_EQ(b_size, kBTxtContents.size()); buffer = std::unique_ptr(new uint8_t[b_size]); ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size)); @@ -439,9 +439,9 @@ TEST(ziparchive, ExtractToMemory) { ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); // An entry that's deflated. - ZipEntry data; + ZipEntry64 data; ASSERT_EQ(0, FindEntry(handle, "a.txt", &data)); - const uint32_t a_size = data.uncompressed_length; + const auto a_size = static_cast(data.uncompressed_length); ASSERT_EQ(a_size, kATxtContents.size()); uint8_t* buffer = new uint8_t[a_size]; ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size)); @@ -450,7 +450,7 @@ TEST(ziparchive, ExtractToMemory) { // An entry that's stored. ASSERT_EQ(0, FindEntry(handle, "b.txt", &data)); - const uint32_t b_size = data.uncompressed_length; + const auto b_size = static_cast(data.uncompressed_length); ASSERT_EQ(b_size, kBTxtContents.size()); buffer = new uint8_t[b_size]; ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size)); @@ -503,7 +503,7 @@ TEST(ziparchive, EmptyEntries) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false)); - ZipEntry entry; + ZipEntry64 entry; ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry)); ASSERT_EQ(static_cast(0), entry.uncompressed_length); uint8_t buffer[1]; @@ -526,7 +526,7 @@ TEST(ziparchive, EntryLargerThan32K) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false)); - ZipEntry entry; + ZipEntry64 entry; ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry)); ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length); @@ -583,7 +583,7 @@ TEST(ziparchive, ExtractToFile) { ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); - ZipEntry entry; + ZipEntry64 entry; ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry)); ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd)); @@ -594,9 +594,9 @@ TEST(ziparchive, ExtractToFile) { ASSERT_EQ(0, memcmp(read_buffer, data, data_size)); // Assert that the remainder of the file contains the incompressed data. - std::vector uncompressed_data(entry.uncompressed_length); - ASSERT_TRUE( - android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length)); + std::vector uncompressed_data(static_cast(entry.uncompressed_length)); + ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), + static_cast(entry.uncompressed_length))); ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size())); // Assert that the total length of the file is sane @@ -620,7 +620,7 @@ TEST(ziparchive, OpenFromMemory) { OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle)); // Assert one entry can be found and extracted correctly. - ZipEntry binary_entry; + ZipEntry64 binary_entry; ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry)); TemporaryFile tmp_binary; ASSERT_NE(-1, tmp_binary.fd); @@ -635,13 +635,13 @@ static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& en if (raw) { stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry)); if (entry->method == kCompressStored) { - read_data->resize(entry->uncompressed_length); + read_data->resize(static_cast(entry->uncompressed_length)); } else { - read_data->resize(entry->compressed_length); + read_data->resize(static_cast(entry->compressed_length)); } } else { stream.reset(ZipArchiveStreamEntry::Create(handle, *entry)); - read_data->resize(entry->uncompressed_length); + read_data->resize(static_cast(entry->uncompressed_length)); } uint8_t* read_data_ptr = read_data->data(); ASSERT_TRUE(stream.get() != nullptr); @@ -681,7 +681,7 @@ static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, std::vector read_data; ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data); - std::vector cmp_data(entry.uncompressed_length); + std::vector cmp_data(static_cast(entry.uncompressed_length)); ASSERT_EQ(entry.uncompressed_length, read_data.size()); ASSERT_EQ( 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast(cmp_data.size()))); @@ -741,8 +741,8 @@ TEST(ziparchive, StreamUncompressedBadCrc) { // FileOutputStream fos = new // FileOutputStream("/tmp/data_descriptor.zip"); // ZipOutputStream zos = new ZipOutputStream(fos); -// ZipEntry ze = new ZipEntry("name"); -// ze.setMethod(ZipEntry.DEFLATED); +// ZipEntry64 ze = new ZipEntry64("name"); +// ze.setMethod(ZipEntry64.DEFLATED); // zos.putNextEntry(ze); // zos.write("abdcdefghijk".getBytes()); // zos.closeEntry(); @@ -780,7 +780,7 @@ static void ExtractEntryToMemory(const std::vector& zip_data, // This function expects a variant of kDataDescriptorZipFile, for look for // an entry whose name is "name" and whose size is 12 (contents = // "abdcdefghijk"). - ZipEntry entry; + ZipEntry64 entry; ASSERT_EQ(0, FindEntry(handle, "name", &entry)); ASSERT_EQ(static_cast(12), entry.uncompressed_length); @@ -887,12 +887,12 @@ class VectorReader : public zip_archive::Reader { public: VectorReader(const std::vector& input) : Reader(), input_(input) {} - bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { if ((offset + len) < input_.size()) { return false; } - memcpy(buf, &input_[offset], len); + memcpy(buf, &input_[static_cast(offset)], len); return true; } @@ -919,7 +919,7 @@ class BadReader : public zip_archive::Reader { public: BadReader() : Reader() {} - bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; } + bool ReadAtOffset(uint8_t*, size_t, off64_t) const { return false; } }; class BadWriter : public zip_archive::Writer { @@ -1222,7 +1222,7 @@ TEST_F(Zip64ParseTest, findEntry) { ZipArchiveHandle handle; ASSERT_EQ( 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle)); - ZipEntry entry; + ZipEntry64 entry; ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry)); ASSERT_EQ(200, entry.uncompressed_length); ASSERT_EQ(200, entry.compressed_length); @@ -1245,7 +1245,7 @@ TEST_F(Zip64ParseTest, openFileIncorrectDataSizeInLocalExtendedField) { ZipArchiveHandle handle; ASSERT_EQ( 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle)); - ZipEntry entry; + ZipEntry64 entry; ASSERT_NE(0, FindEntry(handle, "a.txt", &entry)); CloseArchive(handle); @@ -1267,7 +1267,7 @@ TEST_F(Zip64ParseTest, iterates) { ASSERT_EQ(0, StartIteration(handle, &iteration_cookie)); std::set result; std::string_view name; - ZipEntry entry; + ZipEntry64 entry; while (Next(iteration_cookie, &entry, &name) == 0) result.emplace(name); ASSERT_EQ(names, result); @@ -1297,7 +1297,7 @@ TEST_F(Zip64ParseTest, extract) { ZipArchiveHandle handle; ASSERT_EQ( 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle)); - ZipEntry entry; + ZipEntry64 entry; ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry)); VectorWriter writer; @@ -1315,7 +1315,7 @@ TEST_F(Zip64ParseTest, extractWithDataDescriptor) { ZipArchiveHandle handle; ASSERT_EQ( 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle)); - ZipEntry entry; + ZipEntry64 entry; ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry)); VectorWriter writer; diff --git a/libziparchive/zip_error.cpp b/libziparchive/zip_error.cpp index 107ec4797..14e49bb40 100644 --- a/libziparchive/zip_error.cpp +++ b/libziparchive/zip_error.cpp @@ -33,6 +33,7 @@ static const char* kErrorMessages[] = { "I/O error", "File mapping failed", "Allocation failed", + "Unsupported zip entry size", }; const char* ErrorCodeString(int32_t error_code) { diff --git a/libziparchive/zip_error.h b/libziparchive/zip_error.h index 37fd55f38..3d7285d5e 100644 --- a/libziparchive/zip_error.h +++ b/libziparchive/zip_error.h @@ -66,5 +66,9 @@ enum ZipError : int32_t { // An allocation failed. kAllocationFailed = -13, - kLastErrorCode = kAllocationFailed, + // The compressed or uncompressed size is larger than UINT32_MAX and + // doesn't fit into the 32 bits zip entry. + kUnsupportedEntrySize = -14, + + kLastErrorCode = kUnsupportedEntrySize, }; diff --git a/libziparchive/ziptool.cpp b/libziparchive/ziptool.cpp index f345ffc4f..17d4833a5 100644 --- a/libziparchive/ziptool.cpp +++ b/libziparchive/ziptool.cpp @@ -193,21 +193,25 @@ static bool PromptOverwrite(const std::string& dst) { } } -static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) { +static void ExtractToPipe(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) { // We need to extract to memory because ExtractEntryToFile insists on // being able to seek and truncate, and you can't do that with stdout. - uint8_t* buffer = new uint8_t[entry.uncompressed_length]; - int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length); + if (entry.uncompressed_length > SIZE_MAX) { + die(0, "entry size %" PRIu64 " is too large to extract.", entry.uncompressed_length); + } + auto uncompressed_length = static_cast(entry.uncompressed_length); + uint8_t* buffer = new uint8_t[uncompressed_length]; + int err = ExtractToMemory(zah, &entry, buffer, uncompressed_length); if (err < 0) { die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err)); } - if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) { + if (!android::base::WriteFully(1, buffer, uncompressed_length)) { die(errno, "failed to write %s to stdout", name.c_str()); } delete[] buffer; } -static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) { +static void ExtractOne(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) { // Bad filename? if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) { die(0, "bad filename %s", name.c_str()); @@ -253,22 +257,22 @@ static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& close(fd); } -static void ListOne(const ZipEntry& entry, const std::string& name) { +static void ListOne(const ZipEntry64& entry, const std::string& name) { tm t = entry.GetModificationTime(); char time[32]; snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min); if (flag_v) { - printf("%8d %s %7d %3.0f%% %s %08x %s\n", entry.uncompressed_length, + printf("%8" PRIu64 " %s %8" PRIu64 " %3.0f%% %s %08x %s\n", entry.uncompressed_length, (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length, CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32, name.c_str()); } else { - printf("%9d %s %s\n", entry.uncompressed_length, time, name.c_str()); + printf("%9" PRIu64 " %s %s\n", entry.uncompressed_length, time, name.c_str()); } } -static void InfoOne(const ZipEntry& entry, const std::string& name) { +static void InfoOne(const ZipEntry64& entry, const std::string& name) { if (flag_1) { // "android-ndk-r19b/sources/android/NOTICE" printf("%s\n", name.c_str()); @@ -323,12 +327,12 @@ static void InfoOne(const ZipEntry& entry, const std::string& name) { t.tm_mday, t.tm_hour, t.tm_min); // "-rw-r--r-- 3.0 unx 577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE" - printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs, + printf("%s %2d.%d %s %8" PRIu64 " %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs, entry.uncompressed_length, entry.is_text ? 't' : 'b', entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str()); } -static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) { +static void ProcessOne(ZipArchiveHandle zah, const ZipEntry64& entry, const std::string& name) { if (role == kUnzip) { if (flag_l || flag_v) { // -l or -lv or -lq or -v. @@ -361,7 +365,7 @@ static void ProcessAll(ZipArchiveHandle zah) { die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err)); } - ZipEntry entry; + ZipEntry64 entry; std::string name; while ((err = Next(cookie, &entry, &name)) >= 0) { if (ShouldInclude(name)) ProcessOne(zah, entry, name);