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