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