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 #include <assert.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <inttypes.h> 25 #include <limits.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include <memory> 31 #include <vector> 32 33 #include "base/file.h" 34 #include "base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd 35 #include "base/memory.h" 36 #include "log/log.h" 37 #include "utils/Compat.h" 38 #include "utils/FileMap.h" 39 #include "zlib.h" 40 41 #include "entry_name_utils-inl.h" 42 #include "ziparchive/zip_archive.h" 43 44 using android::base::get_unaligned; 45 46 // This is for windows. If we don't open a file in binary mode, weird 47 // things will happen. 48 #ifndef O_BINARY 49 #define O_BINARY 0 50 #endif 51 52 // The "end of central directory" (EOCD) record. Each archive 53 // contains exactly once such record which appears at the end of 54 // the archive. It contains archive wide information like the 55 // number of entries in the archive and the offset to the central 56 // directory of the offset. 57 struct EocdRecord { 58 static const uint32_t kSignature = 0x06054b50; 59 60 // End of central directory signature, should always be 61 // |kSignature|. 62 uint32_t eocd_signature; 63 // The number of the current "disk", i.e, the "disk" that this 64 // central directory is on. 65 // 66 // This implementation assumes that each archive spans a single 67 // disk only. i.e, that disk_num == 1. 68 uint16_t disk_num; 69 // The disk where the central directory starts. 70 // 71 // This implementation assumes that each archive spans a single 72 // disk only. i.e, that cd_start_disk == 1. 73 uint16_t cd_start_disk; 74 // The number of central directory records on this disk. 75 // 76 // This implementation assumes that each archive spans a single 77 // disk only. i.e, that num_records_on_disk == num_records. 78 uint16_t num_records_on_disk; 79 // The total number of central directory records. 80 uint16_t num_records; 81 // The size of the central directory (in bytes). 82 uint32_t cd_size; 83 // The offset of the start of the central directory, relative 84 // to the start of the file. 85 uint32_t cd_start_offset; 86 // Length of the central directory comment. 87 uint16_t comment_length; 88 private: 89 EocdRecord() = default; 90 DISALLOW_COPY_AND_ASSIGN(EocdRecord); 91 } __attribute__((packed)); 92 93 // A structure representing the fixed length fields for a single 94 // record in the central directory of the archive. In addition to 95 // the fixed length fields listed here, each central directory 96 // record contains a variable length "file_name" and "extra_field" 97 // whose lengths are given by |file_name_length| and |extra_field_length| 98 // respectively. 99 struct CentralDirectoryRecord { 100 static const uint32_t kSignature = 0x02014b50; 101 102 // The start of record signature. Must be |kSignature|. 103 uint32_t record_signature; 104 // Tool version. Ignored by this implementation. 105 uint16_t version_made_by; 106 // Tool version. Ignored by this implementation. 107 uint16_t version_needed; 108 // The "general purpose bit flags" for this entry. The only 109 // flag value that we currently check for is the "data descriptor" 110 // flag. 111 uint16_t gpb_flags; 112 // The compression method for this entry, one of |kCompressStored| 113 // and |kCompressDeflated|. 114 uint16_t compression_method; 115 // The file modification time and date for this entry. 116 uint16_t last_mod_time; 117 uint16_t last_mod_date; 118 // The CRC-32 checksum for this entry. 119 uint32_t crc32; 120 // The compressed size (in bytes) of this entry. 121 uint32_t compressed_size; 122 // The uncompressed size (in bytes) of this entry. 123 uint32_t uncompressed_size; 124 // The length of the entry file name in bytes. The file name 125 // will appear immediately after this record. 126 uint16_t file_name_length; 127 // The length of the extra field info (in bytes). This data 128 // will appear immediately after the entry file name. 129 uint16_t extra_field_length; 130 // The length of the entry comment (in bytes). This data will 131 // appear immediately after the extra field. 132 uint16_t comment_length; 133 // The start disk for this entry. Ignored by this implementation). 134 uint16_t file_start_disk; 135 // File attributes. Ignored by this implementation. 136 uint16_t internal_file_attributes; 137 // File attributes. Ignored by this implementation. 138 uint32_t external_file_attributes; 139 // The offset to the local file header for this entry, from the 140 // beginning of this archive. 141 uint32_t local_file_header_offset; 142 private: 143 CentralDirectoryRecord() = default; 144 DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord); 145 } __attribute__((packed)); 146 147 // The local file header for a given entry. This duplicates information 148 // present in the central directory of the archive. It is an error for 149 // the information here to be different from the central directory 150 // information for a given entry. 151 struct LocalFileHeader { 152 static const uint32_t kSignature = 0x04034b50; 153 154 // The local file header signature, must be |kSignature|. 155 uint32_t lfh_signature; 156 // Tool version. Ignored by this implementation. 157 uint16_t version_needed; 158 // The "general purpose bit flags" for this entry. The only 159 // flag value that we currently check for is the "data descriptor" 160 // flag. 161 uint16_t gpb_flags; 162 // The compression method for this entry, one of |kCompressStored| 163 // and |kCompressDeflated|. 164 uint16_t compression_method; 165 // The file modification time and date for this entry. 166 uint16_t last_mod_time; 167 uint16_t last_mod_date; 168 // The CRC-32 checksum for this entry. 169 uint32_t crc32; 170 // The compressed size (in bytes) of this entry. 171 uint32_t compressed_size; 172 // The uncompressed size (in bytes) of this entry. 173 uint32_t uncompressed_size; 174 // The length of the entry file name in bytes. The file name 175 // will appear immediately after this record. 176 uint16_t file_name_length; 177 // The length of the extra field info (in bytes). This data 178 // will appear immediately after the entry file name. 179 uint16_t extra_field_length; 180 private: 181 LocalFileHeader() = default; 182 DISALLOW_COPY_AND_ASSIGN(LocalFileHeader); 183 } __attribute__((packed)); 184 185 struct DataDescriptor { 186 // The *optional* data descriptor start signature. 187 static const uint32_t kOptSignature = 0x08074b50; 188 189 // CRC-32 checksum of the entry. 190 uint32_t crc32; 191 // Compressed size of the entry. 192 uint32_t compressed_size; 193 // Uncompressed size of the entry. 194 uint32_t uncompressed_size; 195 private: 196 DataDescriptor() = default; 197 DISALLOW_COPY_AND_ASSIGN(DataDescriptor); 198 } __attribute__((packed)); 199 200 201 static const uint32_t kGPBDDFlagMask = 0x0008; // mask value that signifies that the entry has a DD 202 203 // The maximum size of a central directory or a file 204 // comment in bytes. 205 static const uint32_t kMaxCommentLen = 65535; 206 207 // The maximum number of bytes to scan backwards for the EOCD start. 208 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord); 209 210 static const char* kErrorMessages[] = { 211 "Unknown return code.", 212 "Iteration ended", 213 "Zlib error", 214 "Invalid file", 215 "Invalid handle", 216 "Duplicate entries in archive", 217 "Empty archive", 218 "Entry not found", 219 "Invalid offset", 220 "Inconsistent information", 221 "Invalid entry name", 222 "I/O Error", 223 "File mapping failed" 224 }; 225 226 static const int32_t kErrorMessageUpperBound = 0; 227 228 static const int32_t kIterationEnd = -1; 229 230 // We encountered a Zlib error when inflating a stream from this file. 231 // Usually indicates file corruption. 232 static const int32_t kZlibError = -2; 233 234 // The input file cannot be processed as a zip archive. Usually because 235 // it's too small, too large or does not have a valid signature. 236 static const int32_t kInvalidFile = -3; 237 238 // An invalid iteration / ziparchive handle was passed in as an input 239 // argument. 240 static const int32_t kInvalidHandle = -4; 241 242 // The zip archive contained two (or possibly more) entries with the same 243 // name. 244 static const int32_t kDuplicateEntry = -5; 245 246 // The zip archive contains no entries. 247 static const int32_t kEmptyArchive = -6; 248 249 // The specified entry was not found in the archive. 250 static const int32_t kEntryNotFound = -7; 251 252 // The zip archive contained an invalid local file header pointer. 253 static const int32_t kInvalidOffset = -8; 254 255 // The zip archive contained inconsistent entry information. This could 256 // be because the central directory & local file header did not agree, or 257 // if the actual uncompressed length or crc32 do not match their declared 258 // values. 259 static const int32_t kInconsistentInformation = -9; 260 261 // An invalid entry name was encountered. 262 static const int32_t kInvalidEntryName = -10; 263 264 // An I/O related system call (read, lseek, ftruncate, map) failed. 265 static const int32_t kIoError = -11; 266 267 // We were not able to mmap the central directory or entry contents. 268 static const int32_t kMmapFailed = -12; 269 270 static const int32_t kErrorMessageLowerBound = -13; 271 272 /* 273 * A Read-only Zip archive. 274 * 275 * We want "open" and "find entry by name" to be fast operations, and 276 * we want to use as little memory as possible. We memory-map the zip 277 * central directory, and load a hash table with pointers to the filenames 278 * (which aren't null-terminated). The other fields are at a fixed offset 279 * from the filename, so we don't need to extract those (but we do need 280 * to byte-read and endian-swap them every time we want them). 281 * 282 * It's possible that somebody has handed us a massive (~1GB) zip archive, 283 * so we can't expect to mmap the entire file. 284 * 285 * To speed comparisons when doing a lookup by name, we could make the mapping 286 * "private" (copy-on-write) and null-terminate the filenames after verifying 287 * the record structure. However, this requires a private mapping of 288 * every page that the Central Directory touches. Easier to tuck a copy 289 * of the string length into the hash table entry. 290 */ 291 struct ZipArchive { 292 /* open Zip archive */ 293 const int fd; 294 const bool close_file; 295 296 /* mapped central directory area */ 297 off64_t directory_offset; 298 android::FileMap directory_map; 299 300 /* number of entries in the Zip archive */ 301 uint16_t num_entries; 302 303 /* 304 * We know how many entries are in the Zip archive, so we can have a 305 * fixed-size hash table. We define a load factor of 0.75 and overallocat 306 * so the maximum number entries can never be higher than 307 * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t. 308 */ 309 uint32_t hash_table_size; 310 ZipEntryName* hash_table; 311 312 ZipArchive(const int fd, bool assume_ownership) : 313 fd(fd), 314 close_file(assume_ownership), 315 directory_offset(0), 316 num_entries(0), 317 hash_table_size(0), 318 hash_table(NULL) {} 319 320 ~ZipArchive() { 321 if (close_file && fd >= 0) { 322 close(fd); 323 } 324 325 free(hash_table); 326 } 327 }; 328 329 /* 330 * Round up to the next highest power of 2. 331 * 332 * Found on http://graphics.stanford.edu/~seander/bithacks.html. 333 */ 334 static uint32_t RoundUpPower2(uint32_t val) { 335 val--; 336 val |= val >> 1; 337 val |= val >> 2; 338 val |= val >> 4; 339 val |= val >> 8; 340 val |= val >> 16; 341 val++; 342 343 return val; 344 } 345 346 static uint32_t ComputeHash(const ZipEntryName& name) { 347 uint32_t hash = 0; 348 uint16_t len = name.name_length; 349 const uint8_t* str = name.name; 350 351 while (len--) { 352 hash = hash * 31 + *str++; 353 } 354 355 return hash; 356 } 357 358 /* 359 * Convert a ZipEntry to a hash table index, verifying that it's in a 360 * valid range. 361 */ 362 static int64_t EntryToIndex(const ZipEntryName* hash_table, 363 const uint32_t hash_table_size, 364 const ZipEntryName& name) { 365 const uint32_t hash = ComputeHash(name); 366 367 // NOTE: (hash_table_size - 1) is guaranteed to be non-negative. 368 uint32_t ent = hash & (hash_table_size - 1); 369 while (hash_table[ent].name != NULL) { 370 if (hash_table[ent].name_length == name.name_length && 371 memcmp(hash_table[ent].name, name.name, name.name_length) == 0) { 372 return ent; 373 } 374 375 ent = (ent + 1) & (hash_table_size - 1); 376 } 377 378 ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name); 379 return kEntryNotFound; 380 } 381 382 /* 383 * Add a new entry to the hash table. 384 */ 385 static int32_t AddToHash(ZipEntryName *hash_table, const uint64_t hash_table_size, 386 const ZipEntryName& name) { 387 const uint64_t hash = ComputeHash(name); 388 uint32_t ent = hash & (hash_table_size - 1); 389 390 /* 391 * We over-allocated the table, so we're guaranteed to find an empty slot. 392 * Further, we guarantee that the hashtable size is not 0. 393 */ 394 while (hash_table[ent].name != NULL) { 395 if (hash_table[ent].name_length == name.name_length && 396 memcmp(hash_table[ent].name, name.name, name.name_length) == 0) { 397 // We've found a duplicate entry. We don't accept it 398 ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name); 399 return kDuplicateEntry; 400 } 401 ent = (ent + 1) & (hash_table_size - 1); 402 } 403 404 hash_table[ent].name = name.name; 405 hash_table[ent].name_length = name.name_length; 406 return 0; 407 } 408 409 static int32_t MapCentralDirectory0(int fd, const char* debug_file_name, 410 ZipArchive* archive, off64_t file_length, 411 off64_t read_amount, uint8_t* scan_buffer) { 412 const off64_t search_start = file_length - read_amount; 413 414 if (lseek64(fd, search_start, SEEK_SET) != search_start) { 415 ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start), 416 strerror(errno)); 417 return kIoError; 418 } 419 ssize_t actual = TEMP_FAILURE_RETRY( 420 read(fd, scan_buffer, static_cast<size_t>(read_amount))); 421 if (actual != static_cast<ssize_t>(read_amount)) { 422 ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount), 423 strerror(errno)); 424 return kIoError; 425 } 426 427 /* 428 * Scan backward for the EOCD magic. In an archive without a trailing 429 * comment, we'll find it on the first try. (We may want to consider 430 * doing an initial minimal read; if we don't find it, retry with a 431 * second read as above.) 432 */ 433 int i = read_amount - sizeof(EocdRecord); 434 for (; i >= 0; i--) { 435 if (scan_buffer[i] == 0x50) { 436 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]); 437 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) { 438 ALOGV("+++ Found EOCD at buf+%d", i); 439 break; 440 } 441 } 442 } 443 if (i < 0) { 444 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name); 445 return kInvalidFile; 446 } 447 448 const off64_t eocd_offset = search_start + i; 449 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i); 450 /* 451 * Verify that there's no trailing space at the end of the central directory 452 * and its comment. 453 */ 454 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) 455 + eocd->comment_length; 456 if (calculated_length != file_length) { 457 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory", 458 static_cast<int64_t>(file_length - calculated_length)); 459 return kInvalidFile; 460 } 461 462 /* 463 * Grab the CD offset and size, and the number of entries in the 464 * archive and verify that they look reasonable. 465 */ 466 if (eocd->cd_start_offset + eocd->cd_size > eocd_offset) { 467 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")", 468 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset)); 469 return kInvalidOffset; 470 } 471 if (eocd->num_records == 0) { 472 ALOGW("Zip: empty archive?"); 473 return kEmptyArchive; 474 } 475 476 ALOGV("+++ num_entries=%" PRIu32 "dir_size=%" PRIu32 " dir_offset=%" PRIu32, 477 eocd->num_records, eocd->cd_size, eocd->cd_start_offset); 478 479 /* 480 * It all looks good. Create a mapping for the CD, and set the fields 481 * in archive. 482 */ 483 if (!archive->directory_map.create(debug_file_name, fd, 484 static_cast<off64_t>(eocd->cd_start_offset), 485 static_cast<size_t>(eocd->cd_size), true /* read only */) ) { 486 return kMmapFailed; 487 } 488 489 archive->num_entries = eocd->num_records; 490 archive->directory_offset = eocd->cd_start_offset; 491 492 return 0; 493 } 494 495 /* 496 * Find the zip Central Directory and memory-map it. 497 * 498 * On success, returns 0 after populating fields from the EOCD area: 499 * directory_offset 500 * directory_map 501 * num_entries 502 */ 503 static int32_t MapCentralDirectory(int fd, const char* debug_file_name, 504 ZipArchive* archive) { 505 506 // Test file length. We use lseek64 to make sure the file 507 // is small enough to be a zip file (Its size must be less than 508 // 0xffffffff bytes). 509 off64_t file_length = lseek64(fd, 0, SEEK_END); 510 if (file_length == -1) { 511 ALOGV("Zip: lseek on fd %d failed", fd); 512 return kInvalidFile; 513 } 514 515 if (file_length > static_cast<off64_t>(0xffffffff)) { 516 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length)); 517 return kInvalidFile; 518 } 519 520 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) { 521 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length)); 522 return kInvalidFile; 523 } 524 525 /* 526 * Perform the traditional EOCD snipe hunt. 527 * 528 * We're searching for the End of Central Directory magic number, 529 * which appears at the start of the EOCD block. It's followed by 530 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We 531 * need to read the last part of the file into a buffer, dig through 532 * it to find the magic number, parse some values out, and use those 533 * to determine the extent of the CD. 534 * 535 * We start by pulling in the last part of the file. 536 */ 537 off64_t read_amount = kMaxEOCDSearch; 538 if (file_length < read_amount) { 539 read_amount = file_length; 540 } 541 542 uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount)); 543 int32_t result = MapCentralDirectory0(fd, debug_file_name, archive, 544 file_length, read_amount, scan_buffer); 545 546 free(scan_buffer); 547 return result; 548 } 549 550 /* 551 * Parses the Zip archive's Central Directory. Allocates and populates the 552 * hash table. 553 * 554 * Returns 0 on success. 555 */ 556 static int32_t ParseZipArchive(ZipArchive* archive) { 557 const uint8_t* const cd_ptr = 558 reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr()); 559 const size_t cd_length = archive->directory_map.getDataLength(); 560 const uint16_t num_entries = archive->num_entries; 561 562 /* 563 * Create hash table. We have a minimum 75% load factor, possibly as 564 * low as 50% after we round off to a power of 2. There must be at 565 * least one unused entry to avoid an infinite loop during creation. 566 */ 567 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3); 568 archive->hash_table = reinterpret_cast<ZipEntryName*>(calloc(archive->hash_table_size, 569 sizeof(ZipEntryName))); 570 571 /* 572 * Walk through the central directory, adding entries to the hash 573 * table and verifying values. 574 */ 575 const uint8_t* const cd_end = cd_ptr + cd_length; 576 const uint8_t* ptr = cd_ptr; 577 for (uint16_t i = 0; i < num_entries; i++) { 578 const CentralDirectoryRecord* cdr = 579 reinterpret_cast<const CentralDirectoryRecord*>(ptr); 580 if (cdr->record_signature != CentralDirectoryRecord::kSignature) { 581 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i); 582 return -1; 583 } 584 585 if (ptr + sizeof(CentralDirectoryRecord) > cd_end) { 586 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i); 587 return -1; 588 } 589 590 const off64_t local_header_offset = cdr->local_file_header_offset; 591 if (local_header_offset >= archive->directory_offset) { 592 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16, 593 static_cast<int64_t>(local_header_offset), i); 594 return -1; 595 } 596 597 const uint16_t file_name_length = cdr->file_name_length; 598 const uint16_t extra_length = cdr->extra_field_length; 599 const uint16_t comment_length = cdr->comment_length; 600 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord); 601 602 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */ 603 if (!IsValidEntryName(file_name, file_name_length)) { 604 return -1; 605 } 606 607 /* add the CDE filename to the hash table */ 608 ZipEntryName entry_name; 609 entry_name.name = file_name; 610 entry_name.name_length = file_name_length; 611 const int add_result = AddToHash(archive->hash_table, 612 archive->hash_table_size, entry_name); 613 if (add_result != 0) { 614 ALOGW("Zip: Error adding entry to hash table %d", add_result); 615 return add_result; 616 } 617 618 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length; 619 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) { 620 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, 621 ptr - cd_ptr, cd_length, i); 622 return -1; 623 } 624 } 625 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries); 626 627 return 0; 628 } 629 630 static int32_t OpenArchiveInternal(ZipArchive* archive, 631 const char* debug_file_name) { 632 int32_t result = -1; 633 if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) { 634 return result; 635 } 636 637 if ((result = ParseZipArchive(archive))) { 638 return result; 639 } 640 641 return 0; 642 } 643 644 int32_t OpenArchiveFd(int fd, const char* debug_file_name, 645 ZipArchiveHandle* handle, bool assume_ownership) { 646 ZipArchive* archive = new ZipArchive(fd, assume_ownership); 647 *handle = archive; 648 return OpenArchiveInternal(archive, debug_file_name); 649 } 650 651 int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) { 652 const int fd = open(fileName, O_RDONLY | O_BINARY, 0); 653 ZipArchive* archive = new ZipArchive(fd, true); 654 *handle = archive; 655 656 if (fd < 0) { 657 ALOGW("Unable to open '%s': %s", fileName, strerror(errno)); 658 return kIoError; 659 } 660 661 return OpenArchiveInternal(archive, fileName); 662 } 663 664 /* 665 * Close a ZipArchive, closing the file and freeing the contents. 666 */ 667 void CloseArchive(ZipArchiveHandle handle) { 668 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); 669 ALOGV("Closing archive %p", archive); 670 delete archive; 671 } 672 673 static int32_t UpdateEntryFromDataDescriptor(int fd, 674 ZipEntry *entry) { 675 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)]; 676 ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf))); 677 if (actual != sizeof(ddBuf)) { 678 return kIoError; 679 } 680 681 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf)); 682 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0; 683 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset); 684 685 entry->crc32 = descriptor->crc32; 686 entry->compressed_length = descriptor->compressed_size; 687 entry->uncompressed_length = descriptor->uncompressed_size; 688 689 return 0; 690 } 691 692 // Attempts to read |len| bytes into |buf| at offset |off|. 693 // 694 // This method uses pread64 on platforms that support it and 695 // lseek64 + read on platforms that don't. This implies that 696 // callers should not rely on the |fd| offset being incremented 697 // as a side effect of this call. 698 static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len, 699 off64_t off) { 700 #if !defined(_WIN32) 701 return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off)); 702 #else 703 // The only supported platform that doesn't support pread at the moment 704 // is Windows. Only recent versions of windows support unix like forks, 705 // and even there the semantics are quite different. 706 if (lseek64(fd, off, SEEK_SET) != off) { 707 ALOGW("Zip: failed seek to offset %" PRId64, off); 708 return kIoError; 709 } 710 711 return TEMP_FAILURE_RETRY(read(fd, buf, len)); 712 #endif 713 } 714 715 static int32_t FindEntry(const ZipArchive* archive, const int ent, 716 ZipEntry* data) { 717 const uint16_t nameLen = archive->hash_table[ent].name_length; 718 719 // Recover the start of the central directory entry from the filename 720 // pointer. The filename is the first entry past the fixed-size data, 721 // so we can just subtract back from that. 722 const uint8_t* ptr = archive->hash_table[ent].name; 723 ptr -= sizeof(CentralDirectoryRecord); 724 725 // This is the base of our mmapped region, we have to sanity check that 726 // the name that's in the hash table is a pointer to a location within 727 // this mapped region. 728 const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>( 729 archive->directory_map.getDataPtr()); 730 if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) { 731 ALOGW("Zip: Invalid entry pointer"); 732 return kInvalidOffset; 733 } 734 735 const CentralDirectoryRecord *cdr = 736 reinterpret_cast<const CentralDirectoryRecord*>(ptr); 737 738 // The offset of the start of the central directory in the zipfile. 739 // We keep this lying around so that we can sanity check all our lengths 740 // and our per-file structures. 741 const off64_t cd_offset = archive->directory_offset; 742 743 // Fill out the compression method, modification time, crc32 744 // and other interesting attributes from the central directory. These 745 // will later be compared against values from the local file header. 746 data->method = cdr->compression_method; 747 data->mod_time = cdr->last_mod_time; 748 data->crc32 = cdr->crc32; 749 data->compressed_length = cdr->compressed_size; 750 data->uncompressed_length = cdr->uncompressed_size; 751 752 // Figure out the local header offset from the central directory. The 753 // actual file data will begin after the local header and the name / 754 // extra comments. 755 const off64_t local_header_offset = cdr->local_file_header_offset; 756 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) { 757 ALOGW("Zip: bad local hdr offset in zip"); 758 return kInvalidOffset; 759 } 760 761 uint8_t lfh_buf[sizeof(LocalFileHeader)]; 762 ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), 763 local_header_offset); 764 if (actual != sizeof(lfh_buf)) { 765 ALOGW("Zip: failed reading lfh name from offset %" PRId64, 766 static_cast<int64_t>(local_header_offset)); 767 return kIoError; 768 } 769 770 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf); 771 772 if (lfh->lfh_signature != LocalFileHeader::kSignature) { 773 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64, 774 static_cast<int64_t>(local_header_offset)); 775 return kInvalidOffset; 776 } 777 778 // Paranoia: Match the values specified in the local file header 779 // to those specified in the central directory. 780 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) { 781 data->has_data_descriptor = 0; 782 if (data->compressed_length != lfh->compressed_size 783 || data->uncompressed_length != lfh->uncompressed_size 784 || data->crc32 != lfh->crc32) { 785 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 786 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}", 787 data->compressed_length, data->uncompressed_length, data->crc32, 788 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32); 789 return kInconsistentInformation; 790 } 791 } else { 792 data->has_data_descriptor = 1; 793 } 794 795 // Check that the local file header name matches the declared 796 // name in the central directory. 797 if (lfh->file_name_length == nameLen) { 798 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader); 799 if (name_offset + lfh->file_name_length > cd_offset) { 800 ALOGW("Zip: Invalid declared length"); 801 return kInvalidOffset; 802 } 803 804 uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen)); 805 ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen, 806 name_offset); 807 808 if (actual != nameLen) { 809 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset)); 810 free(name_buf); 811 return kIoError; 812 } 813 814 if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) { 815 free(name_buf); 816 return kInconsistentInformation; 817 } 818 819 free(name_buf); 820 } else { 821 ALOGW("Zip: lfh name did not match central directory."); 822 return kInconsistentInformation; 823 } 824 825 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) 826 + lfh->file_name_length + lfh->extra_field_length; 827 if (data_offset > cd_offset) { 828 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset)); 829 return kInvalidOffset; 830 } 831 832 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) { 833 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", 834 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset)); 835 return kInvalidOffset; 836 } 837 838 if (data->method == kCompressStored && 839 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) { 840 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")", 841 static_cast<int64_t>(data_offset), data->uncompressed_length, 842 static_cast<int64_t>(cd_offset)); 843 return kInvalidOffset; 844 } 845 846 data->offset = data_offset; 847 return 0; 848 } 849 850 struct IterationHandle { 851 uint32_t position; 852 // We're not using vector here because this code is used in the Windows SDK 853 // where the STL is not available. 854 const uint8_t* prefix; 855 const uint16_t prefix_len; 856 const uint8_t* suffix; 857 const uint16_t suffix_len; 858 ZipArchive* archive; 859 860 IterationHandle(const ZipEntryName* prefix_name, 861 const ZipEntryName* suffix_name) 862 : prefix(NULL), 863 prefix_len(prefix_name ? prefix_name->name_length : 0), 864 suffix(NULL), 865 suffix_len(suffix_name ? suffix_name->name_length : 0) { 866 if (prefix_name) { 867 uint8_t* prefix_copy = new uint8_t[prefix_len]; 868 memcpy(prefix_copy, prefix_name->name, prefix_len); 869 prefix = prefix_copy; 870 } 871 if (suffix_name) { 872 uint8_t* suffix_copy = new uint8_t[suffix_len]; 873 memcpy(suffix_copy, suffix_name->name, suffix_len); 874 suffix = suffix_copy; 875 } 876 } 877 878 ~IterationHandle() { 879 delete[] prefix; 880 delete[] suffix; 881 } 882 }; 883 884 int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, 885 const ZipEntryName* optional_prefix, 886 const ZipEntryName* optional_suffix) { 887 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); 888 889 if (archive == NULL || archive->hash_table == NULL) { 890 ALOGW("Zip: Invalid ZipArchiveHandle"); 891 return kInvalidHandle; 892 } 893 894 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix); 895 cookie->position = 0; 896 cookie->archive = archive; 897 898 *cookie_ptr = cookie ; 899 return 0; 900 } 901 902 void EndIteration(void* cookie) { 903 delete reinterpret_cast<IterationHandle*>(cookie); 904 } 905 906 int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName, 907 ZipEntry* data) { 908 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); 909 if (entryName.name_length == 0) { 910 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name); 911 return kInvalidEntryName; 912 } 913 914 const int64_t ent = EntryToIndex(archive->hash_table, 915 archive->hash_table_size, entryName); 916 917 if (ent < 0) { 918 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name); 919 return ent; 920 } 921 922 return FindEntry(archive, ent, data); 923 } 924 925 int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) { 926 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie); 927 if (handle == NULL) { 928 return kInvalidHandle; 929 } 930 931 ZipArchive* archive = handle->archive; 932 if (archive == NULL || archive->hash_table == NULL) { 933 ALOGW("Zip: Invalid ZipArchiveHandle"); 934 return kInvalidHandle; 935 } 936 937 const uint32_t currentOffset = handle->position; 938 const uint32_t hash_table_length = archive->hash_table_size; 939 const ZipEntryName *hash_table = archive->hash_table; 940 941 for (uint32_t i = currentOffset; i < hash_table_length; ++i) { 942 if (hash_table[i].name != NULL && 943 (handle->prefix_len == 0 || 944 (hash_table[i].name_length >= handle->prefix_len && 945 memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0)) && 946 (handle->suffix_len == 0 || 947 (hash_table[i].name_length >= handle->suffix_len && 948 memcmp(handle->suffix, 949 hash_table[i].name + hash_table[i].name_length - handle->suffix_len, 950 handle->suffix_len) == 0))) { 951 handle->position = (i + 1); 952 const int error = FindEntry(archive, i, data); 953 if (!error) { 954 name->name = hash_table[i].name; 955 name->name_length = hash_table[i].name_length; 956 } 957 958 return error; 959 } 960 } 961 962 handle->position = 0; 963 return kIterationEnd; 964 } 965 966 class Writer { 967 public: 968 virtual bool Append(uint8_t* buf, size_t buf_size) = 0; 969 virtual ~Writer() {} 970 protected: 971 Writer() = default; 972 private: 973 DISALLOW_COPY_AND_ASSIGN(Writer); 974 }; 975 976 // A Writer that writes data to a fixed size memory region. 977 // The size of the memory region must be equal to the total size of 978 // the data appended to it. 979 class MemoryWriter : public Writer { 980 public: 981 MemoryWriter(uint8_t* buf, size_t size) : Writer(), 982 buf_(buf), size_(size), bytes_written_(0) { 983 } 984 985 virtual bool Append(uint8_t* buf, size_t buf_size) override { 986 if (bytes_written_ + buf_size > size_) { 987 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", 988 size_, bytes_written_ + buf_size); 989 return false; 990 } 991 992 memcpy(buf_ + bytes_written_, buf, buf_size); 993 bytes_written_ += buf_size; 994 return true; 995 } 996 997 private: 998 uint8_t* const buf_; 999 const size_t size_; 1000 size_t bytes_written_; 1001 }; 1002 1003 // A Writer that appends data to a file |fd| at its current position. 1004 // The file will be truncated to the end of the written data. 1005 class FileWriter : public Writer { 1006 public: 1007 1008 // Creates a FileWriter for |fd| and prepare to write |entry| to it, 1009 // guaranteeing that the file descriptor is valid and that there's enough 1010 // space on the volume to write out the entry completely and that the file 1011 // is truncated to the correct length. 1012 // 1013 // Returns a valid FileWriter on success, |nullptr| if an error occurred. 1014 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) { 1015 const uint32_t declared_length = entry->uncompressed_length; 1016 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR); 1017 if (current_offset == -1) { 1018 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno)); 1019 return nullptr; 1020 } 1021 1022 int result = 0; 1023 #if defined(__linux__) 1024 if (declared_length > 0) { 1025 // Make sure we have enough space on the volume to extract the compressed 1026 // entry. Note that the call to ftruncate below will change the file size but 1027 // will not allocate space on disk and this call to fallocate will not 1028 // change the file size. 1029 // Note: fallocate is only supported by the following filesystems - 1030 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with 1031 // EOPNOTSUPP error when issued in other filesystems. 1032 // Hence, check for the return error code before concluding that the 1033 // disk does not have enough space. 1034 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length)); 1035 if (result == -1 && errno == ENOSPC) { 1036 ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s", 1037 static_cast<int64_t>(declared_length + current_offset), strerror(errno)); 1038 return std::unique_ptr<FileWriter>(nullptr); 1039 } 1040 } 1041 #endif // __linux__ 1042 1043 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset)); 1044 if (result == -1) { 1045 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s", 1046 static_cast<int64_t>(declared_length + current_offset), strerror(errno)); 1047 return std::unique_ptr<FileWriter>(nullptr); 1048 } 1049 1050 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length)); 1051 } 1052 1053 virtual bool Append(uint8_t* buf, size_t buf_size) override { 1054 if (total_bytes_written_ + buf_size > declared_length_) { 1055 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", 1056 declared_length_, total_bytes_written_ + buf_size); 1057 return false; 1058 } 1059 1060 const bool result = android::base::WriteFully(fd_, buf, buf_size); 1061 if (result) { 1062 total_bytes_written_ += buf_size; 1063 } else { 1064 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno)); 1065 } 1066 1067 return result; 1068 } 1069 private: 1070 FileWriter(const int fd, const size_t declared_length) : 1071 Writer(), 1072 fd_(fd), 1073 declared_length_(declared_length), 1074 total_bytes_written_(0) { 1075 } 1076 1077 const int fd_; 1078 const size_t declared_length_; 1079 size_t total_bytes_written_; 1080 }; 1081 1082 // This method is using libz macros with old-style-casts 1083 #pragma GCC diagnostic push 1084 #pragma GCC diagnostic ignored "-Wold-style-cast" 1085 static inline int zlib_inflateInit2(z_stream* stream, int window_bits) { 1086 return inflateInit2(stream, window_bits); 1087 } 1088 #pragma GCC diagnostic pop 1089 1090 static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry, 1091 Writer* writer, uint64_t* crc_out) { 1092 const size_t kBufSize = 32768; 1093 std::vector<uint8_t> read_buf(kBufSize); 1094 std::vector<uint8_t> write_buf(kBufSize); 1095 z_stream zstream; 1096 int zerr; 1097 1098 /* 1099 * Initialize the zlib stream struct. 1100 */ 1101 memset(&zstream, 0, sizeof(zstream)); 1102 zstream.zalloc = Z_NULL; 1103 zstream.zfree = Z_NULL; 1104 zstream.opaque = Z_NULL; 1105 zstream.next_in = NULL; 1106 zstream.avail_in = 0; 1107 zstream.next_out = &write_buf[0]; 1108 zstream.avail_out = kBufSize; 1109 zstream.data_type = Z_UNKNOWN; 1110 1111 /* 1112 * Use the undocumented "negative window bits" feature to tell zlib 1113 * that there's no zlib header waiting for it. 1114 */ 1115 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS); 1116 if (zerr != Z_OK) { 1117 if (zerr == Z_VERSION_ERROR) { 1118 ALOGE("Installed zlib is not compatible with linked version (%s)", 1119 ZLIB_VERSION); 1120 } else { 1121 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr); 1122 } 1123 1124 return kZlibError; 1125 } 1126 1127 auto zstream_deleter = [](z_stream* stream) { 1128 inflateEnd(stream); /* free up any allocated structures */ 1129 }; 1130 1131 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter); 1132 1133 const uint32_t uncompressed_length = entry->uncompressed_length; 1134 1135 uint32_t compressed_length = entry->compressed_length; 1136 do { 1137 /* read as much as we can */ 1138 if (zstream.avail_in == 0) { 1139 const ZD_TYPE getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length; 1140 const ZD_TYPE actual = TEMP_FAILURE_RETRY(read(fd, &read_buf[0], getSize)); 1141 if (actual != getSize) { 1142 ALOGW("Zip: inflate read failed (" ZD " vs " ZD ")", actual, getSize); 1143 return kIoError; 1144 } 1145 1146 compressed_length -= getSize; 1147 1148 zstream.next_in = &read_buf[0]; 1149 zstream.avail_in = getSize; 1150 } 1151 1152 /* uncompress the data */ 1153 zerr = inflate(&zstream, Z_NO_FLUSH); 1154 if (zerr != Z_OK && zerr != Z_STREAM_END) { 1155 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", 1156 zerr, zstream.next_in, zstream.avail_in, 1157 zstream.next_out, zstream.avail_out); 1158 return kZlibError; 1159 } 1160 1161 /* write when we're full or when we're done */ 1162 if (zstream.avail_out == 0 || 1163 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) { 1164 const size_t write_size = zstream.next_out - &write_buf[0]; 1165 if (!writer->Append(&write_buf[0], write_size)) { 1166 // The file might have declared a bogus length. 1167 return kInconsistentInformation; 1168 } 1169 1170 zstream.next_out = &write_buf[0]; 1171 zstream.avail_out = kBufSize; 1172 } 1173 } while (zerr == Z_OK); 1174 1175 assert(zerr == Z_STREAM_END); /* other errors should've been caught */ 1176 1177 // stream.adler holds the crc32 value for such streams. 1178 *crc_out = zstream.adler; 1179 1180 if (zstream.total_out != uncompressed_length || compressed_length != 0) { 1181 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", 1182 zstream.total_out, uncompressed_length); 1183 return kInconsistentInformation; 1184 } 1185 1186 return 0; 1187 } 1188 1189 static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer, 1190 uint64_t *crc_out) { 1191 static const uint32_t kBufSize = 32768; 1192 std::vector<uint8_t> buf(kBufSize); 1193 1194 const uint32_t length = entry->uncompressed_length; 1195 uint32_t count = 0; 1196 uint64_t crc = 0; 1197 while (count < length) { 1198 uint32_t remaining = length - count; 1199 1200 // Safe conversion because kBufSize is narrow enough for a 32 bit signed 1201 // value. 1202 const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining; 1203 const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size)); 1204 1205 if (actual != block_size) { 1206 ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size); 1207 return kIoError; 1208 } 1209 1210 if (!writer->Append(&buf[0], block_size)) { 1211 return kIoError; 1212 } 1213 crc = crc32(crc, &buf[0], block_size); 1214 count += block_size; 1215 } 1216 1217 *crc_out = crc; 1218 1219 return 0; 1220 } 1221 1222 int32_t ExtractToWriter(ZipArchiveHandle handle, 1223 ZipEntry* entry, Writer* writer) { 1224 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle); 1225 const uint16_t method = entry->method; 1226 off64_t data_offset = entry->offset; 1227 1228 if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) { 1229 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset)); 1230 return kIoError; 1231 } 1232 1233 // this should default to kUnknownCompressionMethod. 1234 int32_t return_value = -1; 1235 uint64_t crc = 0; 1236 if (method == kCompressStored) { 1237 return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc); 1238 } else if (method == kCompressDeflated) { 1239 return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc); 1240 } 1241 1242 if (!return_value && entry->has_data_descriptor) { 1243 return_value = UpdateEntryFromDataDescriptor(archive->fd, entry); 1244 if (return_value) { 1245 return return_value; 1246 } 1247 } 1248 1249 // TODO: Fix this check by passing the right flags to inflate2 so that 1250 // it calculates the CRC for us. 1251 if (entry->crc32 != crc && false) { 1252 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc); 1253 return kInconsistentInformation; 1254 } 1255 1256 return return_value; 1257 } 1258 1259 int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, 1260 uint8_t* begin, uint32_t size) { 1261 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size)); 1262 return ExtractToWriter(handle, entry, writer.get()); 1263 } 1264 1265 int32_t ExtractEntryToFile(ZipArchiveHandle handle, 1266 ZipEntry* entry, int fd) { 1267 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry)); 1268 if (writer.get() == nullptr) { 1269 return kIoError; 1270 } 1271 1272 return ExtractToWriter(handle, entry, writer.get()); 1273 } 1274 1275 const char* ErrorCodeString(int32_t error_code) { 1276 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) { 1277 return kErrorMessages[error_code * -1]; 1278 } 1279 1280 return kErrorMessages[0]; 1281 } 1282 1283 int GetFileDescriptor(const ZipArchiveHandle handle) { 1284 return reinterpret_cast<ZipArchive*>(handle)->fd; 1285 } 1286