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