1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * Read-only access to Zip archives, with minimal heap allocation.
19  */
20 
21 #define LOG_TAG "ziparchive"
22 
23 #include "ziparchive/zip_archive.h"
24 
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <inttypes.h>
28 #include <limits.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <unistd.h>
33 
34 #include <memory>
35 #include <optional>
36 #include <vector>
37 
38 #if defined(__APPLE__)
39 #define lseek64 lseek
40 #endif
41 
42 #if defined(__BIONIC__)
43 #include <android/fdsan.h>
44 #endif
45 
46 #include <android-base/file.h>
47 #include <android-base/logging.h>
48 #include <android-base/macros.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
49 #include <android-base/mapped_file.h>
50 #include <android-base/memory.h>
51 #include <android-base/strings.h>
52 #include <android-base/utf8.h>
53 #include <log/log.h>
54 #include "zlib.h"
55 
56 #include "entry_name_utils-inl.h"
57 #include "zip_archive_common.h"
58 #include "zip_archive_private.h"
59 
60 // Used to turn on crc checks - verify that the content CRC matches the values
61 // specified in the local file header and the central directory.
62 static constexpr bool kCrcChecksEnabled = false;
63 
64 // The maximum number of bytes to scan backwards for the EOCD start.
65 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
66 
67 // Set a reasonable cap (256 GiB) for the zip file size. So the data is always valid when
68 // we parse the fields in cd or local headers as 64 bits signed integers.
69 static constexpr uint64_t kMaxFileLength = 256 * static_cast<uint64_t>(1u << 30u);
70 
71 /*
72  * A Read-only Zip archive.
73  *
74  * We want "open" and "find entry by name" to be fast operations, and
75  * we want to use as little memory as possible.  We memory-map the zip
76  * central directory, and load a hash table with pointers to the filenames
77  * (which aren't null-terminated).  The other fields are at a fixed offset
78  * from the filename, so we don't need to extract those (but we do need
79  * to byte-read and endian-swap them every time we want them).
80  *
81  * It's possible that somebody has handed us a massive (~1GB) zip archive,
82  * so we can't expect to mmap the entire file.
83  *
84  * To speed comparisons when doing a lookup by name, we could make the mapping
85  * "private" (copy-on-write) and null-terminate the filenames after verifying
86  * the record structure.  However, this requires a private mapping of
87  * every page that the Central Directory touches.  Easier to tuck a copy
88  * of the string length into the hash table entry.
89  */
90 
91 #if defined(__BIONIC__)
GetOwnerTag(const ZipArchive * archive)92 uint64_t GetOwnerTag(const ZipArchive* archive) {
93   return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
94                                         reinterpret_cast<uint64_t>(archive));
95 }
96 #endif
97 
ZipArchive(MappedZipFile && map,bool assume_ownership)98 ZipArchive::ZipArchive(MappedZipFile&& map, bool assume_ownership)
99     : mapped_zip(map),
100       close_file(assume_ownership),
101       directory_offset(0),
102       central_directory(),
103       directory_map(),
104       num_entries(0) {
105 #if defined(__BIONIC__)
106   if (assume_ownership) {
107     CHECK(mapped_zip.HasFd());
108     android_fdsan_exchange_owner_tag(mapped_zip.GetFileDescriptor(), 0, GetOwnerTag(this));
109   }
110 #endif
111 }
112 
ZipArchive(const void * address,size_t length)113 ZipArchive::ZipArchive(const void* address, size_t length)
114     : mapped_zip(address, length),
115       close_file(false),
116       directory_offset(0),
117       central_directory(),
118       directory_map(),
119       num_entries(0) {}
120 
~ZipArchive()121 ZipArchive::~ZipArchive() {
122   if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
123 #if defined(__BIONIC__)
124     android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
125 #else
126     close(mapped_zip.GetFileDescriptor());
127 #endif
128   }
129 }
130 
131 struct CentralDirectoryInfo {
132   uint64_t num_records;
133   // The size of the central directory (in bytes).
134   uint64_t cd_size;
135   // The offset of the start of the central directory, relative
136   // to the start of the file.
137   uint64_t cd_start_offset;
138 };
139 
140 // Reads |T| at |readPtr| and increments |readPtr|. Returns std::nullopt if the boundary check
141 // fails.
142 template <typename T>
TryConsumeUnaligned(uint8_t ** readPtr,const uint8_t * bufStart,size_t bufSize)143 static std::optional<T> TryConsumeUnaligned(uint8_t** readPtr, const uint8_t* bufStart,
144                                             size_t bufSize) {
145   if (bufSize < sizeof(T) || *readPtr - bufStart > bufSize - sizeof(T)) {
146     ALOGW("Zip: %zu byte read exceeds the boundary of allocated buf, offset %zu, bufSize %zu",
147           sizeof(T), *readPtr - bufStart, bufSize);
148     return std::nullopt;
149   }
150   return ConsumeUnaligned<T>(readPtr);
151 }
152 
FindCentralDirectoryInfoForZip64(const char * debugFileName,ZipArchive * archive,off64_t eocdOffset,CentralDirectoryInfo * cdInfo)153 static ZipError FindCentralDirectoryInfoForZip64(const char* debugFileName, ZipArchive* archive,
154                                                  off64_t eocdOffset, CentralDirectoryInfo* cdInfo) {
155   if (eocdOffset <= sizeof(Zip64EocdLocator)) {
156     ALOGW("Zip: %s: Not enough space for zip64 eocd locator", debugFileName);
157     return kInvalidFile;
158   }
159   // We expect to find the zip64 eocd locator immediately before the zip eocd.
160   const int64_t locatorOffset = eocdOffset - sizeof(Zip64EocdLocator);
161   Zip64EocdLocator zip64EocdLocator{};
162   if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>((&zip64EocdLocator)),
163                                         sizeof(Zip64EocdLocator), locatorOffset)) {
164     ALOGW("Zip: %s: Read %zu from offset %" PRId64 " failed %s", debugFileName,
165           sizeof(Zip64EocdLocator), locatorOffset, debugFileName);
166     return kIoError;
167   }
168 
169   if (zip64EocdLocator.locator_signature != Zip64EocdLocator::kSignature) {
170     ALOGW("Zip: %s: Zip64 eocd locator signature not found at offset %" PRId64, debugFileName,
171           locatorOffset);
172     return kInvalidFile;
173   }
174 
175   const int64_t zip64EocdOffset = zip64EocdLocator.zip64_eocd_offset;
176   if (locatorOffset <= sizeof(Zip64EocdRecord) ||
177       zip64EocdOffset > locatorOffset - sizeof(Zip64EocdRecord)) {
178     ALOGW("Zip: %s: Bad zip64 eocd offset %" PRId64 ", eocd locator offset %" PRId64, debugFileName,
179           zip64EocdOffset, locatorOffset);
180     return kInvalidOffset;
181   }
182 
183   Zip64EocdRecord zip64EocdRecord{};
184   if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&zip64EocdRecord),
185                                         sizeof(Zip64EocdRecord), zip64EocdOffset)) {
186     ALOGW("Zip: %s: read %zu from offset %" PRId64 " failed %s", debugFileName,
187           sizeof(Zip64EocdLocator), zip64EocdOffset, debugFileName);
188     return kIoError;
189   }
190 
191   if (zip64EocdRecord.record_signature != Zip64EocdRecord::kSignature) {
192     ALOGW("Zip: %s: Zip64 eocd record signature not found at offset %" PRId64, debugFileName,
193           zip64EocdOffset);
194     return kInvalidFile;
195   }
196 
197   if (zip64EocdOffset <= zip64EocdRecord.cd_size ||
198       zip64EocdRecord.cd_start_offset > zip64EocdOffset - zip64EocdRecord.cd_size) {
199     ALOGW("Zip: %s: Bad offset for zip64 central directory. cd offset %" PRIu64 ", cd size %" PRIu64
200           ", zip64 eocd offset %" PRIu64,
201           debugFileName, zip64EocdRecord.cd_start_offset, zip64EocdRecord.cd_size, zip64EocdOffset);
202     return kInvalidOffset;
203   }
204 
205   *cdInfo = {.num_records = zip64EocdRecord.num_records,
206              .cd_size = zip64EocdRecord.cd_size,
207              .cd_start_offset = zip64EocdRecord.cd_start_offset};
208 
209   return kSuccess;
210 }
211 
FindCentralDirectoryInfo(const char * debug_file_name,ZipArchive * archive,off64_t file_length,uint32_t read_amount,CentralDirectoryInfo * cdInfo)212 static ZipError FindCentralDirectoryInfo(const char* debug_file_name, ZipArchive* archive,
213                                          off64_t file_length, uint32_t read_amount,
214                                          CentralDirectoryInfo* cdInfo) {
215   std::vector<uint8_t> scan_buffer(read_amount);
216   const off64_t search_start = file_length - read_amount;
217 
218   if (!archive->mapped_zip.ReadAtOffset(scan_buffer.data(), read_amount, search_start)) {
219     ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
220           static_cast<int64_t>(search_start));
221     return kIoError;
222   }
223 
224   /*
225    * Scan backward for the EOCD magic.  In an archive without a trailing
226    * comment, we'll find it on the first try.  (We may want to consider
227    * doing an initial minimal read; if we don't find it, retry with a
228    * second read as above.)
229    */
230   CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
231   int32_t i = read_amount - sizeof(EocdRecord);
232   for (; i >= 0; i--) {
233     if (scan_buffer[i] == 0x50) {
234       uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
235       if (android::base::get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
236         ALOGV("+++ Found EOCD at buf+%d", i);
237         break;
238       }
239     }
240   }
241   if (i < 0) {
242     ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
243     return kInvalidFile;
244   }
245 
246   const off64_t eocd_offset = search_start + i;
247   auto eocd = reinterpret_cast<const EocdRecord*>(scan_buffer.data() + i);
248   /*
249    * Verify that there's no trailing space at the end of the central directory
250    * and its comment.
251    */
252   const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
253   if (calculated_length != file_length) {
254     ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
255           static_cast<int64_t>(file_length - calculated_length));
256     return kInvalidFile;
257   }
258 
259   // One of the field is 0xFFFFFFFF, look for the zip64 EOCD instead.
260   if (eocd->cd_size == UINT32_MAX || eocd->cd_start_offset == UINT32_MAX) {
261     ALOGV("Looking for the zip64 EOCD, cd_size: %" PRIu32 "cd_start_offset: %" PRId32,
262           eocd->cd_size, eocd->cd_start_offset);
263     return FindCentralDirectoryInfoForZip64(debug_file_name, archive, eocd_offset, cdInfo);
264   }
265 
266   /*
267    * Grab the CD offset and size, and the number of entries in the
268    * archive and verify that they look reasonable.
269    */
270   if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
271     ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
272           eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
273     return kInvalidOffset;
274   }
275 
276   *cdInfo = {.num_records = eocd->num_records,
277              .cd_size = eocd->cd_size,
278              .cd_start_offset = eocd->cd_start_offset};
279   return kSuccess;
280 }
281 
282 /*
283  * Find the zip Central Directory and memory-map it.
284  *
285  * On success, returns kSuccess after populating fields from the EOCD area:
286  *   directory_offset
287  *   directory_ptr
288  *   num_entries
289  */
MapCentralDirectory(const char * debug_file_name,ZipArchive * archive)290 static ZipError MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
291   // Test file length. We use lseek64 to make sure the file is small enough to be a zip file.
292   off64_t file_length = archive->mapped_zip.GetFileLength();
293   if (file_length == -1) {
294     return kInvalidFile;
295   }
296 
297   if (file_length > kMaxFileLength) {
298     ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
299     return kInvalidFile;
300   }
301 
302   if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
303     ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
304     return kInvalidFile;
305   }
306 
307   /*
308    * Perform the traditional EOCD snipe hunt.
309    *
310    * We're searching for the End of Central Directory magic number,
311    * which appears at the start of the EOCD block.  It's followed by
312    * 18 bytes of EOCD stuff and up to 64KB of archive comment.  We
313    * need to read the last part of the file into a buffer, dig through
314    * it to find the magic number, parse some values out, and use those
315    * to determine the extent of the CD.
316    *
317    * We start by pulling in the last part of the file.
318    */
319   uint32_t read_amount = kMaxEOCDSearch;
320   if (file_length < read_amount) {
321     read_amount = static_cast<uint32_t>(file_length);
322   }
323 
324   CentralDirectoryInfo cdInfo = {};
325   if (auto result =
326           FindCentralDirectoryInfo(debug_file_name, archive, file_length, read_amount, &cdInfo);
327       result != kSuccess) {
328     return result;
329   }
330 
331   if (cdInfo.num_records == 0) {
332 #if defined(__ANDROID__)
333     ALOGW("Zip: empty archive?");
334 #endif
335     return kEmptyArchive;
336   }
337 
338   if (cdInfo.cd_size >= SIZE_MAX) {
339     ALOGW("Zip: The size of central directory doesn't fit in range of size_t: %" PRIu64,
340           cdInfo.cd_size);
341     return kInvalidFile;
342   }
343 
344   ALOGV("+++ num_entries=%" PRIu64 " dir_size=%" PRIu64 " dir_offset=%" PRIu64, cdInfo.num_records,
345         cdInfo.cd_size, cdInfo.cd_start_offset);
346 
347   // It all looks good.  Create a mapping for the CD, and set the fields in archive.
348   if (!archive->InitializeCentralDirectory(static_cast<off64_t>(cdInfo.cd_start_offset),
349                                            static_cast<size_t>(cdInfo.cd_size))) {
350     return kMmapFailed;
351   }
352 
353   archive->num_entries = cdInfo.num_records;
354   archive->directory_offset = cdInfo.cd_start_offset;
355 
356   return kSuccess;
357 }
358 
ParseZip64ExtendedInfoInExtraField(const uint8_t * extraFieldStart,uint16_t extraFieldLength,uint32_t zip32UncompressedSize,uint32_t zip32CompressedSize,std::optional<uint32_t> zip32LocalFileHeaderOffset,Zip64ExtendedInfo * zip64Info)359 static ZipError ParseZip64ExtendedInfoInExtraField(
360     const uint8_t* extraFieldStart, uint16_t extraFieldLength, uint32_t zip32UncompressedSize,
361     uint32_t zip32CompressedSize, std::optional<uint32_t> zip32LocalFileHeaderOffset,
362     Zip64ExtendedInfo* zip64Info) {
363   if (extraFieldLength <= 4) {
364     ALOGW("Zip: Extra field isn't large enough to hold zip64 info, size %" PRIu16,
365           extraFieldLength);
366     return kInvalidFile;
367   }
368 
369   // Each header MUST consist of:
370   // Header ID - 2 bytes
371   // Data Size - 2 bytes
372   uint16_t offset = 0;
373   while (offset < extraFieldLength - 4) {
374     auto readPtr = const_cast<uint8_t*>(extraFieldStart + offset);
375     auto headerId = ConsumeUnaligned<uint16_t>(&readPtr);
376     auto dataSize = ConsumeUnaligned<uint16_t>(&readPtr);
377 
378     offset += 4;
379     if (dataSize > extraFieldLength - offset) {
380       ALOGW("Zip: Data size exceeds the boundary of extra field, data size %" PRIu16, dataSize);
381       return kInvalidOffset;
382     }
383 
384     // Skip the other types of extensible data fields. Details in
385     // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT section 4.5
386     if (headerId != Zip64ExtendedInfo::kHeaderId) {
387       offset += dataSize;
388       continue;
389     }
390 
391     std::optional<uint64_t> uncompressedFileSize;
392     std::optional<uint64_t> compressedFileSize;
393     std::optional<uint64_t> localHeaderOffset;
394     if (zip32UncompressedSize == UINT32_MAX) {
395       uncompressedFileSize =
396           TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
397       if (!uncompressedFileSize.has_value()) return kInvalidOffset;
398     }
399     if (zip32CompressedSize == UINT32_MAX) {
400       compressedFileSize =
401           TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
402       if (!compressedFileSize.has_value()) return kInvalidOffset;
403     }
404     if (zip32LocalFileHeaderOffset == UINT32_MAX) {
405       localHeaderOffset =
406           TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
407       if (!localHeaderOffset.has_value()) return kInvalidOffset;
408     }
409 
410     // calculate how many bytes we read after the data size field.
411     size_t bytesRead = readPtr - (extraFieldStart + offset);
412     if (bytesRead == 0) {
413       ALOGW("Zip: Data size should not be 0 in zip64 extended field");
414       return kInvalidFile;
415     }
416 
417     if (dataSize != bytesRead) {
418       auto localOffsetString = zip32LocalFileHeaderOffset.has_value()
419                                    ? std::to_string(zip32LocalFileHeaderOffset.value())
420                                    : "missing";
421       ALOGW("Zip: Invalid data size in zip64 extended field, expect %zu , get %" PRIu16
422             ", uncompressed size %" PRIu32 ", compressed size %" PRIu32 ", local header offset %s",
423             bytesRead, dataSize, zip32UncompressedSize, zip32CompressedSize,
424             localOffsetString.c_str());
425       return kInvalidFile;
426     }
427 
428     zip64Info->uncompressed_file_size = uncompressedFileSize;
429     zip64Info->compressed_file_size = compressedFileSize;
430     zip64Info->local_header_offset = localHeaderOffset;
431     return kSuccess;
432   }
433 
434   ALOGW("Zip: zip64 extended info isn't found in the extra field.");
435   return kInvalidFile;
436 }
437 
438 /*
439  * Parses the Zip archive's Central Directory.  Allocates and populates the
440  * hash table.
441  *
442  * Returns 0 on success.
443  */
ParseZipArchive(ZipArchive * archive)444 static ZipError ParseZipArchive(ZipArchive* archive) {
445   const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
446   const size_t cd_length = archive->central_directory.GetMapLength();
447   const uint64_t num_entries = archive->num_entries;
448 
449   if (num_entries <= UINT16_MAX) {
450     archive->cd_entry_map = CdEntryMapZip32::Create(static_cast<uint16_t>(num_entries));
451   } else {
452     archive->cd_entry_map = CdEntryMapZip64::Create();
453   }
454   if (archive->cd_entry_map == nullptr) {
455     return kAllocationFailed;
456   }
457 
458   /*
459    * Walk through the central directory, adding entries to the hash
460    * table and verifying values.
461    */
462   const uint8_t* const cd_end = cd_ptr + cd_length;
463   const uint8_t* ptr = cd_ptr;
464   for (uint64_t i = 0; i < num_entries; i++) {
465     if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
466       ALOGW("Zip: ran off the end (item #%" PRIu64 ", %zu bytes of central directory)", i,
467             cd_length);
468 #if defined(__ANDROID__)
469       android_errorWriteLog(0x534e4554, "36392138");
470 #endif
471       return kInvalidFile;
472     }
473 
474     auto cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
475     if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
476       ALOGW("Zip: missed a central dir sig (at %" PRIu64 ")", i);
477       return kInvalidFile;
478     }
479 
480     const uint16_t file_name_length = cdr->file_name_length;
481     const uint16_t extra_length = cdr->extra_field_length;
482     const uint16_t comment_length = cdr->comment_length;
483     const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
484 
485     if (file_name_length >= cd_length || file_name > cd_end - file_name_length) {
486       ALOGW("Zip: file name for entry %" PRIu64
487             " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
488             i, file_name_length, cd_length);
489       return kInvalidEntryName;
490     }
491 
492     const uint8_t* extra_field = file_name + file_name_length;
493     if (extra_length >= cd_length || extra_field > cd_end - extra_length) {
494       ALOGW("Zip: extra field for entry %" PRIu64
495             " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
496             i, extra_length, cd_length);
497       return kInvalidFile;
498     }
499 
500     off64_t local_header_offset = cdr->local_file_header_offset;
501     if (local_header_offset == UINT32_MAX) {
502       Zip64ExtendedInfo zip64_info{};
503       if (auto status = ParseZip64ExtendedInfoInExtraField(
504               extra_field, extra_length, cdr->uncompressed_size, cdr->compressed_size,
505               cdr->local_file_header_offset, &zip64_info);
506           status != kSuccess) {
507         return status;
508       }
509       CHECK(zip64_info.local_header_offset.has_value());
510       local_header_offset = zip64_info.local_header_offset.value();
511     }
512 
513     if (local_header_offset >= archive->directory_offset) {
514       ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu64,
515             static_cast<int64_t>(local_header_offset), i);
516       return kInvalidFile;
517     }
518 
519     // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
520     if (!IsValidEntryName(file_name, file_name_length)) {
521       ALOGW("Zip: invalid file name at entry %" PRIu64, i);
522       return kInvalidEntryName;
523     }
524 
525     // Add the CDE filename to the hash table.
526     std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
527     if (auto add_result =
528             archive->cd_entry_map->AddToMap(entry_name, archive->central_directory.GetBasePtr());
529         add_result != 0) {
530       ALOGW("Zip: Error adding entry to hash table %d", add_result);
531       return add_result;
532     }
533 
534     ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
535     if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
536       ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu64, ptr - cd_ptr, cd_length, i);
537       return kInvalidFile;
538     }
539   }
540 
541   uint32_t lfh_start_bytes;
542   if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&lfh_start_bytes),
543                                         sizeof(uint32_t), 0)) {
544     ALOGW("Zip: Unable to read header for entry at offset == 0.");
545     return kInvalidFile;
546   }
547 
548   if (lfh_start_bytes != LocalFileHeader::kSignature) {
549     ALOGW("Zip: Entry at offset zero has invalid LFH signature %" PRIx32, lfh_start_bytes);
550 #if defined(__ANDROID__)
551     android_errorWriteLog(0x534e4554, "64211847");
552 #endif
553     return kInvalidFile;
554   }
555 
556   ALOGV("+++ zip good scan %" PRIu64 " entries", num_entries);
557 
558   return kSuccess;
559 }
560 
OpenArchiveInternal(ZipArchive * archive,const char * debug_file_name)561 static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
562   int32_t result = MapCentralDirectory(debug_file_name, archive);
563   return result != kSuccess ? result : ParseZipArchive(archive);
564 }
565 
OpenArchiveFd(int fd,const char * debug_file_name,ZipArchiveHandle * handle,bool assume_ownership)566 int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
567                       bool assume_ownership) {
568   ZipArchive* archive = new ZipArchive(MappedZipFile(fd), assume_ownership);
569   *handle = archive;
570   return OpenArchiveInternal(archive, debug_file_name);
571 }
572 
OpenArchiveFdRange(int fd,const char * debug_file_name,ZipArchiveHandle * handle,off64_t length,off64_t offset,bool assume_ownership)573 int32_t OpenArchiveFdRange(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
574                            off64_t length, off64_t offset, bool assume_ownership) {
575   ZipArchive* archive = new ZipArchive(MappedZipFile(fd, length, offset), assume_ownership);
576   *handle = archive;
577 
578   if (length < 0) {
579     ALOGW("Invalid zip length %" PRId64, length);
580     return kIoError;
581   }
582 
583   if (offset < 0) {
584     ALOGW("Invalid zip offset %" PRId64, offset);
585     return kIoError;
586   }
587 
588   return OpenArchiveInternal(archive, debug_file_name);
589 }
590 
OpenArchive(const char * fileName,ZipArchiveHandle * handle)591 int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
592   const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
593   ZipArchive* archive = new ZipArchive(MappedZipFile(fd), true);
594   *handle = archive;
595 
596   if (fd < 0) {
597     ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
598     return kIoError;
599   }
600 
601   return OpenArchiveInternal(archive, fileName);
602 }
603 
OpenArchiveFromMemory(const void * address,size_t length,const char * debug_file_name,ZipArchiveHandle * handle)604 int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debug_file_name,
605                               ZipArchiveHandle* handle) {
606   ZipArchive* archive = new ZipArchive(address, length);
607   *handle = archive;
608   return OpenArchiveInternal(archive, debug_file_name);
609 }
610 
GetArchiveInfo(ZipArchiveHandle archive)611 ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
612   ZipArchiveInfo result;
613   result.archive_size = archive->mapped_zip.GetFileLength();
614   result.entry_count = archive->num_entries;
615   return result;
616 }
617 
618 /*
619  * Close a ZipArchive, closing the file and freeing the contents.
620  */
CloseArchive(ZipArchiveHandle archive)621 void CloseArchive(ZipArchiveHandle archive) {
622   ALOGV("Closing archive %p", archive);
623   delete archive;
624 }
625 
ValidateDataDescriptor(MappedZipFile & mapped_zip,const ZipEntry64 * entry)626 static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, const ZipEntry64* entry) {
627   // Maximum possible size for data descriptor: 2 * 4 + 2 * 8 = 24 bytes
628   // The zip format doesn't specify the size of data descriptor. But we won't read OOB here even
629   // if the descriptor isn't present. Because the size cd + eocd in the end of the zipfile is
630   // larger than 24 bytes. And if the descriptor contains invalid data, we'll abort due to
631   // kInconsistentInformation.
632   uint8_t ddBuf[24];
633   off64_t offset = entry->offset;
634   if (entry->method != kCompressStored) {
635     offset += entry->compressed_length;
636   } else {
637     offset += entry->uncompressed_length;
638   }
639 
640   if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
641     return kIoError;
642   }
643 
644   const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
645   uint8_t* ddReadPtr = (ddSignature == DataDescriptor::kOptSignature) ? ddBuf + 4 : ddBuf;
646   DataDescriptor descriptor{};
647   descriptor.crc32 = ConsumeUnaligned<uint32_t>(&ddReadPtr);
648   if (entry->zip64_format_size) {
649     descriptor.compressed_size = ConsumeUnaligned<uint64_t>(&ddReadPtr);
650     descriptor.uncompressed_size = ConsumeUnaligned<uint64_t>(&ddReadPtr);
651   } else {
652     descriptor.compressed_size = ConsumeUnaligned<uint32_t>(&ddReadPtr);
653     descriptor.uncompressed_size = ConsumeUnaligned<uint32_t>(&ddReadPtr);
654   }
655 
656   // Validate that the values in the data descriptor match those in the central
657   // directory.
658   if (entry->compressed_length != descriptor.compressed_size ||
659       entry->uncompressed_length != descriptor.uncompressed_size ||
660       entry->crc32 != descriptor.crc32) {
661     ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32
662           "}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}",
663           entry->compressed_length, entry->uncompressed_length, entry->crc32,
664           descriptor.compressed_size, descriptor.uncompressed_size, descriptor.crc32);
665     return kInconsistentInformation;
666   }
667 
668   return 0;
669 }
670 
FindEntry(const ZipArchive * archive,std::string_view entryName,const uint64_t nameOffset,ZipEntry64 * data)671 static int32_t FindEntry(const ZipArchive* archive, std::string_view entryName,
672                          const uint64_t nameOffset, ZipEntry64* data) {
673   // Recover the start of the central directory entry from the filename
674   // pointer.  The filename is the first entry past the fixed-size data,
675   // so we can just subtract back from that.
676   const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
677   const uint8_t* ptr = base_ptr + nameOffset;
678   ptr -= sizeof(CentralDirectoryRecord);
679 
680   // This is the base of our mmapped region, we have to check that
681   // the name that's in the hash table is a pointer to a location within
682   // this mapped region.
683   if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
684     ALOGW("Zip: Invalid entry pointer");
685     return kInvalidOffset;
686   }
687 
688   auto cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
689 
690   // The offset of the start of the central directory in the zipfile.
691   // We keep this lying around so that we can check all our lengths
692   // and our per-file structures.
693   const off64_t cd_offset = archive->directory_offset;
694 
695   // Fill out the compression method, modification time, crc32
696   // and other interesting attributes from the central directory. These
697   // will later be compared against values from the local file header.
698   data->method = cdr->compression_method;
699   data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
700   data->crc32 = cdr->crc32;
701   data->compressed_length = cdr->compressed_size;
702   data->uncompressed_length = cdr->uncompressed_size;
703 
704   // Figure out the local header offset from the central directory. The
705   // actual file data will begin after the local header and the name /
706   // extra comments.
707   off64_t local_header_offset = cdr->local_file_header_offset;
708   // One of the info field is UINT32_MAX, try to parse the real value in the zip64 extended info in
709   // the extra field.
710   if (cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX ||
711       cdr->local_file_header_offset == UINT32_MAX) {
712     const uint8_t* extra_field = ptr + sizeof(CentralDirectoryRecord) + cdr->file_name_length;
713     Zip64ExtendedInfo zip64_info{};
714     if (auto status = ParseZip64ExtendedInfoInExtraField(
715             extra_field, cdr->extra_field_length, cdr->uncompressed_size, cdr->compressed_size,
716             cdr->local_file_header_offset, &zip64_info);
717         status != kSuccess) {
718       return status;
719     }
720 
721     data->uncompressed_length = zip64_info.uncompressed_file_size.value_or(cdr->uncompressed_size);
722     data->compressed_length = zip64_info.compressed_file_size.value_or(cdr->compressed_size);
723     local_header_offset = zip64_info.local_header_offset.value_or(local_header_offset);
724     data->zip64_format_size =
725         cdr->uncompressed_size == UINT32_MAX || cdr->compressed_size == UINT32_MAX;
726   }
727 
728   if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
729     ALOGW("Zip: bad local hdr offset in zip");
730     return kInvalidOffset;
731   }
732 
733   uint8_t lfh_buf[sizeof(LocalFileHeader)];
734   if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
735     ALOGW("Zip: failed reading lfh name from offset %" PRId64,
736           static_cast<int64_t>(local_header_offset));
737     return kIoError;
738   }
739 
740   auto lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
741   if (lfh->lfh_signature != LocalFileHeader::kSignature) {
742     ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
743           static_cast<int64_t>(local_header_offset));
744     return kInvalidOffset;
745   }
746 
747   // Check that the local file header name matches the declared name in the central directory.
748   CHECK_LE(entryName.size(), UINT16_MAX);
749   auto nameLen = static_cast<uint16_t>(entryName.size());
750   if (lfh->file_name_length != nameLen) {
751     ALOGW("Zip: lfh name length did not match central directory for %s: %" PRIu16 " %" PRIu16,
752           std::string(entryName).c_str(), lfh->file_name_length, nameLen);
753     return kInconsistentInformation;
754   }
755   const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
756   if (name_offset > cd_offset - lfh->file_name_length) {
757     ALOGW("Zip: lfh name has invalid declared length");
758     return kInvalidOffset;
759   }
760 
761   std::vector<uint8_t> name_buf(nameLen);
762   if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
763     ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
764     return kIoError;
765   }
766   if (memcmp(entryName.data(), name_buf.data(), nameLen) != 0) {
767     ALOGW("Zip: lfh name did not match central directory");
768     return kInconsistentInformation;
769   }
770 
771   uint64_t lfh_uncompressed_size = lfh->uncompressed_size;
772   uint64_t lfh_compressed_size = lfh->compressed_size;
773   if (lfh_uncompressed_size == UINT32_MAX || lfh_compressed_size == UINT32_MAX) {
774     if (lfh_uncompressed_size != UINT32_MAX || lfh_compressed_size != UINT32_MAX) {
775       ALOGW(
776           "Zip: The zip64 extended field in the local header MUST include BOTH original and "
777           "compressed file size fields.");
778       return kInvalidFile;
779     }
780 
781     const off64_t lfh_extra_field_offset = name_offset + lfh->file_name_length;
782     const uint16_t lfh_extra_field_size = lfh->extra_field_length;
783     if (lfh_extra_field_offset > cd_offset - lfh_extra_field_size) {
784       ALOGW("Zip: extra field has a bad size for entry %s", std::string(entryName).c_str());
785       return kInvalidOffset;
786     }
787 
788     std::vector<uint8_t> local_extra_field(lfh_extra_field_size);
789     if (!archive->mapped_zip.ReadAtOffset(local_extra_field.data(), lfh_extra_field_size,
790                                           lfh_extra_field_offset)) {
791       ALOGW("Zip: failed reading lfh extra field from offset %" PRId64, lfh_extra_field_offset);
792       return kIoError;
793     }
794 
795     Zip64ExtendedInfo zip64_info{};
796     if (auto status = ParseZip64ExtendedInfoInExtraField(
797             local_extra_field.data(), lfh_extra_field_size, lfh->uncompressed_size,
798             lfh->compressed_size, std::nullopt, &zip64_info);
799         status != kSuccess) {
800       return status;
801     }
802 
803     CHECK(zip64_info.uncompressed_file_size.has_value());
804     CHECK(zip64_info.compressed_file_size.has_value());
805     lfh_uncompressed_size = zip64_info.uncompressed_file_size.value();
806     lfh_compressed_size = zip64_info.compressed_file_size.value();
807   }
808 
809   // Paranoia: Match the values specified in the local file header
810   // to those specified in the central directory.
811 
812   // Warn if central directory and local file header don't agree on the use
813   // of a trailing Data Descriptor. The reference implementation is inconsistent
814   // and appears to use the LFH value during extraction (unzip) but the CD value
815   // while displayng information about archives (zipinfo). The spec remains
816   // silent on this inconsistency as well.
817   //
818   // For now, always use the version from the LFH but make sure that the values
819   // specified in the central directory match those in the data descriptor.
820   //
821   // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
822   // bit 11 (EFS: The language encoding flag, marking that filename and comment are
823   // encoded using UTF-8). This implementation does not check for the presence of
824   // that flag and always enforces that entry names are valid UTF-8.
825   if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
826     ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
827           cdr->gpb_flags, lfh->gpb_flags);
828   }
829 
830   // If there is no trailing data descriptor, verify that the central directory and local file
831   // header agree on the crc, compressed, and uncompressed sizes of the entry.
832   if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
833     data->has_data_descriptor = 0;
834     if (data->compressed_length != lfh_compressed_size ||
835         data->uncompressed_length != lfh_uncompressed_size || data->crc32 != lfh->crc32) {
836       ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu64 ", %" PRIu64 ", %" PRIx32
837             "}, was {%" PRIu64 ", %" PRIu64 ", %" PRIx32 "}",
838             data->compressed_length, data->uncompressed_length, data->crc32, lfh_compressed_size,
839             lfh_uncompressed_size, lfh->crc32);
840       return kInconsistentInformation;
841     }
842   } else {
843     data->has_data_descriptor = 1;
844   }
845 
846   // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
847   data->version_made_by = cdr->version_made_by;
848   data->external_file_attributes = cdr->external_file_attributes;
849   if ((data->version_made_by >> 8) == 3) {
850     data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
851   } else {
852     data->unix_mode = 0777;
853   }
854 
855   // 4.4.4: general purpose bit flags.
856   data->gpbf = lfh->gpb_flags;
857 
858   // 4.4.14: the lowest bit of the internal file attributes field indicates text.
859   // Currently only needed to implement zipinfo.
860   data->is_text = (cdr->internal_file_attributes & 1);
861 
862   const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
863                               lfh->file_name_length + lfh->extra_field_length;
864   if (data_offset > cd_offset) {
865     ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
866     return kInvalidOffset;
867   }
868 
869   if (data->compressed_length > cd_offset - data_offset) {
870     ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")",
871           static_cast<int64_t>(data_offset), data->compressed_length,
872           static_cast<int64_t>(cd_offset));
873     return kInvalidOffset;
874   }
875 
876   if (data->method == kCompressStored && data->uncompressed_length > cd_offset - data_offset) {
877     ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu64 " > %" PRId64 ")",
878           static_cast<int64_t>(data_offset), data->uncompressed_length,
879           static_cast<int64_t>(cd_offset));
880     return kInvalidOffset;
881   }
882 
883   data->offset = data_offset;
884   return 0;
885 }
886 
887 struct IterationHandle {
888   ZipArchive* archive;
889 
890   std::function<bool(std::string_view)> matcher;
891 
892   uint32_t position = 0;
893 
IterationHandleIterationHandle894   IterationHandle(ZipArchive* archive, std::function<bool(std::string_view)> in_matcher)
895       : archive(archive), matcher(std::move(in_matcher)) {}
896 
MatchIterationHandle897   bool Match(std::string_view entry_name) const { return matcher(entry_name); }
898 };
899 
StartIteration(ZipArchiveHandle archive,void ** cookie_ptr,const std::string_view optional_prefix,const std::string_view optional_suffix)900 int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
901                        const std::string_view optional_prefix,
902                        const std::string_view optional_suffix) {
903   if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) ||
904       optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) {
905     ALOGW("Zip: prefix/suffix too long");
906     return kInvalidEntryName;
907   }
908   auto matcher = [prefix = std::string(optional_prefix),
909                   suffix = std::string(optional_suffix)](std::string_view name) mutable {
910     return android::base::StartsWith(name, prefix) && android::base::EndsWith(name, suffix);
911   };
912   return StartIteration(archive, cookie_ptr, std::move(matcher));
913 }
914 
StartIteration(ZipArchiveHandle archive,void ** cookie_ptr,std::function<bool (std::string_view)> matcher)915 int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
916                        std::function<bool(std::string_view)> matcher) {
917   if (archive == nullptr || archive->cd_entry_map == nullptr) {
918     ALOGW("Zip: Invalid ZipArchiveHandle");
919     return kInvalidHandle;
920   }
921 
922   archive->cd_entry_map->ResetIteration();
923   *cookie_ptr = new IterationHandle(archive, matcher);
924   return 0;
925 }
926 
EndIteration(void * cookie)927 void EndIteration(void* cookie) {
928   delete reinterpret_cast<IterationHandle*>(cookie);
929 }
930 
CopyFromZipEntry64(ZipEntry * dst,const ZipEntry64 * src)931 int32_t ZipEntry::CopyFromZipEntry64(ZipEntry* dst, const ZipEntry64* src) {
932   if (src->compressed_length > UINT32_MAX || src->uncompressed_length > UINT32_MAX) {
933     ALOGW(
934         "Zip: the entry size is too large to fit into the 32 bits ZipEntry, uncompressed "
935         "length %" PRIu64 ", compressed length %" PRIu64,
936         src->uncompressed_length, src->compressed_length);
937     return kUnsupportedEntrySize;
938   }
939 
940   *dst = *src;
941   dst->uncompressed_length = static_cast<uint32_t>(src->uncompressed_length);
942   dst->compressed_length = static_cast<uint32_t>(src->compressed_length);
943   return kSuccess;
944 }
945 
FindEntry(const ZipArchiveHandle archive,const std::string_view entryName,ZipEntry * data)946 int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
947                   ZipEntry* data) {
948   ZipEntry64 entry64;
949   if (auto status = FindEntry(archive, entryName, &entry64); status != kSuccess) {
950     return status;
951   }
952 
953   return ZipEntry::CopyFromZipEntry64(data, &entry64);
954 }
955 
FindEntry(const ZipArchiveHandle archive,const std::string_view entryName,ZipEntry64 * data)956 int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
957                   ZipEntry64* data) {
958   if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
959     ALOGW("Zip: Invalid filename of length %zu", entryName.size());
960     return kInvalidEntryName;
961   }
962 
963   const auto [result, offset] =
964       archive->cd_entry_map->GetCdEntryOffset(entryName, archive->central_directory.GetBasePtr());
965   if (result != 0) {
966     ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
967     return static_cast<int32_t>(result);  // kEntryNotFound is safe to truncate.
968   }
969   // We know there are at most hash_table_size entries, safe to truncate.
970   return FindEntry(archive, entryName, offset, data);
971 }
972 
Next(void * cookie,ZipEntry * data,std::string * name)973 int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
974   ZipEntry64 entry64;
975   if (auto status = Next(cookie, &entry64, name); status != kSuccess) {
976     return status;
977   }
978 
979   return ZipEntry::CopyFromZipEntry64(data, &entry64);
980 }
981 
Next(void * cookie,ZipEntry * data,std::string_view * name)982 int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
983   ZipEntry64 entry64;
984   if (auto status = Next(cookie, &entry64, name); status != kSuccess) {
985     return status;
986   }
987 
988   return ZipEntry::CopyFromZipEntry64(data, &entry64);
989 }
990 
Next(void * cookie,ZipEntry64 * data,std::string * name)991 int32_t Next(void* cookie, ZipEntry64* data, std::string* name) {
992   std::string_view sv;
993   int32_t result = Next(cookie, data, &sv);
994   if (result == 0 && name) {
995     *name = std::string(sv);
996   }
997   return result;
998 }
999 
Next(void * cookie,ZipEntry64 * data,std::string_view * name)1000 int32_t Next(void* cookie, ZipEntry64* data, std::string_view* name) {
1001   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
1002   if (handle == nullptr) {
1003     ALOGW("Zip: Null ZipArchiveHandle");
1004     return kInvalidHandle;
1005   }
1006 
1007   ZipArchive* archive = handle->archive;
1008   if (archive == nullptr || archive->cd_entry_map == nullptr) {
1009     ALOGW("Zip: Invalid ZipArchiveHandle");
1010     return kInvalidHandle;
1011   }
1012 
1013   auto entry = archive->cd_entry_map->Next(archive->central_directory.GetBasePtr());
1014   while (entry != std::pair<std::string_view, uint64_t>()) {
1015     const auto [entry_name, offset] = entry;
1016     if (handle->Match(entry_name)) {
1017       const int error = FindEntry(archive, entry_name, offset, data);
1018       if (!error && name) {
1019         *name = entry_name;
1020       }
1021       return error;
1022     }
1023     entry = archive->cd_entry_map->Next(archive->central_directory.GetBasePtr());
1024   }
1025 
1026   archive->cd_entry_map->ResetIteration();
1027   return kIterationEnd;
1028 }
1029 
1030 // A Writer that writes data to a fixed size memory region.
1031 // The size of the memory region must be equal to the total size of
1032 // the data appended to it.
1033 class MemoryWriter : public zip_archive::Writer {
1034  public:
Create(uint8_t * buf,size_t size,const ZipEntry64 * entry)1035   static std::unique_ptr<MemoryWriter> Create(uint8_t* buf, size_t size, const ZipEntry64* entry) {
1036     const uint64_t declared_length = entry->uncompressed_length;
1037     if (declared_length > size) {
1038       ALOGW("Zip: file size %" PRIu64 " is larger than the buffer size %zu.", declared_length,
1039             size);
1040       return nullptr;
1041     }
1042 
1043     return std::unique_ptr<MemoryWriter>(new MemoryWriter(buf, size));
1044   }
1045 
Append(uint8_t * buf,size_t buf_size)1046   virtual bool Append(uint8_t* buf, size_t buf_size) override {
1047     if (size_ < buf_size || bytes_written_ > size_ - buf_size) {
1048       ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
1049             bytes_written_ + buf_size);
1050       return false;
1051     }
1052 
1053     memcpy(buf_ + bytes_written_, buf, buf_size);
1054     bytes_written_ += buf_size;
1055     return true;
1056   }
1057 
1058  private:
MemoryWriter(uint8_t * buf,size_t size)1059   MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
1060 
1061   uint8_t* const buf_{nullptr};
1062   const size_t size_;
1063   size_t bytes_written_;
1064 };
1065 
1066 // A Writer that appends data to a file |fd| at its current position.
1067 // The file will be truncated to the end of the written data.
1068 class FileWriter : public zip_archive::Writer {
1069  public:
1070   // Creates a FileWriter for |fd| and prepare to write |entry| to it,
1071   // guaranteeing that the file descriptor is valid and that there's enough
1072   // space on the volume to write out the entry completely and that the file
1073   // is truncated to the correct length (no truncation if |fd| references a
1074   // block device).
1075   //
1076   // Returns a valid FileWriter on success, |nullptr| if an error occurred.
Create(int fd,const ZipEntry64 * entry)1077   static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry64* entry) {
1078     const uint64_t declared_length = entry->uncompressed_length;
1079     const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
1080     if (current_offset == -1) {
1081       ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
1082       return nullptr;
1083     }
1084 
1085     if (declared_length > SIZE_MAX || declared_length > INT64_MAX) {
1086       ALOGW("Zip: file size %" PRIu64 " is too large to extract.", declared_length);
1087       return nullptr;
1088     }
1089 
1090 #if defined(__linux__)
1091     if (declared_length > 0) {
1092       // Make sure we have enough space on the volume to extract the compressed
1093       // entry. Note that the call to ftruncate below will change the file size but
1094       // will not allocate space on disk and this call to fallocate will not
1095       // change the file size.
1096       // Note: fallocate is only supported by the following filesystems -
1097       // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
1098       // EOPNOTSUPP error when issued in other filesystems.
1099       // Hence, check for the return error code before concluding that the
1100       // disk does not have enough space.
1101       long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
1102       if (result == -1 && errno == ENOSPC) {
1103         ALOGW("Zip: unable to allocate %" PRIu64 " bytes at offset %" PRId64 ": %s",
1104               declared_length, static_cast<int64_t>(current_offset), strerror(errno));
1105         return nullptr;
1106       }
1107     }
1108 #endif  // __linux__
1109 
1110     struct stat sb;
1111     if (fstat(fd, &sb) == -1) {
1112       ALOGW("Zip: unable to fstat file: %s", strerror(errno));
1113       return nullptr;
1114     }
1115 
1116     // Block device doesn't support ftruncate(2).
1117     if (!S_ISBLK(sb.st_mode)) {
1118       long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
1119       if (result == -1) {
1120         ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
1121               static_cast<int64_t>(declared_length + current_offset), strerror(errno));
1122         return nullptr;
1123       }
1124     }
1125 
1126     return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
1127   }
1128 
FileWriter(FileWriter && other)1129   FileWriter(FileWriter&& other) noexcept
1130       : fd_(other.fd_),
1131         declared_length_(other.declared_length_),
1132         total_bytes_written_(other.total_bytes_written_) {
1133     other.fd_ = -1;
1134   }
1135 
Append(uint8_t * buf,size_t buf_size)1136   virtual bool Append(uint8_t* buf, size_t buf_size) override {
1137     if (declared_length_ < buf_size || total_bytes_written_ > declared_length_ - buf_size) {
1138       ALOGW("Zip: Unexpected size %zu  (declared) vs %zu (actual)", declared_length_,
1139             total_bytes_written_ + buf_size);
1140       return false;
1141     }
1142 
1143     const bool result = android::base::WriteFully(fd_, buf, buf_size);
1144     if (result) {
1145       total_bytes_written_ += buf_size;
1146     } else {
1147       ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
1148     }
1149 
1150     return result;
1151   }
1152 
1153  private:
FileWriter(const int fd=-1,const uint64_t declared_length=0)1154   explicit FileWriter(const int fd = -1, const uint64_t declared_length = 0)
1155       : Writer(),
1156         fd_(fd),
1157         declared_length_(static_cast<size_t>(declared_length)),
1158         total_bytes_written_(0) {
1159     CHECK_LE(declared_length, SIZE_MAX);
1160   }
1161 
1162   int fd_;
1163   const size_t declared_length_;
1164   size_t total_bytes_written_;
1165 };
1166 
1167 class EntryReader : public zip_archive::Reader {
1168  public:
EntryReader(const MappedZipFile & zip_file,const ZipEntry64 * entry)1169   EntryReader(const MappedZipFile& zip_file, const ZipEntry64* entry)
1170       : Reader(), zip_file_(zip_file), entry_(entry) {}
1171 
ReadAtOffset(uint8_t * buf,size_t len,off64_t offset) const1172   virtual bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
1173     return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
1174   }
1175 
~EntryReader()1176   virtual ~EntryReader() {}
1177 
1178  private:
1179   const MappedZipFile& zip_file_;
1180   const ZipEntry64* entry_;
1181 };
1182 
1183 // This method is using libz macros with old-style-casts
1184 #pragma GCC diagnostic push
1185 #pragma GCC diagnostic ignored "-Wold-style-cast"
zlib_inflateInit2(z_stream * stream,int window_bits)1186 static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
1187   return inflateInit2(stream, window_bits);
1188 }
1189 #pragma GCC diagnostic pop
1190 
1191 namespace zip_archive {
1192 
1193 // Moved out of line to avoid -Wweak-vtables.
~Reader()1194 Reader::~Reader() {}
~Writer()1195 Writer::~Writer() {}
1196 
Inflate(const Reader & reader,const uint64_t compressed_length,const uint64_t uncompressed_length,Writer * writer,uint64_t * crc_out)1197 int32_t Inflate(const Reader& reader, const uint64_t compressed_length,
1198                 const uint64_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
1199   const size_t kBufSize = 32768;
1200   std::vector<uint8_t> read_buf(kBufSize);
1201   std::vector<uint8_t> write_buf(kBufSize);
1202   z_stream zstream;
1203   int zerr;
1204 
1205   /*
1206    * Initialize the zlib stream struct.
1207    */
1208   memset(&zstream, 0, sizeof(zstream));
1209   zstream.zalloc = Z_NULL;
1210   zstream.zfree = Z_NULL;
1211   zstream.opaque = Z_NULL;
1212   zstream.next_in = NULL;
1213   zstream.avail_in = 0;
1214   zstream.next_out = &write_buf[0];
1215   zstream.avail_out = kBufSize;
1216   zstream.data_type = Z_UNKNOWN;
1217 
1218   /*
1219    * Use the undocumented "negative window bits" feature to tell zlib
1220    * that there's no zlib header waiting for it.
1221    */
1222   zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
1223   if (zerr != Z_OK) {
1224     if (zerr == Z_VERSION_ERROR) {
1225       ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
1226     } else {
1227       ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
1228     }
1229 
1230     return kZlibError;
1231   }
1232 
1233   auto zstream_deleter = [](z_stream* stream) {
1234     inflateEnd(stream); /* free up any allocated structures */
1235   };
1236 
1237   std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
1238 
1239   const bool compute_crc = (crc_out != nullptr);
1240   uLong crc = 0;
1241   uint64_t remaining_bytes = compressed_length;
1242   uint64_t total_output = 0;
1243   do {
1244     /* read as much as we can */
1245     if (zstream.avail_in == 0) {
1246       const uint32_t read_size =
1247           (remaining_bytes > kBufSize) ? kBufSize : static_cast<uint32_t>(remaining_bytes);
1248       const off64_t offset = (compressed_length - remaining_bytes);
1249       // Make sure to read at offset to ensure concurrent access to the fd.
1250       if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
1251         ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
1252         return kIoError;
1253       }
1254 
1255       remaining_bytes -= read_size;
1256 
1257       zstream.next_in = &read_buf[0];
1258       zstream.avail_in = read_size;
1259     }
1260 
1261     /* uncompress the data */
1262     zerr = inflate(&zstream, Z_NO_FLUSH);
1263     if (zerr != Z_OK && zerr != Z_STREAM_END) {
1264       ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
1265             zstream.avail_in, zstream.next_out, zstream.avail_out);
1266       return kZlibError;
1267     }
1268 
1269     /* write when we're full or when we're done */
1270     if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
1271       const size_t write_size = zstream.next_out - &write_buf[0];
1272       if (!writer->Append(&write_buf[0], write_size)) {
1273         return kIoError;
1274       } else if (compute_crc) {
1275         DCHECK_LE(write_size, kBufSize);
1276         crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
1277       }
1278 
1279       total_output += kBufSize - zstream.avail_out;
1280       zstream.next_out = &write_buf[0];
1281       zstream.avail_out = kBufSize;
1282     }
1283   } while (zerr == Z_OK);
1284 
1285   CHECK_EQ(zerr, Z_STREAM_END); /* other errors should've been caught */
1286 
1287   // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
1288   // "feature" of zlib to tell it there won't be a zlib file header. zlib
1289   // doesn't bother calculating the checksum in that scenario. We just do
1290   // it ourselves above because there are no additional gains to be made by
1291   // having zlib calculate it for us, since they do it by calling crc32 in
1292   // the same manner that we have above.
1293   if (compute_crc) {
1294     *crc_out = crc;
1295   }
1296   if (total_output != uncompressed_length || remaining_bytes != 0) {
1297     ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu64 ")", zstream.total_out,
1298           uncompressed_length);
1299     return kInconsistentInformation;
1300   }
1301 
1302   return 0;
1303 }
1304 }  // namespace zip_archive
1305 
InflateEntryToWriter(MappedZipFile & mapped_zip,const ZipEntry64 * entry,zip_archive::Writer * writer,uint64_t * crc_out)1306 static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry,
1307                                     zip_archive::Writer* writer, uint64_t* crc_out) {
1308   const EntryReader reader(mapped_zip, entry);
1309 
1310   return zip_archive::Inflate(reader, entry->compressed_length, entry->uncompressed_length, writer,
1311                               crc_out);
1312 }
1313 
CopyEntryToWriter(MappedZipFile & mapped_zip,const ZipEntry64 * entry,zip_archive::Writer * writer,uint64_t * crc_out)1314 static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry64* entry,
1315                                  zip_archive::Writer* writer, uint64_t* crc_out) {
1316   static const uint32_t kBufSize = 32768;
1317   std::vector<uint8_t> buf(kBufSize);
1318 
1319   const uint64_t length = entry->uncompressed_length;
1320   uint64_t count = 0;
1321   uLong crc = 0;
1322   while (count < length) {
1323     uint64_t remaining = length - count;
1324     off64_t offset = entry->offset + count;
1325 
1326     // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
1327     const uint32_t block_size =
1328         (remaining > kBufSize) ? kBufSize : static_cast<uint32_t>(remaining);
1329 
1330     // Make sure to read at offset to ensure concurrent access to the fd.
1331     if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
1332       ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s",
1333             block_size, static_cast<int64_t>(offset), strerror(errno));
1334       return kIoError;
1335     }
1336 
1337     if (!writer->Append(&buf[0], block_size)) {
1338       return kIoError;
1339     }
1340     if (crc_out) {
1341       crc = crc32(crc, &buf[0], block_size);
1342     }
1343     count += block_size;
1344   }
1345 
1346   if (crc_out) {
1347     *crc_out = crc;
1348   }
1349 
1350   return 0;
1351 }
1352 
ExtractToWriter(ZipArchiveHandle handle,const ZipEntry64 * entry,zip_archive::Writer * writer)1353 int32_t ExtractToWriter(ZipArchiveHandle handle, const ZipEntry64* entry,
1354                         zip_archive::Writer* writer) {
1355   const uint16_t method = entry->method;
1356 
1357   // this should default to kUnknownCompressionMethod.
1358   int32_t return_value = -1;
1359   uint64_t crc = 0;
1360   if (method == kCompressStored) {
1361     return_value =
1362         CopyEntryToWriter(handle->mapped_zip, entry, writer, kCrcChecksEnabled ? &crc : nullptr);
1363   } else if (method == kCompressDeflated) {
1364     return_value =
1365         InflateEntryToWriter(handle->mapped_zip, entry, writer, kCrcChecksEnabled ? &crc : nullptr);
1366   }
1367 
1368   if (!return_value && entry->has_data_descriptor) {
1369     return_value = ValidateDataDescriptor(handle->mapped_zip, entry);
1370     if (return_value) {
1371       return return_value;
1372     }
1373   }
1374 
1375   // Validate that the CRC matches the calculated value.
1376   if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
1377     ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
1378     return kInconsistentInformation;
1379   }
1380 
1381   return return_value;
1382 }
1383 
ExtractToMemory(ZipArchiveHandle archive,const ZipEntry * entry,uint8_t * begin,size_t size)1384 int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry* entry, uint8_t* begin,
1385                         size_t size) {
1386   ZipEntry64 entry64(*entry);
1387   return ExtractToMemory(archive, &entry64, begin, size);
1388 }
1389 
ExtractToMemory(ZipArchiveHandle archive,const ZipEntry64 * entry,uint8_t * begin,size_t size)1390 int32_t ExtractToMemory(ZipArchiveHandle archive, const ZipEntry64* entry, uint8_t* begin,
1391                         size_t size) {
1392   auto writer = MemoryWriter::Create(begin, size, entry);
1393   if (!writer) {
1394     return kIoError;
1395   }
1396 
1397   return ExtractToWriter(archive, entry, writer.get());
1398 }
1399 
ExtractEntryToFile(ZipArchiveHandle archive,const ZipEntry * entry,int fd)1400 int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry* entry, int fd) {
1401   ZipEntry64 entry64(*entry);
1402   return ExtractEntryToFile(archive, &entry64, fd);
1403 }
1404 
ExtractEntryToFile(ZipArchiveHandle archive,const ZipEntry64 * entry,int fd)1405 int32_t ExtractEntryToFile(ZipArchiveHandle archive, const ZipEntry64* entry, int fd) {
1406   auto writer = FileWriter::Create(fd, entry);
1407   if (!writer) {
1408     return kIoError;
1409   }
1410 
1411   return ExtractToWriter(archive, entry, writer.get());
1412 }
1413 
GetFileDescriptor(const ZipArchiveHandle archive)1414 int GetFileDescriptor(const ZipArchiveHandle archive) {
1415   return archive->mapped_zip.GetFileDescriptor();
1416 }
1417 
GetFileDescriptorOffset(const ZipArchiveHandle archive)1418 off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive) {
1419   return archive->mapped_zip.GetFileOffset();
1420 }
1421 
1422 #if !defined(_WIN32)
1423 class ProcessWriter : public zip_archive::Writer {
1424  public:
ProcessWriter(ProcessZipEntryFunction func,void * cookie)1425   ProcessWriter(ProcessZipEntryFunction func, void* cookie)
1426       : Writer(), proc_function_(func), cookie_(cookie) {}
1427 
Append(uint8_t * buf,size_t buf_size)1428   virtual bool Append(uint8_t* buf, size_t buf_size) override {
1429     return proc_function_(buf, buf_size, cookie_);
1430   }
1431 
1432  private:
1433   ProcessZipEntryFunction proc_function_;
1434   void* cookie_;
1435 };
1436 
ProcessZipEntryContents(ZipArchiveHandle archive,const ZipEntry * entry,ProcessZipEntryFunction func,void * cookie)1437 int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry* entry,
1438                                 ProcessZipEntryFunction func, void* cookie) {
1439   ZipEntry64 entry64(*entry);
1440   return ProcessZipEntryContents(archive, &entry64, func, cookie);
1441 }
1442 
ProcessZipEntryContents(ZipArchiveHandle archive,const ZipEntry64 * entry,ProcessZipEntryFunction func,void * cookie)1443 int32_t ProcessZipEntryContents(ZipArchiveHandle archive, const ZipEntry64* entry,
1444                                 ProcessZipEntryFunction func, void* cookie) {
1445   ProcessWriter writer(func, cookie);
1446   return ExtractToWriter(archive, entry, &writer);
1447 }
1448 
1449 #endif  //! defined(_WIN32)
1450 
GetFileDescriptor() const1451 int MappedZipFile::GetFileDescriptor() const {
1452   if (!has_fd_) {
1453     ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
1454     return -1;
1455   }
1456   return fd_;
1457 }
1458 
GetBasePtr() const1459 const void* MappedZipFile::GetBasePtr() const {
1460   if (has_fd_) {
1461     ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
1462     return nullptr;
1463   }
1464   return base_ptr_;
1465 }
1466 
GetFileOffset() const1467 off64_t MappedZipFile::GetFileOffset() const {
1468   return fd_offset_;
1469 }
1470 
GetFileLength() const1471 off64_t MappedZipFile::GetFileLength() const {
1472   if (has_fd_) {
1473     if (data_length_ != -1) {
1474       return data_length_;
1475     }
1476     data_length_ = lseek64(fd_, 0, SEEK_END);
1477     if (data_length_ == -1) {
1478       ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
1479     }
1480     return data_length_;
1481   } else {
1482     if (base_ptr_ == nullptr) {
1483       ALOGE("Zip: invalid file map");
1484       return -1;
1485     }
1486     return data_length_;
1487   }
1488 }
1489 
1490 // Attempts to read |len| bytes into |buf| at offset |off|.
ReadAtOffset(uint8_t * buf,size_t len,off64_t off) const1491 bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
1492   if (has_fd_) {
1493     if (off < 0) {
1494       ALOGE("Zip: invalid offset %" PRId64, off);
1495       return false;
1496     }
1497 
1498     off64_t read_offset;
1499     if (__builtin_add_overflow(fd_offset_, off, &read_offset)) {
1500       ALOGE("Zip: invalid read offset %" PRId64 " overflows, fd offset %" PRId64, off, fd_offset_);
1501       return false;
1502     }
1503 
1504     if (data_length_ != -1) {
1505       off64_t read_end;
1506       if (len > std::numeric_limits<off64_t>::max() ||
1507           __builtin_add_overflow(off, static_cast<off64_t>(len), &read_end)) {
1508         ALOGE("Zip: invalid read length %" PRId64 " overflows, offset %" PRId64,
1509               static_cast<off64_t>(len), off);
1510         return false;
1511       }
1512 
1513       if (read_end > data_length_) {
1514         ALOGE("Zip: invalid read length %" PRId64 " exceeds data length %" PRId64 ", offset %"
1515               PRId64, static_cast<off64_t>(len), data_length_, off);
1516         return false;
1517       }
1518     }
1519 
1520     if (!android::base::ReadFullyAtOffset(fd_, buf, len, read_offset)) {
1521       ALOGE("Zip: failed to read at offset %" PRId64, off);
1522       return false;
1523     }
1524   } else {
1525     if (off < 0 || data_length_ < len || off > data_length_ - len) {
1526       ALOGE("Zip: invalid offset: %" PRId64 ", read length: %zu, data length: %" PRId64, off, len,
1527             data_length_);
1528       return false;
1529     }
1530     memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
1531   }
1532   return true;
1533 }
1534 
Initialize(const void * map_base_ptr,off64_t cd_start_offset,size_t cd_size)1535 void CentralDirectory::Initialize(const void* map_base_ptr, off64_t cd_start_offset,
1536                                   size_t cd_size) {
1537   base_ptr_ = static_cast<const uint8_t*>(map_base_ptr) + cd_start_offset;
1538   length_ = cd_size;
1539 }
1540 
InitializeCentralDirectory(off64_t cd_start_offset,size_t cd_size)1541 bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
1542   if (mapped_zip.HasFd()) {
1543     directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
1544                                                       mapped_zip.GetFileOffset() + cd_start_offset,
1545                                                       cd_size, PROT_READ);
1546     if (!directory_map) {
1547       ALOGE("Zip: failed to map central directory (offset %" PRId64 ", size %zu): %s",
1548             cd_start_offset, cd_size, strerror(errno));
1549       return false;
1550     }
1551 
1552     CHECK_EQ(directory_map->size(), cd_size);
1553     central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
1554   } else {
1555     if (mapped_zip.GetBasePtr() == nullptr) {
1556       ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer");
1557       return false;
1558     }
1559     if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
1560         mapped_zip.GetFileLength()) {
1561       ALOGE(
1562           "Zip: Failed to map central directory, offset exceeds mapped memory region ("
1563           "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
1564           static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
1565       return false;
1566     }
1567 
1568     central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
1569   }
1570   return true;
1571 }
1572 
1573 // This function returns the embedded timestamp as is; and doesn't perform validations.
GetModificationTime() const1574 tm ZipEntryCommon::GetModificationTime() const {
1575   tm t = {};
1576 
1577   t.tm_hour = (mod_time >> 11) & 0x1f;
1578   t.tm_min = (mod_time >> 5) & 0x3f;
1579   t.tm_sec = (mod_time & 0x1f) << 1;
1580 
1581   t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
1582   t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
1583   t.tm_mday = (mod_time >> 16) & 0x1f;
1584 
1585   return t;
1586 }
1587