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 "zip_archive_private.h"
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include <memory>
27 #include <vector>
28
29 #include <android-base/file.h>
30 #include <android-base/logging.h>
31 #include <android-base/mapped_file.h>
32 #include <android-base/unique_fd.h>
33 #include <gtest/gtest.h>
34 #include <ziparchive/zip_archive.h>
35 #include <ziparchive/zip_archive_stream_entry.h>
36
37 static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
38
39 static const std::string kValidZip = "valid.zip";
40 static const std::string kLargeZip = "large.zip";
41 static const std::string kBadCrcZip = "bad_crc.zip";
42
43 static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
44 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
45
46 static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I', 'M', 'K',
47 207, 'H', 132, 210, '\\', '\0'};
48
49 static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
50
OpenArchiveWrapper(const std::string & name,ZipArchiveHandle * handle)51 static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
52 const std::string abs_path = test_data_dir + "/" + name;
53 return OpenArchive(abs_path.c_str(), handle);
54 }
55
TEST(ziparchive,Open)56 TEST(ziparchive, Open) {
57 ZipArchiveHandle handle;
58 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
59 CloseArchive(handle);
60
61 ASSERT_EQ(kInvalidEntryName, OpenArchiveWrapper("bad_filename.zip", &handle));
62 CloseArchive(handle);
63 }
64
TEST(ziparchive,OutOfBound)65 TEST(ziparchive, OutOfBound) {
66 ZipArchiveHandle handle;
67 ASSERT_EQ(kInvalidOffset, OpenArchiveWrapper("crash.apk", &handle));
68 CloseArchive(handle);
69 }
70
TEST(ziparchive,EmptyArchive)71 TEST(ziparchive, EmptyArchive) {
72 ZipArchiveHandle handle;
73 ASSERT_EQ(kEmptyArchive, OpenArchiveWrapper("empty.zip", &handle));
74 CloseArchive(handle);
75 }
76
TEST(ziparchive,ZeroSizeCentralDirectory)77 TEST(ziparchive, ZeroSizeCentralDirectory) {
78 ZipArchiveHandle handle;
79 ASSERT_EQ(kInvalidFile, OpenArchiveWrapper("zero-size-cd.zip", &handle));
80 CloseArchive(handle);
81 }
82
TEST(ziparchive,OpenMissing)83 TEST(ziparchive, OpenMissing) {
84 ZipArchiveHandle handle;
85 ASSERT_NE(0, OpenArchiveWrapper("missing.zip", &handle));
86
87 // Confirm the file descriptor is not going to be mistaken for a valid one.
88 ASSERT_EQ(-1, GetFileDescriptor(handle));
89 }
90
TEST(ziparchive,OpenAssumeFdOwnership)91 TEST(ziparchive, OpenAssumeFdOwnership) {
92 int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
93 ASSERT_NE(-1, fd);
94 ZipArchiveHandle handle;
95 ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
96 CloseArchive(handle);
97 ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
98 ASSERT_EQ(EBADF, errno);
99 }
100
TEST(ziparchive,OpenDoNotAssumeFdOwnership)101 TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
102 int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
103 ASSERT_NE(-1, fd);
104 ZipArchiveHandle handle;
105 ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
106 CloseArchive(handle);
107 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
108 close(fd);
109 }
110
TEST(ziparchive,OpenAssumeFdRangeOwnership)111 TEST(ziparchive, OpenAssumeFdRangeOwnership) {
112 int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
113 ASSERT_NE(-1, fd);
114 const off64_t length = lseek64(fd, 0, SEEK_END);
115 ASSERT_NE(-1, length);
116 ZipArchiveHandle handle;
117 ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
118 static_cast<size_t>(length), 0));
119 CloseArchive(handle);
120 ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
121 ASSERT_EQ(EBADF, errno);
122 }
123
TEST(ziparchive,OpenDoNotAssumeFdRangeOwnership)124 TEST(ziparchive, OpenDoNotAssumeFdRangeOwnership) {
125 int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
126 ASSERT_NE(-1, fd);
127 const off64_t length = lseek(fd, 0, SEEK_END);
128 ASSERT_NE(-1, length);
129 ZipArchiveHandle handle;
130 ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
131 static_cast<size_t>(length), 0, false));
132 CloseArchive(handle);
133 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
134 close(fd);
135 }
136
TEST(ziparchive,Iteration_std_string_view)137 TEST(ziparchive, Iteration_std_string_view) {
138 ZipArchiveHandle handle;
139 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
140
141 void* iteration_cookie;
142 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
143
144 ZipEntry data;
145 std::vector<std::string_view> names;
146 std::string_view name;
147 while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
148
149 // Assert that the names are as expected.
150 std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
151 std::sort(names.begin(), names.end());
152 ASSERT_EQ(expected_names, names);
153
154 CloseArchive(handle);
155 }
156
AssertIterationOrder(const std::string_view prefix,const std::string_view suffix,const std::vector<std::string> & expected_names_sorted)157 static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
158 const std::vector<std::string>& expected_names_sorted) {
159 ZipArchiveHandle handle;
160 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
161
162 void* iteration_cookie;
163 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
164
165 ZipEntry data;
166 std::vector<std::string> names;
167
168 std::string name;
169 for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
170 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
171 names.push_back(name);
172 }
173
174 // End of iteration.
175 ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
176 CloseArchive(handle);
177
178 // Assert that the names are as expected.
179 std::sort(names.begin(), names.end());
180 ASSERT_EQ(expected_names_sorted, names);
181 }
182
TEST(ziparchive,Iteration)183 TEST(ziparchive, Iteration) {
184 static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
185 "b/d.txt"};
186
187 AssertIterationOrder("", "", kExpectedMatchesSorted);
188 }
189
TEST(ziparchive,IterationWithPrefix)190 TEST(ziparchive, IterationWithPrefix) {
191 static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
192
193 AssertIterationOrder("b/", "", kExpectedMatchesSorted);
194 }
195
TEST(ziparchive,IterationWithSuffix)196 TEST(ziparchive, IterationWithSuffix) {
197 static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
198 "b/d.txt"};
199
200 AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
201 }
202
TEST(ziparchive,IterationWithPrefixAndSuffix)203 TEST(ziparchive, IterationWithPrefixAndSuffix) {
204 static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
205
206 AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
207 }
208
TEST(ziparchive,IterationWithBadPrefixAndSuffix)209 TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
210 ZipArchiveHandle handle;
211 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
212
213 void* iteration_cookie;
214 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
215
216 ZipEntry data;
217 std::string name;
218
219 // End of iteration.
220 ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
221
222 CloseArchive(handle);
223 }
224
TEST(ziparchive,FindEntry)225 TEST(ziparchive, FindEntry) {
226 ZipArchiveHandle handle;
227 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
228
229 ZipEntry data;
230 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
231
232 // Known facts about a.txt, from zipinfo -v.
233 ASSERT_EQ(63, data.offset);
234 ASSERT_EQ(kCompressDeflated, data.method);
235 ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
236 ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
237 ASSERT_EQ(0x950821c5, data.crc32);
238 ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
239
240 // An entry that doesn't exist. Should be a negative return code.
241 ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
242
243 CloseArchive(handle);
244 }
245
TEST(ziparchive,FindEntry_empty)246 TEST(ziparchive, FindEntry_empty) {
247 ZipArchiveHandle handle;
248 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
249
250 ZipEntry data;
251 ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
252
253 CloseArchive(handle);
254 }
255
TEST(ziparchive,FindEntry_too_long)256 TEST(ziparchive, FindEntry_too_long) {
257 ZipArchiveHandle handle;
258 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
259
260 std::string very_long_name(65536, 'x');
261 ZipEntry data;
262 ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
263
264 CloseArchive(handle);
265 }
266
TEST(ziparchive,TestInvalidDeclaredLength)267 TEST(ziparchive, TestInvalidDeclaredLength) {
268 ZipArchiveHandle handle;
269 ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
270
271 void* iteration_cookie;
272 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
273
274 std::string name;
275 ZipEntry data;
276
277 ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
278 ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
279
280 CloseArchive(handle);
281 }
282
TEST(ziparchive,OpenArchiveFdRange)283 TEST(ziparchive, OpenArchiveFdRange) {
284 TemporaryFile tmp_file;
285 ASSERT_NE(-1, tmp_file.fd);
286
287 const std::string leading_garbage(21, 'x');
288 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
289 leading_garbage.size()));
290
291 std::string valid_content;
292 ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
293 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
294
295 const std::string ending_garbage(42, 'x');
296 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
297 ending_garbage.size()));
298
299 ZipArchiveHandle handle;
300 ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
301 ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
302 valid_content.size(),
303 static_cast<off64_t>(leading_garbage.size())));
304
305 // An entry that's deflated.
306 ZipEntry data;
307 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
308 const uint32_t a_size = data.uncompressed_length;
309 ASSERT_EQ(a_size, kATxtContents.size());
310 auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
311 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
312 ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
313
314 // An entry that's stored.
315 ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
316 const uint32_t b_size = data.uncompressed_length;
317 ASSERT_EQ(b_size, kBTxtContents.size());
318 buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
319 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
320 ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
321
322 CloseArchive(handle);
323 }
324
TEST(ziparchive,ExtractToMemory)325 TEST(ziparchive, ExtractToMemory) {
326 ZipArchiveHandle handle;
327 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
328
329 // An entry that's deflated.
330 ZipEntry data;
331 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
332 const uint32_t a_size = data.uncompressed_length;
333 ASSERT_EQ(a_size, kATxtContents.size());
334 uint8_t* buffer = new uint8_t[a_size];
335 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
336 ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
337 delete[] buffer;
338
339 // An entry that's stored.
340 ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
341 const uint32_t b_size = data.uncompressed_length;
342 ASSERT_EQ(b_size, kBTxtContents.size());
343 buffer = new uint8_t[b_size];
344 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
345 ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
346 delete[] buffer;
347
348 CloseArchive(handle);
349 }
350
351 static const uint32_t kEmptyEntriesZip[] = {
352 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
353 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
354 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
355 0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
356 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
357 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
358
359 // This is a zip file containing a single entry (ab.txt) that contains
360 // 90072 repetitions of the string "ab\n" and has an uncompressed length
361 // of 270216 bytes.
362 static const uint16_t kAbZip[] = {
363 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
364 0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
365 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
366 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
367 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
368 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
369 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
370 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
371 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
372 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
373 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
374 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
375 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
376 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
377 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
378 0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
379 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
380 0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
381 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
382
383 static const std::string kAbTxtName("ab.txt");
384 static const size_t kAbUncompressedSize = 270216;
385
TEST(ziparchive,EmptyEntries)386 TEST(ziparchive, EmptyEntries) {
387 TemporaryFile tmp_file;
388 ASSERT_NE(-1, tmp_file.fd);
389 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
390
391 ZipArchiveHandle handle;
392 ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
393
394 ZipEntry entry;
395 ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
396 ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
397 uint8_t buffer[1];
398 ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
399
400 TemporaryFile tmp_output_file;
401 ASSERT_NE(-1, tmp_output_file.fd);
402 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
403
404 struct stat stat_buf;
405 ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
406 ASSERT_EQ(0, stat_buf.st_size);
407 }
408
TEST(ziparchive,EntryLargerThan32K)409 TEST(ziparchive, EntryLargerThan32K) {
410 TemporaryFile tmp_file;
411 ASSERT_NE(-1, tmp_file.fd);
412 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
413 sizeof(kAbZip) - 1));
414 ZipArchiveHandle handle;
415 ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
416
417 ZipEntry entry;
418 ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
419 ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
420
421 // Extract the entry to memory.
422 std::vector<uint8_t> buffer(kAbUncompressedSize);
423 ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
424
425 // Extract the entry to a file.
426 TemporaryFile tmp_output_file;
427 ASSERT_NE(-1, tmp_output_file.fd);
428 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
429
430 // Make sure the extracted file size is as expected.
431 struct stat stat_buf;
432 ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
433 ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
434
435 // Read the file back to a buffer and make sure the contents are
436 // the same as the memory buffer we extracted directly to.
437 std::vector<uint8_t> file_contents(kAbUncompressedSize);
438 ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
439 ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
440 ASSERT_EQ(file_contents, buffer);
441
442 for (int i = 0; i < 90072; ++i) {
443 const uint8_t* line = &file_contents[0] + (3 * i);
444 ASSERT_EQ('a', line[0]);
445 ASSERT_EQ('b', line[1]);
446 ASSERT_EQ('\n', line[2]);
447 }
448 }
449
TEST(ziparchive,TrailerAfterEOCD)450 TEST(ziparchive, TrailerAfterEOCD) {
451 TemporaryFile tmp_file;
452 ASSERT_NE(-1, tmp_file.fd);
453
454 // Create a file with 8 bytes of random garbage.
455 static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
456 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
457 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
458
459 ZipArchiveHandle handle;
460 ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
461 }
462
TEST(ziparchive,ExtractToFile)463 TEST(ziparchive, ExtractToFile) {
464 TemporaryFile tmp_file;
465 ASSERT_NE(-1, tmp_file.fd);
466 const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
467 const size_t data_size = sizeof(data);
468
469 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
470
471 ZipArchiveHandle handle;
472 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
473
474 ZipEntry entry;
475 ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
476 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
477
478 // Assert that the first 8 bytes of the file haven't been clobbered.
479 uint8_t read_buffer[data_size];
480 ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
481 ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
482 ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
483
484 // Assert that the remainder of the file contains the incompressed data.
485 std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
486 ASSERT_TRUE(
487 android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
488 ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
489
490 // Assert that the total length of the file is sane
491 ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
492 lseek(tmp_file.fd, 0, SEEK_END));
493 }
494
495 #if !defined(_WIN32)
TEST(ziparchive,OpenFromMemory)496 TEST(ziparchive, OpenFromMemory) {
497 const std::string zip_path = test_data_dir + "/dummy-update.zip";
498 android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
499 ASSERT_NE(-1, fd);
500 struct stat sb;
501 ASSERT_EQ(0, fstat(fd, &sb));
502
503 // Memory map the file first and open the archive from the memory region.
504 auto file_map{
505 android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
506 ZipArchiveHandle handle;
507 ASSERT_EQ(0,
508 OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
509
510 // Assert one entry can be found and extracted correctly.
511 ZipEntry binary_entry;
512 ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
513 TemporaryFile tmp_binary;
514 ASSERT_NE(-1, tmp_binary.fd);
515 ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
516 }
517 #endif
518
ZipArchiveStreamTest(ZipArchiveHandle & handle,const std::string & entry_name,bool raw,bool verified,ZipEntry * entry,std::vector<uint8_t> * read_data)519 static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
520 bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
521 ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
522 std::unique_ptr<ZipArchiveStreamEntry> stream;
523 if (raw) {
524 stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
525 if (entry->method == kCompressStored) {
526 read_data->resize(entry->uncompressed_length);
527 } else {
528 read_data->resize(entry->compressed_length);
529 }
530 } else {
531 stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
532 read_data->resize(entry->uncompressed_length);
533 }
534 uint8_t* read_data_ptr = read_data->data();
535 ASSERT_TRUE(stream.get() != nullptr);
536 const std::vector<uint8_t>* data;
537 uint64_t total_size = 0;
538 while ((data = stream->Read()) != nullptr) {
539 total_size += data->size();
540 memcpy(read_data_ptr, data->data(), data->size());
541 read_data_ptr += data->size();
542 }
543 ASSERT_EQ(verified, stream->Verify());
544 ASSERT_EQ(total_size, read_data->size());
545 }
546
ZipArchiveStreamTestUsingContents(const std::string & zip_file,const std::string & entry_name,const std::vector<uint8_t> & contents,bool raw)547 static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
548 const std::string& entry_name,
549 const std::vector<uint8_t>& contents, bool raw) {
550 ZipArchiveHandle handle;
551 ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
552
553 ZipEntry entry;
554 std::vector<uint8_t> read_data;
555 ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
556
557 ASSERT_EQ(contents.size(), read_data.size());
558 ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
559
560 CloseArchive(handle);
561 }
562
ZipArchiveStreamTestUsingMemory(const std::string & zip_file,const std::string & entry_name)563 static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
564 const std::string& entry_name) {
565 ZipArchiveHandle handle;
566 ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
567
568 ZipEntry entry;
569 std::vector<uint8_t> read_data;
570 ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
571
572 std::vector<uint8_t> cmp_data(entry.uncompressed_length);
573 ASSERT_EQ(entry.uncompressed_length, read_data.size());
574 ASSERT_EQ(
575 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
576 ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
577
578 CloseArchive(handle);
579 }
580
TEST(ziparchive,StreamCompressed)581 TEST(ziparchive, StreamCompressed) {
582 ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
583 }
584
TEST(ziparchive,StreamUncompressed)585 TEST(ziparchive, StreamUncompressed) {
586 ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
587 }
588
TEST(ziparchive,StreamRawCompressed)589 TEST(ziparchive, StreamRawCompressed) {
590 ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
591 }
592
TEST(ziparchive,StreamRawUncompressed)593 TEST(ziparchive, StreamRawUncompressed) {
594 ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
595 }
596
TEST(ziparchive,StreamLargeCompressed)597 TEST(ziparchive, StreamLargeCompressed) {
598 ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
599 }
600
TEST(ziparchive,StreamLargeUncompressed)601 TEST(ziparchive, StreamLargeUncompressed) {
602 ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
603 }
604
TEST(ziparchive,StreamCompressedBadCrc)605 TEST(ziparchive, StreamCompressedBadCrc) {
606 ZipArchiveHandle handle;
607 ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
608
609 ZipEntry entry;
610 std::vector<uint8_t> read_data;
611 ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
612
613 CloseArchive(handle);
614 }
615
TEST(ziparchive,StreamUncompressedBadCrc)616 TEST(ziparchive, StreamUncompressedBadCrc) {
617 ZipArchiveHandle handle;
618 ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
619
620 ZipEntry entry;
621 std::vector<uint8_t> read_data;
622 ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
623
624 CloseArchive(handle);
625 }
626
627 // Generated using the following Java program:
628 // public static void main(String[] foo) throws Exception {
629 // FileOutputStream fos = new
630 // FileOutputStream("/tmp/data_descriptor.zip");
631 // ZipOutputStream zos = new ZipOutputStream(fos);
632 // ZipEntry ze = new ZipEntry("name");
633 // ze.setMethod(ZipEntry.DEFLATED);
634 // zos.putNextEntry(ze);
635 // zos.write("abdcdefghijk".getBytes());
636 // zos.closeEntry();
637 // zos.close();
638 // }
639 //
640 // cat /tmp/data_descriptor.zip | xxd -i
641 //
642 static const std::vector<uint8_t> kDataDescriptorZipFile{
643 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
644 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
645 0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
646 //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
647 0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
648 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
649 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
650 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
651 0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
652 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
653
654 // The offsets of the data descriptor in this file, so we can mess with
655 // them later in the test.
656 static constexpr uint32_t kDataDescriptorOffset = 48;
657 static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
658 static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
659
ExtractEntryToMemory(const std::vector<uint8_t> & zip_data,std::vector<uint8_t> * entry_out,int32_t * error_code_out)660 static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
661 std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
662 TemporaryFile tmp_file;
663 ASSERT_NE(-1, tmp_file.fd);
664 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
665 ZipArchiveHandle handle;
666 ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
667
668 // This function expects a variant of kDataDescriptorZipFile, for look for
669 // an entry whose name is "name" and whose size is 12 (contents =
670 // "abdcdefghijk").
671 ZipEntry entry;
672 ASSERT_EQ(0, FindEntry(handle, "name", &entry));
673 ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
674
675 entry_out->resize(12);
676 (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
677
678 CloseArchive(handle);
679 }
680
TEST(ziparchive,ValidDataDescriptors)681 TEST(ziparchive, ValidDataDescriptors) {
682 std::vector<uint8_t> entry;
683 int32_t error_code = 0;
684 ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
685
686 ASSERT_EQ(0, error_code);
687 ASSERT_EQ(12u, entry.size());
688 ASSERT_EQ('a', entry[0]);
689 ASSERT_EQ('k', entry[11]);
690 }
691
TEST(ziparchive,InvalidDataDescriptors_csize)692 TEST(ziparchive, InvalidDataDescriptors_csize) {
693 std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
694 invalid_csize[kCSizeOffset] = 0xfe;
695
696 std::vector<uint8_t> entry;
697 int32_t error_code = 0;
698 ExtractEntryToMemory(invalid_csize, &entry, &error_code);
699
700 ASSERT_EQ(kInconsistentInformation, error_code);
701 }
702
TEST(ziparchive,InvalidDataDescriptors_size)703 TEST(ziparchive, InvalidDataDescriptors_size) {
704 std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
705 invalid_size[kSizeOffset] = 0xfe;
706
707 std::vector<uint8_t> entry;
708 int32_t error_code = 0;
709 ExtractEntryToMemory(invalid_size, &entry, &error_code);
710
711 ASSERT_EQ(kInconsistentInformation, error_code);
712 }
713
TEST(ziparchive,ErrorCodeString)714 TEST(ziparchive, ErrorCodeString) {
715 ASSERT_STREQ("Success", ErrorCodeString(0));
716
717 // Out of bounds.
718 ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
719 ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
720 ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
721
722 ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
723 }
724
725 // A zip file whose local file header at offset zero is corrupted.
726 //
727 // ---------------
728 // cat foo > a.txt
729 // zip a.zip a.txt
730 // cat a.zip | xxd -i
731 //
732 // Manual changes :
733 // [2] = 0xff // Corrupt the LFH signature of entry 0.
734 // [3] = 0xff // Corrupt the LFH signature of entry 0.
735 static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
736 //[lfh-sig-----------], [lfh contents---------------------------------
737 0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
738 //--------------------------------------------------------------------
739 0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
740 //-------------------------------] [file-name-----------------], [---
741 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
742 // entry-contents------------------------------------------------------
743 0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
744 //--------------------------------------------------------------------
745 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
746 //-------------------------------------], [cd-record-sig-------], [---
747 0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
748 // cd-record-----------------------------------------------------------
749 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
750 //--------------------------------------------------------------------
751 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
752 //--------------------------------------------------------------------
753 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
754 //-] [lfh-file-header-off-], [file-name-----------------], [extra----
755 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
756 //--------------------------------------------------------------------
757 0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
758 //-------------------------------------------------------], [eocd-sig-
759 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
760 //-------], [---------------------------------------------------------
761 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
762 //-------------------------------------------]
763 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
764
TEST(ziparchive,BrokenLfhSignature)765 TEST(ziparchive, BrokenLfhSignature) {
766 TemporaryFile tmp_file;
767 ASSERT_NE(-1, tmp_file.fd);
768 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
769 kZipFileWithBrokenLfhSignature.size()));
770 ZipArchiveHandle handle;
771 ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
772 }
773
774 class VectorReader : public zip_archive::Reader {
775 public:
VectorReader(const std::vector<uint8_t> & input)776 VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
777
ReadAtOffset(uint8_t * buf,size_t len,uint32_t offset) const778 bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
779 if ((offset + len) < input_.size()) {
780 return false;
781 }
782
783 memcpy(buf, &input_[offset], len);
784 return true;
785 }
786
787 private:
788 const std::vector<uint8_t>& input_;
789 };
790
791 class VectorWriter : public zip_archive::Writer {
792 public:
VectorWriter()793 VectorWriter() : Writer() {}
794
Append(uint8_t * buf,size_t size)795 bool Append(uint8_t* buf, size_t size) {
796 output_.insert(output_.end(), buf, buf + size);
797 return true;
798 }
799
GetOutput()800 std::vector<uint8_t>& GetOutput() { return output_; }
801
802 private:
803 std::vector<uint8_t> output_;
804 };
805
806 class BadReader : public zip_archive::Reader {
807 public:
BadReader()808 BadReader() : Reader() {}
809
ReadAtOffset(uint8_t *,size_t,uint32_t) const810 bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
811 };
812
813 class BadWriter : public zip_archive::Writer {
814 public:
BadWriter()815 BadWriter() : Writer() {}
816
Append(uint8_t *,size_t)817 bool Append(uint8_t*, size_t) { return false; }
818 };
819
TEST(ziparchive,Inflate)820 TEST(ziparchive, Inflate) {
821 const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
822 const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
823
824 const VectorReader reader(kATxtContentsCompressed);
825 {
826 VectorWriter writer;
827 uint64_t crc_out = 0;
828
829 int32_t ret =
830 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
831 ASSERT_EQ(0, ret);
832 ASSERT_EQ(kATxtContents, writer.GetOutput());
833 ASSERT_EQ(0x950821C5u, crc_out);
834 }
835
836 {
837 VectorWriter writer;
838 int32_t ret =
839 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
840 ASSERT_EQ(0, ret);
841 ASSERT_EQ(kATxtContents, writer.GetOutput());
842 }
843
844 {
845 BadWriter writer;
846 int32_t ret =
847 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
848 ASSERT_EQ(kIoError, ret);
849 }
850
851 {
852 BadReader reader;
853 VectorWriter writer;
854 int32_t ret =
855 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
856 ASSERT_EQ(kIoError, ret);
857 ASSERT_EQ(0u, writer.GetOutput().size());
858 }
859 }
860