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