• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
42   off64_t data_offset = entry.offset;
43   if (!archive->mapped_zip.SeekToOffset(data_offset)) {
44     ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
45     return false;
46   }
47   crc32_ = entry.crc32;
48   return true;
49 }
50 
51 class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
52  public:
ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)53   explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
54       : ZipArchiveStreamEntry(handle) {}
~ZipArchiveStreamEntryUncompressed()55   virtual ~ZipArchiveStreamEntryUncompressed() {}
56 
57   const std::vector<uint8_t>* Read() override;
58 
59   bool Verify() override;
60 
61  protected:
62   bool Init(const ZipEntry& entry) override;
63 
64   uint32_t length_;
65 
66  private:
67   std::vector<uint8_t> data_;
68   uint32_t computed_crc32_;
69 };
70 
Init(const ZipEntry & entry)71 bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
72   if (!ZipArchiveStreamEntry::Init(entry)) {
73     return false;
74   }
75 
76   length_ = entry.uncompressed_length;
77 
78   data_.resize(kBufSize);
79   computed_crc32_ = 0;
80 
81   return true;
82 }
83 
Read()84 const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
85   if (length_ == 0) {
86     return nullptr;
87   }
88 
89   size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
90   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
91   errno = 0;
92   if (!archive->mapped_zip.ReadData(data_.data(), bytes)) {
93     if (errno != 0) {
94       ALOGE("Error reading from archive fd: %s", strerror(errno));
95     } else {
96       ALOGE("Short read of zip file, possibly corrupted zip?");
97     }
98     length_ = 0;
99     return nullptr;
100   }
101 
102   if (bytes < data_.size()) {
103     data_.resize(bytes);
104   }
105   computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
106   length_ -= bytes;
107   return &data_;
108 }
109 
Verify()110 bool ZipArchiveStreamEntryUncompressed::Verify() {
111   return length_ == 0 && crc32_ == computed_crc32_;
112 }
113 
114 class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
115  public:
ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)116   explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
117       : ZipArchiveStreamEntry(handle) {}
118   virtual ~ZipArchiveStreamEntryCompressed();
119 
120   const std::vector<uint8_t>* Read() override;
121 
122   bool Verify() override;
123 
124  protected:
125   bool Init(const ZipEntry& entry) override;
126 
127  private:
128   bool z_stream_init_ = false;
129   z_stream z_stream_;
130   std::vector<uint8_t> in_;
131   std::vector<uint8_t> out_;
132   uint32_t uncompressed_length_;
133   uint32_t compressed_length_;
134   uint32_t computed_crc32_;
135 };
136 
137 // This method is using libz macros with old-style-casts
138 #pragma GCC diagnostic push
139 #pragma GCC diagnostic ignored "-Wold-style-cast"
zlib_inflateInit2(z_stream * stream,int window_bits)140 static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
141   return inflateInit2(stream, window_bits);
142 }
143 #pragma GCC diagnostic pop
144 
Init(const ZipEntry & entry)145 bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
146   if (!ZipArchiveStreamEntry::Init(entry)) {
147     return false;
148   }
149 
150   // Initialize the zlib stream struct.
151   memset(&z_stream_, 0, sizeof(z_stream_));
152   z_stream_.zalloc = Z_NULL;
153   z_stream_.zfree = Z_NULL;
154   z_stream_.opaque = Z_NULL;
155   z_stream_.next_in = nullptr;
156   z_stream_.avail_in = 0;
157   z_stream_.avail_out = 0;
158   z_stream_.data_type = Z_UNKNOWN;
159 
160   // Use the undocumented "negative window bits" feature to tell zlib
161   // that there's no zlib header waiting for it.
162   int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
163   if (zerr != Z_OK) {
164     if (zerr == Z_VERSION_ERROR) {
165       ALOGE("Installed zlib is not compatible with linked version (%s)",
166         ZLIB_VERSION);
167     } else {
168       ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
169     }
170 
171     return false;
172   }
173 
174   z_stream_init_ = true;
175 
176   uncompressed_length_ = entry.uncompressed_length;
177   compressed_length_ = entry.compressed_length;
178 
179   out_.resize(kBufSize);
180   in_.resize(kBufSize);
181 
182   computed_crc32_ = 0;
183 
184   return true;
185 }
186 
~ZipArchiveStreamEntryCompressed()187 ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
188   if (z_stream_init_) {
189     inflateEnd(&z_stream_);
190     z_stream_init_ = false;
191   }
192 }
193 
Verify()194 bool ZipArchiveStreamEntryCompressed::Verify() {
195   return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
196       crc32_ == computed_crc32_;
197 }
198 
Read()199 const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
200   if (z_stream_.avail_out == 0) {
201     z_stream_.next_out = out_.data();
202     z_stream_.avail_out = out_.size();;
203   }
204 
205   while (true) {
206     if (z_stream_.avail_in == 0) {
207       if (compressed_length_ == 0) {
208         return nullptr;
209       }
210       size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
211       ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
212       errno = 0;
213       if (!archive->mapped_zip.ReadData(in_.data(), bytes)) {
214         if (errno != 0) {
215           ALOGE("Error reading from archive fd: %s", strerror(errno));
216         } else {
217           ALOGE("Short read of zip file, possibly corrupted zip?");
218         }
219         return nullptr;
220       }
221 
222       compressed_length_ -= bytes;
223       z_stream_.next_in = in_.data();
224       z_stream_.avail_in = bytes;
225     }
226 
227     int zerr = inflate(&z_stream_, Z_NO_FLUSH);
228     if (zerr != Z_OK && zerr != Z_STREAM_END) {
229       ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
230           zerr, z_stream_.next_in, z_stream_.avail_in,
231           z_stream_.next_out, z_stream_.avail_out);
232       return nullptr;
233     }
234 
235     if (z_stream_.avail_out == 0) {
236       uncompressed_length_ -= out_.size();
237       computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
238       return &out_;
239     }
240     if (zerr == Z_STREAM_END) {
241       if (z_stream_.avail_out != 0) {
242         // Resize the vector down to the actual size of the data.
243         out_.resize(out_.size() - z_stream_.avail_out);
244         computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
245         uncompressed_length_ -= out_.size();
246         return &out_;
247       }
248       return nullptr;
249     }
250   }
251   return nullptr;
252 }
253 
254 class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
255  public:
ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)256   explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
257       : ZipArchiveStreamEntryUncompressed(handle) {}
~ZipArchiveStreamEntryRawCompressed()258   virtual ~ZipArchiveStreamEntryRawCompressed() {}
259 
260   bool Verify() override;
261 
262  protected:
263   bool Init(const ZipEntry& entry) override;
264 };
265 
Init(const ZipEntry & entry)266 bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
267   if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
268     return false;
269   }
270   length_ = entry.compressed_length;
271 
272   return true;
273 }
274 
Verify()275 bool ZipArchiveStreamEntryRawCompressed::Verify() {
276   return length_ == 0;
277 }
278 
Create(ZipArchiveHandle handle,const ZipEntry & entry)279 ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(
280     ZipArchiveHandle handle, const ZipEntry& entry) {
281   ZipArchiveStreamEntry* stream = nullptr;
282   if (entry.method != kCompressStored) {
283     stream = new ZipArchiveStreamEntryCompressed(handle);
284   } else {
285     stream = new ZipArchiveStreamEntryUncompressed(handle);
286   }
287   if (stream && !stream->Init(entry)) {
288     delete stream;
289     stream = nullptr;
290   }
291 
292   return stream;
293 }
294 
CreateRaw(ZipArchiveHandle handle,const ZipEntry & entry)295 ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(
296     ZipArchiveHandle handle, const ZipEntry& entry) {
297   ZipArchiveStreamEntry* stream = nullptr;
298   if (entry.method == kCompressStored) {
299     // Not compressed, don't need to do anything special.
300     stream = new ZipArchiveStreamEntryUncompressed(handle);
301   } else {
302     stream = new ZipArchiveStreamEntryRawCompressed(handle);
303   }
304   if (stream && !stream->Init(entry)) {
305     delete stream;
306     stream = nullptr;
307   }
308   return stream;
309 }
310