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