Allow parsing zip entries larger than 4GiB

This cl supports the parsing and extraction of the zip entry who
has a large size than UINT32_MAX. Also add a few checks in the
entry writers to make sure callers have enough space for extraction.

As many users of the library assume the entry size to be 32 bits long,
we keep the 32 bit ZipEntry. We also keep the functions that expect
the 32 bit ZipEntry in the public header file. These 32 bit wrappers
could be removed later once all users recognize the 64 bit ZipEntry.

Bug: 150900468
Test: unit tests pass
Change-Id: Ia6760638ccf51e97dbef6bd55dff352f1e7ce816
This commit is contained in:
Tianjie 2020-04-01 23:08:34 -07:00
parent fcf2d8b89b
commit 85c5d23100
8 changed files with 308 additions and 141 deletions

View file

@ -25,6 +25,7 @@
#include <sys/cdefs.h>
#include <sys/types.h>
#include <functional>
#include <string>
#include <string_view>
@ -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

View file

@ -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__)))

View file

@ -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<uint32_t>(zip64_info.uncompressed_file_size.value_or(cdr->uncompressed_size));
data->compressed_length =
static_cast<uint32_t>(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<off64_t>(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<int64_t>(data_offset), data->compressed_length,
static_cast<int64_t>(cd_offset));
return kInvalidOffset;
}
if (data->method == kCompressStored &&
static_cast<off64_t>(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<int64_t>(data_offset), data->uncompressed_length,
static_cast<int64_t>(cd_offset));
return kInvalidOffset;
@ -918,8 +905,33 @@ void EndIteration(void* cookie) {
delete reinterpret_cast<IterationHandle*>(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<uint32_t>(src->uncompressed_length);
dst->compressed_length = static_cast<uint32_t>(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<size_t>(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<IterationHandle*>(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<int64_t>(declared_length), static_cast<int64_t>(current_offset),
strerror(errno));
ALOGW("Zip: unable to allocate %" PRIu64 " bytes at offset %" PRId64 ": %s",
declared_length, static_cast<int64_t>(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<size_t>(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<uint8_t> read_buf(kBufSize);
std::vector<uint8_t> 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<uint32_t>(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<uint32_t>(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<uint8_t> 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<uint32_t>(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;

View file

@ -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 <typename T>

View file

@ -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<std::string_view> 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<std::string>& expected_names_sorted) {
ZipEntry data;
ZipEntry64 data;
std::vector<std::string> 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<off64_t>(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<size_t>(data.uncompressed_length);
ASSERT_EQ(a_size, kATxtContents.size());
auto buffer = std::unique_ptr<uint8_t[]>(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<size_t>(data.uncompressed_length);
ASSERT_EQ(b_size, kBTxtContents.size());
buffer = std::unique_ptr<uint8_t[]>(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<size_t>(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<size_t>(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<uint32_t>(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<uint8_t> uncompressed_data(entry.uncompressed_length);
ASSERT_TRUE(
android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
std::vector<uint8_t> uncompressed_data(static_cast<size_t>(entry.uncompressed_length));
ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
static_cast<size_t>(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<size_t>(entry->uncompressed_length));
} else {
read_data->resize(entry->compressed_length);
read_data->resize(static_cast<size_t>(entry->compressed_length));
}
} else {
stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
read_data->resize(entry->uncompressed_length);
read_data->resize(static_cast<size_t>(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<uint8_t> read_data;
ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
std::vector<uint8_t> cmp_data(entry.uncompressed_length);
std::vector<uint8_t> cmp_data(static_cast<size_t>(entry.uncompressed_length));
ASSERT_EQ(entry.uncompressed_length, read_data.size());
ASSERT_EQ(
0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(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<uint8_t>& 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<uint32_t>(12), entry.uncompressed_length);
@ -887,12 +887,12 @@ class VectorReader : public zip_archive::Reader {
public:
VectorReader(const std::vector<uint8_t>& 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<size_t>(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<std::string_view> 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;

View file

@ -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) {

View file

@ -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,
};

View file

@ -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<size_t>(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);