blob: 875b6dec74c4fefd1f93b83e3d065c27bada6f0e [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
Narayan Kamath58aaf462013-12-10 16:47:14 +000029static const std::string kValidZip = "valid.zip";
30
31static const uint8_t kATxtContents[] = {
32 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
33 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
34 '\n'
35};
36
37static const uint8_t kBTxtContents[] = {
38 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
39 '\n'
40};
41
42static int32_t OpenArchiveWrapper(const std::string& name,
43 ZipArchiveHandle* handle) {
44 const std::string abs_path = test_data_dir + "/" + name;
45 return OpenArchive(abs_path.c_str(), handle);
46}
47
48static void AssertNameEquals(const std::string& name_str,
49 const ZipEntryName& name) {
50 ASSERT_EQ(name_str.size(), name.name_length);
51 ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
52}
53
54TEST(ziparchive, Open) {
55 ZipArchiveHandle handle;
56 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
57
58 CloseArchive(handle);
59}
60
61TEST(ziparchive, Iteration) {
62 ZipArchiveHandle handle;
63 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
64
65 void* iteration_cookie;
66 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
Narayan Kamath7462f022013-11-21 13:05:04 +000067
68 ZipEntry data;
69 ZipEntryName name;
Narayan Kamath58aaf462013-12-10 16:47:14 +000070
71 // b/c.txt
72 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
73 AssertNameEquals("b/c.txt", name);
74
75 // b/d.txt
76 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
77 AssertNameEquals("b/d.txt", name);
78
79 // a.txt
80 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
81 AssertNameEquals("a.txt", name);
82
83 // b.txt
84 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
85 AssertNameEquals("b.txt", name);
86
87 // b/
88 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
89 AssertNameEquals("b/", name);
90
91 // End of iteration.
92 ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
93
94 CloseArchive(handle);
95}
96
97TEST(ziparchive, FindEntry) {
98 ZipArchiveHandle handle;
99 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
100
101 ZipEntry data;
102 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
103
104 // Known facts about a.txt, from zipinfo -v.
105 ASSERT_EQ(63, data.offset);
106 ASSERT_EQ(kCompressDeflated, data.method);
107 ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
108 ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
109 ASSERT_EQ(0x950821c5, data.crc32);
110
111 // An entry that doesn't exist. Should be a negative return code.
112 ASSERT_LT(FindEntry(handle, "nonexistent.txt", &data), 0);
113
114 CloseArchive(handle);
115}
116
117TEST(ziparchive, ExtractToMemory) {
118 ZipArchiveHandle handle;
119 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
120
121 // An entry that's deflated.
122 ZipEntry data;
123 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
124 const uint32_t a_size = data.uncompressed_length;
125 ASSERT_EQ(a_size, sizeof(kATxtContents));
126 uint8_t* buffer = new uint8_t[a_size];
127 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
128 ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
129 delete[] buffer;
130
131 // An entry that's stored.
132 ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
133 const uint32_t b_size = data.uncompressed_length;
134 ASSERT_EQ(b_size, sizeof(kBTxtContents));
135 buffer = new uint8_t[b_size];
136 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
137 ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
138 delete[] buffer;
139
140 CloseArchive(handle);
141}
142
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100143static const uint32_t kEmptyEntriesZip[] = {
Narayan Kamath48953a12014-01-24 12:32:39 +0000144 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
145 0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
146 0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
147 0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
148 0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
149 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
150 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100151
Narayan Kamath926973e2014-06-09 14:18:14 +0100152static int make_temporary_file(const char* file_name_pattern) {
153 char full_path[1024];
154 // Account for differences between the host and the target.
155 //
156 // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
157 snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
158 int fd = mkstemp(full_path);
159 if (fd == -1) {
160 snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
161 fd = mkstemp(full_path);
162 }
163
164 return fd;
165}
166
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100167TEST(ziparchive, EmptyEntries) {
168 char temp_file_pattern[] = "empty_entries_test_XXXXXX";
Narayan Kamath926973e2014-06-09 14:18:14 +0100169 int fd = make_temporary_file(temp_file_pattern);
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100170 ASSERT_NE(-1, fd);
171 const ssize_t file_size = sizeof(kEmptyEntriesZip);
172 ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
Narayan Kamath48953a12014-01-24 12:32:39 +0000173
174 ZipArchiveHandle handle;
175 ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
176
177 ZipEntry entry;
178 ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
179 ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
180 uint8_t buffer[1];
181 ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
182
183 char output_file_pattern[] = "empty_entries_output_XXXXXX";
Narayan Kamath926973e2014-06-09 14:18:14 +0100184 int output_fd = make_temporary_file(output_file_pattern);
Narayan Kamath48953a12014-01-24 12:32:39 +0000185 ASSERT_NE(-1, output_fd);
186 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
187
188 struct stat stat_buf;
189 ASSERT_EQ(0, fstat(output_fd, &stat_buf));
190 ASSERT_EQ(0, stat_buf.st_size);
191
192 close(fd);
193 close(output_fd);
194}
195
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100196TEST(ziparchive, TrailerAfterEOCD) {
197 char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
Narayan Kamath926973e2014-06-09 14:18:14 +0100198 int fd = make_temporary_file(temp_file_pattern);
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100199 ASSERT_NE(-1, fd);
200
201 // Create a file with 8 bytes of random garbage.
202 static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
203 const ssize_t file_size = sizeof(kEmptyEntriesZip);
204 const ssize_t trailer_size = sizeof(trailer);
205 ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
206 ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
207
208 ZipArchiveHandle handle;
209 ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
210}
211
Narayan Kamath00a258c2013-12-13 16:06:19 +0000212TEST(ziparchive, ExtractToFile) {
Narayan Kamath48953a12014-01-24 12:32:39 +0000213 char kTempFilePattern[] = "zip_archive_input_XXXXXX";
Narayan Kamath926973e2014-06-09 14:18:14 +0100214 int fd = make_temporary_file(kTempFilePattern);
Narayan Kamath00a258c2013-12-13 16:06:19 +0000215 ASSERT_NE(-1, fd);
216 const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
217 const ssize_t data_size = sizeof(data);
218
219 ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
220
221 ZipArchiveHandle handle;
222 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
223
224 ZipEntry entry;
225 ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
226 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
227
228
229 // Assert that the first 8 bytes of the file haven't been clobbered.
230 uint8_t read_buffer[data_size];
231 ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
232 ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
233 ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
234
235 // Assert that the remainder of the file contains the incompressed data.
236 std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
237 ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
238 TEMP_FAILURE_RETRY(
239 read(fd, &uncompressed_data[0], entry.uncompressed_length)));
240 ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
241 sizeof(kATxtContents)));
242
243 // Assert that the total length of the file is sane
Ying Wangcf86e2f2014-05-15 20:06:40 -0700244 ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)),
245 lseek64(fd, 0, SEEK_END));
Narayan Kamath00a258c2013-12-13 16:06:19 +0000246
247 close(fd);
248}
249
Narayan Kamath58aaf462013-12-10 16:47:14 +0000250int main(int argc, char** argv) {
251 ::testing::InitGoogleTest(&argc, argv);
252
253 static struct option options[] = {
254 { "test_data_dir", required_argument, NULL, 't' },
255 { NULL, 0, NULL, 0 }
256 };
257
258 while (true) {
259 int option_index;
260 const int c = getopt_long_only(argc, argv, "", options, &option_index);
261 if (c == -1) {
262 break;
263 }
264
265 if (c == 't') {
266 test_data_dir = optarg;
267 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000268 }
269
Narayan Kamath58aaf462013-12-10 16:47:14 +0000270 if (test_data_dir.size() == 0) {
271 printf("Test data flag (--test_data_dir) required\n\n");
272 return -1;
273 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000274
Narayan Kamath58aaf462013-12-10 16:47:14 +0000275 if (test_data_dir[0] != '/') {
276 printf("Test data must be an absolute path, was %s\n\n",
277 test_data_dir.c_str());
278 return -2;
279 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000280
Narayan Kamath58aaf462013-12-10 16:47:14 +0000281 return RUN_ALL_TESTS();
Narayan Kamath7462f022013-11-21 13:05:04 +0000282}