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 <map>
25 #include <memory>
26 #include <set>
27 #include <string_view>
28 #include <vector>
29 
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/mapped_file.h>
33 #include <android-base/memory.h>
34 #include <android-base/strings.h>
35 #include <android-base/unique_fd.h>
36 #include <gtest/gtest.h>
37 #include <ziparchive/zip_archive.h>
38 #include <ziparchive/zip_archive_stream_entry.h>
39 
40 #include "zip_archive_common.h"
41 #include "zip_archive_private.h"
42 
43 static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
44 
45 static const std::string kValidZip = "valid.zip";
46 static const std::string kLargeZip = "large.zip";
47 static const std::string kBadCrcZip = "bad_crc.zip";
48 
49 static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
50                                                 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
51 
52 static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I',  'M', 'K',
53                                                           207, 'H', 132, 210, '\\', '\0'};
54 
55 static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
56 
OpenArchiveWrapper(const std::string & name,ZipArchiveHandle * handle)57 static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
58   const std::string abs_path = test_data_dir + "/" + name;
59   return OpenArchive(abs_path.c_str(), handle);
60 }
61 
62 class CdEntryMapTest : public ::testing::Test {
63  protected:
SetUp()64   void SetUp() override {
65     names_ = {
66         "a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt",
67     };
68     separator_ = "separator";
69     header_ = "metadata";
70     joined_names_ = header_ + android::base::Join(names_, separator_);
71     base_ptr_ = reinterpret_cast<uint8_t*>(&joined_names_[0]);
72 
73     entry_maps_.emplace_back(CdEntryMapZip32::Create(static_cast<uint16_t>(names_.size())));
74     entry_maps_.emplace_back(CdEntryMapZip64::Create());
75     for (auto& cd_map : entry_maps_) {
76       ASSERT_NE(nullptr, cd_map);
77       size_t offset = header_.size();
78       for (const auto& name : names_) {
79         auto status = cd_map->AddToMap(
80             std::string_view{joined_names_.c_str() + offset, name.size()}, base_ptr_);
81         ASSERT_EQ(0, status);
82         offset += name.size() + separator_.size();
83       }
84     }
85   }
86 
87   std::vector<std::string> names_;
88   // A continuous region of memory serves as a mock of the central directory.
89   std::string joined_names_;
90   // We expect some metadata at the beginning of the central directory and between filenames.
91   std::string header_;
92   std::string separator_;
93 
94   std::vector<std::unique_ptr<CdEntryMapInterface>> entry_maps_;
95   uint8_t* base_ptr_{nullptr};  // Points to the start of the central directory.
96 };
97 
TEST_F(CdEntryMapTest,AddDuplicatedEntry)98 TEST_F(CdEntryMapTest, AddDuplicatedEntry) {
99   for (auto& cd_map : entry_maps_) {
100     std::string_view name = "b.txt";
101     ASSERT_NE(0, cd_map->AddToMap(name, base_ptr_));
102   }
103 }
104 
TEST_F(CdEntryMapTest,FindEntry)105 TEST_F(CdEntryMapTest, FindEntry) {
106   for (auto& cd_map : entry_maps_) {
107     uint64_t expected_offset = header_.size();
108     for (const auto& name : names_) {
109       auto [status, offset] = cd_map->GetCdEntryOffset(name, base_ptr_);
110       ASSERT_EQ(status, kSuccess);
111       ASSERT_EQ(offset, expected_offset);
112       expected_offset += name.size() + separator_.size();
113     }
114   }
115 }
116 
TEST_F(CdEntryMapTest,Iteration)117 TEST_F(CdEntryMapTest, Iteration) {
118   std::set<std::string_view> expected(names_.begin(), names_.end());
119   for (auto& cd_map : entry_maps_) {
120     cd_map->ResetIteration();
121     std::set<std::string_view> entry_set;
122     auto ret = cd_map->Next(base_ptr_);
123     while (ret != std::pair<std::string_view, uint64_t>{}) {
124       auto [it, insert_status] = entry_set.insert(ret.first);
125       ASSERT_TRUE(insert_status);
126       ret = cd_map->Next(base_ptr_);
127     }
128     ASSERT_EQ(expected, entry_set);
129   }
130 }
131 
TEST(ziparchive,Open)132 TEST(ziparchive, Open) {
133   ZipArchiveHandle handle;
134   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
135   CloseArchive(handle);
136 
137   ASSERT_EQ(kInvalidEntryName, OpenArchiveWrapper("bad_filename.zip", &handle));
138   CloseArchive(handle);
139 }
140 
TEST(ziparchive,OutOfBound)141 TEST(ziparchive, OutOfBound) {
142   ZipArchiveHandle handle;
143   ASSERT_EQ(kInvalidOffset, OpenArchiveWrapper("crash.apk", &handle));
144   CloseArchive(handle);
145 }
146 
TEST(ziparchive,EmptyArchive)147 TEST(ziparchive, EmptyArchive) {
148   ZipArchiveHandle handle;
149   ASSERT_EQ(kEmptyArchive, OpenArchiveWrapper("empty.zip", &handle));
150   CloseArchive(handle);
151 }
152 
TEST(ziparchive,ZeroSizeCentralDirectory)153 TEST(ziparchive, ZeroSizeCentralDirectory) {
154   ZipArchiveHandle handle;
155   ASSERT_EQ(kInvalidFile, OpenArchiveWrapper("zero-size-cd.zip", &handle));
156   CloseArchive(handle);
157 }
158 
TEST(ziparchive,OpenMissing)159 TEST(ziparchive, OpenMissing) {
160   ZipArchiveHandle handle;
161   ASSERT_NE(0, OpenArchiveWrapper("missing.zip", &handle));
162 
163   // Confirm the file descriptor is not going to be mistaken for a valid one.
164   ASSERT_EQ(-1, GetFileDescriptor(handle));
165 }
166 
TEST(ziparchive,OpenAssumeFdOwnership)167 TEST(ziparchive, OpenAssumeFdOwnership) {
168   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
169   ASSERT_NE(-1, fd);
170   ZipArchiveHandle handle;
171   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
172   CloseArchive(handle);
173   ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
174   ASSERT_EQ(EBADF, errno);
175 }
176 
TEST(ziparchive,OpenDoNotAssumeFdOwnership)177 TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
178   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
179   ASSERT_NE(-1, fd);
180   ZipArchiveHandle handle;
181   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
182   CloseArchive(handle);
183   ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
184   close(fd);
185 }
186 
TEST(ziparchive,OpenAssumeFdRangeOwnership)187 TEST(ziparchive, OpenAssumeFdRangeOwnership) {
188   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
189   ASSERT_NE(-1, fd);
190   const off64_t length = lseek64(fd, 0, SEEK_END);
191   ASSERT_NE(-1, length);
192   ZipArchiveHandle handle;
193   ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
194                                   static_cast<size_t>(length), 0));
195   CloseArchive(handle);
196   ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
197   ASSERT_EQ(EBADF, errno);
198 }
199 
TEST(ziparchive,OpenDoNotAssumeFdRangeOwnership)200 TEST(ziparchive, OpenDoNotAssumeFdRangeOwnership) {
201   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
202   ASSERT_NE(-1, fd);
203   const off64_t length = lseek(fd, 0, SEEK_END);
204   ASSERT_NE(-1, length);
205   ZipArchiveHandle handle;
206   ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
207                                   static_cast<size_t>(length), 0, false));
208   CloseArchive(handle);
209   ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
210   close(fd);
211 }
212 
TEST(ziparchive,Iteration_std_string_view)213 TEST(ziparchive, Iteration_std_string_view) {
214   ZipArchiveHandle handle;
215   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
216 
217   void* iteration_cookie;
218   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
219 
220   ZipEntry64 data;
221   std::vector<std::string_view> names;
222   std::string_view name;
223   while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
224 
225   // Assert that the names are as expected.
226   std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
227   std::sort(names.begin(), names.end());
228   ASSERT_EQ(expected_names, names);
229 
230   CloseArchive(handle);
231 }
232 
AssertIterationNames(void * iteration_cookie,const std::vector<std::string> & expected_names_sorted)233 static void AssertIterationNames(void* iteration_cookie,
234                                  const std::vector<std::string>& expected_names_sorted) {
235   ZipEntry64 data;
236   std::vector<std::string> names;
237   std::string_view name;
238   for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
239     ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
240     names.push_back(std::string(name));
241   }
242   // End of iteration.
243   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
244   // Assert that the names are as expected.
245   std::sort(names.begin(), names.end());
246   ASSERT_EQ(expected_names_sorted, names);
247 }
248 
AssertIterationOrder(const std::string_view prefix,const std::string_view suffix,const std::vector<std::string> & expected_names_sorted)249 static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
250                                  const std::vector<std::string>& expected_names_sorted) {
251   ZipArchiveHandle handle;
252   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
253 
254   void* iteration_cookie;
255   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
256   AssertIterationNames(iteration_cookie, expected_names_sorted);
257   CloseArchive(handle);
258 }
259 
AssertIterationOrderWithMatcher(std::function<bool (std::string_view)> matcher,const std::vector<std::string> & expected_names_sorted)260 static void AssertIterationOrderWithMatcher(std::function<bool(std::string_view)> matcher,
261                                             const std::vector<std::string>& expected_names_sorted) {
262   ZipArchiveHandle handle;
263   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
264 
265   void* iteration_cookie;
266   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, matcher));
267   AssertIterationNames(iteration_cookie, expected_names_sorted);
268   CloseArchive(handle);
269 }
270 
TEST(ziparchive,Iteration)271 TEST(ziparchive, Iteration) {
272   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
273                                                                   "b/d.txt"};
274 
275   AssertIterationOrder("", "", kExpectedMatchesSorted);
276 }
277 
TEST(ziparchive,IterationWithPrefix)278 TEST(ziparchive, IterationWithPrefix) {
279   static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
280 
281   AssertIterationOrder("b/", "", kExpectedMatchesSorted);
282 }
283 
TEST(ziparchive,IterationWithSuffix)284 TEST(ziparchive, IterationWithSuffix) {
285   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
286                                                                   "b/d.txt"};
287 
288   AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
289 }
290 
TEST(ziparchive,IterationWithPrefixAndSuffix)291 TEST(ziparchive, IterationWithPrefixAndSuffix) {
292   static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
293 
294   AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
295 }
296 
TEST(ziparchive,IterationWithAdditionalMatchesExactly)297 TEST(ziparchive, IterationWithAdditionalMatchesExactly) {
298   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt"};
299   auto matcher = [](std::string_view name) { return name == "a.txt"; };
300   AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
301 }
302 
TEST(ziparchive,IterationWithAdditionalMatchesWithSuffix)303 TEST(ziparchive, IterationWithAdditionalMatchesWithSuffix) {
304   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
305                                                                   "b/d.txt"};
306   auto matcher = [](std::string_view name) {
307     return name == "a.txt" || android::base::EndsWith(name, ".txt");
308   };
309   AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
310 }
311 
TEST(ziparchive,IterationWithAdditionalMatchesWithPrefixAndSuffix)312 TEST(ziparchive, IterationWithAdditionalMatchesWithPrefixAndSuffix) {
313   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b/c.txt", "b/d.txt"};
314   auto matcher = [](std::string_view name) {
315     return name == "a.txt" ||
316            (android::base::EndsWith(name, ".txt") && android::base::StartsWith(name, "b/"));
317   };
318   AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
319 }
320 
TEST(ziparchive,IterationWithBadPrefixAndSuffix)321 TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
322   ZipArchiveHandle handle;
323   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
324 
325   void* iteration_cookie;
326   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
327 
328   ZipEntry64 data;
329   std::string_view name;
330 
331   // End of iteration.
332   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
333 
334   CloseArchive(handle);
335 }
336 
TEST(ziparchive,FindEntry)337 TEST(ziparchive, FindEntry) {
338   ZipArchiveHandle handle;
339   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
340 
341   ZipEntry64 data;
342   ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
343 
344   // Known facts about a.txt, from zipinfo -v.
345   ASSERT_EQ(63, data.offset);
346   ASSERT_EQ(kCompressDeflated, data.method);
347   ASSERT_EQ(17u, data.uncompressed_length);
348   ASSERT_EQ(13u, data.compressed_length);
349   ASSERT_EQ(0x950821c5, data.crc32);
350   ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
351 
352   // An entry that doesn't exist. Should be a negative return code.
353   ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
354 
355   CloseArchive(handle);
356 }
357 
TEST(ziparchive,FindEntry_empty)358 TEST(ziparchive, FindEntry_empty) {
359   ZipArchiveHandle handle;
360   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
361 
362   ZipEntry64 data;
363   ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
364 
365   CloseArchive(handle);
366 }
367 
TEST(ziparchive,FindEntry_too_long)368 TEST(ziparchive, FindEntry_too_long) {
369   ZipArchiveHandle handle;
370   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
371 
372   std::string very_long_name(65536, 'x');
373   ZipEntry64 data;
374   ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
375 
376   CloseArchive(handle);
377 }
378 
TEST(ziparchive,TestInvalidDeclaredLength)379 TEST(ziparchive, TestInvalidDeclaredLength) {
380   ZipArchiveHandle handle;
381   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
382 
383   void* iteration_cookie;
384   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
385 
386   std::string_view name;
387   ZipEntry64 data;
388 
389   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
390   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
391 
392   CloseArchive(handle);
393 }
394 
TEST(ziparchive,OpenArchiveFdRange)395 TEST(ziparchive, OpenArchiveFdRange) {
396   TemporaryFile tmp_file;
397   ASSERT_NE(-1, tmp_file.fd);
398 
399   const std::string leading_garbage(21, 'x');
400   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
401                                         leading_garbage.size()));
402 
403   std::string valid_content;
404   ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
405   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
406 
407   const std::string ending_garbage(42, 'x');
408   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
409                                         ending_garbage.size()));
410 
411   ZipArchiveHandle handle;
412   ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
413   ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
414                                   valid_content.size(),
415                                   static_cast<off64_t>(leading_garbage.size())));
416 
417   // An entry that's deflated.
418   ZipEntry64 data;
419   ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
420   const auto a_size = static_cast<size_t>(data.uncompressed_length);
421   ASSERT_EQ(a_size, kATxtContents.size());
422   auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
423   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
424   ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
425 
426   // An entry that's stored.
427   ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
428   const auto b_size = static_cast<size_t>(data.uncompressed_length);
429   ASSERT_EQ(b_size, kBTxtContents.size());
430   buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
431   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
432   ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
433 
434   CloseArchive(handle);
435 }
436 
TEST(ziparchive,ExtractToMemory)437 TEST(ziparchive, ExtractToMemory) {
438   ZipArchiveHandle handle;
439   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
440 
441   // An entry that's deflated.
442   ZipEntry64 data;
443   ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
444   const auto a_size = static_cast<size_t>(data.uncompressed_length);
445   ASSERT_EQ(a_size, kATxtContents.size());
446   uint8_t* buffer = new uint8_t[a_size];
447   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
448   ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
449   delete[] buffer;
450 
451   // An entry that's stored.
452   ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
453   const auto b_size = static_cast<size_t>(data.uncompressed_length);
454   ASSERT_EQ(b_size, kBTxtContents.size());
455   buffer = new uint8_t[b_size];
456   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
457   ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
458   delete[] buffer;
459 
460   CloseArchive(handle);
461 }
462 
463 static const uint32_t kEmptyEntriesZip[] = {
464     0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
465     0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
466     0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
467     0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
468     0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
469     0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
470 
471 // This is a zip file containing a single entry (ab.txt) that contains
472 // 90072 repetitions of the string "ab\n" and has an uncompressed length
473 // of 270216 bytes.
474 static const uint16_t kAbZip[] = {
475     0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
476     0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
477     0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
478     0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
479     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
480     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
481     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
482     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
483     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
484     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
485     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
486     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
487     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
488     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
489     0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
490     0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
491     0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
492     0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
493     0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
494 
495 static const std::string kAbTxtName("ab.txt");
496 static const size_t kAbUncompressedSize = 270216;
497 
TEST(ziparchive,EmptyEntries)498 TEST(ziparchive, EmptyEntries) {
499   TemporaryFile tmp_file;
500   ASSERT_NE(-1, tmp_file.fd);
501   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
502 
503   ZipArchiveHandle handle;
504   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
505 
506   ZipEntry64 entry;
507   ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
508   ASSERT_EQ(0u, entry.uncompressed_length);
509   // Extraction to a 1 byte buffer should succeed.
510   uint8_t buffer[1];
511   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
512   // Extraction to an empty buffer should succeed.
513   ASSERT_EQ(0, ExtractToMemory(handle, &entry, nullptr, 0));
514 
515   TemporaryFile tmp_output_file;
516   ASSERT_NE(-1, tmp_output_file.fd);
517   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
518 
519   struct stat stat_buf;
520   ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
521   ASSERT_EQ(0, stat_buf.st_size);
522 }
523 
TEST(ziparchive,EntryLargerThan32K)524 TEST(ziparchive, EntryLargerThan32K) {
525   TemporaryFile tmp_file;
526   ASSERT_NE(-1, tmp_file.fd);
527   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
528                                         sizeof(kAbZip) - 1));
529   ZipArchiveHandle handle;
530   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
531 
532   ZipEntry64 entry;
533   ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
534   ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
535 
536   // Extract the entry to memory.
537   std::vector<uint8_t> buffer(kAbUncompressedSize);
538   ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
539 
540   // Extract the entry to a file.
541   TemporaryFile tmp_output_file;
542   ASSERT_NE(-1, tmp_output_file.fd);
543   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
544 
545   // Make sure the extracted file size is as expected.
546   struct stat stat_buf;
547   ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
548   ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
549 
550   // Read the file back to a buffer and make sure the contents are
551   // the same as the memory buffer we extracted directly to.
552   std::vector<uint8_t> file_contents(kAbUncompressedSize);
553   ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
554   ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
555   ASSERT_EQ(file_contents, buffer);
556 
557   for (int i = 0; i < 90072; ++i) {
558     const uint8_t* line = &file_contents[0] + (3 * i);
559     ASSERT_EQ('a', line[0]);
560     ASSERT_EQ('b', line[1]);
561     ASSERT_EQ('\n', line[2]);
562   }
563 }
564 
TEST(ziparchive,TrailerAfterEOCD)565 TEST(ziparchive, TrailerAfterEOCD) {
566   TemporaryFile tmp_file;
567   ASSERT_NE(-1, tmp_file.fd);
568 
569   // Create a file with 8 bytes of random garbage.
570   static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
571   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
572   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
573 
574   ZipArchiveHandle handle;
575   ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
576 }
577 
TEST(ziparchive,ExtractToFile)578 TEST(ziparchive, ExtractToFile) {
579   TemporaryFile tmp_file;
580   ASSERT_NE(-1, tmp_file.fd);
581   const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
582   const size_t data_size = sizeof(data);
583 
584   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
585 
586   ZipArchiveHandle handle;
587   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
588 
589   ZipEntry64 entry;
590   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
591   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
592 
593   // Assert that the first 8 bytes of the file haven't been clobbered.
594   uint8_t read_buffer[data_size];
595   ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
596   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
597   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
598 
599   // Assert that the remainder of the file contains the incompressed data.
600   std::vector<uint8_t> uncompressed_data(static_cast<size_t>(entry.uncompressed_length));
601   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
602                                        static_cast<size_t>(entry.uncompressed_length)));
603   ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
604 
605   // Assert that the total length of the file is sane
606   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
607             lseek(tmp_file.fd, 0, SEEK_END));
608 }
609 
610 #if !defined(_WIN32)
TEST(ziparchive,OpenFromMemory)611 TEST(ziparchive, OpenFromMemory) {
612   const std::string zip_path = test_data_dir + "/dummy-update.zip";
613   android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
614   ASSERT_NE(-1, fd);
615   struct stat sb;
616   ASSERT_EQ(0, fstat(fd, &sb));
617 
618   // Memory map the file first and open the archive from the memory region.
619   auto file_map{
620       android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
621   ZipArchiveHandle handle;
622   ASSERT_EQ(0,
623             OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
624 
625   // Assert one entry can be found and extracted correctly.
626   ZipEntry64 binary_entry;
627   ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
628   TemporaryFile tmp_binary;
629   ASSERT_NE(-1, tmp_binary.fd);
630   ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
631 }
632 #endif
633 
ZipArchiveStreamTest(ZipArchiveHandle & handle,const std::string & entry_name,bool raw,bool verified,ZipEntry * entry,std::vector<uint8_t> * read_data)634 static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
635                                  bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
636   ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
637   std::unique_ptr<ZipArchiveStreamEntry> stream;
638   if (raw) {
639     stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
640     if (entry->method == kCompressStored) {
641       read_data->resize(static_cast<size_t>(entry->uncompressed_length));
642     } else {
643       read_data->resize(static_cast<size_t>(entry->compressed_length));
644     }
645   } else {
646     stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
647     read_data->resize(static_cast<size_t>(entry->uncompressed_length));
648   }
649   uint8_t* read_data_ptr = read_data->data();
650   ASSERT_TRUE(stream.get() != nullptr);
651   const std::vector<uint8_t>* data;
652   uint64_t total_size = 0;
653   while ((data = stream->Read()) != nullptr) {
654     total_size += data->size();
655     memcpy(read_data_ptr, data->data(), data->size());
656     read_data_ptr += data->size();
657   }
658   ASSERT_EQ(verified, stream->Verify());
659   ASSERT_EQ(total_size, read_data->size());
660 }
661 
ZipArchiveStreamTestUsingContents(const std::string & zip_file,const std::string & entry_name,const std::vector<uint8_t> & contents,bool raw)662 static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
663                                               const std::string& entry_name,
664                                               const std::vector<uint8_t>& contents, bool raw) {
665   ZipArchiveHandle handle;
666   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
667 
668   ZipEntry entry;
669   std::vector<uint8_t> read_data;
670   ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
671 
672   ASSERT_EQ(contents.size(), read_data.size());
673   ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
674 
675   CloseArchive(handle);
676 }
677 
ZipArchiveStreamTestUsingMemory(const std::string & zip_file,const std::string & entry_name)678 static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
679                                             const std::string& entry_name) {
680   ZipArchiveHandle handle;
681   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
682 
683   ZipEntry entry;
684   std::vector<uint8_t> read_data;
685   ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
686 
687   std::vector<uint8_t> cmp_data(static_cast<size_t>(entry.uncompressed_length));
688   ASSERT_EQ(entry.uncompressed_length, read_data.size());
689   ASSERT_EQ(
690       0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
691   ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
692 
693   CloseArchive(handle);
694 }
695 
TEST(ziparchive,StreamCompressed)696 TEST(ziparchive, StreamCompressed) {
697   ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
698 }
699 
TEST(ziparchive,StreamUncompressed)700 TEST(ziparchive, StreamUncompressed) {
701   ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
702 }
703 
TEST(ziparchive,StreamRawCompressed)704 TEST(ziparchive, StreamRawCompressed) {
705   ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
706 }
707 
TEST(ziparchive,StreamRawUncompressed)708 TEST(ziparchive, StreamRawUncompressed) {
709   ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
710 }
711 
TEST(ziparchive,StreamLargeCompressed)712 TEST(ziparchive, StreamLargeCompressed) {
713   ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
714 }
715 
TEST(ziparchive,StreamLargeUncompressed)716 TEST(ziparchive, StreamLargeUncompressed) {
717   ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
718 }
719 
TEST(ziparchive,StreamCompressedBadCrc)720 TEST(ziparchive, StreamCompressedBadCrc) {
721   ZipArchiveHandle handle;
722   ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
723 
724   ZipEntry entry;
725   std::vector<uint8_t> read_data;
726   ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
727 
728   CloseArchive(handle);
729 }
730 
TEST(ziparchive,StreamUncompressedBadCrc)731 TEST(ziparchive, StreamUncompressedBadCrc) {
732   ZipArchiveHandle handle;
733   ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
734 
735   ZipEntry entry;
736   std::vector<uint8_t> read_data;
737   ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
738 
739   CloseArchive(handle);
740 }
741 
742 // Generated using the following Java program:
743 //     public static void main(String[] foo) throws Exception {
744 //       FileOutputStream fos = new
745 //       FileOutputStream("/tmp/data_descriptor.zip");
746 //       ZipOutputStream zos = new ZipOutputStream(fos);
747 //       ZipEntry64 ze = new ZipEntry64("name");
748 //       ze.setMethod(ZipEntry64.DEFLATED);
749 //       zos.putNextEntry(ze);
750 //       zos.write("abdcdefghijk".getBytes());
751 //       zos.closeEntry();
752 //       zos.close();
753 //     }
754 //
755 // cat /tmp/data_descriptor.zip | xxd -i
756 //
757 static const std::vector<uint8_t> kDataDescriptorZipFile{
758     0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
759     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
760     0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
761     //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
762     0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
763     0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
764     0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
765     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
766     0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
767     0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
768 
769 // The offsets of the data descriptor in this file, so we can mess with
770 // them later in the test.
771 static constexpr uint32_t kDataDescriptorOffset = 48;
772 static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
773 static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
774 
ExtractEntryToMemory(const std::vector<uint8_t> & zip_data,std::vector<uint8_t> * entry_out,int32_t * error_code_out)775 static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
776                                  std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
777   TemporaryFile tmp_file;
778   ASSERT_NE(-1, tmp_file.fd);
779   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
780   ZipArchiveHandle handle;
781   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
782 
783   // This function expects a variant of kDataDescriptorZipFile, for look for
784   // an entry whose name is "name" and whose size is 12 (contents =
785   // "abdcdefghijk").
786   ZipEntry64 entry;
787   ASSERT_EQ(0, FindEntry(handle, "name", &entry));
788   ASSERT_EQ(12u, entry.uncompressed_length);
789 
790   entry_out->resize(12);
791   (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
792 
793   CloseArchive(handle);
794 }
795 
TEST(ziparchive,ValidDataDescriptors)796 TEST(ziparchive, ValidDataDescriptors) {
797   std::vector<uint8_t> entry;
798   int32_t error_code = 0;
799   ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
800 
801   ASSERT_EQ(0, error_code);
802   ASSERT_EQ(12u, entry.size());
803   ASSERT_EQ('a', entry[0]);
804   ASSERT_EQ('k', entry[11]);
805 }
806 
TEST(ziparchive,InvalidDataDescriptors_csize)807 TEST(ziparchive, InvalidDataDescriptors_csize) {
808   std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
809   invalid_csize[kCSizeOffset] = 0xfe;
810 
811   std::vector<uint8_t> entry;
812   int32_t error_code = 0;
813   ExtractEntryToMemory(invalid_csize, &entry, &error_code);
814 
815   ASSERT_EQ(kInconsistentInformation, error_code);
816 }
817 
TEST(ziparchive,InvalidDataDescriptors_size)818 TEST(ziparchive, InvalidDataDescriptors_size) {
819   std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
820   invalid_size[kSizeOffset] = 0xfe;
821 
822   std::vector<uint8_t> entry;
823   int32_t error_code = 0;
824   ExtractEntryToMemory(invalid_size, &entry, &error_code);
825 
826   ASSERT_EQ(kInconsistentInformation, error_code);
827 }
828 
TEST(ziparchive,ErrorCodeString)829 TEST(ziparchive, ErrorCodeString) {
830   ASSERT_STREQ("Success", ErrorCodeString(0));
831 
832   // Out of bounds.
833   ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
834   ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
835   ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
836 
837   ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
838 }
839 
840 // A zip file whose local file header at offset zero is corrupted.
841 //
842 // ---------------
843 // cat foo > a.txt
844 // zip a.zip a.txt
845 // cat a.zip | xxd -i
846 //
847 // Manual changes :
848 // [2] = 0xff  // Corrupt the LFH signature of entry 0.
849 // [3] = 0xff  // Corrupt the LFH signature of entry 0.
850 static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
851     //[lfh-sig-----------], [lfh contents---------------------------------
852     0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
853     //--------------------------------------------------------------------
854     0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
855     //-------------------------------]  [file-name-----------------], [---
856     0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
857     // entry-contents------------------------------------------------------
858     0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
859     //--------------------------------------------------------------------
860     0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
861     //-------------------------------------], [cd-record-sig-------], [---
862     0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
863     // cd-record-----------------------------------------------------------
864     0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
865     //--------------------------------------------------------------------
866     0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
867     //--------------------------------------------------------------------
868     0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
869     //-]  [lfh-file-header-off-], [file-name-----------------], [extra----
870     0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
871     //--------------------------------------------------------------------
872     0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
873     //-------------------------------------------------------], [eocd-sig-
874     0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
875     //-------], [---------------------------------------------------------
876     0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
877     //-------------------------------------------]
878     0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
879 
TEST(ziparchive,BrokenLfhSignature)880 TEST(ziparchive, BrokenLfhSignature) {
881   TemporaryFile tmp_file;
882   ASSERT_NE(-1, tmp_file.fd);
883   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
884                                         kZipFileWithBrokenLfhSignature.size()));
885   ZipArchiveHandle handle;
886   ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
887 }
888 
889 class VectorReader : public zip_archive::Reader {
890  public:
VectorReader(const std::vector<uint8_t> & input)891   VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
892 
ReadAtOffset(uint8_t * buf,size_t len,off64_t offset) const893   bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
894     if ((offset + len) < input_.size()) {
895       return false;
896     }
897 
898     memcpy(buf, &input_[static_cast<size_t>(offset)], len);
899     return true;
900   }
901 
902  private:
903   const std::vector<uint8_t>& input_;
904 };
905 
906 class VectorWriter : public zip_archive::Writer {
907  public:
VectorWriter()908   VectorWriter() : Writer() {}
909 
Append(uint8_t * buf,size_t size)910   bool Append(uint8_t* buf, size_t size) {
911     output_.insert(output_.end(), buf, buf + size);
912     return true;
913   }
914 
GetOutput()915   std::vector<uint8_t>& GetOutput() { return output_; }
916 
917  private:
918   std::vector<uint8_t> output_;
919 };
920 
921 class BadReader : public zip_archive::Reader {
922  public:
BadReader()923   BadReader() : Reader() {}
924 
ReadAtOffset(uint8_t *,size_t,off64_t) const925   bool ReadAtOffset(uint8_t*, size_t, off64_t) const { return false; }
926 };
927 
928 class BadWriter : public zip_archive::Writer {
929  public:
BadWriter()930   BadWriter() : Writer() {}
931 
Append(uint8_t *,size_t)932   bool Append(uint8_t*, size_t) { return false; }
933 };
934 
TEST(ziparchive,Inflate)935 TEST(ziparchive, Inflate) {
936   const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
937   const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
938 
939   const VectorReader reader(kATxtContentsCompressed);
940   {
941     VectorWriter writer;
942     uint64_t crc_out = 0;
943 
944     int32_t ret =
945         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
946     ASSERT_EQ(0, ret);
947     ASSERT_EQ(kATxtContents, writer.GetOutput());
948     ASSERT_EQ(0x950821C5u, crc_out);
949   }
950 
951   {
952     VectorWriter writer;
953     int32_t ret =
954         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
955     ASSERT_EQ(0, ret);
956     ASSERT_EQ(kATxtContents, writer.GetOutput());
957   }
958 
959   {
960     BadWriter writer;
961     int32_t ret =
962         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
963     ASSERT_EQ(kIoError, ret);
964   }
965 
966   {
967     BadReader reader;
968     VectorWriter writer;
969     int32_t ret =
970         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
971     ASSERT_EQ(kIoError, ret);
972     ASSERT_EQ(0u, writer.GetOutput().size());
973   }
974 }
975 
976 // The class constructs a zipfile with zip64 format, and test the parsing logic.
977 class Zip64ParseTest : public ::testing::Test {
978  protected:
979   struct LocalFileEntry {
980     std::vector<uint8_t> local_file_header;
981     std::string file_name;
982     std::vector<uint8_t> extended_field;
983     // Fake data to mimic the compressed bytes in the zipfile.
984     std::vector<uint8_t> compressed_bytes;
985     std::vector<uint8_t> data_descriptor;
986 
GetSizeZip64ParseTest::LocalFileEntry987     size_t GetSize() const {
988       return local_file_header.size() + file_name.size() + extended_field.size() +
989              compressed_bytes.size() + data_descriptor.size();
990     }
991 
CopyToOutputZip64ParseTest::LocalFileEntry992     void CopyToOutput(std::vector<uint8_t>* output) const {
993       std::copy(local_file_header.begin(), local_file_header.end(), std::back_inserter(*output));
994       std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
995       std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
996       std::copy(compressed_bytes.begin(), compressed_bytes.end(), std::back_inserter(*output));
997       std::copy(data_descriptor.begin(), data_descriptor.end(), std::back_inserter(*output));
998     }
999   };
1000 
1001   struct CdRecordEntry {
1002     std::vector<uint8_t> central_directory_record;
1003     std::string file_name;
1004     std::vector<uint8_t> extended_field;
1005 
GetSizeZip64ParseTest::CdRecordEntry1006     size_t GetSize() const {
1007       return central_directory_record.size() + file_name.size() + extended_field.size();
1008     }
1009 
CopyToOutputZip64ParseTest::CdRecordEntry1010     void CopyToOutput(std::vector<uint8_t>* output) const {
1011       std::copy(central_directory_record.begin(), central_directory_record.end(),
1012                 std::back_inserter(*output));
1013       std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
1014       std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
1015     }
1016   };
1017 
ConstructLocalFileHeader(const std::string & name,std::vector<uint8_t> * output,uint32_t uncompressed_size,uint32_t compressed_size)1018   static void ConstructLocalFileHeader(const std::string& name, std::vector<uint8_t>* output,
1019                                        uint32_t uncompressed_size, uint32_t compressed_size) {
1020     LocalFileHeader lfh = {};
1021     lfh.lfh_signature = LocalFileHeader::kSignature;
1022     lfh.compressed_size = compressed_size;
1023     lfh.uncompressed_size = uncompressed_size;
1024     lfh.file_name_length = static_cast<uint16_t>(name.size());
1025     lfh.extra_field_length = 20;
1026     *output = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&lfh),
1027                                    reinterpret_cast<uint8_t*>(&lfh) + sizeof(LocalFileHeader));
1028   }
1029 
1030   // Put one zip64 extended info in the extended field.
ConstructExtendedField(const std::vector<uint64_t> & zip64_fields,std::vector<uint8_t> * output)1031   static void ConstructExtendedField(const std::vector<uint64_t>& zip64_fields,
1032                                      std::vector<uint8_t>* output) {
1033     ASSERT_FALSE(zip64_fields.empty());
1034     uint16_t data_size = 8 * static_cast<uint16_t>(zip64_fields.size());
1035     std::vector<uint8_t> extended_field(data_size + 4);
1036     android::base::put_unaligned(extended_field.data(), Zip64ExtendedInfo::kHeaderId);
1037     android::base::put_unaligned(extended_field.data() + 2, data_size);
1038     size_t offset = 4;
1039     for (const auto& field : zip64_fields) {
1040       android::base::put_unaligned(extended_field.data() + offset, field);
1041       offset += 8;
1042     }
1043 
1044     *output = std::move(extended_field);
1045   }
1046 
ConstructCentralDirectoryRecord(const std::string & name,uint32_t uncompressed_size,uint32_t compressed_size,uint32_t local_offset,std::vector<uint8_t> * output)1047   static void ConstructCentralDirectoryRecord(const std::string& name, uint32_t uncompressed_size,
1048                                               uint32_t compressed_size, uint32_t local_offset,
1049                                               std::vector<uint8_t>* output) {
1050     CentralDirectoryRecord cdr = {};
1051     cdr.record_signature = CentralDirectoryRecord::kSignature;
1052     cdr.compressed_size = uncompressed_size;
1053     cdr.uncompressed_size = compressed_size;
1054     cdr.file_name_length = static_cast<uint16_t>(name.size());
1055     cdr.extra_field_length = local_offset == UINT32_MAX ? 28 : 20;
1056     cdr.local_file_header_offset = local_offset;
1057     *output =
1058         std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&cdr),
1059                              reinterpret_cast<uint8_t*>(&cdr) + sizeof(CentralDirectoryRecord));
1060   }
1061 
1062   // Add an entry to the zipfile, construct the corresponding local header and cd entry.
AddEntry(const std::string & name,const std::vector<uint8_t> & content,bool uncompressed_size_in_extended,bool compressed_size_in_extended,bool local_offset_in_extended,bool include_data_descriptor=false)1063   void AddEntry(const std::string& name, const std::vector<uint8_t>& content,
1064                 bool uncompressed_size_in_extended, bool compressed_size_in_extended,
1065                 bool local_offset_in_extended, bool include_data_descriptor = false) {
1066     auto uncompressed_size = static_cast<uint32_t>(content.size());
1067     auto compressed_size = static_cast<uint32_t>(content.size());
1068     uint32_t local_file_header_offset = 0;
1069     std::for_each(file_entries_.begin(), file_entries_.end(),
1070                   [&local_file_header_offset](const LocalFileEntry& file_entry) {
1071                     local_file_header_offset += file_entry.GetSize();
1072                   });
1073 
1074     std::vector<uint64_t> zip64_fields;
1075     if (uncompressed_size_in_extended) {
1076       zip64_fields.push_back(uncompressed_size);
1077       uncompressed_size = UINT32_MAX;
1078     }
1079     if (compressed_size_in_extended) {
1080       zip64_fields.push_back(compressed_size);
1081       compressed_size = UINT32_MAX;
1082     }
1083     LocalFileEntry local_entry = {
1084         .local_file_header = {},
1085         .file_name = name,
1086         .extended_field = {},
1087         .compressed_bytes = content,
1088     };
1089     ConstructLocalFileHeader(name, &local_entry.local_file_header, uncompressed_size,
1090                              compressed_size);
1091     ConstructExtendedField(zip64_fields, &local_entry.extended_field);
1092     if (include_data_descriptor) {
1093       size_t descriptor_size = compressed_size_in_extended ? 24 : 16;
1094       local_entry.data_descriptor.resize(descriptor_size);
1095       uint8_t* write_ptr = local_entry.data_descriptor.data();
1096       EmitUnaligned<uint32_t>(&write_ptr, DataDescriptor::kOptSignature);
1097       EmitUnaligned<uint32_t>(&write_ptr, 0 /* crc */);
1098       if (compressed_size_in_extended) {
1099         EmitUnaligned<uint64_t>(&write_ptr, compressed_size_in_extended);
1100         EmitUnaligned<uint64_t>(&write_ptr, uncompressed_size_in_extended);
1101       } else {
1102         EmitUnaligned<uint32_t>(&write_ptr, compressed_size_in_extended);
1103         EmitUnaligned<uint32_t>(&write_ptr, uncompressed_size_in_extended);
1104       }
1105     }
1106 
1107     file_entries_.push_back(std::move(local_entry));
1108 
1109     if (local_offset_in_extended) {
1110       zip64_fields.push_back(local_file_header_offset);
1111       local_file_header_offset = UINT32_MAX;
1112     }
1113     CdRecordEntry cd_entry = {
1114         .central_directory_record = {},
1115         .file_name = name,
1116         .extended_field = {},
1117     };
1118     ConstructCentralDirectoryRecord(name, uncompressed_size, compressed_size,
1119                                     local_file_header_offset, &cd_entry.central_directory_record);
1120     ConstructExtendedField(zip64_fields, &cd_entry.extended_field);
1121     cd_entries_.push_back(std::move(cd_entry));
1122   }
1123 
ConstructEocd()1124   void ConstructEocd() {
1125     ASSERT_EQ(file_entries_.size(), cd_entries_.size());
1126     Zip64EocdRecord zip64_eocd = {};
1127     zip64_eocd.record_signature = Zip64EocdRecord::kSignature;
1128     zip64_eocd.num_records = file_entries_.size();
1129     zip64_eocd.cd_size = 0;
1130     std::for_each(
1131         cd_entries_.begin(), cd_entries_.end(),
1132         [&zip64_eocd](const CdRecordEntry& cd_entry) { zip64_eocd.cd_size += cd_entry.GetSize(); });
1133     zip64_eocd.cd_start_offset = 0;
1134     std::for_each(file_entries_.begin(), file_entries_.end(),
1135                   [&zip64_eocd](const LocalFileEntry& file_entry) {
1136                     zip64_eocd.cd_start_offset += file_entry.GetSize();
1137                   });
1138     zip64_eocd_record_ =
1139         std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_eocd),
1140                              reinterpret_cast<uint8_t*>(&zip64_eocd) + sizeof(Zip64EocdRecord));
1141 
1142     Zip64EocdLocator zip64_locator = {};
1143     zip64_locator.locator_signature = Zip64EocdLocator::kSignature;
1144     zip64_locator.zip64_eocd_offset = zip64_eocd.cd_start_offset + zip64_eocd.cd_size;
1145     zip64_eocd_locator_ =
1146         std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_locator),
1147                              reinterpret_cast<uint8_t*>(&zip64_locator) + sizeof(Zip64EocdLocator));
1148 
1149     EocdRecord eocd = {};
1150     eocd.eocd_signature = EocdRecord::kSignature,
1151     eocd.num_records = file_entries_.size() > UINT16_MAX
1152                            ? UINT16_MAX
1153                            : static_cast<uint16_t>(file_entries_.size());
1154     eocd.cd_size = UINT32_MAX;
1155     eocd.cd_start_offset = UINT32_MAX;
1156     eocd_record_ = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&eocd),
1157                                         reinterpret_cast<uint8_t*>(&eocd) + sizeof(EocdRecord));
1158   }
1159 
1160   // Concatenate all the local file entries, cd entries, and eocd metadata.
ConstructZipFile()1161   void ConstructZipFile() {
1162     for (const auto& file_entry : file_entries_) {
1163       file_entry.CopyToOutput(&zip_content_);
1164     }
1165     for (const auto& cd_entry : cd_entries_) {
1166       cd_entry.CopyToOutput(&zip_content_);
1167     }
1168     std::copy(zip64_eocd_record_.begin(), zip64_eocd_record_.end(),
1169               std::back_inserter(zip_content_));
1170     std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
1171               std::back_inserter(zip_content_));
1172     std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
1173   }
1174 
1175   std::vector<uint8_t> zip_content_;
1176 
1177   std::vector<LocalFileEntry> file_entries_;
1178   std::vector<CdRecordEntry> cd_entries_;
1179   std::vector<uint8_t> zip64_eocd_record_;
1180   std::vector<uint8_t> zip64_eocd_locator_;
1181   std::vector<uint8_t> eocd_record_;
1182 };
1183 
TEST_F(Zip64ParseTest,openFile)1184 TEST_F(Zip64ParseTest, openFile) {
1185   AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
1186   ConstructEocd();
1187   ConstructZipFile();
1188 
1189   ZipArchiveHandle handle;
1190   ASSERT_EQ(
1191       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1192   CloseArchive(handle);
1193 }
1194 
TEST_F(Zip64ParseTest,openFilelocalOffsetInExtendedField)1195 TEST_F(Zip64ParseTest, openFilelocalOffsetInExtendedField) {
1196   AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, true);
1197   AddEntry("b.txt", std::vector<uint8_t>(200, 'b'), true, true, true);
1198   ConstructEocd();
1199   ConstructZipFile();
1200 
1201   ZipArchiveHandle handle;
1202   ASSERT_EQ(
1203       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1204   CloseArchive(handle);
1205 }
1206 
TEST_F(Zip64ParseTest,openFileCompressedNotInExtendedField)1207 TEST_F(Zip64ParseTest, openFileCompressedNotInExtendedField) {
1208   AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, false, false);
1209   ConstructEocd();
1210   ConstructZipFile();
1211 
1212   ZipArchiveHandle handle;
1213   // Zip64 extended fields must include both uncompressed and compressed size.
1214   ASSERT_NE(
1215       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1216   CloseArchive(handle);
1217 }
1218 
TEST_F(Zip64ParseTest,findEntry)1219 TEST_F(Zip64ParseTest, findEntry) {
1220   AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1221   AddEntry("b.txt", std::vector<uint8_t>(300, 'b'), true, true, false);
1222   ConstructEocd();
1223   ConstructZipFile();
1224 
1225   ZipArchiveHandle handle;
1226   ASSERT_EQ(
1227       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1228   ZipEntry64 entry;
1229   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1230   ASSERT_EQ(200, entry.uncompressed_length);
1231   ASSERT_EQ(200, entry.compressed_length);
1232 
1233   ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1234   ASSERT_EQ(300, entry.uncompressed_length);
1235   ASSERT_EQ(300, entry.compressed_length);
1236   CloseArchive(handle);
1237 }
1238 
TEST_F(Zip64ParseTest,dataDescriptor)1239 TEST_F(Zip64ParseTest, dataDescriptor) {
1240   AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true, false);
1241   AddEntry("b.txt", std::vector<uint8_t>(300, 'b'), true, true, true, false);
1242   // We want a file with compressed size in extended fields, but
1243   // data descriptor still in 32 bit values.
1244   auto& local_entry = file_entries_.back();
1245   local_entry.data_descriptor.resize(16);
1246   uint8_t* write_ptr = local_entry.data_descriptor.data();
1247   EmitUnaligned<uint32_t>(&write_ptr, DataDescriptor::kOptSignature);
1248   EmitUnaligned<uint32_t>(&write_ptr, 0 /* crc */);
1249   EmitUnaligned<uint32_t>(&write_ptr, 300);
1250   EmitUnaligned<uint32_t>(&write_ptr, 300);
1251 
1252   ConstructEocd();
1253   ConstructZipFile();
1254 
1255   ZipArchiveHandle handle;
1256   ASSERT_EQ(0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(),
1257                                      "debug_zip64", &handle));
1258   ZipEntry64 entry;
1259   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1260   ASSERT_EQ(200, entry.uncompressed_length);
1261   ASSERT_EQ(200, entry.compressed_length);
1262 
1263   ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1264   ASSERT_EQ(300, entry.uncompressed_length);
1265   ASSERT_EQ(300, entry.compressed_length);
1266   CloseArchive(handle);
1267 }
1268 
TEST_F(Zip64ParseTest,openFileIncorrectDataSizeInLocalExtendedField)1269 TEST_F(Zip64ParseTest, openFileIncorrectDataSizeInLocalExtendedField) {
1270   AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
1271   ASSERT_EQ(1, file_entries_.size());
1272   auto& extended_field = file_entries_[0].extended_field;
1273   // data size exceeds the extended field size in local header.
1274   android::base::put_unaligned<uint16_t>(extended_field.data() + 2, 30);
1275   ConstructEocd();
1276   ConstructZipFile();
1277 
1278   ZipArchiveHandle handle;
1279   ASSERT_EQ(
1280       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1281   ZipEntry64 entry;
1282   ASSERT_NE(0, FindEntry(handle, "a.txt", &entry));
1283 
1284   CloseArchive(handle);
1285 }
1286 
TEST_F(Zip64ParseTest,iterates)1287 TEST_F(Zip64ParseTest, iterates) {
1288   std::set<std::string_view> names{"a.txt", "b.txt", "c.txt", "d.txt", "e.txt"};
1289   for (const auto& name : names) {
1290     AddEntry(std::string(name), std::vector<uint8_t>(100, name[0]), true, true, true);
1291   }
1292   ConstructEocd();
1293   ConstructZipFile();
1294 
1295   ZipArchiveHandle handle;
1296   ASSERT_EQ(
1297       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1298 
1299   void* iteration_cookie;
1300   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
1301   std::set<std::string_view> result;
1302   std::string_view name;
1303   ZipEntry64 entry;
1304   while (Next(iteration_cookie, &entry, &name) == 0) result.emplace(name);
1305   ASSERT_EQ(names, result);
1306 
1307   CloseArchive(handle);
1308 }
1309 
TEST_F(Zip64ParseTest,zip64EocdWrongLocatorOffset)1310 TEST_F(Zip64ParseTest, zip64EocdWrongLocatorOffset) {
1311   AddEntry("a.txt", std::vector<uint8_t>(1, 'a'), true, true, true);
1312   ConstructEocd();
1313   zip_content_.resize(20, 'a');
1314   std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
1315             std::back_inserter(zip_content_));
1316   std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
1317 
1318   ZipArchiveHandle handle;
1319   ASSERT_NE(
1320       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1321   CloseArchive(handle);
1322 }
1323 
TEST_F(Zip64ParseTest,extract)1324 TEST_F(Zip64ParseTest, extract) {
1325   std::vector<uint8_t> content(200, 'a');
1326   AddEntry("a.txt", content, true, true, true);
1327   ConstructEocd();
1328   ConstructZipFile();
1329 
1330   ZipArchiveHandle handle;
1331   ASSERT_EQ(
1332       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1333   ZipEntry64 entry;
1334   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1335 
1336   VectorWriter writer;
1337   ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1338   ASSERT_EQ(content, writer.GetOutput());
1339 }
1340 
TEST_F(Zip64ParseTest,extractWithDataDescriptor)1341 TEST_F(Zip64ParseTest, extractWithDataDescriptor) {
1342   std::vector<uint8_t> content(300, 'b');
1343   AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1344   AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
1345   ConstructEocd();
1346   ConstructZipFile();
1347 
1348   ZipArchiveHandle handle;
1349   ASSERT_EQ(
1350       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1351   ZipEntry64 entry;
1352   ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1353 
1354   VectorWriter writer;
1355   ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1356   ASSERT_EQ(content, writer.GetOutput());
1357 }
1358 
TEST_F(Zip64ParseTest,extraLFHOffset)1359 TEST_F(Zip64ParseTest, extraLFHOffset) {
1360   std::vector<uint8_t> content(300, 'b');
1361   AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1362   AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
1363 
1364   ASSERT_EQ(cd_entries_.back().extended_field.size(), 4 + 8 * 3)
1365       << "Extended field should contain 2 bytes id, 2 bytes size, and 3 "
1366          "values, each 64 bit";
1367   uint32_t local_file_header_offset = 0;
1368   std::for_each(file_entries_.begin(), file_entries_.end() - 1,
1369                 [&local_file_header_offset](const LocalFileEntry& file_entry) {
1370                   local_file_header_offset += file_entry.GetSize();
1371                 });
1372   auto& cd_entry = cd_entries_.back();
1373   // We want to construct a central directory record with LFH < 0xFFFFFFFF
1374   // but still comes with a 64 bit LFH in extended field.
1375   ConstructCentralDirectoryRecord(
1376       "b.txt", static_cast<uint32_t>(content.size()),
1377       static_cast<uint32_t>(content.size()), local_file_header_offset,
1378       &cd_entry.central_directory_record);
1379   ConstructEocd();
1380   ConstructZipFile();
1381 
1382   ZipArchiveHandle handle;
1383   ASSERT_EQ(0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(),
1384                                      "debug_zip64", &handle));
1385   ZipEntry64 entry;
1386   ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1387 
1388   VectorWriter writer;
1389   ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1390   ASSERT_EQ(content, writer.GetOutput());
1391 }
1392 
TEST(ziparchive,Bug174945959)1393 TEST(ziparchive, Bug174945959) {
1394   static const std::vector<uint8_t> zip {
1395     0x50, 0x4b, 0x03, 0x04, 0x50, 0x4b, 0x01, 0x02, 0x01, 0x53, 0x46, 0x5b,
1396     0xa4, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff,
1397     0xff, 0xff, 0xff, 0xff, 0x03, 0x12, 0x00, 0x07, 0x00, 0x00, 0x3b, 0x00,
1398     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0x00,
1399     0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0xa4, 0x2e, 0x00, 0x00, 0x00,
1400     0x24, 0x24, 0xb6, 0x3f, 0xff, 0xff, 0x31, 0x51, 0x49, 0xff, 0xff, 0xff,
1401     0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1402     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1403     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1404     0x4b, 0x05, 0x50, 0x4b, 0x05, 0x06, 0xc5, 0x1f, 0x4a, 0x04, 0x00, 0x21,
1405     0x01, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00};
1406   ZipArchiveHandle handle;
1407   ASSERT_EQ(0, OpenArchiveFromMemory(&zip[0], zip.size(), "name", &handle));
1408 
1409   void* cookie;
1410   ASSERT_EQ(0, StartIteration(handle, &cookie));
1411   ZipEntry ze;
1412   std::string name;
1413   int result;
1414   while ((result = Next(cookie, &ze, &name)) == 0) {
1415   }
1416   EndIteration(cookie);
1417 }
1418