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 
OpenArchiveWrapper(const std::string & name,ZipArchiveHandle * handle)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 
AssertNameEquals(const std::string & name_str,const ZipEntryName & name)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 
TEST(ziparchive,Open)55 TEST(ziparchive, Open) {
56   ZipArchiveHandle handle;
57   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
58 
59   CloseArchive(handle);
60 }
61 
TEST(ziparchive,OpenMissing)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 
TEST(ziparchive,Iteration)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 
TEST(ziparchive,FindEntry)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 
TEST(ziparchive,ExtractToMemory)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 
make_temporary_file(const char * file_name_pattern)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 
TEST(ziparchive,EmptyEntries)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 
TEST(ziparchive,TrailerAfterEOCD)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 
TEST(ziparchive,ExtractToFile)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 
main(int argc,char ** argv)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