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,openFileIncorrectDataSizeInLocalExtendedField)1239 TEST_F(Zip64ParseTest, openFileIncorrectDataSizeInLocalExtendedField) {
1240 AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
1241 ASSERT_EQ(1, file_entries_.size());
1242 auto& extended_field = file_entries_[0].extended_field;
1243 // data size exceeds the extended field size in local header.
1244 android::base::put_unaligned<uint16_t>(extended_field.data() + 2, 30);
1245 ConstructEocd();
1246 ConstructZipFile();
1247
1248 ZipArchiveHandle handle;
1249 ASSERT_EQ(
1250 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1251 ZipEntry64 entry;
1252 ASSERT_NE(0, FindEntry(handle, "a.txt", &entry));
1253
1254 CloseArchive(handle);
1255 }
1256
TEST_F(Zip64ParseTest,iterates)1257 TEST_F(Zip64ParseTest, iterates) {
1258 std::set<std::string_view> names{"a.txt", "b.txt", "c.txt", "d.txt", "e.txt"};
1259 for (const auto& name : names) {
1260 AddEntry(std::string(name), std::vector<uint8_t>(100, name[0]), true, true, true);
1261 }
1262 ConstructEocd();
1263 ConstructZipFile();
1264
1265 ZipArchiveHandle handle;
1266 ASSERT_EQ(
1267 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1268
1269 void* iteration_cookie;
1270 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
1271 std::set<std::string_view> result;
1272 std::string_view name;
1273 ZipEntry64 entry;
1274 while (Next(iteration_cookie, &entry, &name) == 0) result.emplace(name);
1275 ASSERT_EQ(names, result);
1276
1277 CloseArchive(handle);
1278 }
1279
TEST_F(Zip64ParseTest,zip64EocdWrongLocatorOffset)1280 TEST_F(Zip64ParseTest, zip64EocdWrongLocatorOffset) {
1281 AddEntry("a.txt", std::vector<uint8_t>(1, 'a'), true, true, true);
1282 ConstructEocd();
1283 zip_content_.resize(20, 'a');
1284 std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
1285 std::back_inserter(zip_content_));
1286 std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
1287
1288 ZipArchiveHandle handle;
1289 ASSERT_NE(
1290 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1291 CloseArchive(handle);
1292 }
1293
TEST_F(Zip64ParseTest,extract)1294 TEST_F(Zip64ParseTest, extract) {
1295 std::vector<uint8_t> content(200, 'a');
1296 AddEntry("a.txt", content, true, true, true);
1297 ConstructEocd();
1298 ConstructZipFile();
1299
1300 ZipArchiveHandle handle;
1301 ASSERT_EQ(
1302 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1303 ZipEntry64 entry;
1304 ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1305
1306 VectorWriter writer;
1307 ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1308 ASSERT_EQ(content, writer.GetOutput());
1309 }
1310
TEST_F(Zip64ParseTest,extractWithDataDescriptor)1311 TEST_F(Zip64ParseTest, extractWithDataDescriptor) {
1312 std::vector<uint8_t> content(300, 'b');
1313 AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1314 AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
1315 ConstructEocd();
1316 ConstructZipFile();
1317
1318 ZipArchiveHandle handle;
1319 ASSERT_EQ(
1320 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1321 ZipEntry64 entry;
1322 ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1323
1324 VectorWriter writer;
1325 ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1326 ASSERT_EQ(content, writer.GetOutput());
1327 }
1328