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