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