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