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