• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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 #include "zip_archive_private.h"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include <memory>
27 #include <vector>
28 
29 #include <android-base/file.h>
30 #include <android-base/mapped_file.h>
31 #include <android-base/unique_fd.h>
32 #include <gtest/gtest.h>
33 #include <ziparchive/zip_archive.h>
34 #include <ziparchive/zip_archive_stream_entry.h>
35 
36 static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
37 
38 static const std::string kMissingZip = "missing.zip";
39 static const std::string kValidZip = "valid.zip";
40 static const std::string kLargeZip = "large.zip";
41 static const std::string kBadCrcZip = "bad_crc.zip";
42 static const std::string kCrashApk = "crash.apk";
43 static const std::string kBadFilenameZip = "bad_filename.zip";
44 static const std::string kUpdateZip = "dummy-update.zip";
45 
46 static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
47                                                 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
48 
49 static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I',  'M', 'K',
50                                                           207, 'H', 132, 210, '\\', '\0'};
51 
52 static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
53 
54 static const std::string kATxtName("a.txt");
55 static const std::string kBTxtName("b.txt");
56 static const std::string kNonexistentTxtName("nonexistent.txt");
57 static const std::string kEmptyTxtName("empty.txt");
58 static const std::string kLargeCompressTxtName("compress.txt");
59 static const std::string kLargeUncompressTxtName("uncompress.txt");
60 
OpenArchiveWrapper(const std::string & name,ZipArchiveHandle * handle)61 static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
62   const std::string abs_path = test_data_dir + "/" + name;
63   return OpenArchive(abs_path.c_str(), handle);
64 }
65 
SetZipString(ZipString * zip_str,const std::string & str)66 static void SetZipString(ZipString* zip_str, const std::string& str) {
67   zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
68   zip_str->name_length = str.size();
69 }
70 
TEST(ziparchive,Open)71 TEST(ziparchive, Open) {
72   ZipArchiveHandle handle;
73   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
74   CloseArchive(handle);
75 
76   ASSERT_EQ(-1, OpenArchiveWrapper(kBadFilenameZip, &handle));
77   CloseArchive(handle);
78 }
79 
TEST(ziparchive,OutOfBound)80 TEST(ziparchive, OutOfBound) {
81   ZipArchiveHandle handle;
82   ASSERT_EQ(-8, OpenArchiveWrapper(kCrashApk, &handle));
83   CloseArchive(handle);
84 }
85 
TEST(ziparchive,OpenMissing)86 TEST(ziparchive, OpenMissing) {
87   ZipArchiveHandle handle;
88   ASSERT_NE(0, OpenArchiveWrapper(kMissingZip, &handle));
89 
90   // Confirm the file descriptor is not going to be mistaken for a valid one.
91   ASSERT_EQ(-1, GetFileDescriptor(handle));
92 }
93 
TEST(ziparchive,OpenAssumeFdOwnership)94 TEST(ziparchive, OpenAssumeFdOwnership) {
95   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
96   ASSERT_NE(-1, fd);
97   ZipArchiveHandle handle;
98   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
99   CloseArchive(handle);
100   ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
101   ASSERT_EQ(EBADF, errno);
102 }
103 
TEST(ziparchive,OpenDoNotAssumeFdOwnership)104 TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
105   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
106   ASSERT_NE(-1, fd);
107   ZipArchiveHandle handle;
108   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
109   CloseArchive(handle);
110   ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
111   close(fd);
112 }
113 
AssertIterationOrder(const ZipString * prefix,const ZipString * suffix,const std::vector<std::string> & expected_names_sorted)114 static void AssertIterationOrder(const ZipString* prefix, const ZipString* suffix,
115                                  const std::vector<std::string>& expected_names_sorted) {
116   ZipArchiveHandle handle;
117   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
118 
119   void* iteration_cookie;
120   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
121 
122   ZipEntry data;
123   std::vector<std::string> names;
124 
125   ZipString name;
126   for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
127     ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
128     names.push_back(std::string(reinterpret_cast<const char*>(name.name), name.name_length));
129   }
130 
131   // End of iteration.
132   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
133   CloseArchive(handle);
134 
135   // Assert that the names are as expected.
136   std::sort(names.begin(), names.end());
137   ASSERT_EQ(expected_names_sorted, names);
138 }
139 
TEST(ziparchive,Iteration)140 TEST(ziparchive, Iteration) {
141   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
142                                                                   "b/d.txt"};
143 
144   AssertIterationOrder(nullptr, nullptr, kExpectedMatchesSorted);
145 }
146 
TEST(ziparchive,IterationWithPrefix)147 TEST(ziparchive, IterationWithPrefix) {
148   ZipString prefix("b/");
149   static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
150 
151   AssertIterationOrder(&prefix, nullptr, kExpectedMatchesSorted);
152 }
153 
TEST(ziparchive,IterationWithSuffix)154 TEST(ziparchive, IterationWithSuffix) {
155   ZipString suffix(".txt");
156   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
157                                                                   "b/d.txt"};
158 
159   AssertIterationOrder(nullptr, &suffix, kExpectedMatchesSorted);
160 }
161 
TEST(ziparchive,IterationWithPrefixAndSuffix)162 TEST(ziparchive, IterationWithPrefixAndSuffix) {
163   ZipString prefix("b");
164   ZipString suffix(".txt");
165   static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
166 
167   AssertIterationOrder(&prefix, &suffix, kExpectedMatchesSorted);
168 }
169 
TEST(ziparchive,IterationWithBadPrefixAndSuffix)170 TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
171   ZipArchiveHandle handle;
172   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
173 
174   void* iteration_cookie;
175   ZipString prefix("x");
176   ZipString suffix("y");
177   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
178 
179   ZipEntry data;
180   ZipString name;
181 
182   // End of iteration.
183   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
184 
185   CloseArchive(handle);
186 }
187 
TEST(ziparchive,FindEntry)188 TEST(ziparchive, FindEntry) {
189   ZipArchiveHandle handle;
190   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
191 
192   ZipEntry data;
193   ZipString name;
194   SetZipString(&name, kATxtName);
195   ASSERT_EQ(0, FindEntry(handle, name, &data));
196 
197   // Known facts about a.txt, from zipinfo -v.
198   ASSERT_EQ(63, data.offset);
199   ASSERT_EQ(kCompressDeflated, data.method);
200   ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
201   ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
202   ASSERT_EQ(0x950821c5, data.crc32);
203   ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
204 
205   // An entry that doesn't exist. Should be a negative return code.
206   ZipString absent_name;
207   SetZipString(&absent_name, kNonexistentTxtName);
208   ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
209 
210   CloseArchive(handle);
211 }
212 
TEST(ziparchive,TestInvalidDeclaredLength)213 TEST(ziparchive, TestInvalidDeclaredLength) {
214   ZipArchiveHandle handle;
215   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
216 
217   void* iteration_cookie;
218   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
219 
220   ZipString name;
221   ZipEntry data;
222 
223   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
224   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
225 
226   CloseArchive(handle);
227 }
228 
TEST(ziparchive,ExtractToMemory)229 TEST(ziparchive, ExtractToMemory) {
230   ZipArchiveHandle handle;
231   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
232 
233   // An entry that's deflated.
234   ZipEntry data;
235   ZipString a_name;
236   SetZipString(&a_name, kATxtName);
237   ASSERT_EQ(0, FindEntry(handle, a_name, &data));
238   const uint32_t a_size = data.uncompressed_length;
239   ASSERT_EQ(a_size, kATxtContents.size());
240   uint8_t* buffer = new uint8_t[a_size];
241   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
242   ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
243   delete[] buffer;
244 
245   // An entry that's stored.
246   ZipString b_name;
247   SetZipString(&b_name, kBTxtName);
248   ASSERT_EQ(0, FindEntry(handle, b_name, &data));
249   const uint32_t b_size = data.uncompressed_length;
250   ASSERT_EQ(b_size, kBTxtContents.size());
251   buffer = new uint8_t[b_size];
252   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
253   ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
254   delete[] buffer;
255 
256   CloseArchive(handle);
257 }
258 
259 static const uint32_t kEmptyEntriesZip[] = {
260     0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
261     0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
262     0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
263     0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
264     0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
265     0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
266 
267 // This is a zip file containing a single entry (ab.txt) that contains
268 // 90072 repetitions of the string "ab\n" and has an uncompressed length
269 // of 270216 bytes.
270 static const uint16_t kAbZip[] = {
271     0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
272     0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
273     0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
274     0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
275     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
276     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
277     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
278     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
279     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
280     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
281     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
282     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
283     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
284     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
285     0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
286     0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
287     0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
288     0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
289     0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
290 
291 static const std::string kAbTxtName("ab.txt");
292 static const size_t kAbUncompressedSize = 270216;
293 
TEST(ziparchive,EmptyEntries)294 TEST(ziparchive, EmptyEntries) {
295   TemporaryFile tmp_file;
296   ASSERT_NE(-1, tmp_file.fd);
297   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
298 
299   ZipArchiveHandle handle;
300   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
301 
302   ZipEntry entry;
303   ZipString empty_name;
304   SetZipString(&empty_name, kEmptyTxtName);
305   ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
306   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
307   uint8_t buffer[1];
308   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
309 
310   TemporaryFile tmp_output_file;
311   ASSERT_NE(-1, tmp_output_file.fd);
312   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
313 
314   struct stat stat_buf;
315   ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
316   ASSERT_EQ(0, stat_buf.st_size);
317 }
318 
TEST(ziparchive,EntryLargerThan32K)319 TEST(ziparchive, EntryLargerThan32K) {
320   TemporaryFile tmp_file;
321   ASSERT_NE(-1, tmp_file.fd);
322   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
323                                         sizeof(kAbZip) - 1));
324   ZipArchiveHandle handle;
325   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
326 
327   ZipEntry entry;
328   ZipString ab_name;
329   SetZipString(&ab_name, kAbTxtName);
330   ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
331   ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
332 
333   // Extract the entry to memory.
334   std::vector<uint8_t> buffer(kAbUncompressedSize);
335   ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
336 
337   // Extract the entry to a file.
338   TemporaryFile tmp_output_file;
339   ASSERT_NE(-1, tmp_output_file.fd);
340   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
341 
342   // Make sure the extracted file size is as expected.
343   struct stat stat_buf;
344   ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
345   ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
346 
347   // Read the file back to a buffer and make sure the contents are
348   // the same as the memory buffer we extracted directly to.
349   std::vector<uint8_t> file_contents(kAbUncompressedSize);
350   ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
351   ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
352   ASSERT_EQ(file_contents, buffer);
353 
354   for (int i = 0; i < 90072; ++i) {
355     const uint8_t* line = &file_contents[0] + (3 * i);
356     ASSERT_EQ('a', line[0]);
357     ASSERT_EQ('b', line[1]);
358     ASSERT_EQ('\n', line[2]);
359   }
360 }
361 
TEST(ziparchive,TrailerAfterEOCD)362 TEST(ziparchive, TrailerAfterEOCD) {
363   TemporaryFile tmp_file;
364   ASSERT_NE(-1, tmp_file.fd);
365 
366   // Create a file with 8 bytes of random garbage.
367   static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
368   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
369   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
370 
371   ZipArchiveHandle handle;
372   ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
373 }
374 
TEST(ziparchive,ExtractToFile)375 TEST(ziparchive, ExtractToFile) {
376   TemporaryFile tmp_file;
377   ASSERT_NE(-1, tmp_file.fd);
378   const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
379   const size_t data_size = sizeof(data);
380 
381   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
382 
383   ZipArchiveHandle handle;
384   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
385 
386   ZipEntry entry;
387   ZipString name;
388   SetZipString(&name, kATxtName);
389   ASSERT_EQ(0, FindEntry(handle, name, &entry));
390   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
391 
392   // Assert that the first 8 bytes of the file haven't been clobbered.
393   uint8_t read_buffer[data_size];
394   ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
395   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
396   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
397 
398   // Assert that the remainder of the file contains the incompressed data.
399   std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
400   ASSERT_TRUE(
401       android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
402   ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
403 
404   // Assert that the total length of the file is sane
405   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
406             lseek(tmp_file.fd, 0, SEEK_END));
407 }
408 
409 #if !defined(_WIN32)
TEST(ziparchive,OpenFromMemory)410 TEST(ziparchive, OpenFromMemory) {
411   const std::string zip_path = test_data_dir + "/" + kUpdateZip;
412   android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
413   ASSERT_NE(-1, fd);
414   struct stat sb;
415   ASSERT_EQ(0, fstat(fd, &sb));
416 
417   // Memory map the file first and open the archive from the memory region.
418   auto file_map{android::base::MappedFile::FromFd(fd, 0, sb.st_size, PROT_READ)};
419   ZipArchiveHandle handle;
420   ASSERT_EQ(0,
421             OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
422 
423   // Assert one entry can be found and extracted correctly.
424   std::string BINARY_PATH("META-INF/com/google/android/update-binary");
425   ZipString binary_path(BINARY_PATH.c_str());
426   ZipEntry binary_entry;
427   ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
428   TemporaryFile tmp_binary;
429   ASSERT_NE(-1, tmp_binary.fd);
430   ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
431 }
432 #endif
433 
ZipArchiveStreamTest(ZipArchiveHandle & handle,const std::string & entry_name,bool raw,bool verified,ZipEntry * entry,std::vector<uint8_t> * read_data)434 static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
435                                  bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
436   ZipString name;
437   SetZipString(&name, entry_name);
438   ASSERT_EQ(0, FindEntry(handle, name, entry));
439   std::unique_ptr<ZipArchiveStreamEntry> stream;
440   if (raw) {
441     stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
442     if (entry->method == kCompressStored) {
443       read_data->resize(entry->uncompressed_length);
444     } else {
445       read_data->resize(entry->compressed_length);
446     }
447   } else {
448     stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
449     read_data->resize(entry->uncompressed_length);
450   }
451   uint8_t* read_data_ptr = read_data->data();
452   ASSERT_TRUE(stream.get() != nullptr);
453   const std::vector<uint8_t>* data;
454   uint64_t total_size = 0;
455   while ((data = stream->Read()) != nullptr) {
456     total_size += data->size();
457     memcpy(read_data_ptr, data->data(), data->size());
458     read_data_ptr += data->size();
459   }
460   ASSERT_EQ(verified, stream->Verify());
461   ASSERT_EQ(total_size, read_data->size());
462 }
463 
ZipArchiveStreamTestUsingContents(const std::string & zip_file,const std::string & entry_name,const std::vector<uint8_t> & contents,bool raw)464 static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
465                                               const std::string& entry_name,
466                                               const std::vector<uint8_t>& contents, bool raw) {
467   ZipArchiveHandle handle;
468   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
469 
470   ZipEntry entry;
471   std::vector<uint8_t> read_data;
472   ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
473 
474   ASSERT_EQ(contents.size(), read_data.size());
475   ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
476 
477   CloseArchive(handle);
478 }
479 
ZipArchiveStreamTestUsingMemory(const std::string & zip_file,const std::string & entry_name)480 static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
481                                             const std::string& entry_name) {
482   ZipArchiveHandle handle;
483   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
484 
485   ZipEntry entry;
486   std::vector<uint8_t> read_data;
487   ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
488 
489   std::vector<uint8_t> cmp_data(entry.uncompressed_length);
490   ASSERT_EQ(entry.uncompressed_length, read_data.size());
491   ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size()));
492   ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
493 
494   CloseArchive(handle);
495 }
496 
TEST(ziparchive,StreamCompressed)497 TEST(ziparchive, StreamCompressed) {
498   ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContents, false);
499 }
500 
TEST(ziparchive,StreamUncompressed)501 TEST(ziparchive, StreamUncompressed) {
502   ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, false);
503 }
504 
TEST(ziparchive,StreamRawCompressed)505 TEST(ziparchive, StreamRawCompressed) {
506   ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContentsCompressed, true);
507 }
508 
TEST(ziparchive,StreamRawUncompressed)509 TEST(ziparchive, StreamRawUncompressed) {
510   ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, true);
511 }
512 
TEST(ziparchive,StreamLargeCompressed)513 TEST(ziparchive, StreamLargeCompressed) {
514   ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeCompressTxtName);
515 }
516 
TEST(ziparchive,StreamLargeUncompressed)517 TEST(ziparchive, StreamLargeUncompressed) {
518   ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeUncompressTxtName);
519 }
520 
TEST(ziparchive,StreamCompressedBadCrc)521 TEST(ziparchive, StreamCompressedBadCrc) {
522   ZipArchiveHandle handle;
523   ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
524 
525   ZipEntry entry;
526   std::vector<uint8_t> read_data;
527   ZipArchiveStreamTest(handle, kATxtName, false, false, &entry, &read_data);
528 
529   CloseArchive(handle);
530 }
531 
TEST(ziparchive,StreamUncompressedBadCrc)532 TEST(ziparchive, StreamUncompressedBadCrc) {
533   ZipArchiveHandle handle;
534   ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
535 
536   ZipEntry entry;
537   std::vector<uint8_t> read_data;
538   ZipArchiveStreamTest(handle, kBTxtName, false, false, &entry, &read_data);
539 
540   CloseArchive(handle);
541 }
542 
543 // Generated using the following Java program:
544 //     public static void main(String[] foo) throws Exception {
545 //       FileOutputStream fos = new
546 //       FileOutputStream("/tmp/data_descriptor.zip");
547 //       ZipOutputStream zos = new ZipOutputStream(fos);
548 //       ZipEntry ze = new ZipEntry("name");
549 //       ze.setMethod(ZipEntry.DEFLATED);
550 //       zos.putNextEntry(ze);
551 //       zos.write("abdcdefghijk".getBytes());
552 //       zos.closeEntry();
553 //       zos.close();
554 //     }
555 //
556 // cat /tmp/data_descriptor.zip | xxd -i
557 //
558 static const std::vector<uint8_t> kDataDescriptorZipFile{
559     0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
560     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
561     0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
562     //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
563     0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
564     0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
565     0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
566     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
567     0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
568     0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
569 
570 // The offsets of the data descriptor in this file, so we can mess with
571 // them later in the test.
572 static constexpr uint32_t kDataDescriptorOffset = 48;
573 static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
574 static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
575 
ExtractEntryToMemory(const std::vector<uint8_t> & zip_data,std::vector<uint8_t> * entry_out,int32_t * error_code_out)576 static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
577                                  std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
578   TemporaryFile tmp_file;
579   ASSERT_NE(-1, tmp_file.fd);
580   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
581   ZipArchiveHandle handle;
582   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
583 
584   // This function expects a variant of kDataDescriptorZipFile, for look for
585   // an entry whose name is "name" and whose size is 12 (contents =
586   // "abdcdefghijk").
587   ZipEntry entry;
588   ZipString name;
589   std::string name_str = "name";
590   SetZipString(&name, name_str);
591 
592   ASSERT_EQ(0, FindEntry(handle, name, &entry));
593   ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
594 
595   entry_out->resize(12);
596   (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
597 
598   CloseArchive(handle);
599 }
600 
TEST(ziparchive,ValidDataDescriptors)601 TEST(ziparchive, ValidDataDescriptors) {
602   std::vector<uint8_t> entry;
603   int32_t error_code = 0;
604   ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
605 
606   ASSERT_EQ(0, error_code);
607   ASSERT_EQ(12u, entry.size());
608   ASSERT_EQ('a', entry[0]);
609   ASSERT_EQ('k', entry[11]);
610 }
611 
TEST(ziparchive,InvalidDataDescriptors_csize)612 TEST(ziparchive, InvalidDataDescriptors_csize) {
613   std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
614   invalid_csize[kCSizeOffset] = 0xfe;
615 
616   std::vector<uint8_t> entry;
617   int32_t error_code = 0;
618   ExtractEntryToMemory(invalid_csize, &entry, &error_code);
619 
620   ASSERT_EQ(kInconsistentInformation, error_code);
621 }
622 
TEST(ziparchive,InvalidDataDescriptors_size)623 TEST(ziparchive, InvalidDataDescriptors_size) {
624   std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
625   invalid_size[kSizeOffset] = 0xfe;
626 
627   std::vector<uint8_t> entry;
628   int32_t error_code = 0;
629   ExtractEntryToMemory(invalid_size, &entry, &error_code);
630 
631   ASSERT_EQ(kInconsistentInformation, error_code);
632 }
633 
TEST(ziparchive,ErrorCodeString)634 TEST(ziparchive, ErrorCodeString) {
635   ASSERT_STREQ("Success", ErrorCodeString(0));
636 
637   // Out of bounds.
638   ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
639   ASSERT_STREQ("Unknown return code", ErrorCodeString(-13));
640 
641   ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
642 }
643 
644 // A zip file whose local file header at offset zero is corrupted.
645 //
646 // ---------------
647 // cat foo > a.txt
648 // zip a.zip a.txt
649 // cat a.zip | xxd -i
650 //
651 // Manual changes :
652 // [2] = 0xff  // Corrupt the LFH signature of entry 0.
653 // [3] = 0xff  // Corrupt the LFH signature of entry 0.
654 static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
655     //[lfh-sig-----------], [lfh contents---------------------------------
656     0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
657     //--------------------------------------------------------------------
658     0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
659     //-------------------------------]  [file-name-----------------], [---
660     0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
661     // entry-contents------------------------------------------------------
662     0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
663     //--------------------------------------------------------------------
664     0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
665     //-------------------------------------], [cd-record-sig-------], [---
666     0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
667     // cd-record-----------------------------------------------------------
668     0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
669     //--------------------------------------------------------------------
670     0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
671     //--------------------------------------------------------------------
672     0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
673     //-]  [lfh-file-header-off-], [file-name-----------------], [extra----
674     0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
675     //--------------------------------------------------------------------
676     0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
677     //-------------------------------------------------------], [eocd-sig-
678     0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
679     //-------], [---------------------------------------------------------
680     0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
681     //-------------------------------------------]
682     0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
683 
TEST(ziparchive,BrokenLfhSignature)684 TEST(ziparchive, BrokenLfhSignature) {
685   TemporaryFile tmp_file;
686   ASSERT_NE(-1, tmp_file.fd);
687   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
688                                         kZipFileWithBrokenLfhSignature.size()));
689   ZipArchiveHandle handle;
690   ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
691 }
692 
693 class VectorReader : public zip_archive::Reader {
694  public:
VectorReader(const std::vector<uint8_t> & input)695   VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
696 
ReadAtOffset(uint8_t * buf,size_t len,uint32_t offset) const697   bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
698     if ((offset + len) < input_.size()) {
699       return false;
700     }
701 
702     memcpy(buf, &input_[offset], len);
703     return true;
704   }
705 
706  private:
707   const std::vector<uint8_t>& input_;
708 };
709 
710 class VectorWriter : public zip_archive::Writer {
711  public:
VectorWriter()712   VectorWriter() : Writer() {}
713 
Append(uint8_t * buf,size_t size)714   bool Append(uint8_t* buf, size_t size) {
715     output_.insert(output_.end(), buf, buf + size);
716     return true;
717   }
718 
GetOutput()719   std::vector<uint8_t>& GetOutput() { return output_; }
720 
721  private:
722   std::vector<uint8_t> output_;
723 };
724 
725 class BadReader : public zip_archive::Reader {
726  public:
BadReader()727   BadReader() : Reader() {}
728 
ReadAtOffset(uint8_t *,size_t,uint32_t) const729   bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
730 };
731 
732 class BadWriter : public zip_archive::Writer {
733  public:
BadWriter()734   BadWriter() : Writer() {}
735 
Append(uint8_t *,size_t)736   bool Append(uint8_t*, size_t) { return false; }
737 };
738 
TEST(ziparchive,Inflate)739 TEST(ziparchive, Inflate) {
740   const uint32_t compressed_length = kATxtContentsCompressed.size();
741   const uint32_t uncompressed_length = kATxtContents.size();
742 
743   const VectorReader reader(kATxtContentsCompressed);
744   {
745     VectorWriter writer;
746     uint64_t crc_out = 0;
747 
748     int32_t ret =
749         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
750     ASSERT_EQ(0, ret);
751     ASSERT_EQ(kATxtContents, writer.GetOutput());
752     ASSERT_EQ(0x950821C5u, crc_out);
753   }
754 
755   {
756     VectorWriter writer;
757     int32_t ret =
758         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
759     ASSERT_EQ(0, ret);
760     ASSERT_EQ(kATxtContents, writer.GetOutput());
761   }
762 
763   {
764     BadWriter writer;
765     int32_t ret =
766         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
767     ASSERT_EQ(kIoError, ret);
768   }
769 
770   {
771     BadReader reader;
772     VectorWriter writer;
773     int32_t ret =
774         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
775     ASSERT_EQ(kIoError, ret);
776     ASSERT_EQ(0u, writer.GetOutput().size());
777   }
778 }
779