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 27static std::string test_data_dir; 28 29static const std::string kMissingZip = "missing.zip"; 30static const std::string kValidZip = "valid.zip"; 31 32static 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 38static const uint8_t kBTxtContents[] = { 39 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 40 '\n' 41}; 42 43static 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 49static 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 55TEST(ziparchive, Open) { 56 ZipArchiveHandle handle; 57 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle)); 58 59 CloseArchive(handle); 60} 61 62TEST(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 70TEST(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 106TEST(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 126TEST(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 152static 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 161static 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 176TEST(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 205TEST(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 221TEST(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 259int 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