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 <limits>
27 #include <memory>
28 #include <vector>
29
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <log/log.h>
33
34 #include <ziparchive/zip_archive.h>
35 #include <ziparchive/zip_archive_stream_entry.h>
36 #include <zlib.h>
37
38 #include "zip_archive_private.h"
39
40 static constexpr size_t kBufSize = 65535;
41
Init(const ZipEntry & entry)42 bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
43 crc32_ = entry.crc32;
44 offset_ = entry.offset;
45 return true;
46 }
47
48 class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
49 public:
ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)50 explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
51 : ZipArchiveStreamEntry(handle) {}
~ZipArchiveStreamEntryUncompressed()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_ = 0u;
62
63 private:
64 std::vector<uint8_t> data_;
65 uint32_t computed_crc32_ = 0u;
66 };
67
Init(const ZipEntry & entry)68 bool 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
Read()81 const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
82 // Simple validity check. The vector should *only* be handled by this code. A caller
83 // should not const-cast and modify the capacity. This may invalidate next_out.
84 //
85 // Note: it would be better to store the results of data() across Read calls.
86 CHECK_EQ(data_.capacity(), kBufSize);
87
88 if (length_ == 0) {
89 return nullptr;
90 }
91
92 size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
93 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
94 errno = 0;
95 auto res = archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_);
96 if (!res) {
97 if (errno != 0) {
98 ALOGE("Error reading from archive fd: %s", strerror(errno));
99 } else {
100 ALOGE("Short read of zip file, possibly corrupted zip?");
101 }
102 length_ = 0;
103 return nullptr;
104 }
105
106 if (res != data_.data()) {
107 data_.assign(res, res + bytes);
108 } else if (bytes < data_.size()) {
109 data_.resize(bytes);
110 }
111 computed_crc32_ = static_cast<uint32_t>(
112 crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
113 length_ -= bytes;
114 offset_ += bytes;
115 return &data_;
116 }
117
Verify()118 bool ZipArchiveStreamEntryUncompressed::Verify() {
119 return length_ == 0 && crc32_ == computed_crc32_;
120 }
121
122 class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
123 public:
ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)124 explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
125 : ZipArchiveStreamEntry(handle) {}
126 virtual ~ZipArchiveStreamEntryCompressed();
127
128 const std::vector<uint8_t>* Read() override;
129
130 bool Verify() override;
131
132 protected:
133 bool Init(const ZipEntry& entry) override;
134
135 private:
136 bool z_stream_init_ = false;
137 z_stream z_stream_;
138 std::vector<uint8_t> in_;
139 std::vector<uint8_t> out_;
140 uint32_t uncompressed_length_ = 0u;
141 uint32_t compressed_length_ = 0u;
142 uint32_t computed_crc32_ = 0u;
143 };
144
145 // This method is using libz macros with old-style-casts
146 #pragma GCC diagnostic push
147 #pragma GCC diagnostic ignored "-Wold-style-cast"
zlib_inflateInit2(z_stream * stream,int window_bits)148 static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
149 return inflateInit2(stream, window_bits);
150 }
151 #pragma GCC diagnostic pop
152
Init(const ZipEntry & entry)153 bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
154 if (!ZipArchiveStreamEntry::Init(entry)) {
155 return false;
156 }
157
158 // Initialize the zlib stream struct.
159 memset(&z_stream_, 0, sizeof(z_stream_));
160 z_stream_.zalloc = Z_NULL;
161 z_stream_.zfree = Z_NULL;
162 z_stream_.opaque = Z_NULL;
163 z_stream_.next_in = nullptr;
164 z_stream_.avail_in = 0;
165 z_stream_.avail_out = 0;
166 z_stream_.data_type = Z_UNKNOWN;
167
168 // Use the undocumented "negative window bits" feature to tell zlib
169 // that there's no zlib header waiting for it.
170 int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
171 if (zerr != Z_OK) {
172 if (zerr == Z_VERSION_ERROR) {
173 ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
174 } else {
175 ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
176 }
177
178 return false;
179 }
180
181 z_stream_init_ = true;
182
183 uncompressed_length_ = entry.uncompressed_length;
184 compressed_length_ = entry.compressed_length;
185
186 out_.resize(kBufSize);
187 in_.resize(kBufSize);
188
189 computed_crc32_ = 0;
190
191 return true;
192 }
193
~ZipArchiveStreamEntryCompressed()194 ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
195 if (z_stream_init_) {
196 inflateEnd(&z_stream_);
197 z_stream_init_ = false;
198 }
199 }
200
Verify()201 bool ZipArchiveStreamEntryCompressed::Verify() {
202 return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
203 crc32_ == computed_crc32_;
204 }
205
Read()206 const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
207 // Simple validity check. The vector should *only* be handled by this code. A caller
208 // should not const-cast and modify the capacity. This may invalidate next_out.
209 //
210 // Note: it would be better to store the results of data() across Read calls.
211 CHECK_EQ(out_.capacity(), kBufSize);
212
213 if (z_stream_.avail_out == 0) {
214 z_stream_.next_out = out_.data();
215 z_stream_.avail_out = static_cast<uint32_t>(out_.size());
216 }
217
218 while (true) {
219 if (z_stream_.avail_in == 0) {
220 if (compressed_length_ == 0) {
221 return nullptr;
222 }
223 DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max()); // Should be buf size = 64k.
224 auto bytes = std::min(uint32_t(in_.size()), compressed_length_);
225 auto archive = reinterpret_cast<ZipArchive*>(handle_);
226 errno = 0;
227 auto res = archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_);
228 if (!res) {
229 if (errno != 0) {
230 ALOGE("Error reading from archive fd: %s", strerror(errno));
231 } else {
232 ALOGE("Short read of zip file, possibly corrupted zip?");
233 }
234 return nullptr;
235 }
236
237 compressed_length_ -= bytes;
238 offset_ += bytes;
239 z_stream_.next_in = res;
240 z_stream_.avail_in = bytes;
241 }
242
243 int zerr = inflate(&z_stream_, Z_NO_FLUSH);
244 if (zerr != Z_OK && zerr != Z_STREAM_END) {
245 ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
246 z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
247 return nullptr;
248 }
249
250 if (z_stream_.avail_out == 0) {
251 uncompressed_length_ -= out_.size();
252 computed_crc32_ = static_cast<uint32_t>(
253 crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
254 return &out_;
255 }
256 if (zerr == Z_STREAM_END) {
257 if (z_stream_.avail_out != 0) {
258 // Resize the vector down to the actual size of the data.
259 out_.resize(out_.size() - z_stream_.avail_out);
260 computed_crc32_ = static_cast<uint32_t>(
261 crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
262 uncompressed_length_ -= out_.size();
263 return &out_;
264 }
265 return nullptr;
266 }
267 }
268 return nullptr;
269 }
270
271 class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
272 public:
ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)273 explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
274 : ZipArchiveStreamEntryUncompressed(handle) {}
~ZipArchiveStreamEntryRawCompressed()275 virtual ~ZipArchiveStreamEntryRawCompressed() {}
276
277 bool Verify() override;
278
279 protected:
280 bool Init(const ZipEntry& entry) override;
281 };
282
Init(const ZipEntry & entry)283 bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
284 if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
285 return false;
286 }
287 length_ = entry.compressed_length;
288
289 return true;
290 }
291
Verify()292 bool ZipArchiveStreamEntryRawCompressed::Verify() {
293 return length_ == 0;
294 }
295
Create(ZipArchiveHandle handle,const ZipEntry & entry)296 ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
297 const ZipEntry& entry) {
298 ZipArchiveStreamEntry* stream = nullptr;
299 if (entry.method != kCompressStored) {
300 stream = new ZipArchiveStreamEntryCompressed(handle);
301 } else {
302 stream = new ZipArchiveStreamEntryUncompressed(handle);
303 }
304 if (stream && !stream->Init(entry)) {
305 delete stream;
306 stream = nullptr;
307 }
308
309 return stream;
310 }
311
CreateRaw(ZipArchiveHandle handle,const ZipEntry & entry)312 ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
313 const ZipEntry& entry) {
314 ZipArchiveStreamEntry* stream = nullptr;
315 if (entry.method == kCompressStored) {
316 // Not compressed, don't need to do anything special.
317 stream = new ZipArchiveStreamEntryUncompressed(handle);
318 } else {
319 stream = new ZipArchiveStreamEntryRawCompressed(handle);
320 }
321 if (stream && !stream->Init(entry)) {
322 delete stream;
323 stream = nullptr;
324 }
325 return stream;
326 }
327