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