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