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