zip_archive_test.cc revision 50afc159b3bba06cd02648b1e287ba06d4fac059
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 "ziparchive/zip_archive.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <getopt.h>
22#include <stdio.h>
23#include <unistd.h>
24#include <vector>
25
26#include <gtest/gtest.h>
27
28static std::string test_data_dir;
29
30static const std::string kMissingZip = "missing.zip";
31static const std::string kValidZip = "valid.zip";
32
33static const uint8_t kATxtContents[] = {
34  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
35  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
36  '\n'
37};
38
39static const uint8_t kBTxtContents[] = {
40  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
41  '\n'
42};
43
44static const uint16_t kATxtNameLength = 5;
45static const uint16_t kBTxtNameLength = 5;
46static const uint16_t kNonexistentTxtNameLength = 15;
47static const uint16_t kEmptyTxtNameLength = 9;
48
49static const uint8_t kATxtName[kATxtNameLength] = {
50  'a', '.', 't', 'x', 't'
51};
52
53static const uint8_t kBTxtName[kBTxtNameLength] = {
54  'b', '.', 't', 'x', 't'
55};
56
57static const uint8_t kNonexistentTxtName[kNonexistentTxtNameLength] = {
58  'n', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 't', 'x' ,'t'
59};
60
61static const uint8_t kEmptyTxtName[kEmptyTxtNameLength] = {
62  'e', 'm', 'p', 't', 'y', '.', 't', 'x', 't'
63};
64
65static int32_t OpenArchiveWrapper(const std::string& name,
66                                  ZipArchiveHandle* handle) {
67  const std::string abs_path = test_data_dir + "/" + name;
68  return OpenArchive(abs_path.c_str(), handle);
69}
70
71static void AssertNameEquals(const std::string& name_str,
72                             const ZipEntryName& name) {
73  ASSERT_EQ(name_str.size(), name.name_length);
74  ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
75}
76
77TEST(ziparchive, Open) {
78  ZipArchiveHandle handle;
79  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
80
81  CloseArchive(handle);
82}
83
84TEST(ziparchive, OpenMissing) {
85  ZipArchiveHandle handle;
86  ASSERT_NE(0, OpenArchiveWrapper(kMissingZip, &handle));
87
88  // Confirm the file descriptor is not going to be mistaken for a valid one.
89  ASSERT_EQ(-1, GetFileDescriptor(handle));
90}
91
92TEST(ziparchive, OpenAssumeFdOwnership) {
93  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
94  ASSERT_NE(-1, fd);
95  ZipArchiveHandle handle;
96  ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
97  CloseArchive(handle);
98  ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
99  ASSERT_EQ(EBADF, errno);
100}
101
102TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
103  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
104  ASSERT_NE(-1, fd);
105  ZipArchiveHandle handle;
106  ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
107  CloseArchive(handle);
108  ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
109  close(fd);
110}
111
112TEST(ziparchive, Iteration) {
113  ZipArchiveHandle handle;
114  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
115
116  void* iteration_cookie;
117  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
118
119  ZipEntry data;
120  ZipEntryName name;
121
122  // b/c.txt
123  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
124  AssertNameEquals("b/c.txt", name);
125
126  // b/d.txt
127  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
128  AssertNameEquals("b/d.txt", name);
129
130  // a.txt
131  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
132  AssertNameEquals("a.txt", name);
133
134  // b.txt
135  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
136  AssertNameEquals("b.txt", name);
137
138  // b/
139  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
140  AssertNameEquals("b/", name);
141
142  // End of iteration.
143  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
144
145  CloseArchive(handle);
146}
147
148TEST(ziparchive, FindEntry) {
149  ZipArchiveHandle handle;
150  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
151
152  ZipEntry data;
153  ZipEntryName name;
154  name.name = kATxtName;
155  name.name_length = kATxtNameLength;
156  ASSERT_EQ(0, FindEntry(handle, name, &data));
157
158  // Known facts about a.txt, from zipinfo -v.
159  ASSERT_EQ(63, data.offset);
160  ASSERT_EQ(kCompressDeflated, data.method);
161  ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
162  ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
163  ASSERT_EQ(0x950821c5, data.crc32);
164
165  // An entry that doesn't exist. Should be a negative return code.
166  ZipEntryName absent_name;
167  absent_name.name = kNonexistentTxtName;
168  absent_name.name_length = kNonexistentTxtNameLength;
169  ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
170
171  CloseArchive(handle);
172}
173
174TEST(ziparchive, TestInvalidDeclaredLength) {
175  ZipArchiveHandle handle;
176  ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
177
178  void* iteration_cookie;
179  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
180
181  ZipEntryName name;
182  ZipEntry data;
183
184  ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
185  ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
186
187  CloseArchive(handle);
188}
189
190TEST(ziparchive, ExtractToMemory) {
191  ZipArchiveHandle handle;
192  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
193
194  // An entry that's deflated.
195  ZipEntry data;
196  ZipEntryName a_name;
197  a_name.name = kATxtName;
198  a_name.name_length = kATxtNameLength;
199  ASSERT_EQ(0, FindEntry(handle, a_name, &data));
200  const uint32_t a_size = data.uncompressed_length;
201  ASSERT_EQ(a_size, sizeof(kATxtContents));
202  uint8_t* buffer = new uint8_t[a_size];
203  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
204  ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
205  delete[] buffer;
206
207  // An entry that's stored.
208  ZipEntryName b_name;
209  b_name.name = kBTxtName;
210  b_name.name_length = kBTxtNameLength;
211  ASSERT_EQ(0, FindEntry(handle, b_name, &data));
212  const uint32_t b_size = data.uncompressed_length;
213  ASSERT_EQ(b_size, sizeof(kBTxtContents));
214  buffer = new uint8_t[b_size];
215  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
216  ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
217  delete[] buffer;
218
219  CloseArchive(handle);
220}
221
222static const uint32_t kEmptyEntriesZip[] = {
223      0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
224      0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
225      0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
226      0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
227      0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
228      0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
229      0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
230
231static int make_temporary_file(const char* file_name_pattern) {
232  char full_path[1024];
233  // Account for differences between the host and the target.
234  //
235  // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
236  snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
237  int fd = mkstemp(full_path);
238  if (fd == -1) {
239    snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
240    fd = mkstemp(full_path);
241  }
242
243  return fd;
244}
245
246TEST(ziparchive, EmptyEntries) {
247  char temp_file_pattern[] = "empty_entries_test_XXXXXX";
248  int fd = make_temporary_file(temp_file_pattern);
249  ASSERT_NE(-1, fd);
250  const ssize_t file_size = sizeof(kEmptyEntriesZip);
251  ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
252
253  ZipArchiveHandle handle;
254  ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
255
256  ZipEntry entry;
257  ZipEntryName empty_name;
258  empty_name.name = kEmptyTxtName;
259  empty_name.name_length = kEmptyTxtNameLength;
260  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
261  ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
262  uint8_t buffer[1];
263  ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
264
265  char output_file_pattern[] = "empty_entries_output_XXXXXX";
266  int output_fd = make_temporary_file(output_file_pattern);
267  ASSERT_NE(-1, output_fd);
268  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
269
270  struct stat stat_buf;
271  ASSERT_EQ(0, fstat(output_fd, &stat_buf));
272  ASSERT_EQ(0, stat_buf.st_size);
273
274  close(fd);
275  close(output_fd);
276}
277
278TEST(ziparchive, TrailerAfterEOCD) {
279  char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
280  int fd = make_temporary_file(temp_file_pattern);
281  ASSERT_NE(-1, fd);
282
283  // Create a file with 8 bytes of random garbage.
284  static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
285  const ssize_t file_size = sizeof(kEmptyEntriesZip);
286  const ssize_t trailer_size = sizeof(trailer);
287  ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
288  ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
289
290  ZipArchiveHandle handle;
291  ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
292}
293
294TEST(ziparchive, ExtractToFile) {
295  char kTempFilePattern[] = "zip_archive_input_XXXXXX";
296  int fd = make_temporary_file(kTempFilePattern);
297  ASSERT_NE(-1, fd);
298  const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
299  const ssize_t data_size = sizeof(data);
300
301  ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
302
303  ZipArchiveHandle handle;
304  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
305
306  ZipEntry entry;
307  ZipEntryName name;
308  name.name = kATxtName;
309  name.name_length = kATxtNameLength;
310  ASSERT_EQ(0, FindEntry(handle, name, &entry));
311  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
312
313
314  // Assert that the first 8 bytes of the file haven't been clobbered.
315  uint8_t read_buffer[data_size];
316  ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
317  ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
318  ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
319
320  // Assert that the remainder of the file contains the incompressed data.
321  std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
322  ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
323            TEMP_FAILURE_RETRY(
324                read(fd, &uncompressed_data[0], entry.uncompressed_length)));
325  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
326                      sizeof(kATxtContents)));
327
328  // Assert that the total length of the file is sane
329  ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)),
330            lseek64(fd, 0, SEEK_END));
331
332  close(fd);
333}
334
335int main(int argc, char** argv) {
336  ::testing::InitGoogleTest(&argc, argv);
337
338  static struct option options[] = {
339    { "test_data_dir", required_argument, NULL, 't' },
340    { NULL, 0, NULL, 0 }
341  };
342
343  while (true) {
344    int option_index;
345    const int c = getopt_long_only(argc, argv, "", options, &option_index);
346    if (c == -1) {
347      break;
348    }
349
350    if (c == 't') {
351      test_data_dir = optarg;
352    }
353  }
354
355  if (test_data_dir.size() == 0) {
356    printf("Test data flag (--test_data_dir) required\n\n");
357    return -1;
358  }
359
360  if (test_data_dir[0] != '/') {
361    printf("Test data must be an absolute path, was %s\n\n",
362           test_data_dir.c_str());
363    return -2;
364  }
365
366  return RUN_ALL_TESTS();
367}
368