1 /* 2 * Copyright (C) 2015 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 #include "ziparchive/zip_writer.h" 19 20 #include <android-base/test_utils.h> 21 #include <gtest/gtest.h> 22 #include <time.h> 23 #include <memory> 24 #include <vector> 25 26 static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected, 27 ZipArchiveHandle handle, 28 ZipEntry* zip_entry); 29 30 struct zipwriter : public ::testing::Test { 31 TemporaryFile* temp_file_; 32 int fd_; 33 FILE* file_; 34 SetUpzipwriter35 void SetUp() override { 36 temp_file_ = new TemporaryFile(); 37 fd_ = temp_file_->fd; 38 file_ = fdopen(fd_, "w"); 39 ASSERT_NE(file_, nullptr); 40 } 41 TearDownzipwriter42 void TearDown() override { 43 fclose(file_); 44 delete temp_file_; 45 } 46 }; 47 TEST_F(zipwriter,WriteUncompressedZipWithOneFile)48 TEST_F(zipwriter, WriteUncompressedZipWithOneFile) { 49 ZipWriter writer(file_); 50 51 const char* expected = "hello"; 52 53 ASSERT_EQ(0, writer.StartEntry("file.txt", 0)); 54 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 55 ASSERT_EQ(0, writer.WriteBytes("llo", 3)); 56 ASSERT_EQ(0, writer.FinishEntry()); 57 ASSERT_EQ(0, writer.Finish()); 58 59 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 60 61 ZipArchiveHandle handle; 62 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 63 64 ZipEntry data; 65 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); 66 EXPECT_EQ(kCompressStored, data.method); 67 EXPECT_EQ(0u, data.has_data_descriptor); 68 EXPECT_EQ(strlen(expected), data.compressed_length); 69 ASSERT_EQ(strlen(expected), data.uncompressed_length); 70 ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data)); 71 72 CloseArchive(handle); 73 } 74 TEST_F(zipwriter,WriteUncompressedZipWithMultipleFiles)75 TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) { 76 ZipWriter writer(file_); 77 78 ASSERT_EQ(0, writer.StartEntry("file.txt", 0)); 79 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 80 ASSERT_EQ(0, writer.FinishEntry()); 81 82 ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0)); 83 ASSERT_EQ(0, writer.WriteBytes("llo", 3)); 84 ASSERT_EQ(0, writer.FinishEntry()); 85 86 ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0)); 87 ASSERT_EQ(0, writer.FinishEntry()); 88 89 ASSERT_EQ(0, writer.Finish()); 90 91 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 92 93 ZipArchiveHandle handle; 94 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 95 96 ZipEntry data; 97 98 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); 99 EXPECT_EQ(kCompressStored, data.method); 100 EXPECT_EQ(2u, data.compressed_length); 101 ASSERT_EQ(2u, data.uncompressed_length); 102 ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data)); 103 104 ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data)); 105 EXPECT_EQ(kCompressStored, data.method); 106 EXPECT_EQ(3u, data.compressed_length); 107 ASSERT_EQ(3u, data.uncompressed_length); 108 ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data)); 109 110 ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data)); 111 EXPECT_EQ(kCompressStored, data.method); 112 EXPECT_EQ(0u, data.compressed_length); 113 EXPECT_EQ(0u, data.uncompressed_length); 114 115 CloseArchive(handle); 116 } 117 TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedFlag)118 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) { 119 ZipWriter writer(file_); 120 121 ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32)); 122 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 123 ASSERT_EQ(0, writer.FinishEntry()); 124 ASSERT_EQ(0, writer.Finish()); 125 126 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 127 128 ZipArchiveHandle handle; 129 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 130 131 ZipEntry data; 132 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); 133 EXPECT_EQ(0, data.offset & 0x03); 134 135 CloseArchive(handle); 136 } 137 ConvertZipTimeToTm(uint32_t & zip_time,struct tm * tm)138 static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) { 139 memset(tm, 0, sizeof(struct tm)); 140 tm->tm_hour = (zip_time >> 11) & 0x1f; 141 tm->tm_min = (zip_time >> 5) & 0x3f; 142 tm->tm_sec = (zip_time & 0x1f) << 1; 143 144 tm->tm_year = ((zip_time >> 25) & 0x7f) + 80; 145 tm->tm_mon = ((zip_time >> 21) & 0xf) - 1; 146 tm->tm_mday = (zip_time >> 16) & 0x1f; 147 } 148 MakeTm()149 static struct tm MakeTm() { 150 struct tm tm; 151 memset(&tm, 0, sizeof(struct tm)); 152 tm.tm_year = 2001 - 1900; 153 tm.tm_mon = 1; 154 tm.tm_mday = 12; 155 tm.tm_hour = 18; 156 tm.tm_min = 30; 157 tm.tm_sec = 20; 158 return tm; 159 } 160 TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedFlagAndTime)161 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) { 162 ZipWriter writer(file_); 163 164 struct tm tm = MakeTm(); 165 time_t time = mktime(&tm); 166 ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time)); 167 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 168 ASSERT_EQ(0, writer.FinishEntry()); 169 ASSERT_EQ(0, writer.Finish()); 170 171 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 172 173 ZipArchiveHandle handle; 174 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 175 176 ZipEntry data; 177 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); 178 EXPECT_EQ(0, data.offset & 0x03); 179 180 struct tm mod; 181 ConvertZipTimeToTm(data.mod_time, &mod); 182 EXPECT_EQ(tm.tm_sec, mod.tm_sec); 183 EXPECT_EQ(tm.tm_min, mod.tm_min); 184 EXPECT_EQ(tm.tm_hour, mod.tm_hour); 185 EXPECT_EQ(tm.tm_mday, mod.tm_mday); 186 EXPECT_EQ(tm.tm_mon, mod.tm_mon); 187 EXPECT_EQ(tm.tm_year, mod.tm_year); 188 189 CloseArchive(handle); 190 } 191 TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedValue)192 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) { 193 ZipWriter writer(file_); 194 195 ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096)); 196 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 197 ASSERT_EQ(0, writer.FinishEntry()); 198 ASSERT_EQ(0, writer.Finish()); 199 200 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 201 202 ZipArchiveHandle handle; 203 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 204 205 ZipEntry data; 206 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); 207 EXPECT_EQ(0, data.offset & 0xfff); 208 209 CloseArchive(handle); 210 } 211 TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedValueAndTime)212 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) { 213 ZipWriter writer(file_); 214 215 struct tm tm = MakeTm(); 216 time_t time = mktime(&tm); 217 ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096)); 218 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 219 ASSERT_EQ(0, writer.FinishEntry()); 220 ASSERT_EQ(0, writer.Finish()); 221 222 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 223 224 ZipArchiveHandle handle; 225 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 226 227 ZipEntry data; 228 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); 229 EXPECT_EQ(0, data.offset & 0xfff); 230 231 struct tm mod; 232 ConvertZipTimeToTm(data.mod_time, &mod); 233 EXPECT_EQ(tm.tm_sec, mod.tm_sec); 234 EXPECT_EQ(tm.tm_min, mod.tm_min); 235 EXPECT_EQ(tm.tm_hour, mod.tm_hour); 236 EXPECT_EQ(tm.tm_mday, mod.tm_mday); 237 EXPECT_EQ(tm.tm_mon, mod.tm_mon); 238 EXPECT_EQ(tm.tm_year, mod.tm_year); 239 240 CloseArchive(handle); 241 } 242 TEST_F(zipwriter,WriteCompressedZipWithOneFile)243 TEST_F(zipwriter, WriteCompressedZipWithOneFile) { 244 ZipWriter writer(file_); 245 246 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress)); 247 ASSERT_EQ(0, writer.WriteBytes("helo", 4)); 248 ASSERT_EQ(0, writer.FinishEntry()); 249 ASSERT_EQ(0, writer.Finish()); 250 251 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 252 253 ZipArchiveHandle handle; 254 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 255 256 ZipEntry data; 257 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); 258 EXPECT_EQ(kCompressDeflated, data.method); 259 ASSERT_EQ(4u, data.uncompressed_length); 260 ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data)); 261 262 CloseArchive(handle); 263 } 264 TEST_F(zipwriter,WriteCompressedZipFlushFull)265 TEST_F(zipwriter, WriteCompressedZipFlushFull) { 266 // This exact data will cause the Finish() to require multiple calls 267 // to deflate() because the ZipWriter buffer isn't big enough to hold 268 // the entire compressed data buffer. 269 constexpr size_t kBufSize = 10000000; 270 std::vector<uint8_t> buffer(kBufSize); 271 size_t prev = 1; 272 for (size_t i = 0; i < kBufSize; i++) { 273 buffer[i] = i + prev; 274 prev = i; 275 } 276 277 ZipWriter writer(file_); 278 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress)); 279 ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size())); 280 ASSERT_EQ(0, writer.FinishEntry()); 281 ASSERT_EQ(0, writer.Finish()); 282 283 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 284 285 ZipArchiveHandle handle; 286 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 287 288 ZipEntry data; 289 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); 290 EXPECT_EQ(kCompressDeflated, data.method); 291 EXPECT_EQ(kBufSize, data.uncompressed_length); 292 293 std::vector<uint8_t> decompress(kBufSize); 294 memset(decompress.data(), 0, kBufSize); 295 ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size())); 296 EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize)) 297 << "Input buffer and output buffer are different."; 298 299 CloseArchive(handle); 300 } 301 TEST_F(zipwriter,CheckStartEntryErrors)302 TEST_F(zipwriter, CheckStartEntryErrors) { 303 ZipWriter writer(file_); 304 305 ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096)); 306 ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3)); 307 } 308 TEST_F(zipwriter,BackupRemovesTheLastFile)309 TEST_F(zipwriter, BackupRemovesTheLastFile) { 310 ZipWriter writer(file_); 311 312 const char* kKeepThis = "keep this"; 313 const char* kDropThis = "drop this"; 314 const char* kReplaceWithThis = "replace with this"; 315 316 ZipWriter::FileEntry entry; 317 EXPECT_LT(writer.GetLastEntry(&entry), 0); 318 319 ASSERT_EQ(0, writer.StartEntry("keep.txt", 0)); 320 ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis))); 321 ASSERT_EQ(0, writer.FinishEntry()); 322 323 ASSERT_EQ(0, writer.GetLastEntry(&entry)); 324 EXPECT_EQ("keep.txt", entry.path); 325 326 ASSERT_EQ(0, writer.StartEntry("drop.txt", 0)); 327 ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis))); 328 ASSERT_EQ(0, writer.FinishEntry()); 329 330 ASSERT_EQ(0, writer.GetLastEntry(&entry)); 331 EXPECT_EQ("drop.txt", entry.path); 332 333 ASSERT_EQ(0, writer.DiscardLastEntry()); 334 335 ASSERT_EQ(0, writer.GetLastEntry(&entry)); 336 EXPECT_EQ("keep.txt", entry.path); 337 338 ASSERT_EQ(0, writer.StartEntry("replace.txt", 0)); 339 ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis))); 340 ASSERT_EQ(0, writer.FinishEntry()); 341 342 ASSERT_EQ(0, writer.GetLastEntry(&entry)); 343 EXPECT_EQ("replace.txt", entry.path); 344 345 ASSERT_EQ(0, writer.Finish()); 346 347 // Verify that "drop.txt" does not exist. 348 349 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 350 351 ZipArchiveHandle handle; 352 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 353 354 ZipEntry data; 355 ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data)); 356 ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data)); 357 358 ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data)); 359 360 ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data)); 361 ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data)); 362 363 CloseArchive(handle); 364 } 365 TEST_F(zipwriter,TruncateFileAfterBackup)366 TEST_F(zipwriter, TruncateFileAfterBackup) { 367 ZipWriter writer(file_); 368 369 const char* kSmall = "small"; 370 371 ASSERT_EQ(0, writer.StartEntry("small.txt", 0)); 372 ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall))); 373 ASSERT_EQ(0, writer.FinishEntry()); 374 375 ASSERT_EQ(0, writer.StartEntry("large.txt", 0)); 376 std::vector<uint8_t> data; 377 data.resize(1024*1024, 0xef); 378 ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size())); 379 ASSERT_EQ(0, writer.FinishEntry()); 380 381 off_t before_len = ftello(file_); 382 383 ZipWriter::FileEntry entry; 384 ASSERT_EQ(0, writer.GetLastEntry(&entry)); 385 ASSERT_EQ(0, writer.DiscardLastEntry()); 386 387 ASSERT_EQ(0, writer.Finish()); 388 389 off_t after_len = ftello(file_); 390 391 ASSERT_GT(before_len, after_len); 392 } 393 AssertFileEntryContentsEq(const std::string & expected,ZipArchiveHandle handle,ZipEntry * zip_entry)394 static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected, 395 ZipArchiveHandle handle, 396 ZipEntry* zip_entry) { 397 if (expected.size() != zip_entry->uncompressed_length) { 398 return ::testing::AssertionFailure() << "uncompressed entry size " 399 << zip_entry->uncompressed_length << " does not match expected size " << expected.size(); 400 } 401 402 std::string actual; 403 actual.resize(expected.size()); 404 405 uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin()); 406 if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) { 407 return ::testing::AssertionFailure() << "failed to extract entry"; 408 } 409 410 if (expected != actual) { 411 return ::testing::AssertionFailure() << "actual zip_entry data '" << actual 412 << "' does not match expected '" << expected << "'"; 413 } 414 return ::testing::AssertionSuccess(); 415 } 416