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