blob: 9ec89b1bc3ec55aee317ea1b9d79931007b0518d [file] [log] [blame]
Christopher Ferrise6884ce2015-11-10 14:55:12 -08001/*
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
Mark Salyzynff2dcd92016-09-28 15:54:45 -070017#define LOG_TAG "ZIPARCHIVE"
18
Christopher Ferrise6884ce2015-11-10 14:55:12 -080019// 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
Christopher Ferrise6884ce2015-11-10 14:55:12 -080029#include <android-base/file.h>
Mark Salyzyn30f991f2017-01-10 13:19:54 -080030#include <log/log.h>
31
Christopher Ferrise6884ce2015-11-10 14:55:12 -080032#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
38static constexpr size_t kBufSize = 65535;
39
40bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
Christopher Ferrise6884ce2015-11-10 14:55:12 -080041 crc32_ = entry.crc32;
Adam Lesinskide117e42017-06-19 10:27:38 -070042 offset_ = entry.offset;
Christopher Ferrise6884ce2015-11-10 14:55:12 -080043 return true;
44}
45
46class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
47 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -070048 explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
49 : ZipArchiveStreamEntry(handle) {}
Christopher Ferrise6884ce2015-11-10 14:55:12 -080050 virtual ~ZipArchiveStreamEntryUncompressed() {}
51
52 const std::vector<uint8_t>* Read() override;
53
54 bool Verify() override;
55
56 protected:
57 bool Init(const ZipEntry& entry) override;
58
Adam Lesinskide117e42017-06-19 10:27:38 -070059 uint32_t length_ = 0u;
Christopher Ferrise6884ce2015-11-10 14:55:12 -080060
61 private:
62 std::vector<uint8_t> data_;
Adam Lesinskide117e42017-06-19 10:27:38 -070063 uint32_t computed_crc32_ = 0u;
Christopher Ferrise6884ce2015-11-10 14:55:12 -080064};
65
66bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
67 if (!ZipArchiveStreamEntry::Init(entry)) {
68 return false;
69 }
70
71 length_ = entry.uncompressed_length;
72
73 data_.resize(kBufSize);
74 computed_crc32_ = 0;
75
76 return true;
77}
78
79const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
80 if (length_ == 0) {
81 return nullptr;
82 }
83
84 size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
85 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
86 errno = 0;
Adam Lesinskide117e42017-06-19 10:27:38 -070087 if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
Christopher Ferrise6884ce2015-11-10 14:55:12 -080088 if (errno != 0) {
89 ALOGE("Error reading from archive fd: %s", strerror(errno));
90 } else {
91 ALOGE("Short read of zip file, possibly corrupted zip?");
92 }
93 length_ = 0;
94 return nullptr;
95 }
96
97 if (bytes < data_.size()) {
98 data_.resize(bytes);
99 }
100 computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
101 length_ -= bytes;
Adam Lesinskide117e42017-06-19 10:27:38 -0700102 offset_ += bytes;
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800103 return &data_;
104}
105
106bool ZipArchiveStreamEntryUncompressed::Verify() {
107 return length_ == 0 && crc32_ == computed_crc32_;
108}
109
110class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
111 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -0700112 explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
113 : ZipArchiveStreamEntry(handle) {}
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800114 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_;
Adam Lesinskide117e42017-06-19 10:27:38 -0700128 uint32_t uncompressed_length_ = 0u;
129 uint32_t compressed_length_ = 0u;
130 uint32_t computed_crc32_ = 0u;
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800131};
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"
136static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
137 return inflateInit2(stream, window_bits);
138}
139#pragma GCC diagnostic pop
140
141bool 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) {
Jiyong Parkcd997e62017-06-30 17:23:33 +0900161 ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800162 } else {
163 ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
164 }
165
166 return false;
167 }
168
169 z_stream_init_ = true;
170
171 uncompressed_length_ = entry.uncompressed_length;
172 compressed_length_ = entry.compressed_length;
173
174 out_.resize(kBufSize);
175 in_.resize(kBufSize);
176
177 computed_crc32_ = 0;
178
179 return true;
180}
181
182ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
183 if (z_stream_init_) {
184 inflateEnd(&z_stream_);
185 z_stream_init_ = false;
186 }
187}
188
189bool ZipArchiveStreamEntryCompressed::Verify() {
190 return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
Jiyong Parkcd997e62017-06-30 17:23:33 +0900191 crc32_ == computed_crc32_;
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800192}
193
194const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
195 if (z_stream_.avail_out == 0) {
196 z_stream_.next_out = out_.data();
Jiyong Parkcd997e62017-06-30 17:23:33 +0900197 z_stream_.avail_out = out_.size();
198 ;
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800199 }
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;
Adam Lesinskide117e42017-06-19 10:27:38 -0700209 if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800210 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;
Adam Lesinskide117e42017-06-19 10:27:38 -0700219 offset_ += bytes;
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800220 z_stream_.next_in = in_.data();
221 z_stream_.avail_in = bytes;
222 }
223
224 int zerr = inflate(&z_stream_, Z_NO_FLUSH);
225 if (zerr != Z_OK && zerr != Z_STREAM_END) {
Jiyong Parkcd997e62017-06-30 17:23:33 +0900226 ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
227 z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800228 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
250class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
251 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -0700252 explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800253 : ZipArchiveStreamEntryUncompressed(handle) {}
254 virtual ~ZipArchiveStreamEntryRawCompressed() {}
255
256 bool Verify() override;
257
258 protected:
259 bool Init(const ZipEntry& entry) override;
260};
261
262bool 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
271bool ZipArchiveStreamEntryRawCompressed::Verify() {
272 return length_ == 0;
273}
274
Jiyong Parkcd997e62017-06-30 17:23:33 +0900275ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
276 const ZipEntry& entry) {
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800277 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
Jiyong Parkcd997e62017-06-30 17:23:33 +0900291ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
292 const ZipEntry& entry) {
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800293 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}