|  | /* | 
|  | * Copyright (C) 2015 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #define LOG_TAG "ZIPARCHIVE" | 
|  |  | 
|  | // Read-only stream access to Zip Archive entries. | 
|  | #include <errno.h> | 
|  | #include <inttypes.h> | 
|  | #include <string.h> | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <vector> | 
|  |  | 
|  | #include <android-base/file.h> | 
|  | #include <android-base/logging.h> | 
|  | #include <log/log.h> | 
|  |  | 
|  | #include <ziparchive/zip_archive.h> | 
|  | #include <ziparchive/zip_archive_stream_entry.h> | 
|  | #include <zlib.h> | 
|  |  | 
|  | #include "zip_archive_private.h" | 
|  |  | 
|  | static constexpr size_t kBufSize = 65535; | 
|  |  | 
|  | bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) { | 
|  | crc32_ = entry.crc32; | 
|  | offset_ = entry.offset; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry { | 
|  | public: | 
|  | explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle) | 
|  | : ZipArchiveStreamEntry(handle) {} | 
|  | virtual ~ZipArchiveStreamEntryUncompressed() {} | 
|  |  | 
|  | const std::vector<uint8_t>* Read() override; | 
|  |  | 
|  | bool Verify() override; | 
|  |  | 
|  | protected: | 
|  | bool Init(const ZipEntry& entry) override; | 
|  |  | 
|  | uint32_t length_ = 0u; | 
|  |  | 
|  | private: | 
|  | std::vector<uint8_t> data_; | 
|  | uint32_t computed_crc32_ = 0u; | 
|  | }; | 
|  |  | 
|  | bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) { | 
|  | if (!ZipArchiveStreamEntry::Init(entry)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | length_ = entry.uncompressed_length; | 
|  |  | 
|  | data_.resize(kBufSize); | 
|  | computed_crc32_ = 0; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() { | 
|  | // Simple sanity check. The vector should *only* be handled by this code. A caller | 
|  | // should not const-cast and modify the capacity. This may invalidate next_out. | 
|  | // | 
|  | // Note: it would be better to store the results of data() across Read calls. | 
|  | CHECK_EQ(data_.capacity(), kBufSize); | 
|  |  | 
|  | if (length_ == 0) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | size_t bytes = (length_ > data_.size()) ? data_.size() : length_; | 
|  | ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_); | 
|  | errno = 0; | 
|  | if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) { | 
|  | if (errno != 0) { | 
|  | ALOGE("Error reading from archive fd: %s", strerror(errno)); | 
|  | } else { | 
|  | ALOGE("Short read of zip file, possibly corrupted zip?"); | 
|  | } | 
|  | length_ = 0; | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (bytes < data_.size()) { | 
|  | data_.resize(bytes); | 
|  | } | 
|  | computed_crc32_ = static_cast<uint32_t>( | 
|  | crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size()))); | 
|  | length_ -= bytes; | 
|  | offset_ += bytes; | 
|  | return &data_; | 
|  | } | 
|  |  | 
|  | bool ZipArchiveStreamEntryUncompressed::Verify() { | 
|  | return length_ == 0 && crc32_ == computed_crc32_; | 
|  | } | 
|  |  | 
|  | class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry { | 
|  | public: | 
|  | explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle) | 
|  | : ZipArchiveStreamEntry(handle) {} | 
|  | virtual ~ZipArchiveStreamEntryCompressed(); | 
|  |  | 
|  | const std::vector<uint8_t>* Read() override; | 
|  |  | 
|  | bool Verify() override; | 
|  |  | 
|  | protected: | 
|  | bool Init(const ZipEntry& entry) override; | 
|  |  | 
|  | private: | 
|  | bool z_stream_init_ = false; | 
|  | z_stream z_stream_; | 
|  | std::vector<uint8_t> in_; | 
|  | std::vector<uint8_t> out_; | 
|  | uint32_t uncompressed_length_ = 0u; | 
|  | uint32_t compressed_length_ = 0u; | 
|  | uint32_t computed_crc32_ = 0u; | 
|  | }; | 
|  |  | 
|  | // This method is using libz macros with old-style-casts | 
|  | #pragma GCC diagnostic push | 
|  | #pragma GCC diagnostic ignored "-Wold-style-cast" | 
|  | static inline int zlib_inflateInit2(z_stream* stream, int window_bits) { | 
|  | return inflateInit2(stream, window_bits); | 
|  | } | 
|  | #pragma GCC diagnostic pop | 
|  |  | 
|  | bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) { | 
|  | if (!ZipArchiveStreamEntry::Init(entry)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Initialize the zlib stream struct. | 
|  | memset(&z_stream_, 0, sizeof(z_stream_)); | 
|  | z_stream_.zalloc = Z_NULL; | 
|  | z_stream_.zfree = Z_NULL; | 
|  | z_stream_.opaque = Z_NULL; | 
|  | z_stream_.next_in = nullptr; | 
|  | z_stream_.avail_in = 0; | 
|  | z_stream_.avail_out = 0; | 
|  | z_stream_.data_type = Z_UNKNOWN; | 
|  |  | 
|  | // Use the undocumented "negative window bits" feature to tell zlib | 
|  | // that there's no zlib header waiting for it. | 
|  | int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS); | 
|  | if (zerr != Z_OK) { | 
|  | if (zerr == Z_VERSION_ERROR) { | 
|  | ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION); | 
|  | } else { | 
|  | ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | z_stream_init_ = true; | 
|  |  | 
|  | uncompressed_length_ = entry.uncompressed_length; | 
|  | compressed_length_ = entry.compressed_length; | 
|  |  | 
|  | out_.resize(kBufSize); | 
|  | in_.resize(kBufSize); | 
|  |  | 
|  | computed_crc32_ = 0; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() { | 
|  | if (z_stream_init_) { | 
|  | inflateEnd(&z_stream_); | 
|  | z_stream_init_ = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool ZipArchiveStreamEntryCompressed::Verify() { | 
|  | return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 && | 
|  | crc32_ == computed_crc32_; | 
|  | } | 
|  |  | 
|  | const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() { | 
|  | // Simple sanity check. The vector should *only* be handled by this code. A caller | 
|  | // should not const-cast and modify the capacity. This may invalidate next_out. | 
|  | // | 
|  | // Note: it would be better to store the results of data() across Read calls. | 
|  | CHECK_EQ(out_.capacity(), kBufSize); | 
|  |  | 
|  | if (z_stream_.avail_out == 0) { | 
|  | z_stream_.next_out = out_.data(); | 
|  | z_stream_.avail_out = static_cast<uint32_t>(out_.size()); | 
|  | ; | 
|  | } | 
|  |  | 
|  | while (true) { | 
|  | if (z_stream_.avail_in == 0) { | 
|  | if (compressed_length_ == 0) { | 
|  | return nullptr; | 
|  | } | 
|  | DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max());  // Should be buf size = 64k. | 
|  | uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size()) | 
|  | : compressed_length_; | 
|  | ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_); | 
|  | errno = 0; | 
|  | if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) { | 
|  | if (errno != 0) { | 
|  | ALOGE("Error reading from archive fd: %s", strerror(errno)); | 
|  | } else { | 
|  | ALOGE("Short read of zip file, possibly corrupted zip?"); | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | compressed_length_ -= bytes; | 
|  | offset_ += bytes; | 
|  | z_stream_.next_in = in_.data(); | 
|  | z_stream_.avail_in = bytes; | 
|  | } | 
|  |  | 
|  | int zerr = inflate(&z_stream_, Z_NO_FLUSH); | 
|  | if (zerr != Z_OK && zerr != Z_STREAM_END) { | 
|  | ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in, | 
|  | z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (z_stream_.avail_out == 0) { | 
|  | uncompressed_length_ -= out_.size(); | 
|  | computed_crc32_ = static_cast<uint32_t>( | 
|  | crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size()))); | 
|  | return &out_; | 
|  | } | 
|  | if (zerr == Z_STREAM_END) { | 
|  | if (z_stream_.avail_out != 0) { | 
|  | // Resize the vector down to the actual size of the data. | 
|  | out_.resize(out_.size() - z_stream_.avail_out); | 
|  | computed_crc32_ = static_cast<uint32_t>( | 
|  | crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size()))); | 
|  | uncompressed_length_ -= out_.size(); | 
|  | return &out_; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed { | 
|  | public: | 
|  | explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle) | 
|  | : ZipArchiveStreamEntryUncompressed(handle) {} | 
|  | virtual ~ZipArchiveStreamEntryRawCompressed() {} | 
|  |  | 
|  | bool Verify() override; | 
|  |  | 
|  | protected: | 
|  | bool Init(const ZipEntry& entry) override; | 
|  | }; | 
|  |  | 
|  | bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) { | 
|  | if (!ZipArchiveStreamEntryUncompressed::Init(entry)) { | 
|  | return false; | 
|  | } | 
|  | length_ = entry.compressed_length; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ZipArchiveStreamEntryRawCompressed::Verify() { | 
|  | return length_ == 0; | 
|  | } | 
|  |  | 
|  | ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle, | 
|  | const ZipEntry& entry) { | 
|  | ZipArchiveStreamEntry* stream = nullptr; | 
|  | if (entry.method != kCompressStored) { | 
|  | stream = new ZipArchiveStreamEntryCompressed(handle); | 
|  | } else { | 
|  | stream = new ZipArchiveStreamEntryUncompressed(handle); | 
|  | } | 
|  | if (stream && !stream->Init(entry)) { | 
|  | delete stream; | 
|  | stream = nullptr; | 
|  | } | 
|  |  | 
|  | return stream; | 
|  | } | 
|  |  | 
|  | ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle, | 
|  | const ZipEntry& entry) { | 
|  | ZipArchiveStreamEntry* stream = nullptr; | 
|  | if (entry.method == kCompressStored) { | 
|  | // Not compressed, don't need to do anything special. | 
|  | stream = new ZipArchiveStreamEntryUncompressed(handle); | 
|  | } else { | 
|  | stream = new ZipArchiveStreamEntryRawCompressed(handle); | 
|  | } | 
|  | if (stream && !stream->Init(entry)) { | 
|  | delete stream; | 
|  | stream = nullptr; | 
|  | } | 
|  | return stream; | 
|  | } |