1 /*
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 #define LOG_TAG "ZIPARCHIVE"
18
19 // 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
29 #include <android-base/file.h>
30 #include <log/log.h>
31
32 #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
38 static constexpr size_t kBufSize = 65535;
39
Init(const ZipEntry & entry)40 bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
41 crc32_ = entry.crc32;
42 offset_ = entry.offset;
43 return true;
44 }
45
46 class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
47 public:
ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)48 explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
49 : ZipArchiveStreamEntry(handle) {}
~ZipArchiveStreamEntryUncompressed()50 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
59 uint32_t length_ = 0u;
60
61 private:
62 std::vector<uint8_t> data_;
63 uint32_t computed_crc32_ = 0u;
64 };
65
Init(const ZipEntry & entry)66 bool 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
Read()79 const 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;
87 if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
88 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;
102 offset_ += bytes;
103 return &data_;
104 }
105
Verify()106 bool ZipArchiveStreamEntryUncompressed::Verify() {
107 return length_ == 0 && crc32_ == computed_crc32_;
108 }
109
110 class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
111 public:
ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)112 explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
113 : 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_ = 0u;
129 uint32_t compressed_length_ = 0u;
130 uint32_t computed_crc32_ = 0u;
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"
zlib_inflateInit2(z_stream * stream,int window_bits)136 static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
137 return inflateInit2(stream, window_bits);
138 }
139 #pragma GCC diagnostic pop
140
Init(const ZipEntry & entry)141 bool 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)", ZLIB_VERSION);
162 } 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
~ZipArchiveStreamEntryCompressed()182 ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
183 if (z_stream_init_) {
184 inflateEnd(&z_stream_);
185 z_stream_init_ = false;
186 }
187 }
188
Verify()189 bool ZipArchiveStreamEntryCompressed::Verify() {
190 return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
191 crc32_ == computed_crc32_;
192 }
193
Read()194 const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
195 if (z_stream_.avail_out == 0) {
196 z_stream_.next_out = out_.data();
197 z_stream_.avail_out = out_.size();
198 ;
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 (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
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 offset_ += bytes;
220 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) {
226 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);
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
250 class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
251 public:
ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)252 explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
253 : ZipArchiveStreamEntryUncompressed(handle) {}
~ZipArchiveStreamEntryRawCompressed()254 virtual ~ZipArchiveStreamEntryRawCompressed() {}
255
256 bool Verify() override;
257
258 protected:
259 bool Init(const ZipEntry& entry) override;
260 };
261
Init(const ZipEntry & entry)262 bool 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
Verify()271 bool ZipArchiveStreamEntryRawCompressed::Verify() {
272 return length_ == 0;
273 }
274
Create(ZipArchiveHandle handle,const ZipEntry & entry)275 ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
276 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
CreateRaw(ZipArchiveHandle handle,const ZipEntry & entry)291 ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
292 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 }
306