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 26struct zipwriter : public ::testing::Test { 27 TemporaryFile* temp_file_; 28 int fd_; 29 FILE* file_; 30 31 void SetUp() override { 32 temp_file_ = new TemporaryFile(); 33 fd_ = temp_file_->fd; 34 file_ = fdopen(fd_, "w"); 35 ASSERT_NE(file_, nullptr); 36 } 37 38 void TearDown() override { 39 fclose(file_); 40 delete temp_file_; 41 } 42}; 43 44TEST_F(zipwriter, WriteUncompressedZipWithOneFile) { 45 ZipWriter writer(file_); 46 47 const char* expected = "hello"; 48 49 ASSERT_EQ(0, writer.StartEntry("file.txt", 0)); 50 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 51 ASSERT_EQ(0, writer.WriteBytes("llo", 3)); 52 ASSERT_EQ(0, writer.FinishEntry()); 53 ASSERT_EQ(0, writer.Finish()); 54 55 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 56 57 ZipArchiveHandle handle; 58 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 59 60 ZipEntry data; 61 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); 62 EXPECT_EQ(strlen(expected), data.compressed_length); 63 EXPECT_EQ(strlen(expected), data.uncompressed_length); 64 EXPECT_EQ(kCompressStored, data.method); 65 66 char buffer[6]; 67 EXPECT_EQ(0, 68 ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer))); 69 buffer[5] = 0; 70 71 EXPECT_STREQ(expected, buffer); 72 73 CloseArchive(handle); 74} 75 76TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) { 77 ZipWriter writer(file_); 78 79 ASSERT_EQ(0, writer.StartEntry("file.txt", 0)); 80 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 81 ASSERT_EQ(0, writer.FinishEntry()); 82 83 ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0)); 84 ASSERT_EQ(0, writer.WriteBytes("llo", 3)); 85 ASSERT_EQ(0, writer.FinishEntry()); 86 87 ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0)); 88 ASSERT_EQ(0, writer.FinishEntry()); 89 90 ASSERT_EQ(0, writer.Finish()); 91 92 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 93 94 ZipArchiveHandle handle; 95 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 96 97 char buffer[4]; 98 ZipEntry data; 99 100 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); 101 EXPECT_EQ(kCompressStored, data.method); 102 EXPECT_EQ(2u, data.compressed_length); 103 EXPECT_EQ(2u, data.uncompressed_length); 104 ASSERT_EQ(0, 105 ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer))); 106 buffer[2] = 0; 107 EXPECT_STREQ("he", buffer); 108 109 ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data)); 110 EXPECT_EQ(kCompressStored, data.method); 111 EXPECT_EQ(3u, data.compressed_length); 112 EXPECT_EQ(3u, data.uncompressed_length); 113 ASSERT_EQ(0, 114 ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer))); 115 buffer[3] = 0; 116 EXPECT_STREQ("llo", buffer); 117 118 ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data)); 119 EXPECT_EQ(kCompressStored, data.method); 120 EXPECT_EQ(0u, data.compressed_length); 121 EXPECT_EQ(0u, data.uncompressed_length); 122 123 CloseArchive(handle); 124} 125 126TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) { 127 ZipWriter writer(file_); 128 129 ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32)); 130 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 131 ASSERT_EQ(0, writer.FinishEntry()); 132 ASSERT_EQ(0, writer.Finish()); 133 134 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 135 136 ZipArchiveHandle handle; 137 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 138 139 ZipEntry data; 140 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); 141 EXPECT_EQ(0, data.offset & 0x03); 142 143 CloseArchive(handle); 144} 145 146void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) { 147 memset(tm, 0, sizeof(struct tm)); 148 tm->tm_hour = (zip_time >> 11) & 0x1f; 149 tm->tm_min = (zip_time >> 5) & 0x3f; 150 tm->tm_sec = (zip_time & 0x1f) << 1; 151 152 tm->tm_year = ((zip_time >> 25) & 0x7f) + 80; 153 tm->tm_mon = ((zip_time >> 21) & 0xf) - 1; 154 tm->tm_mday = (zip_time >> 16) & 0x1f; 155} 156 157static struct tm MakeTm() { 158 struct tm tm; 159 memset(&tm, 0, sizeof(struct tm)); 160 tm.tm_year = 2001 - 1900; 161 tm.tm_mon = 1; 162 tm.tm_mday = 12; 163 tm.tm_hour = 18; 164 tm.tm_min = 30; 165 tm.tm_sec = 20; 166 return tm; 167} 168 169TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) { 170 ZipWriter writer(file_); 171 172 struct tm tm = MakeTm(); 173 time_t time = mktime(&tm); 174 ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time)); 175 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 176 ASSERT_EQ(0, writer.FinishEntry()); 177 ASSERT_EQ(0, writer.Finish()); 178 179 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 180 181 ZipArchiveHandle handle; 182 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 183 184 ZipEntry data; 185 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); 186 EXPECT_EQ(0, data.offset & 0x03); 187 188 struct tm mod; 189 ConvertZipTimeToTm(data.mod_time, &mod); 190 EXPECT_EQ(tm.tm_sec, mod.tm_sec); 191 EXPECT_EQ(tm.tm_min, mod.tm_min); 192 EXPECT_EQ(tm.tm_hour, mod.tm_hour); 193 EXPECT_EQ(tm.tm_mday, mod.tm_mday); 194 EXPECT_EQ(tm.tm_mon, mod.tm_mon); 195 EXPECT_EQ(tm.tm_year, mod.tm_year); 196 197 CloseArchive(handle); 198} 199 200TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) { 201 ZipWriter writer(file_); 202 203 ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096)); 204 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 205 ASSERT_EQ(0, writer.FinishEntry()); 206 ASSERT_EQ(0, writer.Finish()); 207 208 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 209 210 ZipArchiveHandle handle; 211 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 212 213 ZipEntry data; 214 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); 215 EXPECT_EQ(0, data.offset & 0xfff); 216 217 CloseArchive(handle); 218} 219 220TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) { 221 ZipWriter writer(file_); 222 223 struct tm tm = MakeTm(); 224 time_t time = mktime(&tm); 225 ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096)); 226 ASSERT_EQ(0, writer.WriteBytes("he", 2)); 227 ASSERT_EQ(0, writer.FinishEntry()); 228 ASSERT_EQ(0, writer.Finish()); 229 230 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 231 232 ZipArchiveHandle handle; 233 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 234 235 ZipEntry data; 236 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data)); 237 EXPECT_EQ(0, data.offset & 0xfff); 238 239 struct tm mod; 240 ConvertZipTimeToTm(data.mod_time, &mod); 241 EXPECT_EQ(tm.tm_sec, mod.tm_sec); 242 EXPECT_EQ(tm.tm_min, mod.tm_min); 243 EXPECT_EQ(tm.tm_hour, mod.tm_hour); 244 EXPECT_EQ(tm.tm_mday, mod.tm_mday); 245 EXPECT_EQ(tm.tm_mon, mod.tm_mon); 246 EXPECT_EQ(tm.tm_year, mod.tm_year); 247 248 CloseArchive(handle); 249} 250 251TEST_F(zipwriter, WriteCompressedZipWithOneFile) { 252 ZipWriter writer(file_); 253 254 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress)); 255 ASSERT_EQ(0, writer.WriteBytes("helo", 4)); 256 ASSERT_EQ(0, writer.FinishEntry()); 257 ASSERT_EQ(0, writer.Finish()); 258 259 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 260 261 ZipArchiveHandle handle; 262 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 263 264 ZipEntry data; 265 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); 266 EXPECT_EQ(kCompressDeflated, data.method); 267 EXPECT_EQ(4u, data.uncompressed_length); 268 269 char buffer[5]; 270 ASSERT_EQ(0, 271 ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer))); 272 buffer[4] = 0; 273 274 EXPECT_STREQ("helo", buffer); 275 276 CloseArchive(handle); 277} 278 279TEST_F(zipwriter, WriteCompressedZipFlushFull) { 280 // This exact data will cause the Finish() to require multiple calls 281 // to deflate() because the ZipWriter buffer isn't big enough to hold 282 // the entire compressed data buffer. 283 constexpr size_t kBufSize = 10000000; 284 std::vector<uint8_t> buffer(kBufSize); 285 size_t prev = 1; 286 for (size_t i = 0; i < kBufSize; i++) { 287 buffer[i] = i + prev; 288 prev = i; 289 } 290 291 ZipWriter writer(file_); 292 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress)); 293 ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size())); 294 ASSERT_EQ(0, writer.FinishEntry()); 295 ASSERT_EQ(0, writer.Finish()); 296 297 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); 298 299 ZipArchiveHandle handle; 300 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); 301 302 ZipEntry data; 303 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); 304 EXPECT_EQ(kCompressDeflated, data.method); 305 EXPECT_EQ(kBufSize, data.uncompressed_length); 306 307 std::vector<uint8_t> decompress(kBufSize); 308 memset(decompress.data(), 0, kBufSize); 309 ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size())); 310 EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize)) 311 << "Input buffer and output buffer are different."; 312 313 CloseArchive(handle); 314} 315 316TEST_F(zipwriter, CheckStartEntryErrors) { 317 ZipWriter writer(file_); 318 319 ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096)); 320 ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3)); 321} 322