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 <getopt.h>
     21 #include <stdio.h>
     22 #include <unistd.h>
     23 #include <vector>
     24 
     25 #include <gtest/gtest.h>
     26 
     27 static std::string test_data_dir;
     28 
     29 static const std::string kMissingZip = "missing.zip";
     30 static const std::string kValidZip = "valid.zip";
     31 
     32 static const uint8_t kATxtContents[] = {
     33   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
     34   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
     35   '\n'
     36 };
     37 
     38 static const uint8_t kBTxtContents[] = {
     39   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
     40   '\n'
     41 };
     42 
     43 static int32_t OpenArchiveWrapper(const std::string& name,
     44                                   ZipArchiveHandle* handle) {
     45   const std::string abs_path = test_data_dir + "/" + name;
     46   return OpenArchive(abs_path.c_str(), handle);
     47 }
     48 
     49 static void AssertNameEquals(const std::string& name_str,
     50                              const ZipEntryName& name) {
     51   ASSERT_EQ(name_str.size(), name.name_length);
     52   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
     53 }
     54 
     55 TEST(ziparchive, Open) {
     56   ZipArchiveHandle handle;
     57   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
     58 
     59   CloseArchive(handle);
     60 }
     61 
     62 TEST(ziparchive, OpenMissing) {
     63   ZipArchiveHandle handle;
     64   ASSERT_NE(0, OpenArchiveWrapper(kMissingZip, &handle));
     65 
     66   // Confirm the file descriptor is not going to be mistaken for a valid one.
     67   ASSERT_EQ(-1, GetFileDescriptor(handle));
     68 }
     69 
     70 TEST(ziparchive, Iteration) {
     71   ZipArchiveHandle handle;
     72   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
     73 
     74   void* iteration_cookie;
     75   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
     76 
     77   ZipEntry data;
     78   ZipEntryName name;
     79 
     80   // b/c.txt
     81   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
     82   AssertNameEquals("b/c.txt", name);
     83 
     84   // b/d.txt
     85   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
     86   AssertNameEquals("b/d.txt", name);
     87 
     88   // a.txt
     89   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
     90   AssertNameEquals("a.txt", name);
     91 
     92   // b.txt
     93   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
     94   AssertNameEquals("b.txt", name);
     95 
     96   // b/
     97   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
     98   AssertNameEquals("b/", name);
     99 
    100   // End of iteration.
    101   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
    102 
    103   CloseArchive(handle);
    104 }
    105 
    106 TEST(ziparchive, FindEntry) {
    107   ZipArchiveHandle handle;
    108   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    109 
    110   ZipEntry data;
    111   ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
    112 
    113   // Known facts about a.txt, from zipinfo -v.
    114   ASSERT_EQ(63, data.offset);
    115   ASSERT_EQ(kCompressDeflated, data.method);
    116   ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
    117   ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
    118   ASSERT_EQ(0x950821c5, data.crc32);
    119 
    120   // An entry that doesn't exist. Should be a negative return code.
    121   ASSERT_LT(FindEntry(handle, "nonexistent.txt", &data), 0);
    122 
    123   CloseArchive(handle);
    124 }
    125 
    126 TEST(ziparchive, ExtractToMemory) {
    127   ZipArchiveHandle handle;
    128   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    129 
    130   // An entry that's deflated.
    131   ZipEntry data;
    132   ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
    133   const uint32_t a_size = data.uncompressed_length;
    134   ASSERT_EQ(a_size, sizeof(kATxtContents));
    135   uint8_t* buffer = new uint8_t[a_size];
    136   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
    137   ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
    138   delete[] buffer;
    139 
    140   // An entry that's stored.
    141   ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
    142   const uint32_t b_size = data.uncompressed_length;
    143   ASSERT_EQ(b_size, sizeof(kBTxtContents));
    144   buffer = new uint8_t[b_size];
    145   ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
    146   ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
    147   delete[] buffer;
    148 
    149   CloseArchive(handle);
    150 }
    151 
    152 static const uint32_t kEmptyEntriesZip[] = {
    153       0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000,
    154       0x00090000, 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13,
    155       0x52e25c24, 0x000b7875, 0x42890401, 0x88040000, 0x50000013, 0x1e02014b,
    156       0x00000a03, 0x60000000, 0x00443863, 0x00000000, 0x00000000, 0x09000000,
    157       0x00001800, 0x00000000, 0xa0000000, 0x00000081, 0x706d6500, 0x742e7974,
    158       0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904, 0x13880400,
    159       0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000 };
    160 
    161 static int make_temporary_file(const char* file_name_pattern) {
    162   char full_path[1024];
    163   // Account for differences between the host and the target.
    164   //
    165   // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
    166   snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
    167   int fd = mkstemp(full_path);
    168   if (fd == -1) {
    169     snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
    170     fd = mkstemp(full_path);
    171   }
    172 
    173   return fd;
    174 }
    175 
    176 TEST(ziparchive, EmptyEntries) {
    177   char temp_file_pattern[] = "empty_entries_test_XXXXXX";
    178   int fd = make_temporary_file(temp_file_pattern);
    179   ASSERT_NE(-1, fd);
    180   const ssize_t file_size = sizeof(kEmptyEntriesZip);
    181   ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
    182 
    183   ZipArchiveHandle handle;
    184   ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
    185 
    186   ZipEntry entry;
    187   ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
    188   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
    189   uint8_t buffer[1];
    190   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
    191 
    192   char output_file_pattern[] = "empty_entries_output_XXXXXX";
    193   int output_fd = make_temporary_file(output_file_pattern);
    194   ASSERT_NE(-1, output_fd);
    195   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
    196 
    197   struct stat stat_buf;
    198   ASSERT_EQ(0, fstat(output_fd, &stat_buf));
    199   ASSERT_EQ(0, stat_buf.st_size);
    200 
    201   close(fd);
    202   close(output_fd);
    203 }
    204 
    205 TEST(ziparchive, TrailerAfterEOCD) {
    206   char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
    207   int fd = make_temporary_file(temp_file_pattern);
    208   ASSERT_NE(-1, fd);
    209 
    210   // Create a file with 8 bytes of random garbage.
    211   static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
    212   const ssize_t file_size = sizeof(kEmptyEntriesZip);
    213   const ssize_t trailer_size = sizeof(trailer);
    214   ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
    215   ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
    216 
    217   ZipArchiveHandle handle;
    218   ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
    219 }
    220 
    221 TEST(ziparchive, ExtractToFile) {
    222   char kTempFilePattern[] = "zip_archive_input_XXXXXX";
    223   int fd = make_temporary_file(kTempFilePattern);
    224   ASSERT_NE(-1, fd);
    225   const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
    226   const ssize_t data_size = sizeof(data);
    227 
    228   ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
    229 
    230   ZipArchiveHandle handle;
    231   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
    232 
    233   ZipEntry entry;
    234   ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
    235   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
    236 
    237 
    238   // Assert that the first 8 bytes of the file haven't been clobbered.
    239   uint8_t read_buffer[data_size];
    240   ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
    241   ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
    242   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
    243 
    244   // Assert that the remainder of the file contains the incompressed data.
    245   std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
    246   ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
    247             TEMP_FAILURE_RETRY(
    248                 read(fd, &uncompressed_data[0], entry.uncompressed_length)));
    249   ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
    250                       sizeof(kATxtContents)));
    251 
    252   // Assert that the total length of the file is sane
    253   ASSERT_EQ(data_size + static_cast<ssize_t>(sizeof(kATxtContents)),
    254             lseek64(fd, 0, SEEK_END));
    255 
    256   close(fd);
    257 }
    258 
    259 int main(int argc, char** argv) {
    260   ::testing::InitGoogleTest(&argc, argv);
    261 
    262   static struct option options[] = {
    263     { "test_data_dir", required_argument, NULL, 't' },
    264     { NULL, 0, NULL, 0 }
    265   };
    266 
    267   while (true) {
    268     int option_index;
    269     const int c = getopt_long_only(argc, argv, "", options, &option_index);
    270     if (c == -1) {
    271       break;
    272     }
    273 
    274     if (c == 't') {
    275       test_data_dir = optarg;
    276     }
    277   }
    278 
    279   if (test_data_dir.size() == 0) {
    280     printf("Test data flag (--test_data_dir) required\n\n");
    281     return -1;
    282   }
    283 
    284   if (test_data_dir[0] != '/') {
    285     printf("Test data must be an absolute path, was %s\n\n",
    286            test_data_dir.c_str());
    287     return -2;
    288   }
    289 
    290   return RUN_ALL_TESTS();
    291 }
    292