blob: 3f336a6f2419b055913cc47ebb9f4ddc7ebf327a [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) {
41 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
42 off64_t data_offset = entry.offset;
Tianjie Xu18c25922016-09-29 15:27:41 -070043 if (!archive->mapped_zip.SeekToOffset(data_offset)) {
Christopher Ferrise6884ce2015-11-10 14:55:12 -080044 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
51class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
52 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -070053 explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
54 : ZipArchiveStreamEntry(handle) {}
Christopher Ferrise6884ce2015-11-10 14:55:12 -080055 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
71bool 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
84const 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;
Tianjie Xu18c25922016-09-29 15:27:41 -070092 if (!archive->mapped_zip.ReadData(data_.data(), bytes)) {
Christopher Ferrise6884ce2015-11-10 14:55:12 -080093 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
110bool ZipArchiveStreamEntryUncompressed::Verify() {
111 return length_ == 0 && crc32_ == computed_crc32_;
112}
113
114class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
115 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -0700116 explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
117 : ZipArchiveStreamEntry(handle) {}
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800118 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"
140static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
141 return inflateInit2(stream, window_bits);
142}
143#pragma GCC diagnostic pop
144
145bool 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
187ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
188 if (z_stream_init_) {
189 inflateEnd(&z_stream_);
190 z_stream_init_ = false;
191 }
192}
193
194bool ZipArchiveStreamEntryCompressed::Verify() {
195 return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
196 crc32_ == computed_crc32_;
197}
198
199const 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;
Tianjie Xu18c25922016-09-29 15:27:41 -0700213 if (!archive->mapped_zip.ReadData(in_.data(), bytes)) {
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800214 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
254class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
255 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -0700256 explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800257 : ZipArchiveStreamEntryUncompressed(handle) {}
258 virtual ~ZipArchiveStreamEntryRawCompressed() {}
259
260 bool Verify() override;
261
262 protected:
263 bool Init(const ZipEntry& entry) override;
264};
265
266bool 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
275bool ZipArchiveStreamEntryRawCompressed::Verify() {
276 return length_ == 0;
277}
278
279ZipArchiveStreamEntry* 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
295ZipArchiveStreamEntry* 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}