blob: 4b205a788c801b9fade485ecae0ce263ce9408c5 [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
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
36static constexpr size_t kBufSize = 65535;
37
38bool 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
49class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
50 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -070051 explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
52 : ZipArchiveStreamEntry(handle) {}
Christopher Ferrise6884ce2015-11-10 14:55:12 -080053 virtual ~ZipArchiveStreamEntryUncompressed() {}
54
55 const std::vector<uint8_t>* Read() override;
56
57 bool Verify() override;
58
59 protected:
60 bool Init(const ZipEntry& entry) override;
61
62 uint32_t length_;
63
64 private:
65 std::vector<uint8_t> data_;
66 uint32_t computed_crc32_;
67};
68
69bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
70 if (!ZipArchiveStreamEntry::Init(entry)) {
71 return false;
72 }
73
74 length_ = entry.uncompressed_length;
75
76 data_.resize(kBufSize);
77 computed_crc32_ = 0;
78
79 return true;
80}
81
82const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
83 if (length_ == 0) {
84 return nullptr;
85 }
86
87 size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
88 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
89 errno = 0;
90 if (!android::base::ReadFully(archive->fd, data_.data(), bytes)) {
91 if (errno != 0) {
92 ALOGE("Error reading from archive fd: %s", strerror(errno));
93 } else {
94 ALOGE("Short read of zip file, possibly corrupted zip?");
95 }
96 length_ = 0;
97 return nullptr;
98 }
99
100 if (bytes < data_.size()) {
101 data_.resize(bytes);
102 }
103 computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
104 length_ -= bytes;
105 return &data_;
106}
107
108bool ZipArchiveStreamEntryUncompressed::Verify() {
109 return length_ == 0 && crc32_ == computed_crc32_;
110}
111
112class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
113 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -0700114 explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
115 : ZipArchiveStreamEntry(handle) {}
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800116 virtual ~ZipArchiveStreamEntryCompressed();
117
118 const std::vector<uint8_t>* Read() override;
119
120 bool Verify() override;
121
122 protected:
123 bool Init(const ZipEntry& entry) override;
124
125 private:
126 bool z_stream_init_ = false;
127 z_stream z_stream_;
128 std::vector<uint8_t> in_;
129 std::vector<uint8_t> out_;
130 uint32_t uncompressed_length_;
131 uint32_t compressed_length_;
132 uint32_t computed_crc32_;
133};
134
135// This method is using libz macros with old-style-casts
136#pragma GCC diagnostic push
137#pragma GCC diagnostic ignored "-Wold-style-cast"
138static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
139 return inflateInit2(stream, window_bits);
140}
141#pragma GCC diagnostic pop
142
143bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
144 if (!ZipArchiveStreamEntry::Init(entry)) {
145 return false;
146 }
147
148 // Initialize the zlib stream struct.
149 memset(&z_stream_, 0, sizeof(z_stream_));
150 z_stream_.zalloc = Z_NULL;
151 z_stream_.zfree = Z_NULL;
152 z_stream_.opaque = Z_NULL;
153 z_stream_.next_in = nullptr;
154 z_stream_.avail_in = 0;
155 z_stream_.avail_out = 0;
156 z_stream_.data_type = Z_UNKNOWN;
157
158 // Use the undocumented "negative window bits" feature to tell zlib
159 // that there's no zlib header waiting for it.
160 int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
161 if (zerr != Z_OK) {
162 if (zerr == Z_VERSION_ERROR) {
163 ALOGE("Installed zlib is not compatible with linked version (%s)",
164 ZLIB_VERSION);
165 } else {
166 ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
167 }
168
169 return false;
170 }
171
172 z_stream_init_ = true;
173
174 uncompressed_length_ = entry.uncompressed_length;
175 compressed_length_ = entry.compressed_length;
176
177 out_.resize(kBufSize);
178 in_.resize(kBufSize);
179
180 computed_crc32_ = 0;
181
182 return true;
183}
184
185ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
186 if (z_stream_init_) {
187 inflateEnd(&z_stream_);
188 z_stream_init_ = false;
189 }
190}
191
192bool ZipArchiveStreamEntryCompressed::Verify() {
193 return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
194 crc32_ == computed_crc32_;
195}
196
197const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
198 if (z_stream_.avail_out == 0) {
199 z_stream_.next_out = out_.data();
200 z_stream_.avail_out = out_.size();;
201 }
202
203 while (true) {
204 if (z_stream_.avail_in == 0) {
205 if (compressed_length_ == 0) {
206 return nullptr;
207 }
208 size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
209 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
210 errno = 0;
211 if (!android::base::ReadFully(archive->fd, in_.data(), bytes)) {
212 if (errno != 0) {
213 ALOGE("Error reading from archive fd: %s", strerror(errno));
214 } else {
215 ALOGE("Short read of zip file, possibly corrupted zip?");
216 }
217 return nullptr;
218 }
219
220 compressed_length_ -= bytes;
221 z_stream_.next_in = in_.data();
222 z_stream_.avail_in = bytes;
223 }
224
225 int zerr = inflate(&z_stream_, Z_NO_FLUSH);
226 if (zerr != Z_OK && zerr != Z_STREAM_END) {
227 ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
228 zerr, z_stream_.next_in, z_stream_.avail_in,
229 z_stream_.next_out, z_stream_.avail_out);
230 return nullptr;
231 }
232
233 if (z_stream_.avail_out == 0) {
234 uncompressed_length_ -= out_.size();
235 computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
236 return &out_;
237 }
238 if (zerr == Z_STREAM_END) {
239 if (z_stream_.avail_out != 0) {
240 // Resize the vector down to the actual size of the data.
241 out_.resize(out_.size() - z_stream_.avail_out);
242 computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
243 uncompressed_length_ -= out_.size();
244 return &out_;
245 }
246 return nullptr;
247 }
248 }
249 return nullptr;
250}
251
252class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
253 public:
Chih-Hung Hsieh3a114e02016-04-25 14:38:37 -0700254 explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
Christopher Ferrise6884ce2015-11-10 14:55:12 -0800255 : ZipArchiveStreamEntryUncompressed(handle) {}
256 virtual ~ZipArchiveStreamEntryRawCompressed() {}
257
258 bool Verify() override;
259
260 protected:
261 bool Init(const ZipEntry& entry) override;
262};
263
264bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
265 if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
266 return false;
267 }
268 length_ = entry.compressed_length;
269
270 return true;
271}
272
273bool ZipArchiveStreamEntryRawCompressed::Verify() {
274 return length_ == 0;
275}
276
277ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
278 ZipArchiveHandle handle, const ZipEntry& entry) {
279 ZipArchiveStreamEntry* stream = nullptr;
280 if (entry.method != kCompressStored) {
281 stream = new ZipArchiveStreamEntryCompressed(handle);
282 } else {
283 stream = new ZipArchiveStreamEntryUncompressed(handle);
284 }
285 if (stream && !stream->Init(entry)) {
286 delete stream;
287 stream = nullptr;
288 }
289
290 return stream;
291}
292
293ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
294 ZipArchiveHandle handle, const ZipEntry& entry) {
295 ZipArchiveStreamEntry* stream = nullptr;
296 if (entry.method == kCompressStored) {
297 // Not compressed, don't need to do anything special.
298 stream = new ZipArchiveStreamEntryUncompressed(handle);
299 } else {
300 stream = new ZipArchiveStreamEntryRawCompressed(handle);
301 }
302 if (stream && !stream->Init(entry)) {
303 delete stream;
304 stream = nullptr;
305 }
306 return stream;
307}