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