Home | History | Annotate | Download | only in libziparchive
      1 /*
      2  * Copyright (C) 2015 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 // Read-only stream access to Zip Archive entries.
     18 #include <errno.h>
     19 #include <inttypes.h>
     20 #include <string.h>
     21 #include <sys/types.h>
     22 #include <unistd.h>
     23 
     24 #include <memory>
     25 #include <vector>
     26 
     27 #define LOG_TAG "ZIPARCHIVE"
     28 #include <android-base/file.h>
     29 #include <log/log.h>
     30 #include <ziparchive/zip_archive.h>
     31 #include <ziparchive/zip_archive_stream_entry.h>
     32 #include <zlib.h>
     33 
     34 #include "zip_archive_private.h"
     35 
     36 static constexpr size_t kBufSize = 65535;
     37 
     38 bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
     39   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
     40   off64_t data_offset = entry.offset;
     41   if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
     42     ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
     43     return false;
     44   }
     45   crc32_ = entry.crc32;
     46   return true;
     47 }
     48 
     49 class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
     50  public:
     51   ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle) : ZipArchiveStreamEntry(handle) {}
     52   virtual ~ZipArchiveStreamEntryUncompressed() {}
     53 
     54   const std::vector<uint8_t>* Read() override;
     55 
     56   bool Verify() override;
     57 
     58  protected:
     59   bool Init(const ZipEntry& entry) override;
     60 
     61   uint32_t length_;
     62 
     63  private:
     64   std::vector<uint8_t> data_;
     65   uint32_t computed_crc32_;
     66 };
     67 
     68 bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
     69   if (!ZipArchiveStreamEntry::Init(entry)) {
     70     return false;
     71   }
     72 
     73   length_ = entry.uncompressed_length;
     74 
     75   data_.resize(kBufSize);
     76   computed_crc32_ = 0;
     77 
     78   return true;
     79 }
     80 
     81 const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
     82   if (length_ == 0) {
     83     return nullptr;
     84   }
     85 
     86   size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
     87   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
     88   errno = 0;
     89   if (!android::base::ReadFully(archive->fd, data_.data(), bytes)) {
     90     if (errno != 0) {
     91       ALOGE("Error reading from archive fd: %s", strerror(errno));
     92     } else {
     93       ALOGE("Short read of zip file, possibly corrupted zip?");
     94     }
     95     length_ = 0;
     96     return nullptr;
     97   }
     98 
     99   if (bytes < data_.size()) {
    100     data_.resize(bytes);
    101   }
    102   computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
    103   length_ -= bytes;
    104   return &data_;
    105 }
    106 
    107 bool ZipArchiveStreamEntryUncompressed::Verify() {
    108   return length_ == 0 && crc32_ == computed_crc32_;
    109 }
    110 
    111 class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
    112  public:
    113   ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle) : ZipArchiveStreamEntry(handle) {}
    114   virtual ~ZipArchiveStreamEntryCompressed();
    115 
    116   const std::vector<uint8_t>* Read() override;
    117 
    118   bool Verify() override;
    119 
    120  protected:
    121   bool Init(const ZipEntry& entry) override;
    122 
    123  private:
    124   bool z_stream_init_ = false;
    125   z_stream z_stream_;
    126   std::vector<uint8_t> in_;
    127   std::vector<uint8_t> out_;
    128   uint32_t uncompressed_length_;
    129   uint32_t compressed_length_;
    130   uint32_t computed_crc32_;
    131 };
    132 
    133 // This method is using libz macros with old-style-casts
    134 #pragma GCC diagnostic push
    135 #pragma GCC diagnostic ignored "-Wold-style-cast"
    136 static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
    137   return inflateInit2(stream, window_bits);
    138 }
    139 #pragma GCC diagnostic pop
    140 
    141 bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
    142   if (!ZipArchiveStreamEntry::Init(entry)) {
    143     return false;
    144   }
    145 
    146   // Initialize the zlib stream struct.
    147   memset(&z_stream_, 0, sizeof(z_stream_));
    148   z_stream_.zalloc = Z_NULL;
    149   z_stream_.zfree = Z_NULL;
    150   z_stream_.opaque = Z_NULL;
    151   z_stream_.next_in = nullptr;
    152   z_stream_.avail_in = 0;
    153   z_stream_.avail_out = 0;
    154   z_stream_.data_type = Z_UNKNOWN;
    155 
    156   // Use the undocumented "negative window bits" feature to tell zlib
    157   // that there's no zlib header waiting for it.
    158   int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
    159   if (zerr != Z_OK) {
    160     if (zerr == Z_VERSION_ERROR) {
    161       ALOGE("Installed zlib is not compatible with linked version (%s)",
    162         ZLIB_VERSION);
    163     } else {
    164       ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
    165     }
    166 
    167     return false;
    168   }
    169 
    170   z_stream_init_ = true;
    171 
    172   uncompressed_length_ = entry.uncompressed_length;
    173   compressed_length_ = entry.compressed_length;
    174 
    175   out_.resize(kBufSize);
    176   in_.resize(kBufSize);
    177 
    178   computed_crc32_ = 0;
    179 
    180   return true;
    181 }
    182 
    183 ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
    184   if (z_stream_init_) {
    185     inflateEnd(&z_stream_);
    186     z_stream_init_ = false;
    187   }
    188 }
    189 
    190 bool ZipArchiveStreamEntryCompressed::Verify() {
    191   return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
    192       crc32_ == computed_crc32_;
    193 }
    194 
    195 const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
    196   if (z_stream_.avail_out == 0) {
    197     z_stream_.next_out = out_.data();
    198     z_stream_.avail_out = out_.size();;
    199   }
    200 
    201   while (true) {
    202     if (z_stream_.avail_in == 0) {
    203       if (compressed_length_ == 0) {
    204         return nullptr;
    205       }
    206       size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
    207       ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
    208       errno = 0;
    209       if (!android::base::ReadFully(archive->fd, in_.data(), bytes)) {
    210         if (errno != 0) {
    211           ALOGE("Error reading from archive fd: %s", strerror(errno));
    212         } else {
    213           ALOGE("Short read of zip file, possibly corrupted zip?");
    214         }
    215         return nullptr;
    216       }
    217 
    218       compressed_length_ -= bytes;
    219       z_stream_.next_in = in_.data();
    220       z_stream_.avail_in = bytes;
    221     }
    222 
    223     int zerr = inflate(&z_stream_, Z_NO_FLUSH);
    224     if (zerr != Z_OK && zerr != Z_STREAM_END) {
    225       ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
    226           zerr, z_stream_.next_in, z_stream_.avail_in,
    227           z_stream_.next_out, z_stream_.avail_out);
    228       return nullptr;
    229     }
    230 
    231     if (z_stream_.avail_out == 0) {
    232       uncompressed_length_ -= out_.size();
    233       computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
    234       return &out_;
    235     }
    236     if (zerr == Z_STREAM_END) {
    237       if (z_stream_.avail_out != 0) {
    238         // Resize the vector down to the actual size of the data.
    239         out_.resize(out_.size() - z_stream_.avail_out);
    240         computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
    241         uncompressed_length_ -= out_.size();
    242         return &out_;
    243       }
    244       return nullptr;
    245     }
    246   }
    247   return nullptr;
    248 }
    249 
    250 class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
    251  public:
    252   ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
    253       : ZipArchiveStreamEntryUncompressed(handle) {}
    254   virtual ~ZipArchiveStreamEntryRawCompressed() {}
    255 
    256   bool Verify() override;
    257 
    258  protected:
    259   bool Init(const ZipEntry& entry) override;
    260 };
    261 
    262 bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
    263   if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
    264     return false;
    265   }
    266   length_ = entry.compressed_length;
    267 
    268   return true;
    269 }
    270 
    271 bool ZipArchiveStreamEntryRawCompressed::Verify() {
    272   return length_ == 0;
    273 }
    274 
    275 ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
    276     ZipArchiveHandle handle, const ZipEntry& entry) {
    277   ZipArchiveStreamEntry* stream = nullptr;
    278   if (entry.method != kCompressStored) {
    279     stream = new ZipArchiveStreamEntryCompressed(handle);
    280   } else {
    281     stream = new ZipArchiveStreamEntryUncompressed(handle);
    282   }
    283   if (stream && !stream->Init(entry)) {
    284     delete stream;
    285     stream = nullptr;
    286   }
    287 
    288   return stream;
    289 }
    290 
    291 ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
    292     ZipArchiveHandle handle, const ZipEntry& entry) {
    293   ZipArchiveStreamEntry* stream = nullptr;
    294   if (entry.method == kCompressStored) {
    295     // Not compressed, don't need to do anything special.
    296     stream = new ZipArchiveStreamEntryUncompressed(handle);
    297   } else {
    298     stream = new ZipArchiveStreamEntryRawCompressed(handle);
    299   }
    300   if (stream && !stream->Init(entry)) {
    301     delete stream;
    302     stream = nullptr;
    303   }
    304   return stream;
    305 }
    306