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 name;
591  std::string name_str = "name";
592  SetZipString(&name, name_str);
593
594  ASSERT_EQ(0, FindEntry(handle, name, &entry));
595  ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
596
597  entry_out->resize(12);
598  (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
599
600  CloseArchive(handle);
601}
602
603TEST(ziparchive, ValidDataDescriptors) {
604  std::vector<uint8_t> entry;
605  int32_t error_code = 0;
606  ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
607
608  ASSERT_EQ(0, error_code);
609  ASSERT_EQ(12u, entry.size());
610  ASSERT_EQ('a', entry[0]);
611  ASSERT_EQ('k', entry[11]);
612}
613
614TEST(ziparchive, InvalidDataDescriptors_csize) {
615  std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
616  invalid_csize[kCSizeOffset] = 0xfe;
617
618  std::vector<uint8_t> entry;
619  int32_t error_code = 0;
620  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
621
622  ASSERT_EQ(kInconsistentInformation, error_code);
623}
624
625TEST(ziparchive, InvalidDataDescriptors_size) {
626  std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
627  invalid_size[kSizeOffset] = 0xfe;
628
629  std::vector<uint8_t> entry;
630  int32_t error_code = 0;
631  ExtractEntryToMemory(invalid_size, &entry, &error_code);
632
633  ASSERT_EQ(kInconsistentInformation, error_code);
634}
635
636TEST(ziparchive, ErrorCodeString) {
637  ASSERT_STREQ("Success", ErrorCodeString(0));
638
639  // Out of bounds.
640  ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
641  ASSERT_STREQ("Unknown return code", ErrorCodeString(-13));
642
643  ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
644}
645
646// A zip file whose local file header at offset zero is corrupted.
647//
648// ---------------
649// cat foo > a.txt
650// zip a.zip a.txt
651// cat a.zip | xxd -i
652//
653// Manual changes :
654// [2] = 0xff  // Corrupt the LFH signature of entry 0.
655// [3] = 0xff  // Corrupt the LFH signature of entry 0.
656static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
657    //[lfh-sig-----------], [lfh contents---------------------------------
658    0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
659    //--------------------------------------------------------------------
660    0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
661    //-------------------------------]  [file-name-----------------], [---
662    0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
663    // entry-contents------------------------------------------------------
664    0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
665    //--------------------------------------------------------------------
666    0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
667    //-------------------------------------], [cd-record-sig-------], [---
668    0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
669    // cd-record-----------------------------------------------------------
670    0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
671    //--------------------------------------------------------------------
672    0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
673    //--------------------------------------------------------------------
674    0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
675    //-]  [lfh-file-header-off-], [file-name-----------------], [extra----
676    0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
677    //--------------------------------------------------------------------
678    0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
679    //-------------------------------------------------------], [eocd-sig-
680    0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
681    //-------], [---------------------------------------------------------
682    0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
683    //-------------------------------------------]
684    0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
685
686TEST(ziparchive, BrokenLfhSignature) {
687  TemporaryFile tmp_file;
688  ASSERT_NE(-1, tmp_file.fd);
689  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
690                                        kZipFileWithBrokenLfhSignature.size()));
691  ZipArchiveHandle handle;
692  ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle));
693}
694
695class VectorReader : public zip_archive::Reader {
696 public:
697  VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
698
699  bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
700    if ((offset + len) < input_.size()) {
701      return false;
702    }
703
704    memcpy(buf, &input_[offset], len);
705    return true;
706  }
707
708 private:
709  const std::vector<uint8_t>& input_;
710};
711
712class VectorWriter : public zip_archive::Writer {
713 public:
714  VectorWriter() : Writer() {}
715
716  bool Append(uint8_t* buf, size_t size) {
717    output_.insert(output_.end(), buf, buf + size);
718    return true;
719  }
720
721  std::vector<uint8_t>& GetOutput() { return output_; }
722
723 private:
724  std::vector<uint8_t> output_;
725};
726
727class BadReader : public zip_archive::Reader {
728 public:
729  BadReader() : Reader() {}
730
731  bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
732};
733
734class BadWriter : public zip_archive::Writer {
735 public:
736  BadWriter() : Writer() {}
737
738  bool Append(uint8_t*, size_t) { return false; }
739};
740
741TEST(ziparchive, Inflate) {
742  const uint32_t compressed_length = kATxtContentsCompressed.size();
743  const uint32_t uncompressed_length = kATxtContents.size();
744
745  const VectorReader reader(kATxtContentsCompressed);
746  {
747    VectorWriter writer;
748    uint64_t crc_out = 0;
749
750    int32_t ret =
751        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
752    ASSERT_EQ(0, ret);
753    ASSERT_EQ(kATxtContents, writer.GetOutput());
754    ASSERT_EQ(0x950821C5u, crc_out);
755  }
756
757  {
758    VectorWriter writer;
759    int32_t ret =
760        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
761    ASSERT_EQ(0, ret);
762    ASSERT_EQ(kATxtContents, writer.GetOutput());
763  }
764
765  {
766    BadWriter writer;
767    int32_t ret =
768        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
769    ASSERT_EQ(kIoError, ret);
770  }
771
772  {
773    BadReader reader;
774    VectorWriter writer;
775    int32_t ret =
776        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
777    ASSERT_EQ(kIoError, ret);
778    ASSERT_EQ(0u, writer.GetOutput().size());
779  }
780}
781
782int main(int argc, char** argv) {
783  ::testing::InitGoogleTest(&argc, argv);
784
785  static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'},
786                                    {nullptr, 0, nullptr, 0}};
787
788  while (true) {
789    int option_index;
790    const int c = getopt_long_only(argc, argv, "", options, &option_index);
791    if (c == -1) {
792      break;
793    }
794
795    if (c == 't') {
796      test_data_dir = optarg;
797    }
798  }
799
800  if (test_data_dir.size() == 0) {
801    printf("Test data flag (--test_data_dir) required\n\n");
802    return -1;
803  }
804
805  if (test_data_dir[0] != '/') {
806    std::vector<char> cwd_buffer(1024);
807    const char* cwd = getcwd(cwd_buffer.data(), cwd_buffer.size() - 1);
808    if (cwd == nullptr) {
809      printf("Cannot get current working directory, use an absolute path instead, was %s\n\n",
810             test_data_dir.c_str());
811      return -2;
812    }
813    test_data_dir = '/' + test_data_dir;
814    test_data_dir = cwd + test_data_dir;
815  }
816
817  return RUN_ALL_TESTS();
818}
819