Home | History | Annotate | Download | only in libziparchive
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "ziparchive/zip_archive.h"
     18 
     19 #include <errno.h>
     20 #include <fcntl.h>
     21 #include <getopt.h>
     22 #include <stdio.h>
     23 #include <unistd.h>
     24 #include <vector>
     25 
     26 #include <base/file.h>
     27 #include <gtest/gtest.h>
     28 
     29 static std::string test_data_dir;
     30 
     31 static const std::string kMissingZip = "missing.zip";
     32 static const std::string kValidZip = "valid.zip";
     33 
     34 static const uint8_t kATxtContents[] = {
     35   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
     36   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
     37   '\n'
     38 };
     39 
     40 static const uint8_t kBTxtContents[] = {
     41   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
     42   '\n'
     43 };
     44 
     45 static const uint16_t kATxtNameLength = 5;
     46 static const uint16_t kBTxtNameLength = 5;
     47 static const uint16_t kNonexistentTxtNameLength = 15;
     48 static const uint16_t kEmptyTxtNameLength = 9;
     49 
     50 static const uint8_t kATxtName[kATxtNameLength] = {
     51   'a', '.', 't', 'x', 't'
     52 };
     53 
     54 static const uint8_t kBTxtName[kBTxtNameLength] = {
     55   'b', '.', 't', 'x', 't'
     56 };
     57 
     58 static const uint8_t kNonexistentTxtName[kNonexistentTxtNameLength] = {
     59   'n', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 't', 'x' ,'t'
     60 };
     61 
     62 static const uint8_t kEmptyTxtName[kEmptyTxtNameLength] = {
     63   'e', 'm', 'p', 't', 'y', '.', 't', 'x', 't'
     64 };
     65 
     66 static int32_t OpenArchiveWrapper(const std::string& name,
     67                                   ZipArchiveHandle* handle) {
     68   const std::string abs_path = test_data_dir + "/" + name;
     69   return OpenArchive(abs_path.c_str(), handle);
     70 }
     71 
     72 static void AssertNameEquals(const std::string& name_str,
     73                              const ZipEntryName& name) {
     74   ASSERT_EQ(name_str.size(), name.name_length);
     75   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
     76 }
     77 
     78 TEST(ziparchive, Open) {
     79   ZipArchiveHandle handle;
     80   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
     81 
     82   CloseArchive(handle);
     83 }
     84 
     85 TEST(ziparchive, OpenMissing) {
     86   ZipArchiveHandle handle;
     87   ASSERT_NE(0, OpenArchiveWrapper(kMissingZip, &handle));
     88 
     89   // Confirm the file descriptor is not going to be mistaken for a valid one.
     90   ASSERT_EQ(-1, GetFileDescriptor(handle));
     91 }
     92 
     93 TEST(ziparchive, OpenAssumeFdOwnership) {
     94   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
     95   ASSERT_NE(-1, fd);
     96   ZipArchiveHandle handle;
     97   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
     98   CloseArchive(handle);
     99   ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
    100   ASSERT_EQ(EBADF, errno);
    101 }
    102 
    103 TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
    104   int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
    105   ASSERT_NE(-1, fd);
    106   ZipArchiveHandle handle;
    107   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
    108   CloseArchive(handle);
    109   ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
    110   close(fd);
    111 }
    112 
    113 TEST(ziparchive, Iteration) {
    114   ZipArchiveHandle handle;
    115   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    116 
    117   void* iteration_cookie;
    118   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
    119 
    120   ZipEntry data;
    121   ZipEntryName name;
    122 
    123   // b/c.txt
    124   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    125   AssertNameEquals("b/c.txt", name);
    126 
    127   // b/d.txt
    128   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    129   AssertNameEquals("b/d.txt", name);
    130 
    131   // a.txt
    132   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    133   AssertNameEquals("a.txt", name);
    134 
    135   // b.txt
    136   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    137   AssertNameEquals("b.txt", name);
    138 
    139   // b/
    140   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    141   AssertNameEquals("b/", name);
    142 
    143   // End of iteration.
    144   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
    145 
    146   CloseArchive(handle);
    147 }
    148 
    149 TEST(ziparchive, IterationWithPrefix) {
    150   ZipArchiveHandle handle;
    151   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    152 
    153   void* iteration_cookie;
    154   ZipEntryName prefix("b/");
    155   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL));
    156 
    157   ZipEntry data;
    158   ZipEntryName name;
    159 
    160   // b/c.txt
    161   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    162   AssertNameEquals("b/c.txt", name);
    163 
    164   // b/d.txt
    165   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    166   AssertNameEquals("b/d.txt", name);
    167 
    168   // b/
    169   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    170   AssertNameEquals("b/", name);
    171 
    172   // End of iteration.
    173   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
    174 
    175   CloseArchive(handle);
    176 }
    177 
    178 TEST(ziparchive, IterationWithSuffix) {
    179   ZipArchiveHandle handle;
    180   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    181 
    182   void* iteration_cookie;
    183   ZipEntryName suffix(".txt");
    184   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix));
    185 
    186   ZipEntry data;
    187   ZipEntryName name;
    188 
    189   // b/c.txt
    190   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    191   AssertNameEquals("b/c.txt", name);
    192 
    193   // b/d.txt
    194   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    195   AssertNameEquals("b/d.txt", name);
    196 
    197   // a.txt
    198   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    199   AssertNameEquals("a.txt", name);
    200 
    201   // b.txt
    202   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    203   AssertNameEquals("b.txt", name);
    204 
    205   // End of iteration.
    206   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
    207 
    208   CloseArchive(handle);
    209 }
    210 
    211 TEST(ziparchive, IterationWithPrefixAndSuffix) {
    212   ZipArchiveHandle handle;
    213   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    214 
    215   void* iteration_cookie;
    216   ZipEntryName prefix("b");
    217   ZipEntryName suffix(".txt");
    218   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
    219 
    220   ZipEntry data;
    221   ZipEntryName name;
    222 
    223   // b/c.txt
    224   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    225   AssertNameEquals("b/c.txt", name);
    226 
    227   // b/d.txt
    228   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    229   AssertNameEquals("b/d.txt", name);
    230 
    231   // b.txt
    232   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
    233   AssertNameEquals("b.txt", name);
    234 
    235   // End of iteration.
    236   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
    237 
    238   CloseArchive(handle);
    239 }
    240 
    241 TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
    242   ZipArchiveHandle handle;
    243   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    244 
    245   void* iteration_cookie;
    246   ZipEntryName prefix("x");
    247   ZipEntryName suffix("y");
    248   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
    249 
    250   ZipEntry data;
    251   ZipEntryName name;
    252 
    253   // End of iteration.
    254   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
    255 
    256   CloseArchive(handle);
    257 }
    258 
    259 TEST(ziparchive, FindEntry) {
    260   ZipArchiveHandle handle;
    261   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    262 
    263   ZipEntry data;
    264   ZipEntryName name;
    265   name.name = kATxtName;
    266   name.name_length = kATxtNameLength;
    267   ASSERT_EQ(0, FindEntry(handle, name, &data));
    268 
    269   // Known facts about a.txt, from zipinfo -v.
    270   ASSERT_EQ(63, data.offset);
    271   ASSERT_EQ(kCompressDeflated, data.method);
    272   ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
    273   ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
    274   ASSERT_EQ(0x950821c5, data.crc32);
    275 
    276   // An entry that doesn't exist. Should be a negative return code.
    277   ZipEntryName absent_name;
    278   absent_name.name = kNonexistentTxtName;
    279   absent_name.name_length = kNonexistentTxtNameLength;
    280   ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
    281 
    282   CloseArchive(handle);
    283 }
    284 
    285 TEST(ziparchive, TestInvalidDeclaredLength) {
    286   ZipArchiveHandle handle;
    287   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
    288 
    289   void* iteration_cookie;
    290   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
    291 
    292   ZipEntryName name;
    293   ZipEntry data;
    294 
    295   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
    296   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
    297 
    298   CloseArchive(handle);
    299 }
    300 
    301 TEST(ziparchive, ExtractToMemory) {
    302   ZipArchiveHandle handle;
    303   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    304 
    305   // An entry that's deflated.
    306   ZipEntry data;
    307   ZipEntryName a_name;
    308   a_name.name = kATxtName;
    309   a_name.name_length = kATxtNameLength;
    310   ASSERT_EQ(0, FindEntry(handle, a_name, &data));
    311   const uint32_t a_size = data.uncompressed_length;
    312   ASSERT_EQ(a_size, sizeof(kATxtContents));
    313   uint8_t* buffer = new uint8_t[a_size];
    314   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
    315   ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
    316   delete[] buffer;
    317 
    318   // An entry that's stored.
    319   ZipEntryName b_name;
    320   b_name.name = kBTxtName;
    321   b_name.name_length = kBTxtNameLength;
    322   ASSERT_EQ(0, FindEntry(handle, b_name, &data));
    323   const uint32_t b_size = data.uncompressed_length;
    324   ASSERT_EQ(b_size, sizeof(kBTxtContents));
    325   buffer = new uint8_t[b_size];
    326   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
    327   ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
    328   delete[] buffer;
    329 
    330   CloseArchive(handle);
    331 }
    332 
    333 static const uint32_t kEmptyEntriesZip[] = {
    334       0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
    335       0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
    336       0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
    337       0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
    338       0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
    339       0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
    340       0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
    341 
    342 // This is a zip file containing a single entry (ab.txt) that contains
    343 // 90072 repetitions of the string "ab\n" and has an uncompressed length
    344 // of 270216 bytes.
    345 static const uint16_t kAbZip[] = {
    346   0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0,
    347   0x2cda, 0x011b, 0x0000, 0x1f88, 0x0004, 0x0006, 0x001c, 0x6261,
    348   0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
    349   0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000,
    350   0xc2ed, 0x0d31, 0x0000, 0x030c, 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa,
    351   0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    352   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    353   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    354   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    355   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    356   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    357   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    358   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    359   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    360   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    361   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    362   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    363   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    364   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    365   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    366   0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
    367   0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02,
    368   0x1403, 0x0000, 0x0800, 0xd200, 0x9851, 0xb046, 0xdac4, 0x1b2c,
    369   0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
    370   0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574,
    371   0x0554, 0x0300, 0x097c, 0x553a, 0x7875, 0x000b, 0x0401, 0x4289,
    372   0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
    373   0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000
    374 };
    375 
    376 static const uint8_t kAbTxtName[] = { 'a', 'b', '.', 't', 'x', 't' };
    377 static const uint16_t kAbTxtNameLength = sizeof(kAbTxtName);
    378 static const size_t kAbUncompressedSize = 270216;
    379 
    380 static int make_temporary_file(const char* file_name_pattern) {
    381   char full_path[1024];
    382   // Account for differences between the host and the target.
    383   //
    384   // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
    385   snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
    386   int fd = mkstemp(full_path);
    387   if (fd == -1) {
    388     snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
    389     fd = mkstemp(full_path);
    390   }
    391 
    392   return fd;
    393 }
    394 
    395 TEST(ziparchive, EmptyEntries) {
    396   char temp_file_pattern[] = "empty_entries_test_XXXXXX";
    397   int fd = make_temporary_file(temp_file_pattern);
    398   ASSERT_NE(-1, fd);
    399   const ssize_t file_size = sizeof(kEmptyEntriesZip);
    400   ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
    401 
    402   ZipArchiveHandle handle;
    403   ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
    404 
    405   ZipEntry entry;
    406   ZipEntryName empty_name;
    407   empty_name.name = kEmptyTxtName;
    408   empty_name.name_length = kEmptyTxtNameLength;
    409   ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
    410   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
    411   uint8_t buffer[1];
    412   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
    413 
    414   char output_file_pattern[] = "empty_entries_output_XXXXXX";
    415   int output_fd = make_temporary_file(output_file_pattern);
    416   ASSERT_NE(-1, output_fd);
    417   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
    418 
    419   struct stat stat_buf;
    420   ASSERT_EQ(0, fstat(output_fd, &stat_buf));
    421   ASSERT_EQ(0, stat_buf.st_size);
    422 
    423   close(fd);
    424   close(output_fd);
    425 }
    426 
    427 TEST(ziparchive, EntryLargerThan32K) {
    428   char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
    429   int fd = make_temporary_file(temp_file_pattern);
    430   ASSERT_NE(-1, fd);
    431   ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
    432                          sizeof(kAbZip) - 1));
    433   ZipArchiveHandle handle;
    434   ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
    435 
    436   ZipEntry entry;
    437   ZipEntryName ab_name;
    438   ab_name.name = kAbTxtName;
    439   ab_name.name_length = kAbTxtNameLength;
    440   ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
    441   ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
    442 
    443   // Extract the entry to memory.
    444   std::vector<uint8_t> buffer(kAbUncompressedSize);
    445   ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
    446 
    447   // Extract the entry to a file.
    448   char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
    449   int output_fd = make_temporary_file(output_file_pattern);
    450   ASSERT_NE(-1, output_fd);
    451   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
    452 
    453   // Make sure the extracted file size is as expected.
    454   struct stat stat_buf;
    455   ASSERT_EQ(0, fstat(output_fd, &stat_buf));
    456   ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
    457 
    458   // Read the file back to a buffer and make sure the contents are
    459   // the same as the memory buffer we extracted directly to.
    460   std::vector<uint8_t> file_contents(kAbUncompressedSize);
    461   ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
    462   ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
    463   ASSERT_EQ(file_contents, buffer);
    464 
    465   for (int i = 0; i < 90072; ++i) {
    466     const uint8_t* line = &file_contents[0] + (3 * i);
    467     ASSERT_EQ('a', line[0]);
    468     ASSERT_EQ('b', line[1]);
    469     ASSERT_EQ('\n', line[2]);
    470   }
    471 
    472   close(fd);
    473   close(output_fd);
    474 }
    475 
    476 TEST(ziparchive, TrailerAfterEOCD) {
    477   char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
    478   int fd = make_temporary_file(temp_file_pattern);
    479   ASSERT_NE(-1, fd);
    480 
    481   // Create a file with 8 bytes of random garbage.
    482   static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
    483   const ssize_t file_size = sizeof(kEmptyEntriesZip);
    484   const ssize_t trailer_size = sizeof(trailer);
    485   ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
    486   ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
    487 
    488   ZipArchiveHandle handle;
    489   ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
    490 }
    491 
    492 TEST(ziparchive, ExtractToFile) {
    493   char kTempFilePattern[] = "zip_archive_input_XXXXXX";
    494   int fd = make_temporary_file(kTempFilePattern);
    495   ASSERT_NE(-1, fd);
    496   const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
    497   const ssize_t data_size = sizeof(data);
    498 
    499   ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
    500 
    501   ZipArchiveHandle handle;
    502   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    503 
    504   ZipEntry entry;
    505   ZipEntryName name;
    506   name.name = kATxtName;
    507   name.name_length = kATxtNameLength;
    508   ASSERT_EQ(0, FindEntry(handle, name, &entry));
    509   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
    510 
    511 
    512   // Assert that the first 8 bytes of the file haven't been clobbered.
    513   uint8_t read_buffer[data_size];
    514   ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
    515   ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
    516   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
    517 
    518   // Assert that the remainder of the file contains the incompressed data.
    519   std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
    520   ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
    521             TEMP_FAILURE_RETRY(
    522                 read(fd, &uncompressed_data[0], entry.uncompressed_length)));
    523   ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
    524                       sizeof(kATxtContents)));
    525 
    526   // Assert that the total length of the file is sane
    527   ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)),
    528             lseek64(fd, 0, SEEK_END));
    529 
    530   close(fd);
    531 }
    532 
    533 int main(int argc, char** argv) {
    534   ::testing::InitGoogleTest(&argc, argv);
    535 
    536   static struct option options[] = {
    537     { "test_data_dir", required_argument, NULL, 't' },
    538     { NULL, 0, NULL, 0 }
    539   };
    540 
    541   while (true) {
    542     int option_index;
    543     const int c = getopt_long_only(argc, argv, "", options, &option_index);
    544     if (c == -1) {
    545       break;
    546     }
    547 
    548     if (c == 't') {
    549       test_data_dir = optarg;
    550     }
    551   }
    552 
    553   if (test_data_dir.size() == 0) {
    554     printf("Test data flag (--test_data_dir) required\n\n");
    555     return -1;
    556   }
    557 
    558   if (test_data_dir[0] != '/') {
    559     printf("Test data must be an absolute path, was %s\n\n",
    560            test_data_dir.c_str());
    561     return -2;
    562   }
    563 
    564   return RUN_ALL_TESTS();
    565 }
    566