• 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 <errno.h>
18 #include <fcntl.h>
19 #include <getopt.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 
24 #include <memory>
25 #include <vector>
26 
27 #include <android-base/file.h>
28 #include <android-base/test_utils.h>
29 #include <gtest/gtest.h>
30 #include <ziparchive/zip_archive.h>
31 #include <ziparchive/zip_archive_stream_entry.h>
32 
33 static std::string test_data_dir;
34 
35 static const std::string kMissingZip = "missing.zip";
36 static const std::string kValidZip = "valid.zip";
37 static const std::string kLargeZip = "large.zip";
38 static const std::string kBadCrcZip = "bad_crc.zip";
39 
40 static const std::vector<uint8_t> kATxtContents {
41   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
42   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
43   '\n'
44 };
45 
46 static const std::vector<uint8_t> kATxtContentsCompressed {
47   'K', 'L', 'J', 'N', 'I', 'M', 'K', 207, 'H',
48   132, 210, '\\', '\0'
49 };
50 
51 static const std::vector<uint8_t> kBTxtContents {
52   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
53   '\n'
54 };
55 
56 static const std::string kATxtName("a.txt");
57 static const std::string kBTxtName("b.txt");
58 static const std::string kNonexistentTxtName("nonexistent.txt");
59 static const std::string kEmptyTxtName("empty.txt");
60 static const std::string kLargeCompressTxtName("compress.txt");
61 static const std::string kLargeUncompressTxtName("uncompress.txt");
62 
OpenArchiveWrapper(const std::string & name,ZipArchiveHandle * handle)63 static int32_t OpenArchiveWrapper(const std::string& name,
64                                   ZipArchiveHandle* handle) {
65   const std::string abs_path = test_data_dir + "/" + name;
66   return OpenArchive(abs_path.c_str(), handle);
67 }
68 
AssertNameEquals(const std::string & name_str,const ZipString & name)69 static void AssertNameEquals(const std::string& name_str,
70                              const ZipString& name) {
71   ASSERT_EQ(name_str.size(), name.name_length);
72   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
73 }
74 
SetZipString(ZipString * zip_str,const std::string & str)75 static void SetZipString(ZipString* zip_str, const std::string& str) {
76   zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
77   zip_str->name_length = str.size();
78 }
79 
TEST(ziparchive,Open)80 TEST(ziparchive, Open) {
81   ZipArchiveHandle handle;
82   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
83 
84   CloseArchive(handle);
85 }
86 
TEST(ziparchive,OpenMissing)87 TEST(ziparchive, OpenMissing) {
88   ZipArchiveHandle handle;
89   ASSERT_NE(0, OpenArchiveWrapper(kMissingZip, &handle));
90 
91   // Confirm the file descriptor is not going to be mistaken for a valid one.
92   ASSERT_EQ(-1, GetFileDescriptor(handle));
93 }
94 
TEST(ziparchive,OpenAssumeFdOwnership)95 TEST(ziparchive, OpenAssumeFdOwnership) {
96   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
97   ASSERT_NE(-1, fd);
98   ZipArchiveHandle handle;
99   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
100   CloseArchive(handle);
101   ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
102   ASSERT_EQ(EBADF, errno);
103 }
104 
TEST(ziparchive,OpenDoNotAssumeFdOwnership)105 TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
106   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
107   ASSERT_NE(-1, fd);
108   ZipArchiveHandle handle;
109   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
110   CloseArchive(handle);
111   ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
112   close(fd);
113 }
114 
TEST(ziparchive,Iteration)115 TEST(ziparchive, Iteration) {
116   ZipArchiveHandle handle;
117   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
118 
119   void* iteration_cookie;
120   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
121 
122   ZipEntry data;
123   ZipString name;
124 
125   // b/c.txt
126   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
127   AssertNameEquals("b/c.txt", name);
128 
129   // b/d.txt
130   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
131   AssertNameEquals("b/d.txt", name);
132 
133   // a.txt
134   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
135   AssertNameEquals("a.txt", name);
136 
137   // b.txt
138   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
139   AssertNameEquals("b.txt", name);
140 
141   // b/
142   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
143   AssertNameEquals("b/", name);
144 
145   // End of iteration.
146   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
147 
148   CloseArchive(handle);
149 }
150 
TEST(ziparchive,IterationWithPrefix)151 TEST(ziparchive, IterationWithPrefix) {
152   ZipArchiveHandle handle;
153   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
154 
155   void* iteration_cookie;
156   ZipString prefix("b/");
157   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, nullptr));
158 
159   ZipEntry data;
160   ZipString name;
161 
162   // b/c.txt
163   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
164   AssertNameEquals("b/c.txt", name);
165 
166   // b/d.txt
167   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
168   AssertNameEquals("b/d.txt", name);
169 
170   // b/
171   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
172   AssertNameEquals("b/", name);
173 
174   // End of iteration.
175   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
176 
177   CloseArchive(handle);
178 }
179 
TEST(ziparchive,IterationWithSuffix)180 TEST(ziparchive, IterationWithSuffix) {
181   ZipArchiveHandle handle;
182   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
183 
184   void* iteration_cookie;
185   ZipString suffix(".txt");
186   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, &suffix));
187 
188   ZipEntry data;
189   ZipString name;
190 
191   // b/c.txt
192   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
193   AssertNameEquals("b/c.txt", name);
194 
195   // b/d.txt
196   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
197   AssertNameEquals("b/d.txt", name);
198 
199   // a.txt
200   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
201   AssertNameEquals("a.txt", name);
202 
203   // b.txt
204   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
205   AssertNameEquals("b.txt", name);
206 
207   // End of iteration.
208   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
209 
210   CloseArchive(handle);
211 }
212 
TEST(ziparchive,IterationWithPrefixAndSuffix)213 TEST(ziparchive, IterationWithPrefixAndSuffix) {
214   ZipArchiveHandle handle;
215   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
216 
217   void* iteration_cookie;
218   ZipString prefix("b");
219   ZipString suffix(".txt");
220   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
221 
222   ZipEntry data;
223   ZipString name;
224 
225   // b/c.txt
226   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
227   AssertNameEquals("b/c.txt", name);
228 
229   // b/d.txt
230   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
231   AssertNameEquals("b/d.txt", name);
232 
233   // b.txt
234   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
235   AssertNameEquals("b.txt", name);
236 
237   // End of iteration.
238   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
239 
240   CloseArchive(handle);
241 }
242 
TEST(ziparchive,IterationWithBadPrefixAndSuffix)243 TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
244   ZipArchiveHandle handle;
245   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
246 
247   void* iteration_cookie;
248   ZipString prefix("x");
249   ZipString suffix("y");
250   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
251 
252   ZipEntry data;
253   ZipString name;
254 
255   // End of iteration.
256   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
257 
258   CloseArchive(handle);
259 }
260 
TEST(ziparchive,FindEntry)261 TEST(ziparchive, FindEntry) {
262   ZipArchiveHandle handle;
263   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
264 
265   ZipEntry data;
266   ZipString name;
267   SetZipString(&name, kATxtName);
268   ASSERT_EQ(0, FindEntry(handle, name, &data));
269 
270   // Known facts about a.txt, from zipinfo -v.
271   ASSERT_EQ(63, data.offset);
272   ASSERT_EQ(kCompressDeflated, data.method);
273   ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
274   ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
275   ASSERT_EQ(0x950821c5, data.crc32);
276   ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
277 
278   // An entry that doesn't exist. Should be a negative return code.
279   ZipString absent_name;
280   SetZipString(&absent_name, kNonexistentTxtName);
281   ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
282 
283   CloseArchive(handle);
284 }
285 
TEST(ziparchive,TestInvalidDeclaredLength)286 TEST(ziparchive, TestInvalidDeclaredLength) {
287   ZipArchiveHandle handle;
288   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
289 
290   void* iteration_cookie;
291   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
292 
293   ZipString name;
294   ZipEntry data;
295 
296   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
297   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
298 
299   CloseArchive(handle);
300 }
301 
TEST(ziparchive,ExtractToMemory)302 TEST(ziparchive, ExtractToMemory) {
303   ZipArchiveHandle handle;
304   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
305 
306   // An entry that's deflated.
307   ZipEntry data;
308   ZipString a_name;
309   SetZipString(&a_name, kATxtName);
310   ASSERT_EQ(0, FindEntry(handle, a_name, &data));
311   const uint32_t a_size = data.uncompressed_length;
312   ASSERT_EQ(a_size, kATxtContents.size());
313   uint8_t* buffer = new uint8_t[a_size];
314   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
315   ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
316   delete[] buffer;
317 
318   // An entry that's stored.
319   ZipString b_name;
320   SetZipString(&b_name, kBTxtName);
321   ASSERT_EQ(0, FindEntry(handle, b_name, &data));
322   const uint32_t b_size = data.uncompressed_length;
323   ASSERT_EQ(b_size, kBTxtContents.size());
324   buffer = new uint8_t[b_size];
325   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
326   ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
327   delete[] buffer;
328 
329   CloseArchive(handle);
330 }
331 
332 static const uint32_t kEmptyEntriesZip[] = {
333       0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
334       0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
335       0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
336       0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
337       0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
338       0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
339       0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
340 
341 // This is a zip file containing a single entry (ab.txt) that contains
342 // 90072 repetitions of the string "ab\n" and has an uncompressed length
343 // of 270216 bytes.
344 static const uint16_t kAbZip[] = {
345   0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
346   0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
347   0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
348   0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
349   0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
350   0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
351   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
352   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
353   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
354   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
355   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
356   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
357   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
358   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
359   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
360   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
361   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
362   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
363   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
364   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
365   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
366   0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
367   0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
368   0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
369   0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
370   0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
371   0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
372   0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
373 };
374 
375 static const std::string kAbTxtName("ab.txt");
376 static const size_t kAbUncompressedSize = 270216;
377 
TEST(ziparchive,EmptyEntries)378 TEST(ziparchive, EmptyEntries) {
379   TemporaryFile tmp_file;
380   ASSERT_NE(-1, tmp_file.fd);
381   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
382 
383   ZipArchiveHandle handle;
384   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
385 
386   ZipEntry entry;
387   ZipString empty_name;
388   SetZipString(&empty_name, kEmptyTxtName);
389   ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
390   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
391   uint8_t buffer[1];
392   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
393 
394 
395   TemporaryFile tmp_output_file;
396   ASSERT_NE(-1, tmp_output_file.fd);
397   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
398 
399   struct stat stat_buf;
400   ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
401   ASSERT_EQ(0, stat_buf.st_size);
402 }
403 
TEST(ziparchive,EntryLargerThan32K)404 TEST(ziparchive, EntryLargerThan32K) {
405   TemporaryFile tmp_file;
406   ASSERT_NE(-1, tmp_file.fd);
407   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
408                          sizeof(kAbZip) - 1));
409   ZipArchiveHandle handle;
410   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
411 
412   ZipEntry entry;
413   ZipString ab_name;
414   SetZipString(&ab_name, kAbTxtName);
415   ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
416   ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
417 
418   // Extract the entry to memory.
419   std::vector<uint8_t> buffer(kAbUncompressedSize);
420   ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
421 
422   // Extract the entry to a file.
423   TemporaryFile tmp_output_file;
424   ASSERT_NE(-1, tmp_output_file.fd);
425   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
426 
427   // Make sure the extracted file size is as expected.
428   struct stat stat_buf;
429   ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
430   ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
431 
432   // Read the file back to a buffer and make sure the contents are
433   // the same as the memory buffer we extracted directly to.
434   std::vector<uint8_t> file_contents(kAbUncompressedSize);
435   ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
436   ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
437                                        file_contents.size()));
438   ASSERT_EQ(file_contents, buffer);
439 
440   for (int i = 0; i < 90072; ++i) {
441     const uint8_t* line = &file_contents[0] + (3 * i);
442     ASSERT_EQ('a', line[0]);
443     ASSERT_EQ('b', line[1]);
444     ASSERT_EQ('\n', line[2]);
445   }
446 }
447 
TEST(ziparchive,TrailerAfterEOCD)448 TEST(ziparchive, TrailerAfterEOCD) {
449   TemporaryFile tmp_file;
450   ASSERT_NE(-1, tmp_file.fd);
451 
452   // Create a file with 8 bytes of random garbage.
453   static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
454   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
455   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
456 
457   ZipArchiveHandle handle;
458   ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
459 }
460 
TEST(ziparchive,ExtractToFile)461 TEST(ziparchive, ExtractToFile) {
462   TemporaryFile tmp_file;
463   ASSERT_NE(-1, tmp_file.fd);
464   const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
465   const size_t data_size = sizeof(data);
466 
467   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
468 
469   ZipArchiveHandle handle;
470   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
471 
472   ZipEntry entry;
473   ZipString name;
474   SetZipString(&name, kATxtName);
475   ASSERT_EQ(0, FindEntry(handle, name, &entry));
476   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
477 
478 
479   // Assert that the first 8 bytes of the file haven't been clobbered.
480   uint8_t read_buffer[data_size];
481   ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
482   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
483   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
484 
485   // Assert that the remainder of the file contains the incompressed data.
486   std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
487   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
488                                        entry.uncompressed_length));
489   ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
490                       kATxtContents.size()));
491 
492   // Assert that the total length of the file is sane
493   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
494             lseek64(tmp_file.fd, 0, SEEK_END));
495 }
496 
ZipArchiveStreamTest(ZipArchiveHandle & handle,const std::string & entry_name,bool raw,bool verified,ZipEntry * entry,std::vector<uint8_t> * read_data)497 static void ZipArchiveStreamTest(
498     ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
499     bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
500   ZipString name;
501   SetZipString(&name, entry_name);
502   ASSERT_EQ(0, FindEntry(handle, name, entry));
503   std::unique_ptr<ZipArchiveStreamEntry> stream;
504   if (raw) {
505     stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
506     if (entry->method == kCompressStored) {
507       read_data->resize(entry->uncompressed_length);
508     } else {
509       read_data->resize(entry->compressed_length);
510     }
511   } else {
512     stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
513     read_data->resize(entry->uncompressed_length);
514   }
515   uint8_t* read_data_ptr = read_data->data();
516   ASSERT_TRUE(stream.get() != nullptr);
517   const std::vector<uint8_t>* data;
518   uint64_t total_size = 0;
519   while ((data = stream->Read()) != nullptr) {
520     total_size += data->size();
521     memcpy(read_data_ptr, data->data(), data->size());
522     read_data_ptr += data->size();
523   }
524   ASSERT_EQ(verified, stream->Verify());
525   ASSERT_EQ(total_size, read_data->size());
526 }
527 
ZipArchiveStreamTestUsingContents(const std::string & zip_file,const std::string & entry_name,const std::vector<uint8_t> & contents,bool raw)528 static void ZipArchiveStreamTestUsingContents(
529     const std::string& zip_file, const std::string& entry_name,
530     const std::vector<uint8_t>& contents, bool raw) {
531   ZipArchiveHandle handle;
532   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
533 
534   ZipEntry entry;
535   std::vector<uint8_t> read_data;
536   ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
537 
538   ASSERT_EQ(contents.size(), read_data.size());
539   ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
540 
541   CloseArchive(handle);
542 }
543 
ZipArchiveStreamTestUsingMemory(const std::string & zip_file,const std::string & entry_name)544 static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file, const std::string& entry_name) {
545   ZipArchiveHandle handle;
546   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
547 
548   ZipEntry entry;
549   std::vector<uint8_t> read_data;
550   ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
551 
552   std::vector<uint8_t> cmp_data(entry.uncompressed_length);
553   ASSERT_EQ(entry.uncompressed_length, read_data.size());
554   ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size()));
555   ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
556 
557   CloseArchive(handle);
558 }
559 
TEST(ziparchive,StreamCompressed)560 TEST(ziparchive, StreamCompressed) {
561   ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContents, false);
562 }
563 
TEST(ziparchive,StreamUncompressed)564 TEST(ziparchive, StreamUncompressed) {
565   ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, false);
566 }
567 
TEST(ziparchive,StreamRawCompressed)568 TEST(ziparchive, StreamRawCompressed) {
569   ZipArchiveStreamTestUsingContents(kValidZip, kATxtName, kATxtContentsCompressed, true);
570 }
571 
TEST(ziparchive,StreamRawUncompressed)572 TEST(ziparchive, StreamRawUncompressed) {
573   ZipArchiveStreamTestUsingContents(kValidZip, kBTxtName, kBTxtContents, true);
574 }
575 
TEST(ziparchive,StreamLargeCompressed)576 TEST(ziparchive, StreamLargeCompressed) {
577   ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeCompressTxtName);
578 }
579 
TEST(ziparchive,StreamLargeUncompressed)580 TEST(ziparchive, StreamLargeUncompressed) {
581   ZipArchiveStreamTestUsingMemory(kLargeZip, kLargeUncompressTxtName);
582 }
583 
TEST(ziparchive,StreamCompressedBadCrc)584 TEST(ziparchive, StreamCompressedBadCrc) {
585   ZipArchiveHandle handle;
586   ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
587 
588   ZipEntry entry;
589   std::vector<uint8_t> read_data;
590   ZipArchiveStreamTest(handle, kATxtName, false, false, &entry, &read_data);
591 
592   CloseArchive(handle);
593 }
594 
TEST(ziparchive,StreamUncompressedBadCrc)595 TEST(ziparchive, StreamUncompressedBadCrc) {
596   ZipArchiveHandle handle;
597   ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
598 
599   ZipEntry entry;
600   std::vector<uint8_t> read_data;
601   ZipArchiveStreamTest(handle, kBTxtName, false, false, &entry, &read_data);
602 
603   CloseArchive(handle);
604 }
605 
main(int argc,char ** argv)606 int main(int argc, char** argv) {
607   ::testing::InitGoogleTest(&argc, argv);
608 
609   static struct option options[] = {
610     { "test_data_dir", required_argument, nullptr, 't' },
611     { nullptr, 0, nullptr, 0 }
612   };
613 
614   while (true) {
615     int option_index;
616     const int c = getopt_long_only(argc, argv, "", options, &option_index);
617     if (c == -1) {
618       break;
619     }
620 
621     if (c == 't') {
622       test_data_dir = optarg;
623     }
624   }
625 
626   if (test_data_dir.size() == 0) {
627     printf("Test data flag (--test_data_dir) required\n\n");
628     return -1;
629   }
630 
631   if (test_data_dir[0] != '/') {
632     std::vector<char> cwd_buffer(1024);
633     const char* cwd = getcwd(cwd_buffer.data(), cwd_buffer.size() - 1);
634     if (cwd == nullptr) {
635       printf("Cannot get current working directory, use an absolute path instead, was %s\n\n",
636              test_data_dir.c_str());
637       return -2;
638     }
639     test_data_dir = '/' + test_data_dir;
640     test_data_dir = cwd + test_data_dir;
641   }
642 
643   return RUN_ALL_TESTS();
644 }
645