zip_archive_test.cc revision 2d1e23f5d6cd6424a261dae5b261197f3611290e
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 720class VectorReader : public zip_archive::Reader { 721 public: 722 VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {} 723 724 bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const { 725 if ((offset + len) < input_.size()) { 726 return false; 727 } 728 729 memcpy(buf, &input_[offset], len); 730 return true; 731 } 732 733 private: 734 const std::vector<uint8_t>& input_; 735}; 736 737class VectorWriter : public zip_archive::Writer { 738 public: 739 VectorWriter() : Writer() {} 740 741 bool Append(uint8_t* buf, size_t size) { 742 output_.insert(output_.end(), buf, buf + size); 743 return true; 744 } 745 746 std::vector<uint8_t>& GetOutput() { return output_; } 747 748 private: 749 std::vector<uint8_t> output_; 750}; 751 752class BadReader : public zip_archive::Reader { 753 public: 754 BadReader() : Reader() {} 755 756 bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; } 757}; 758 759class BadWriter : public zip_archive::Writer { 760 public: 761 BadWriter() : Writer() {} 762 763 bool Append(uint8_t*, size_t) { return false; } 764}; 765 766TEST(ziparchive, Inflate) { 767 const uint32_t compressed_length = kATxtContentsCompressed.size(); 768 const uint32_t uncompressed_length = kATxtContents.size(); 769 770 const VectorReader reader(kATxtContentsCompressed); 771 { 772 VectorWriter writer; 773 uint64_t crc_out = 0; 774 775 int32_t ret = 776 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out); 777 ASSERT_EQ(0, ret); 778 ASSERT_EQ(kATxtContents, writer.GetOutput()); 779 ASSERT_EQ(0x950821C5u, crc_out); 780 } 781 782 { 783 VectorWriter writer; 784 int32_t ret = 785 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr); 786 ASSERT_EQ(0, ret); 787 ASSERT_EQ(kATxtContents, writer.GetOutput()); 788 } 789 790 { 791 BadWriter writer; 792 int32_t ret = 793 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr); 794 ASSERT_EQ(kIoError, ret); 795 } 796 797 { 798 BadReader reader; 799 VectorWriter writer; 800 int32_t ret = 801 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr); 802 ASSERT_EQ(kIoError, ret); 803 ASSERT_EQ(0u, writer.GetOutput().size()); 804 } 805} 806 807int main(int argc, char** argv) { 808 ::testing::InitGoogleTest(&argc, argv); 809 810 static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'}, 811 {nullptr, 0, nullptr, 0}}; 812 813 while (true) { 814 int option_index; 815 const int c = getopt_long_only(argc, argv, "", options, &option_index); 816 if (c == -1) { 817 break; 818 } 819 820 if (c == 't') { 821 test_data_dir = optarg; 822 } 823 } 824 825 if (test_data_dir.size() == 0) { 826 printf("Test data flag (--test_data_dir) required\n\n"); 827 return -1; 828 } 829 830 if (test_data_dir[0] != '/') { 831 std::vector<char> cwd_buffer(1024); 832 const char* cwd = getcwd(cwd_buffer.data(), cwd_buffer.size() - 1); 833 if (cwd == nullptr) { 834 printf("Cannot get current working directory, use an absolute path instead, was %s\n\n", 835 test_data_dir.c_str()); 836 return -2; 837 } 838 test_data_dir = '/' + test_data_dir; 839 test_data_dir = cwd + test_data_dir; 840 } 841 842 return RUN_ALL_TESTS(); 843} 844