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