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