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 <android-base/logging.h>
31 #include <log/log.h>
32 
33 #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 
39 static constexpr size_t kBufSize = 65535;
40 
Init(const ZipEntry & entry)41 bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
42   crc32_ = entry.crc32;
43   offset_ = entry.offset;
44   return true;
45 }
46 
47 class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
48  public:
ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)49   explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
50       : ZipArchiveStreamEntry(handle) {}
~ZipArchiveStreamEntryUncompressed()51   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 
60   uint32_t length_ = 0u;
61 
62  private:
63   std::vector<uint8_t> data_;
64   uint32_t computed_crc32_ = 0u;
65 };
66 
Init(const ZipEntry & entry)67 bool 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 
Read()80 const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
81   // Simple validity check. The vector should *only* be handled by this code. A caller
82   // 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 
87   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;
94   if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
95     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   }
107   computed_crc32_ = static_cast<uint32_t>(
108       crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
109   length_ -= bytes;
110   offset_ += bytes;
111   return &data_;
112 }
113 
Verify()114 bool ZipArchiveStreamEntryUncompressed::Verify() {
115   return length_ == 0 && crc32_ == computed_crc32_;
116 }
117 
118 class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
119  public:
ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)120   explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
121       : ZipArchiveStreamEntry(handle) {}
122   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_;
136   uint32_t uncompressed_length_ = 0u;
137   uint32_t compressed_length_ = 0u;
138   uint32_t computed_crc32_ = 0u;
139 };
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"
zlib_inflateInit2(z_stream * stream,int window_bits)144 static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
145   return inflateInit2(stream, window_bits);
146 }
147 #pragma GCC diagnostic pop
148 
Init(const ZipEntry & entry)149 bool 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) {
169       ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
170     } 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 
~ZipArchiveStreamEntryCompressed()190 ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
191   if (z_stream_init_) {
192     inflateEnd(&z_stream_);
193     z_stream_init_ = false;
194   }
195 }
196 
Verify()197 bool ZipArchiveStreamEntryCompressed::Verify() {
198   return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
199          crc32_ == computed_crc32_;
200 }
201 
Read()202 const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
203   // Simple validity check. The vector should *only* be handled by this code. A caller
204   // 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 
209   if (z_stream_.avail_out == 0) {
210     z_stream_.next_out = out_.data();
211     z_stream_.avail_out = static_cast<uint32_t>(out_.size());
212     ;
213   }
214 
215   while (true) {
216     if (z_stream_.avail_in == 0) {
217       if (compressed_length_ == 0) {
218         return nullptr;
219       }
220       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_;
223       ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
224       errno = 0;
225       if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
226         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;
235       offset_ += bytes;
236       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) {
242       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);
244       return nullptr;
245     }
246 
247     if (z_stream_.avail_out == 0) {
248       uncompressed_length_ -= out_.size();
249       computed_crc32_ = static_cast<uint32_t>(
250           crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
251       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);
257         computed_crc32_ = static_cast<uint32_t>(
258             crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
259         uncompressed_length_ -= out_.size();
260         return &out_;
261       }
262       return nullptr;
263     }
264   }
265   return nullptr;
266 }
267 
268 class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
269  public:
ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)270   explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
271       : ZipArchiveStreamEntryUncompressed(handle) {}
~ZipArchiveStreamEntryRawCompressed()272   virtual ~ZipArchiveStreamEntryRawCompressed() {}
273 
274   bool Verify() override;
275 
276  protected:
277   bool Init(const ZipEntry& entry) override;
278 };
279 
Init(const ZipEntry & entry)280 bool 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 
Verify()289 bool ZipArchiveStreamEntryRawCompressed::Verify() {
290   return length_ == 0;
291 }
292 
Create(ZipArchiveHandle handle,const ZipEntry & entry)293 ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
294                                                      const ZipEntry& entry) {
295   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 
CreateRaw(ZipArchiveHandle handle,const ZipEntry & entry)309 ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
310                                                         const ZipEntry& entry) {
311   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 }
324