blob: 248d3160e06409d328d4aebc60d3a2166b514837 [file] [log] [blame]
Christopher Ferris2b7daba2015-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 Salyzyne101ab92016-09-28 15:54:45 -070017#define LOG_TAG "ZIPARCHIVE"
18
Christopher Ferris2b7daba2015-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 Ferris2b7daba2015-11-10 14:55:12 -080029#include <android-base/file.h>
Andreas Gampe5a5ffb52019-04-05 13:48:02 -070030#include <android-base/logging.h>
Mark Salyzyn90b843d2017-01-10 13:19:54 -080031#include <log/log.h>
32
Christopher Ferris2b7daba2015-11-10 14:55:12 -080033#include <ziparchive/zip_archive.h>
34#include <ziparchive/zip_archive_stream_entry.h>
35#include <zlib.h>
36
37#include "zip_archive_private.h"
38
39static constexpr size_t kBufSize = 65535;
40
41bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
Christopher Ferris2b7daba2015-11-10 14:55:12 -080042 crc32_ = entry.crc32;
Adam Lesinski367ccf62017-06-19 10:27:38 -070043 offset_ = entry.offset;
Christopher Ferris2b7daba2015-11-10 14:55:12 -080044 return true;
45}
46
47class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
48 public:
Chih-Hung Hsieh58094fb2016-04-25 14:38:37 -070049 explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
50 : ZipArchiveStreamEntry(handle) {}
Christopher Ferris2b7daba2015-11-10 14:55:12 -080051 virtual ~ZipArchiveStreamEntryUncompressed() {}
52
53 const std::vector<uint8_t>* Read() override;
54
55 bool Verify() override;
56
57 protected:
58 bool Init(const ZipEntry& entry) override;
59
Adam Lesinski367ccf62017-06-19 10:27:38 -070060 uint32_t length_ = 0u;
Christopher Ferris2b7daba2015-11-10 14:55:12 -080061
62 private:
63 std::vector<uint8_t> data_;
Adam Lesinski367ccf62017-06-19 10:27:38 -070064 uint32_t computed_crc32_ = 0u;
Christopher Ferris2b7daba2015-11-10 14:55:12 -080065};
66
67bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
68 if (!ZipArchiveStreamEntry::Init(entry)) {
69 return false;
70 }
71
72 length_ = entry.uncompressed_length;
73
74 data_.resize(kBufSize);
75 computed_crc32_ = 0;
76
77 return true;
78}
79
80const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
Elliott Hughes27d0eaa2020-07-21 16:43:46 -070081 // Simple validity check. The vector should *only* be handled by this code. A caller
Andreas Gampe5a5ffb52019-04-05 13:48:02 -070082 // should not const-cast and modify the capacity. This may invalidate next_out.
83 //
84 // Note: it would be better to store the results of data() across Read calls.
85 CHECK_EQ(data_.capacity(), kBufSize);
86
Christopher Ferris2b7daba2015-11-10 14:55:12 -080087 if (length_ == 0) {
88 return nullptr;
89 }
90
91 size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
92 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
93 errno = 0;
Adam Lesinski367ccf62017-06-19 10:27:38 -070094 if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
Christopher Ferris2b7daba2015-11-10 14:55:12 -080095 if (errno != 0) {
96 ALOGE("Error reading from archive fd: %s", strerror(errno));
97 } else {
98 ALOGE("Short read of zip file, possibly corrupted zip?");
99 }
100 length_ = 0;
101 return nullptr;
102 }
103
104 if (bytes < data_.size()) {
105 data_.resize(bytes);
106 }
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700107 computed_crc32_ = static_cast<uint32_t>(
108 crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800109 length_ -= bytes;
Adam Lesinski367ccf62017-06-19 10:27:38 -0700110 offset_ += bytes;
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800111 return &data_;
112}
113
114bool ZipArchiveStreamEntryUncompressed::Verify() {
115 return length_ == 0 && crc32_ == computed_crc32_;
116}
117
118class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
119 public:
Chih-Hung Hsieh58094fb2016-04-25 14:38:37 -0700120 explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
121 : ZipArchiveStreamEntry(handle) {}
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800122 virtual ~ZipArchiveStreamEntryCompressed();
123
124 const std::vector<uint8_t>* Read() override;
125
126 bool Verify() override;
127
128 protected:
129 bool Init(const ZipEntry& entry) override;
130
131 private:
132 bool z_stream_init_ = false;
133 z_stream z_stream_;
134 std::vector<uint8_t> in_;
135 std::vector<uint8_t> out_;
Adam Lesinski367ccf62017-06-19 10:27:38 -0700136 uint32_t uncompressed_length_ = 0u;
137 uint32_t compressed_length_ = 0u;
138 uint32_t computed_crc32_ = 0u;
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800139};
140
141// This method is using libz macros with old-style-casts
142#pragma GCC diagnostic push
143#pragma GCC diagnostic ignored "-Wold-style-cast"
144static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
145 return inflateInit2(stream, window_bits);
146}
147#pragma GCC diagnostic pop
148
149bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
150 if (!ZipArchiveStreamEntry::Init(entry)) {
151 return false;
152 }
153
154 // Initialize the zlib stream struct.
155 memset(&z_stream_, 0, sizeof(z_stream_));
156 z_stream_.zalloc = Z_NULL;
157 z_stream_.zfree = Z_NULL;
158 z_stream_.opaque = Z_NULL;
159 z_stream_.next_in = nullptr;
160 z_stream_.avail_in = 0;
161 z_stream_.avail_out = 0;
162 z_stream_.data_type = Z_UNKNOWN;
163
164 // Use the undocumented "negative window bits" feature to tell zlib
165 // that there's no zlib header waiting for it.
166 int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
167 if (zerr != Z_OK) {
168 if (zerr == Z_VERSION_ERROR) {
Jiyong Park6821cc82017-06-30 17:23:33 +0900169 ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800170 } else {
171 ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
172 }
173
174 return false;
175 }
176
177 z_stream_init_ = true;
178
179 uncompressed_length_ = entry.uncompressed_length;
180 compressed_length_ = entry.compressed_length;
181
182 out_.resize(kBufSize);
183 in_.resize(kBufSize);
184
185 computed_crc32_ = 0;
186
187 return true;
188}
189
190ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
191 if (z_stream_init_) {
192 inflateEnd(&z_stream_);
193 z_stream_init_ = false;
194 }
195}
196
197bool ZipArchiveStreamEntryCompressed::Verify() {
198 return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
Jiyong Park6821cc82017-06-30 17:23:33 +0900199 crc32_ == computed_crc32_;
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800200}
201
202const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
Elliott Hughes27d0eaa2020-07-21 16:43:46 -0700203 // Simple validity check. The vector should *only* be handled by this code. A caller
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700204 // should not const-cast and modify the capacity. This may invalidate next_out.
205 //
206 // Note: it would be better to store the results of data() across Read calls.
207 CHECK_EQ(out_.capacity(), kBufSize);
208
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800209 if (z_stream_.avail_out == 0) {
210 z_stream_.next_out = out_.data();
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700211 z_stream_.avail_out = static_cast<uint32_t>(out_.size());
Jiyong Park6821cc82017-06-30 17:23:33 +0900212 ;
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800213 }
214
215 while (true) {
216 if (z_stream_.avail_in == 0) {
217 if (compressed_length_ == 0) {
218 return nullptr;
219 }
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700220 DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max()); // Should be buf size = 64k.
221 uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size())
222 : compressed_length_;
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800223 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
224 errno = 0;
Adam Lesinski367ccf62017-06-19 10:27:38 -0700225 if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800226 if (errno != 0) {
227 ALOGE("Error reading from archive fd: %s", strerror(errno));
228 } else {
229 ALOGE("Short read of zip file, possibly corrupted zip?");
230 }
231 return nullptr;
232 }
233
234 compressed_length_ -= bytes;
Adam Lesinski367ccf62017-06-19 10:27:38 -0700235 offset_ += bytes;
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800236 z_stream_.next_in = in_.data();
237 z_stream_.avail_in = bytes;
238 }
239
240 int zerr = inflate(&z_stream_, Z_NO_FLUSH);
241 if (zerr != Z_OK && zerr != Z_STREAM_END) {
Jiyong Park6821cc82017-06-30 17:23:33 +0900242 ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
243 z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800244 return nullptr;
245 }
246
247 if (z_stream_.avail_out == 0) {
248 uncompressed_length_ -= out_.size();
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700249 computed_crc32_ = static_cast<uint32_t>(
250 crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800251 return &out_;
252 }
253 if (zerr == Z_STREAM_END) {
254 if (z_stream_.avail_out != 0) {
255 // Resize the vector down to the actual size of the data.
256 out_.resize(out_.size() - z_stream_.avail_out);
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700257 computed_crc32_ = static_cast<uint32_t>(
258 crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800259 uncompressed_length_ -= out_.size();
260 return &out_;
261 }
262 return nullptr;
263 }
264 }
265 return nullptr;
266}
267
268class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
269 public:
Chih-Hung Hsieh58094fb2016-04-25 14:38:37 -0700270 explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800271 : ZipArchiveStreamEntryUncompressed(handle) {}
272 virtual ~ZipArchiveStreamEntryRawCompressed() {}
273
274 bool Verify() override;
275
276 protected:
277 bool Init(const ZipEntry& entry) override;
278};
279
280bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
281 if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
282 return false;
283 }
284 length_ = entry.compressed_length;
285
286 return true;
287}
288
289bool ZipArchiveStreamEntryRawCompressed::Verify() {
290 return length_ == 0;
291}
292
Jiyong Park6821cc82017-06-30 17:23:33 +0900293ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
294 const ZipEntry& entry) {
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800295 ZipArchiveStreamEntry* stream = nullptr;
296 if (entry.method != kCompressStored) {
297 stream = new ZipArchiveStreamEntryCompressed(handle);
298 } else {
299 stream = new ZipArchiveStreamEntryUncompressed(handle);
300 }
301 if (stream && !stream->Init(entry)) {
302 delete stream;
303 stream = nullptr;
304 }
305
306 return stream;
307}
308
Jiyong Park6821cc82017-06-30 17:23:33 +0900309ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
310 const ZipEntry& entry) {
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800311 ZipArchiveStreamEntry* stream = nullptr;
312 if (entry.method == kCompressStored) {
313 // Not compressed, don't need to do anything special.
314 stream = new ZipArchiveStreamEntryUncompressed(handle);
315 } else {
316 stream = new ZipArchiveStreamEntryRawCompressed(handle);
317 }
318 if (stream && !stream->Init(entry)) {
319 delete stream;
320 stream = nullptr;
321 }
322 return stream;
323}