blob: f6188350311e7b65ae76e7143ffbf15cf1760e26 [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:
51 ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle) : ZipArchiveStreamEntry(handle) {}
52 virtual ~ZipArchiveStreamEntryUncompressed() {}
53
54 const std::vector<uint8_t>* Read() override;
55
56 bool Verify() override;
57
58 protected:
59 bool Init(const ZipEntry& entry) override;
60
61 uint32_t length_;
62
63 private:
64 std::vector<uint8_t> data_;
65 uint32_t computed_crc32_;
66};
67
68bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
69 if (!ZipArchiveStreamEntry::Init(entry)) {
70 return false;
71 }
72
73 length_ = entry.uncompressed_length;
74
75 data_.resize(kBufSize);
76 computed_crc32_ = 0;
77
78 return true;
79}
80
81const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
82 if (length_ == 0) {
83 return nullptr;
84 }
85
86 size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
87 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
88 errno = 0;
89 if (!android::base::ReadFully(archive->fd, data_.data(), bytes)) {
90 if (errno != 0) {
91 ALOGE("Error reading from archive fd: %s", strerror(errno));
92 } else {
93 ALOGE("Short read of zip file, possibly corrupted zip?");
94 }
95 length_ = 0;
96 return nullptr;
97 }
98
99 if (bytes < data_.size()) {
100 data_.resize(bytes);
101 }
102 computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
103 length_ -= bytes;
104 return &data_;
105}
106
107bool ZipArchiveStreamEntryUncompressed::Verify() {
108 return length_ == 0 && crc32_ == computed_crc32_;
109}
110
111class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
112 public:
113 ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle) : ZipArchiveStreamEntry(handle) {}
114 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_;
128 uint32_t uncompressed_length_;
129 uint32_t compressed_length_;
130 uint32_t computed_crc32_;
131};
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) {
161 ALOGE("Installed zlib is not compatible with linked version (%s)",
162 ZLIB_VERSION);
163 } else {
164 ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
165 }
166
167 return false;
168 }
169
170 z_stream_init_ = true;
171
172 uncompressed_length_ = entry.uncompressed_length;
173 compressed_length_ = entry.compressed_length;
174
175 out_.resize(kBufSize);
176 in_.resize(kBufSize);
177
178 computed_crc32_ = 0;
179
180 return true;
181}
182
183ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
184 if (z_stream_init_) {
185 inflateEnd(&z_stream_);
186 z_stream_init_ = false;
187 }
188}
189
190bool ZipArchiveStreamEntryCompressed::Verify() {
191 return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
192 crc32_ == computed_crc32_;
193}
194
195const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
196 if (z_stream_.avail_out == 0) {
197 z_stream_.next_out = out_.data();
198 z_stream_.avail_out = out_.size();;
199 }
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;
209 if (!android::base::ReadFully(archive->fd, in_.data(), bytes)) {
210 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;
219 z_stream_.next_in = in_.data();
220 z_stream_.avail_in = bytes;
221 }
222
223 int zerr = inflate(&z_stream_, Z_NO_FLUSH);
224 if (zerr != Z_OK && zerr != Z_STREAM_END) {
225 ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
226 zerr, z_stream_.next_in, z_stream_.avail_in,
227 z_stream_.next_out, z_stream_.avail_out);
228 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:
252 ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
253 : 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
275ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
276 ZipArchiveHandle handle, const ZipEntry& entry) {
277 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
291ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
292 ZipArchiveHandle handle, const ZipEntry& entry) {
293 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}