blob: 4775de00c4790eeb041eb20bfa7d08dd529e0d58 [file] [log] [blame]
Narayan Kamath7462f022013-11-21 13:05:04 +00001/*
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
Narayan Kamath00a258c2013-12-13 16:06:19 +000019#include <errno.h>
20#include <getopt.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000021#include <stdio.h>
Narayan Kamath00a258c2013-12-13 16:06:19 +000022#include <unistd.h>
23#include <vector>
24
Narayan Kamath7462f022013-11-21 13:05:04 +000025#include <gtest/gtest.h>
26
Narayan Kamath58aaf462013-12-10 16:47:14 +000027static std::string test_data_dir;
Narayan Kamath7462f022013-11-21 13:05:04 +000028
Neil Fullerb1a113f2014-07-25 14:43:04 +010029static const std::string kMissingZip = "missing.zip";
Narayan Kamath58aaf462013-12-10 16:47:14 +000030static const std::string kValidZip = "valid.zip";
31
32static const uint8_t kATxtContents[] = {
33 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
34 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
35 '\n'
36};
37
38static const uint8_t kBTxtContents[] = {
39 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
40 '\n'
41};
42
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +010043static const uint16_t kATxtNameLength = 5;
44static const uint16_t kBTxtNameLength = 5;
45static const uint16_t kNonexistentTxtNameLength = 15;
46static const uint16_t kEmptyTxtNameLength = 9;
47
48static const uint8_t kATxtName[kATxtNameLength] = {
49 'a', '.', 't', 'x', 't'
50};
51
52static const uint8_t kBTxtName[kBTxtNameLength] = {
53 'b', '.', 't', 'x', 't'
54};
55
56static const uint8_t kNonexistentTxtName[kNonexistentTxtNameLength] = {
57 'n', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 't', 'x' ,'t'
58};
59
60static const uint8_t kEmptyTxtName[kEmptyTxtNameLength] = {
61 'e', 'm', 'p', 't', 'y', '.', 't', 'x', 't'
62};
63
Narayan Kamath58aaf462013-12-10 16:47:14 +000064static int32_t OpenArchiveWrapper(const std::string& name,
65 ZipArchiveHandle* handle) {
66 const std::string abs_path = test_data_dir + "/" + name;
67 return OpenArchive(abs_path.c_str(), handle);
68}
69
70static void AssertNameEquals(const std::string& name_str,
71 const ZipEntryName& name) {
72 ASSERT_EQ(name_str.size(), name.name_length);
73 ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
74}
75
76TEST(ziparchive, Open) {
77 ZipArchiveHandle handle;
78 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
79
80 CloseArchive(handle);
81}
82
Neil Fullerb1a113f2014-07-25 14:43:04 +010083TEST(ziparchive, OpenMissing) {
84 ZipArchiveHandle handle;
85 ASSERT_NE(0, OpenArchiveWrapper(kMissingZip, &handle));
86
87 // Confirm the file descriptor is not going to be mistaken for a valid one.
88 ASSERT_EQ(-1, GetFileDescriptor(handle));
89}
90
Narayan Kamath58aaf462013-12-10 16:47:14 +000091TEST(ziparchive, Iteration) {
92 ZipArchiveHandle handle;
93 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
94
95 void* iteration_cookie;
96 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
Narayan Kamath7462f022013-11-21 13:05:04 +000097
98 ZipEntry data;
99 ZipEntryName name;
Narayan Kamath58aaf462013-12-10 16:47:14 +0000100
101 // b/c.txt
102 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
103 AssertNameEquals("b/c.txt", name);
104
105 // b/d.txt
106 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
107 AssertNameEquals("b/d.txt", name);
108
109 // a.txt
110 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
111 AssertNameEquals("a.txt", name);
112
113 // b.txt
114 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
115 AssertNameEquals("b.txt", name);
116
117 // b/
118 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
119 AssertNameEquals("b/", name);
120
121 // End of iteration.
122 ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
123
124 CloseArchive(handle);
125}
126
127TEST(ziparchive, FindEntry) {
128 ZipArchiveHandle handle;
129 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
130
131 ZipEntry data;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100132 ZipEntryName name;
133 name.name = kATxtName;
134 name.name_length = kATxtNameLength;
135 ASSERT_EQ(0, FindEntry(handle, name, &data));
Narayan Kamath58aaf462013-12-10 16:47:14 +0000136
137 // Known facts about a.txt, from zipinfo -v.
138 ASSERT_EQ(63, data.offset);
139 ASSERT_EQ(kCompressDeflated, data.method);
140 ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
141 ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
142 ASSERT_EQ(0x950821c5, data.crc32);
143
144 // An entry that doesn't exist. Should be a negative return code.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100145 ZipEntryName absent_name;
146 absent_name.name = kNonexistentTxtName;
147 absent_name.name_length = kNonexistentTxtNameLength;
148 ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
Narayan Kamath58aaf462013-12-10 16:47:14 +0000149
150 CloseArchive(handle);
151}
152
153TEST(ziparchive, ExtractToMemory) {
154 ZipArchiveHandle handle;
155 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
156
157 // An entry that's deflated.
158 ZipEntry data;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100159 ZipEntryName a_name;
160 a_name.name = kATxtName;
161 a_name.name_length = kATxtNameLength;
162 ASSERT_EQ(0, FindEntry(handle, a_name, &data));
Narayan Kamath58aaf462013-12-10 16:47:14 +0000163 const uint32_t a_size = data.uncompressed_length;
164 ASSERT_EQ(a_size, sizeof(kATxtContents));
165 uint8_t* buffer = new uint8_t[a_size];
166 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
167 ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
168 delete[] buffer;
169
170 // An entry that's stored.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100171 ZipEntryName b_name;
172 b_name.name = kBTxtName;
173 b_name.name_length = kBTxtNameLength;
174 ASSERT_EQ(0, FindEntry(handle, b_name, &data));
Narayan Kamath58aaf462013-12-10 16:47:14 +0000175 const uint32_t b_size = data.uncompressed_length;
176 ASSERT_EQ(b_size, sizeof(kBTxtContents));
177 buffer = new uint8_t[b_size];
178 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
179 ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
180 delete[] buffer;
181
182 CloseArchive(handle);
183}
184
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100185static const uint32_t kEmptyEntriesZip[] = {
Narayan Kamath48953a12014-01-24 12:32:39 +0000186 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
187 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
188 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
189 0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
190 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
191 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
192 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100193
Narayan Kamath926973e2014-06-09 14:18:14 +0100194static int make_temporary_file(const char* file_name_pattern) {
195 char full_path[1024];
196 // Account for differences between the host and the target.
197 //
198 // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
199 snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
200 int fd = mkstemp(full_path);
201 if (fd == -1) {
202 snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
203 fd = mkstemp(full_path);
204 }
205
206 return fd;
207}
208
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100209TEST(ziparchive, EmptyEntries) {
210 char temp_file_pattern[] = "empty_entries_test_XXXXXX";
Narayan Kamath926973e2014-06-09 14:18:14 +0100211 int fd = make_temporary_file(temp_file_pattern);
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100212 ASSERT_NE(-1, fd);
213 const ssize_t file_size = sizeof(kEmptyEntriesZip);
214 ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
Narayan Kamath48953a12014-01-24 12:32:39 +0000215
216 ZipArchiveHandle handle;
217 ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
218
219 ZipEntry entry;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100220 ZipEntryName empty_name;
221 empty_name.name = kEmptyTxtName;
222 empty_name.name_length = kEmptyTxtNameLength;
223 ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
Narayan Kamath48953a12014-01-24 12:32:39 +0000224 ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
225 uint8_t buffer[1];
226 ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
227
228 char output_file_pattern[] = "empty_entries_output_XXXXXX";
Narayan Kamath926973e2014-06-09 14:18:14 +0100229 int output_fd = make_temporary_file(output_file_pattern);
Narayan Kamath48953a12014-01-24 12:32:39 +0000230 ASSERT_NE(-1, output_fd);
231 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
232
233 struct stat stat_buf;
234 ASSERT_EQ(0, fstat(output_fd, &stat_buf));
235 ASSERT_EQ(0, stat_buf.st_size);
236
237 close(fd);
238 close(output_fd);
239}
240
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100241TEST(ziparchive, TrailerAfterEOCD) {
242 char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
Narayan Kamath926973e2014-06-09 14:18:14 +0100243 int fd = make_temporary_file(temp_file_pattern);
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100244 ASSERT_NE(-1, fd);
245
246 // Create a file with 8 bytes of random garbage.
247 static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
248 const ssize_t file_size = sizeof(kEmptyEntriesZip);
249 const ssize_t trailer_size = sizeof(trailer);
250 ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
251 ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
252
253 ZipArchiveHandle handle;
254 ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
255}
256
Narayan Kamath00a258c2013-12-13 16:06:19 +0000257TEST(ziparchive, ExtractToFile) {
Narayan Kamath48953a12014-01-24 12:32:39 +0000258 char kTempFilePattern[] = "zip_archive_input_XXXXXX";
Narayan Kamath926973e2014-06-09 14:18:14 +0100259 int fd = make_temporary_file(kTempFilePattern);
Narayan Kamath00a258c2013-12-13 16:06:19 +0000260 ASSERT_NE(-1, fd);
261 const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
262 const ssize_t data_size = sizeof(data);
263
264 ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
265
266 ZipArchiveHandle handle;
267 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
268
269 ZipEntry entry;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100270 ZipEntryName name;
271 name.name = kATxtName;
272 name.name_length = kATxtNameLength;
273 ASSERT_EQ(0, FindEntry(handle, name, &entry));
Narayan Kamath00a258c2013-12-13 16:06:19 +0000274 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
275
276
277 // Assert that the first 8 bytes of the file haven't been clobbered.
278 uint8_t read_buffer[data_size];
279 ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
280 ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
281 ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
282
283 // Assert that the remainder of the file contains the incompressed data.
284 std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
285 ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
286 TEMP_FAILURE_RETRY(
287 read(fd, &uncompressed_data[0], entry.uncompressed_length)));
288 ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
289 sizeof(kATxtContents)));
290
291 // Assert that the total length of the file is sane
Ying Wangcf86e2f2014-05-15 20:06:40 -0700292 ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)),
293 lseek64(fd, 0, SEEK_END));
Narayan Kamath00a258c2013-12-13 16:06:19 +0000294
295 close(fd);
296}
297
Narayan Kamath58aaf462013-12-10 16:47:14 +0000298int main(int argc, char** argv) {
299 ::testing::InitGoogleTest(&argc, argv);
300
301 static struct option options[] = {
302 { "test_data_dir", required_argument, NULL, 't' },
303 { NULL, 0, NULL, 0 }
304 };
305
306 while (true) {
307 int option_index;
308 const int c = getopt_long_only(argc, argv, "", options, &option_index);
309 if (c == -1) {
310 break;
311 }
312
313 if (c == 't') {
314 test_data_dir = optarg;
315 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000316 }
317
Narayan Kamath58aaf462013-12-10 16:47:14 +0000318 if (test_data_dir.size() == 0) {
319 printf("Test data flag (--test_data_dir) required\n\n");
320 return -1;
321 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000322
Narayan Kamath58aaf462013-12-10 16:47:14 +0000323 if (test_data_dir[0] != '/') {
324 printf("Test data must be an absolute path, was %s\n\n",
325 test_data_dir.c_str());
326 return -2;
327 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000328
Narayan Kamath58aaf462013-12-10 16:47:14 +0000329 return RUN_ALL_TESTS();
Narayan Kamath7462f022013-11-21 13:05:04 +0000330}