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