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   // size of extra field in local file header confirmed with zipdetails.
363   // zipinfo -v will provide size of extra field from central directory.
364   ASSERT_EQ(28, data.extra_field_size);
365 
366   // An entry that doesn't exist. Should be a negative return code.
367   ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
368 
369   CloseArchive(handle);
370 }
371 
TEST(ziparchive,FindEntry_empty)372 TEST(ziparchive, FindEntry_empty) {
373   ZipArchiveHandle handle;
374   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
375 
376   ZipEntry64 data;
377   ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
378 
379   CloseArchive(handle);
380 }
381 
TEST(ziparchive,FindEntry_too_long)382 TEST(ziparchive, FindEntry_too_long) {
383   ZipArchiveHandle handle;
384   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
385 
386   std::string very_long_name(65536, 'x');
387   ZipEntry64 data;
388   ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
389 
390   CloseArchive(handle);
391 }
392 
TEST(ziparchive,TestInvalidDeclaredLength)393 TEST(ziparchive, TestInvalidDeclaredLength) {
394   ZipArchiveHandle handle;
395   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
396 
397   void* iteration_cookie;
398   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
399 
400   std::string_view name;
401   ZipEntry64 data;
402 
403   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
404   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
405 
406   CloseArchive(handle);
407 }
408 
TEST(ziparchive,OpenArchiveFdRange)409 TEST(ziparchive, OpenArchiveFdRange) {
410   TemporaryFile tmp_file;
411   ASSERT_NE(-1, tmp_file.fd);
412 
413   const std::string leading_garbage(21, 'x');
414   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
415                                         leading_garbage.size()));
416 
417   std::string valid_content;
418   ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
419   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
420 
421   const std::string ending_garbage(42, 'x');
422   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
423                                         ending_garbage.size()));
424 
425   ZipArchiveHandle handle;
426   ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
427   ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
428                                   valid_content.size(),
429                                   static_cast<off64_t>(leading_garbage.size())));
430 
431   // An entry that's deflated.
432   ZipEntry64 data;
433   ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
434   const auto a_size = static_cast<size_t>(data.uncompressed_length);
435   ASSERT_EQ(a_size, kATxtContents.size());
436   auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
437   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
438   ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
439 
440   // An entry that's stored.
441   ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
442   const auto b_size = static_cast<size_t>(data.uncompressed_length);
443   ASSERT_EQ(b_size, kBTxtContents.size());
444   buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
445   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
446   ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
447 
448   CloseArchive(handle);
449 }
450 
TEST(ziparchive,ExtractToMemory)451 TEST(ziparchive, ExtractToMemory) {
452   ZipArchiveHandle handle;
453   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
454 
455   // An entry that's deflated.
456   ZipEntry64 data;
457   ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
458   const auto a_size = static_cast<size_t>(data.uncompressed_length);
459   ASSERT_EQ(a_size, kATxtContents.size());
460   uint8_t* buffer = new uint8_t[a_size];
461   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
462   ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
463   delete[] buffer;
464 
465   // An entry that's stored.
466   ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
467   const auto b_size = static_cast<size_t>(data.uncompressed_length);
468   ASSERT_EQ(b_size, kBTxtContents.size());
469   buffer = new uint8_t[b_size];
470   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
471   ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
472   delete[] buffer;
473 
474   CloseArchive(handle);
475 }
476 
477 static const uint32_t kEmptyEntriesZip[] = {
478     0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
479     0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
480     0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
481     0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
482     0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
483     0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
484 
485 // This is a zip file containing a single entry (ab.txt) that contains
486 // 90072 repetitions of the string "ab\n" and has an uncompressed length
487 // of 270216 bytes.
488 static const uint16_t kAbZip[] = {
489     0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
490     0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
491     0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
492     0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 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, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
500     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
501     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
502     0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
503     0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
504     0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
505     0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
506     0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
507     0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
508 
509 static const std::string kAbTxtName("ab.txt");
510 static const size_t kAbUncompressedSize = 270216;
511 
TEST(ziparchive,EmptyEntries)512 TEST(ziparchive, EmptyEntries) {
513   TemporaryFile tmp_file;
514   ASSERT_NE(-1, tmp_file.fd);
515   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
516 
517   ZipArchiveHandle handle;
518   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
519 
520   ZipEntry64 entry;
521   ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
522   ASSERT_EQ(0u, entry.uncompressed_length);
523   // Extraction to a 1 byte buffer should succeed.
524   uint8_t buffer[1];
525   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
526   // Extraction to an empty buffer should succeed.
527   ASSERT_EQ(0, ExtractToMemory(handle, &entry, nullptr, 0));
528 
529   TemporaryFile tmp_output_file;
530   ASSERT_NE(-1, tmp_output_file.fd);
531   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
532 
533   struct stat stat_buf;
534   ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
535   ASSERT_EQ(0, stat_buf.st_size);
536 }
537 
TEST(ziparchive,EntryLargerThan32K)538 TEST(ziparchive, EntryLargerThan32K) {
539   TemporaryFile tmp_file;
540   ASSERT_NE(-1, tmp_file.fd);
541   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
542                                         sizeof(kAbZip) - 1));
543   ZipArchiveHandle handle;
544   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
545 
546   ZipEntry64 entry;
547   ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
548   ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
549 
550   // Extract the entry to memory.
551   std::vector<uint8_t> buffer(kAbUncompressedSize);
552   ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
553 
554   // Extract the entry to a file.
555   TemporaryFile tmp_output_file;
556   ASSERT_NE(-1, tmp_output_file.fd);
557   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
558 
559   // Make sure the extracted file size is as expected.
560   struct stat stat_buf;
561   ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
562   ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
563 
564   // Read the file back to a buffer and make sure the contents are
565   // the same as the memory buffer we extracted directly to.
566   std::vector<uint8_t> file_contents(kAbUncompressedSize);
567   ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
568   ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
569   ASSERT_EQ(file_contents, buffer);
570 
571   for (int i = 0; i < 90072; ++i) {
572     const uint8_t* line = &file_contents[0] + (3 * i);
573     ASSERT_EQ('a', line[0]);
574     ASSERT_EQ('b', line[1]);
575     ASSERT_EQ('\n', line[2]);
576   }
577 }
578 
TEST(ziparchive,TrailerAfterEOCD)579 TEST(ziparchive, TrailerAfterEOCD) {
580   TemporaryFile tmp_file;
581   ASSERT_NE(-1, tmp_file.fd);
582 
583   // Create a file with 8 bytes of random garbage.
584   static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
585   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
586   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
587 
588   ZipArchiveHandle handle;
589   ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
590 }
591 
TEST(ziparchive,ExtractToFile)592 TEST(ziparchive, ExtractToFile) {
593   TemporaryFile tmp_file;
594   ASSERT_NE(-1, tmp_file.fd);
595   const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
596   const size_t data_size = sizeof(data);
597 
598   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
599 
600   ZipArchiveHandle handle;
601   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
602 
603   ZipEntry64 entry;
604   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
605   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
606 
607   // Assert that the first 8 bytes of the file haven't been clobbered.
608   uint8_t read_buffer[data_size];
609   ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
610   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
611   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
612 
613   // Assert that the remainder of the file contains the incompressed data.
614   std::vector<uint8_t> uncompressed_data(static_cast<size_t>(entry.uncompressed_length));
615   ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
616                                        static_cast<size_t>(entry.uncompressed_length)));
617   ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
618 
619   // Assert that the total length of the file is sane
620   ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
621             lseek(tmp_file.fd, 0, SEEK_END));
622 }
623 
624 #if !defined(_WIN32)
TEST(ziparchive,OpenFromMemory)625 TEST(ziparchive, OpenFromMemory) {
626   const std::string zip_path = test_data_dir + "/dummy-update.zip";
627   android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
628   ASSERT_NE(-1, fd);
629   struct stat sb;
630   ASSERT_EQ(0, fstat(fd, &sb));
631 
632   // Memory map the file first and open the archive from the memory region.
633   auto file_map{
634       android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
635   ZipArchiveHandle handle;
636   ASSERT_EQ(0,
637             OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
638 
639   // Assert one entry can be found and extracted correctly.
640   ZipEntry64 binary_entry;
641   ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
642   TemporaryFile tmp_binary;
643   ASSERT_NE(-1, tmp_binary.fd);
644   ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
645 }
646 #endif
647 
ZipArchiveStreamTest(ZipArchiveHandle & handle,const std::string & entry_name,bool raw,bool verified,ZipEntry * entry,std::vector<uint8_t> * read_data)648 static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
649                                  bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
650   ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
651   std::unique_ptr<ZipArchiveStreamEntry> stream;
652   if (raw) {
653     stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
654     if (entry->method == kCompressStored) {
655       read_data->resize(static_cast<size_t>(entry->uncompressed_length));
656     } else {
657       read_data->resize(static_cast<size_t>(entry->compressed_length));
658     }
659   } else {
660     stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
661     read_data->resize(static_cast<size_t>(entry->uncompressed_length));
662   }
663   uint8_t* read_data_ptr = read_data->data();
664   ASSERT_TRUE(stream.get() != nullptr);
665   const std::vector<uint8_t>* data;
666   uint64_t total_size = 0;
667   while ((data = stream->Read()) != nullptr) {
668     total_size += data->size();
669     memcpy(read_data_ptr, data->data(), data->size());
670     read_data_ptr += data->size();
671   }
672   ASSERT_EQ(verified, stream->Verify());
673   ASSERT_EQ(total_size, read_data->size());
674 }
675 
ZipArchiveStreamTestUsingContents(const std::string & zip_file,const std::string & entry_name,const std::vector<uint8_t> & contents,bool raw)676 static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
677                                               const std::string& entry_name,
678                                               const std::vector<uint8_t>& contents, bool raw) {
679   ZipArchiveHandle handle;
680   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
681 
682   ZipEntry entry;
683   std::vector<uint8_t> read_data;
684   ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
685 
686   ASSERT_EQ(contents.size(), read_data.size());
687   ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
688 
689   CloseArchive(handle);
690 }
691 
ZipArchiveStreamTestUsingMemory(const std::string & zip_file,const std::string & entry_name)692 static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
693                                             const std::string& entry_name) {
694   ZipArchiveHandle handle;
695   ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
696 
697   ZipEntry entry;
698   std::vector<uint8_t> read_data;
699   ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
700 
701   std::vector<uint8_t> cmp_data(static_cast<size_t>(entry.uncompressed_length));
702   ASSERT_EQ(entry.uncompressed_length, read_data.size());
703   ASSERT_EQ(
704       0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
705   ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
706 
707   CloseArchive(handle);
708 }
709 
TEST(ziparchive,StreamCompressed)710 TEST(ziparchive, StreamCompressed) {
711   ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
712 }
713 
TEST(ziparchive,StreamUncompressed)714 TEST(ziparchive, StreamUncompressed) {
715   ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
716 }
717 
TEST(ziparchive,StreamRawCompressed)718 TEST(ziparchive, StreamRawCompressed) {
719   ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
720 }
721 
TEST(ziparchive,StreamRawUncompressed)722 TEST(ziparchive, StreamRawUncompressed) {
723   ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
724 }
725 
TEST(ziparchive,StreamLargeCompressed)726 TEST(ziparchive, StreamLargeCompressed) {
727   ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
728 }
729 
TEST(ziparchive,StreamLargeUncompressed)730 TEST(ziparchive, StreamLargeUncompressed) {
731   ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
732 }
733 
TEST(ziparchive,StreamCompressedBadCrc)734 TEST(ziparchive, StreamCompressedBadCrc) {
735   ZipArchiveHandle handle;
736   ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
737 
738   ZipEntry entry;
739   std::vector<uint8_t> read_data;
740   ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
741 
742   CloseArchive(handle);
743 }
744 
TEST(ziparchive,StreamUncompressedBadCrc)745 TEST(ziparchive, StreamUncompressedBadCrc) {
746   ZipArchiveHandle handle;
747   ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
748 
749   ZipEntry entry;
750   std::vector<uint8_t> read_data;
751   ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
752 
753   CloseArchive(handle);
754 }
755 
756 // Generated using the following Java program:
757 //     public static void main(String[] foo) throws Exception {
758 //       FileOutputStream fos = new
759 //       FileOutputStream("/tmp/data_descriptor.zip");
760 //       ZipOutputStream zos = new ZipOutputStream(fos);
761 //       ZipEntry64 ze = new ZipEntry64("name");
762 //       ze.setMethod(ZipEntry64.DEFLATED);
763 //       zos.putNextEntry(ze);
764 //       zos.write("abdcdefghijk".getBytes());
765 //       zos.closeEntry();
766 //       zos.close();
767 //     }
768 //
769 // cat /tmp/data_descriptor.zip | xxd -i
770 //
771 static const std::vector<uint8_t> kDataDescriptorZipFile{
772     0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
773     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
774     0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
775     //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
776     0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
777     0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
778     0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
779     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
780     0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
781     0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
782 
783 // The offsets of the data descriptor in this file, so we can mess with
784 // them later in the test.
785 static constexpr uint32_t kDataDescriptorOffset = 48;
786 static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
787 static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
788 
ExtractEntryToMemory(const std::vector<uint8_t> & zip_data,std::vector<uint8_t> * entry_out,int32_t * error_code_out)789 static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
790                                  std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
791   TemporaryFile tmp_file;
792   ASSERT_NE(-1, tmp_file.fd);
793   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
794   ZipArchiveHandle handle;
795   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
796 
797   // This function expects a variant of kDataDescriptorZipFile, for look for
798   // an entry whose name is "name" and whose size is 12 (contents =
799   // "abdcdefghijk").
800   ZipEntry64 entry;
801   ASSERT_EQ(0, FindEntry(handle, "name", &entry));
802   ASSERT_EQ(12u, entry.uncompressed_length);
803 
804   entry_out->resize(12);
805   (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
806 
807   CloseArchive(handle);
808 }
809 
TEST(ziparchive,ValidDataDescriptors)810 TEST(ziparchive, ValidDataDescriptors) {
811   std::vector<uint8_t> entry;
812   int32_t error_code = 0;
813   ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
814 
815   ASSERT_EQ(0, error_code);
816   ASSERT_EQ(12u, entry.size());
817   ASSERT_EQ('a', entry[0]);
818   ASSERT_EQ('k', entry[11]);
819 }
820 
TEST(ziparchive,InvalidDataDescriptors_csize)821 TEST(ziparchive, InvalidDataDescriptors_csize) {
822   std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
823   invalid_csize[kCSizeOffset] = 0xfe;
824 
825   std::vector<uint8_t> entry;
826   int32_t error_code = 0;
827   ExtractEntryToMemory(invalid_csize, &entry, &error_code);
828 
829   ASSERT_EQ(kInconsistentInformation, error_code);
830 }
831 
TEST(ziparchive,InvalidDataDescriptors_size)832 TEST(ziparchive, InvalidDataDescriptors_size) {
833   std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
834   invalid_size[kSizeOffset] = 0xfe;
835 
836   std::vector<uint8_t> entry;
837   int32_t error_code = 0;
838   ExtractEntryToMemory(invalid_size, &entry, &error_code);
839 
840   ASSERT_EQ(kInconsistentInformation, error_code);
841 }
842 
TEST(ziparchive,ErrorCodeString)843 TEST(ziparchive, ErrorCodeString) {
844   ASSERT_STREQ("Success", ErrorCodeString(0));
845 
846   // Out of bounds.
847   ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
848   ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
849   ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
850 
851   ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
852 }
853 
854 // A zip file whose local file header at offset zero is corrupted.
855 //
856 // ---------------
857 // cat foo > a.txt
858 // zip a.zip a.txt
859 // cat a.zip | xxd -i
860 //
861 // Manual changes :
862 // [2] = 0xff  // Corrupt the LFH signature of entry 0.
863 // [3] = 0xff  // Corrupt the LFH signature of entry 0.
864 static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
865     //[lfh-sig-----------], [lfh contents---------------------------------
866     0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
867     //--------------------------------------------------------------------
868     0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
869     //-------------------------------]  [file-name-----------------], [---
870     0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
871     // entry-contents------------------------------------------------------
872     0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
873     //--------------------------------------------------------------------
874     0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
875     //-------------------------------------], [cd-record-sig-------], [---
876     0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
877     // cd-record-----------------------------------------------------------
878     0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
879     //--------------------------------------------------------------------
880     0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
881     //--------------------------------------------------------------------
882     0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
883     //-]  [lfh-file-header-off-], [file-name-----------------], [extra----
884     0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
885     //--------------------------------------------------------------------
886     0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
887     //-------------------------------------------------------], [eocd-sig-
888     0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
889     //-------], [---------------------------------------------------------
890     0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
891     //-------------------------------------------]
892     0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
893 
TEST(ziparchive,BrokenLfhSignature)894 TEST(ziparchive, BrokenLfhSignature) {
895   TemporaryFile tmp_file;
896   ASSERT_NE(-1, tmp_file.fd);
897   ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
898                                         kZipFileWithBrokenLfhSignature.size()));
899   ZipArchiveHandle handle;
900   ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
901 }
902 
903 class VectorReader final : public zip_archive::Reader {
904  public:
VectorReader(const std::vector<uint8_t> & input)905   VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
906 
ReadAtOffset(uint8_t * buf,size_t len,off64_t offset) const907   bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
908     if ((offset + len) < input_.size()) {
909       return false;
910     }
911 
912     memcpy(buf, &input_[static_cast<size_t>(offset)], len);
913     return true;
914   }
915 
916  private:
917   const std::vector<uint8_t>& input_;
918 };
919 
920 class VectorWriter final : public zip_archive::Writer {
921  public:
VectorWriter()922   VectorWriter() : Writer() {}
923 
Append(uint8_t * buf,size_t size)924   bool Append(uint8_t* buf, size_t size) {
925     output_.insert(output_.end(), buf, buf + size);
926     return true;
927   }
928 
GetOutput()929   std::vector<uint8_t>& GetOutput() { return output_; }
930 
931  private:
932   std::vector<uint8_t> output_;
933 };
934 
935 class BadReader final : public zip_archive::Reader {
936  public:
BadReader()937   BadReader() : Reader() {}
938 
ReadAtOffset(uint8_t *,size_t,off64_t) const939   bool ReadAtOffset(uint8_t*, size_t, off64_t) const { return false; }
940 };
941 
942 class BadWriter final : public zip_archive::Writer {
943  public:
BadWriter()944   BadWriter() : Writer() {}
945 
Append(uint8_t *,size_t)946   bool Append(uint8_t*, size_t) { return false; }
947 };
948 
TEST(ziparchive,Inflate)949 TEST(ziparchive, Inflate) {
950   const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
951   const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
952 
953   const VectorReader reader(kATxtContentsCompressed);
954   {
955     VectorWriter writer;
956     uint64_t crc_out = 0;
957 
958     int32_t ret =
959         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
960     ASSERT_EQ(0, ret);
961     ASSERT_EQ(kATxtContents, writer.GetOutput());
962     ASSERT_EQ(0x950821C5u, crc_out);
963   }
964 
965   {
966     VectorWriter writer;
967     int32_t ret =
968         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
969     ASSERT_EQ(0, ret);
970     ASSERT_EQ(kATxtContents, writer.GetOutput());
971   }
972 
973   {
974     BadWriter writer;
975     int32_t ret =
976         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
977     ASSERT_EQ(kIoError, ret);
978   }
979 
980   {
981     BadReader reader;
982     VectorWriter writer;
983     int32_t ret =
984         zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
985     ASSERT_EQ(kIoError, ret);
986     ASSERT_EQ(0u, writer.GetOutput().size());
987   }
988 }
989 
990 // The class constructs a zipfile with zip64 format, and test the parsing logic.
991 class Zip64ParseTest : public ::testing::Test {
992  protected:
993   struct LocalFileEntry {
994     std::vector<uint8_t> local_file_header;
995     std::string file_name;
996     std::vector<uint8_t> extended_field;
997     // Fake data to mimic the compressed bytes in the zipfile.
998     std::vector<uint8_t> compressed_bytes;
999     std::vector<uint8_t> data_descriptor;
1000 
GetSizeZip64ParseTest::LocalFileEntry1001     size_t GetSize() const {
1002       return local_file_header.size() + file_name.size() + extended_field.size() +
1003              compressed_bytes.size() + data_descriptor.size();
1004     }
1005 
CopyToOutputZip64ParseTest::LocalFileEntry1006     void CopyToOutput(std::vector<uint8_t>* output) const {
1007       std::copy(local_file_header.begin(), local_file_header.end(), std::back_inserter(*output));
1008       std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
1009       std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
1010       std::copy(compressed_bytes.begin(), compressed_bytes.end(), std::back_inserter(*output));
1011       std::copy(data_descriptor.begin(), data_descriptor.end(), std::back_inserter(*output));
1012     }
1013   };
1014 
1015   struct CdRecordEntry {
1016     std::vector<uint8_t> central_directory_record;
1017     std::string file_name;
1018     std::vector<uint8_t> extended_field;
1019 
GetSizeZip64ParseTest::CdRecordEntry1020     size_t GetSize() const {
1021       return central_directory_record.size() + file_name.size() + extended_field.size();
1022     }
1023 
CopyToOutputZip64ParseTest::CdRecordEntry1024     void CopyToOutput(std::vector<uint8_t>* output) const {
1025       std::copy(central_directory_record.begin(), central_directory_record.end(),
1026                 std::back_inserter(*output));
1027       std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
1028       std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
1029     }
1030   };
1031 
ConstructLocalFileHeader(const std::string & name,std::vector<uint8_t> * output,uint32_t uncompressed_size,uint32_t compressed_size)1032   static void ConstructLocalFileHeader(const std::string& name, std::vector<uint8_t>* output,
1033                                        uint32_t uncompressed_size, uint32_t compressed_size) {
1034     LocalFileHeader lfh = {};
1035     lfh.lfh_signature = LocalFileHeader::kSignature;
1036     lfh.compressed_size = compressed_size;
1037     lfh.uncompressed_size = uncompressed_size;
1038     lfh.file_name_length = static_cast<uint16_t>(name.size());
1039     lfh.extra_field_length = 20;
1040     *output = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&lfh),
1041                                    reinterpret_cast<uint8_t*>(&lfh) + sizeof(LocalFileHeader));
1042   }
1043 
1044   // Put one zip64 extended info in the extended field.
ConstructExtendedField(const std::vector<uint64_t> & zip64_fields,std::vector<uint8_t> * output)1045   static void ConstructExtendedField(const std::vector<uint64_t>& zip64_fields,
1046                                      std::vector<uint8_t>* output) {
1047     ASSERT_FALSE(zip64_fields.empty());
1048     uint16_t data_size = 8 * static_cast<uint16_t>(zip64_fields.size());
1049     std::vector<uint8_t> extended_field(data_size + 4);
1050     android::base::put_unaligned(extended_field.data(), Zip64ExtendedInfo::kHeaderId);
1051     android::base::put_unaligned(extended_field.data() + 2, data_size);
1052     size_t offset = 4;
1053     for (const auto& field : zip64_fields) {
1054       android::base::put_unaligned(extended_field.data() + offset, field);
1055       offset += 8;
1056     }
1057 
1058     *output = std::move(extended_field);
1059   }
1060 
ConstructCentralDirectoryRecord(const std::string & name,uint32_t uncompressed_size,uint32_t compressed_size,uint32_t local_offset,std::vector<uint8_t> * output)1061   static void ConstructCentralDirectoryRecord(const std::string& name, uint32_t uncompressed_size,
1062                                               uint32_t compressed_size, uint32_t local_offset,
1063                                               std::vector<uint8_t>* output) {
1064     CentralDirectoryRecord cdr = {};
1065     cdr.record_signature = CentralDirectoryRecord::kSignature;
1066     cdr.compressed_size = uncompressed_size;
1067     cdr.uncompressed_size = compressed_size;
1068     cdr.file_name_length = static_cast<uint16_t>(name.size());
1069     cdr.extra_field_length = local_offset == UINT32_MAX ? 28 : 20;
1070     cdr.local_file_header_offset = local_offset;
1071     *output =
1072         std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&cdr),
1073                              reinterpret_cast<uint8_t*>(&cdr) + sizeof(CentralDirectoryRecord));
1074   }
1075 
1076   // 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)1077   void AddEntry(const std::string& name, const std::vector<uint8_t>& content,
1078                 bool uncompressed_size_in_extended, bool compressed_size_in_extended,
1079                 bool local_offset_in_extended, bool include_data_descriptor = false) {
1080     auto uncompressed_size = static_cast<uint32_t>(content.size());
1081     auto compressed_size = static_cast<uint32_t>(content.size());
1082     uint32_t local_file_header_offset = 0;
1083     std::for_each(file_entries_.begin(), file_entries_.end(),
1084                   [&local_file_header_offset](const LocalFileEntry& file_entry) {
1085                     local_file_header_offset += file_entry.GetSize();
1086                   });
1087 
1088     std::vector<uint64_t> zip64_fields;
1089     if (uncompressed_size_in_extended) {
1090       zip64_fields.push_back(uncompressed_size);
1091       uncompressed_size = UINT32_MAX;
1092     }
1093     if (compressed_size_in_extended) {
1094       zip64_fields.push_back(compressed_size);
1095       compressed_size = UINT32_MAX;
1096     }
1097     LocalFileEntry local_entry = {
1098         .local_file_header = {},
1099         .file_name = name,
1100         .extended_field = {},
1101         .compressed_bytes = content,
1102     };
1103     ConstructLocalFileHeader(name, &local_entry.local_file_header, uncompressed_size,
1104                              compressed_size);
1105     ConstructExtendedField(zip64_fields, &local_entry.extended_field);
1106     if (include_data_descriptor) {
1107       size_t descriptor_size = compressed_size_in_extended ? 24 : 16;
1108       local_entry.data_descriptor.resize(descriptor_size);
1109       uint8_t* write_ptr = local_entry.data_descriptor.data();
1110       EmitUnaligned<uint32_t>(&write_ptr, DataDescriptor::kOptSignature);
1111       EmitUnaligned<uint32_t>(&write_ptr, 0 /* crc */);
1112       if (compressed_size_in_extended) {
1113         EmitUnaligned<uint64_t>(&write_ptr, compressed_size_in_extended);
1114         EmitUnaligned<uint64_t>(&write_ptr, uncompressed_size_in_extended);
1115       } else {
1116         EmitUnaligned<uint32_t>(&write_ptr, compressed_size_in_extended);
1117         EmitUnaligned<uint32_t>(&write_ptr, uncompressed_size_in_extended);
1118       }
1119     }
1120 
1121     file_entries_.push_back(std::move(local_entry));
1122 
1123     if (local_offset_in_extended) {
1124       zip64_fields.push_back(local_file_header_offset);
1125       local_file_header_offset = UINT32_MAX;
1126     }
1127     CdRecordEntry cd_entry = {
1128         .central_directory_record = {},
1129         .file_name = name,
1130         .extended_field = {},
1131     };
1132     ConstructCentralDirectoryRecord(name, uncompressed_size, compressed_size,
1133                                     local_file_header_offset, &cd_entry.central_directory_record);
1134     ConstructExtendedField(zip64_fields, &cd_entry.extended_field);
1135     cd_entries_.push_back(std::move(cd_entry));
1136   }
1137 
ConstructEocd()1138   void ConstructEocd() {
1139     ASSERT_EQ(file_entries_.size(), cd_entries_.size());
1140     Zip64EocdRecord zip64_eocd = {};
1141     zip64_eocd.record_signature = Zip64EocdRecord::kSignature;
1142     zip64_eocd.num_records = file_entries_.size();
1143     zip64_eocd.cd_size = 0;
1144     std::for_each(
1145         cd_entries_.begin(), cd_entries_.end(),
1146         [&zip64_eocd](const CdRecordEntry& cd_entry) { zip64_eocd.cd_size += cd_entry.GetSize(); });
1147     zip64_eocd.cd_start_offset = 0;
1148     std::for_each(file_entries_.begin(), file_entries_.end(),
1149                   [&zip64_eocd](const LocalFileEntry& file_entry) {
1150                     zip64_eocd.cd_start_offset += file_entry.GetSize();
1151                   });
1152     zip64_eocd_record_ =
1153         std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_eocd),
1154                              reinterpret_cast<uint8_t*>(&zip64_eocd) + sizeof(Zip64EocdRecord));
1155 
1156     Zip64EocdLocator zip64_locator = {};
1157     zip64_locator.locator_signature = Zip64EocdLocator::kSignature;
1158     zip64_locator.zip64_eocd_offset = zip64_eocd.cd_start_offset + zip64_eocd.cd_size;
1159     zip64_eocd_locator_ =
1160         std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_locator),
1161                              reinterpret_cast<uint8_t*>(&zip64_locator) + sizeof(Zip64EocdLocator));
1162 
1163     EocdRecord eocd = {};
1164     eocd.eocd_signature = EocdRecord::kSignature,
1165     eocd.num_records = file_entries_.size() > UINT16_MAX
1166                            ? UINT16_MAX
1167                            : static_cast<uint16_t>(file_entries_.size());
1168     eocd.cd_size = UINT32_MAX;
1169     eocd.cd_start_offset = UINT32_MAX;
1170     eocd_record_ = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&eocd),
1171                                         reinterpret_cast<uint8_t*>(&eocd) + sizeof(EocdRecord));
1172   }
1173 
1174   // Concatenate all the local file entries, cd entries, and eocd metadata.
ConstructZipFile()1175   void ConstructZipFile() {
1176     for (const auto& file_entry : file_entries_) {
1177       file_entry.CopyToOutput(&zip_content_);
1178     }
1179     for (const auto& cd_entry : cd_entries_) {
1180       cd_entry.CopyToOutput(&zip_content_);
1181     }
1182     std::copy(zip64_eocd_record_.begin(), zip64_eocd_record_.end(),
1183               std::back_inserter(zip_content_));
1184     std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
1185               std::back_inserter(zip_content_));
1186     std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
1187   }
1188 
1189   std::vector<uint8_t> zip_content_;
1190 
1191   std::vector<LocalFileEntry> file_entries_;
1192   std::vector<CdRecordEntry> cd_entries_;
1193   std::vector<uint8_t> zip64_eocd_record_;
1194   std::vector<uint8_t> zip64_eocd_locator_;
1195   std::vector<uint8_t> eocd_record_;
1196 };
1197 
TEST_F(Zip64ParseTest,openFile)1198 TEST_F(Zip64ParseTest, openFile) {
1199   AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
1200   ConstructEocd();
1201   ConstructZipFile();
1202 
1203   ZipArchiveHandle handle;
1204   ASSERT_EQ(
1205       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1206   CloseArchive(handle);
1207 }
1208 
TEST_F(Zip64ParseTest,openFilelocalOffsetInExtendedField)1209 TEST_F(Zip64ParseTest, openFilelocalOffsetInExtendedField) {
1210   AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, true);
1211   AddEntry("b.txt", std::vector<uint8_t>(200, 'b'), true, true, true);
1212   ConstructEocd();
1213   ConstructZipFile();
1214 
1215   ZipArchiveHandle handle;
1216   ASSERT_EQ(
1217       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1218   CloseArchive(handle);
1219 }
1220 
TEST_F(Zip64ParseTest,openFileCompressedNotInExtendedField)1221 TEST_F(Zip64ParseTest, openFileCompressedNotInExtendedField) {
1222   AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, false, false);
1223   ConstructEocd();
1224   ConstructZipFile();
1225 
1226   ZipArchiveHandle handle;
1227   // Zip64 extended fields must include both uncompressed and compressed size.
1228   ASSERT_NE(
1229       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1230   CloseArchive(handle);
1231 }
1232 
TEST_F(Zip64ParseTest,findEntry)1233 TEST_F(Zip64ParseTest, findEntry) {
1234   AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1235   AddEntry("b.txt", std::vector<uint8_t>(300, 'b'), true, true, false);
1236   ConstructEocd();
1237   ConstructZipFile();
1238 
1239   ZipArchiveHandle handle;
1240   ASSERT_EQ(
1241       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1242   ZipEntry64 entry;
1243   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1244   ASSERT_EQ(200, entry.uncompressed_length);
1245   ASSERT_EQ(200, entry.compressed_length);
1246 
1247   ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1248   ASSERT_EQ(300, entry.uncompressed_length);
1249   ASSERT_EQ(300, entry.compressed_length);
1250   CloseArchive(handle);
1251 }
1252 
TEST_F(Zip64ParseTest,dataDescriptor)1253 TEST_F(Zip64ParseTest, dataDescriptor) {
1254   AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true, false);
1255   AddEntry("b.txt", std::vector<uint8_t>(300, 'b'), true, true, true, false);
1256   // We want a file with compressed size in extended fields, but
1257   // data descriptor still in 32 bit values.
1258   auto& local_entry = file_entries_.back();
1259   local_entry.data_descriptor.resize(16);
1260   uint8_t* write_ptr = local_entry.data_descriptor.data();
1261   EmitUnaligned<uint32_t>(&write_ptr, DataDescriptor::kOptSignature);
1262   EmitUnaligned<uint32_t>(&write_ptr, 0 /* crc */);
1263   EmitUnaligned<uint32_t>(&write_ptr, 300);
1264   EmitUnaligned<uint32_t>(&write_ptr, 300);
1265 
1266   ConstructEocd();
1267   ConstructZipFile();
1268 
1269   ZipArchiveHandle handle;
1270   ASSERT_EQ(0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(),
1271                                      "debug_zip64", &handle));
1272   ZipEntry64 entry;
1273   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1274   ASSERT_EQ(200, entry.uncompressed_length);
1275   ASSERT_EQ(200, entry.compressed_length);
1276 
1277   ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1278   ASSERT_EQ(300, entry.uncompressed_length);
1279   ASSERT_EQ(300, entry.compressed_length);
1280   CloseArchive(handle);
1281 }
1282 
TEST_F(Zip64ParseTest,openFileIncorrectDataSizeInLocalExtendedField)1283 TEST_F(Zip64ParseTest, openFileIncorrectDataSizeInLocalExtendedField) {
1284   AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
1285   ASSERT_EQ(1, file_entries_.size());
1286   auto& extended_field = file_entries_[0].extended_field;
1287   // data size exceeds the extended field size in local header.
1288   android::base::put_unaligned<uint16_t>(extended_field.data() + 2, 30);
1289   ConstructEocd();
1290   ConstructZipFile();
1291 
1292   ZipArchiveHandle handle;
1293   ASSERT_EQ(
1294       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1295   ZipEntry64 entry;
1296   ASSERT_NE(0, FindEntry(handle, "a.txt", &entry));
1297 
1298   CloseArchive(handle);
1299 }
1300 
TEST_F(Zip64ParseTest,iterates)1301 TEST_F(Zip64ParseTest, iterates) {
1302   std::set<std::string_view> names{"a.txt", "b.txt", "c.txt", "d.txt", "e.txt"};
1303   for (const auto& name : names) {
1304     AddEntry(std::string(name), std::vector<uint8_t>(100, name[0]), true, true, true);
1305   }
1306   ConstructEocd();
1307   ConstructZipFile();
1308 
1309   ZipArchiveHandle handle;
1310   ASSERT_EQ(
1311       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1312 
1313   void* iteration_cookie;
1314   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
1315   std::set<std::string_view> result;
1316   std::string_view name;
1317   ZipEntry64 entry;
1318   while (Next(iteration_cookie, &entry, &name) == 0) result.emplace(name);
1319   ASSERT_EQ(names, result);
1320 
1321   CloseArchive(handle);
1322 }
1323 
TEST_F(Zip64ParseTest,zip64EocdWrongLocatorOffset)1324 TEST_F(Zip64ParseTest, zip64EocdWrongLocatorOffset) {
1325   AddEntry("a.txt", std::vector<uint8_t>(1, 'a'), true, true, true);
1326   ConstructEocd();
1327   zip_content_.resize(20, 'a');
1328   std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
1329             std::back_inserter(zip_content_));
1330   std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
1331 
1332   ZipArchiveHandle handle;
1333   ASSERT_NE(
1334       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1335   CloseArchive(handle);
1336 }
1337 
TEST_F(Zip64ParseTest,extract)1338 TEST_F(Zip64ParseTest, extract) {
1339   std::vector<uint8_t> content(200, 'a');
1340   AddEntry("a.txt", content, true, true, true);
1341   ConstructEocd();
1342   ConstructZipFile();
1343 
1344   ZipArchiveHandle handle;
1345   ASSERT_EQ(
1346       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1347   ZipEntry64 entry;
1348   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1349 
1350   VectorWriter writer;
1351   ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1352   ASSERT_EQ(content, writer.GetOutput());
1353 }
1354 
TEST_F(Zip64ParseTest,extractWithDataDescriptor)1355 TEST_F(Zip64ParseTest, extractWithDataDescriptor) {
1356   std::vector<uint8_t> content(300, 'b');
1357   AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1358   AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
1359   ConstructEocd();
1360   ConstructZipFile();
1361 
1362   ZipArchiveHandle handle;
1363   ASSERT_EQ(
1364       0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1365   ZipEntry64 entry;
1366   ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1367 
1368   VectorWriter writer;
1369   ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1370   ASSERT_EQ(content, writer.GetOutput());
1371 }
1372 
TEST_F(Zip64ParseTest,extraLFHOffset)1373 TEST_F(Zip64ParseTest, extraLFHOffset) {
1374   std::vector<uint8_t> content(300, 'b');
1375   AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1376   AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
1377 
1378   ASSERT_EQ(cd_entries_.back().extended_field.size(), 4 + 8 * 3)
1379       << "Extended field should contain 2 bytes id, 2 bytes size, and 3 "
1380          "values, each 64 bit";
1381   uint32_t local_file_header_offset = 0;
1382   std::for_each(file_entries_.begin(), file_entries_.end() - 1,
1383                 [&local_file_header_offset](const LocalFileEntry& file_entry) {
1384                   local_file_header_offset += file_entry.GetSize();
1385                 });
1386   auto& cd_entry = cd_entries_.back();
1387   // We want to construct a central directory record with LFH < 0xFFFFFFFF
1388   // but still comes with a 64 bit LFH in extended field.
1389   ConstructCentralDirectoryRecord(
1390       "b.txt", static_cast<uint32_t>(content.size()),
1391       static_cast<uint32_t>(content.size()), local_file_header_offset,
1392       &cd_entry.central_directory_record);
1393   ConstructEocd();
1394   ConstructZipFile();
1395 
1396   ZipArchiveHandle handle;
1397   ASSERT_EQ(0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(),
1398                                      "debug_zip64", &handle));
1399   ZipEntry64 entry;
1400   ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1401 
1402   VectorWriter writer;
1403   ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1404   ASSERT_EQ(content, writer.GetOutput());
1405 }
1406 
TEST(ziparchive,Bug174945959)1407 TEST(ziparchive, Bug174945959) {
1408   static const std::vector<uint8_t> zip {
1409     0x50, 0x4b, 0x03, 0x04, 0x50, 0x4b, 0x01, 0x02, 0x01, 0x53, 0x46, 0x5b,
1410     0xa4, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff,
1411     0xff, 0xff, 0xff, 0xff, 0x03, 0x12, 0x00, 0x07, 0x00, 0x00, 0x3b, 0x00,
1412     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0x00,
1413     0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0xa4, 0x2e, 0x00, 0x00, 0x00,
1414     0x24, 0x24, 0xb6, 0x3f, 0xff, 0xff, 0x31, 0x51, 0x49, 0xff, 0xff, 0xff,
1415     0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1416     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1417     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1418     0x4b, 0x05, 0x50, 0x4b, 0x05, 0x06, 0xc5, 0x1f, 0x4a, 0x04, 0x00, 0x21,
1419     0x01, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00};
1420   ZipArchiveHandle handle;
1421   ASSERT_EQ(0, OpenArchiveFromMemory(&zip[0], zip.size(), "name", &handle));
1422 
1423   void* cookie;
1424   ASSERT_EQ(0, StartIteration(handle, &cookie));
1425   ZipEntry ze;
1426   std::string name;
1427   int result;
1428   while ((result = Next(cookie, &ze, &name)) == 0) {
1429   }
1430   EndIteration(cookie);
1431 }
1432