blob: 259bcff5436f18760490c4c8babf6fe012a22637 [file] [log] [blame]
Adam Lesinski622f3042015-10-05 18:16:18 -07001/*
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
Elliott Hughes53039d62015-12-04 22:00:26 -080020#include <android-base/test_utils.h>
Adam Lesinski622f3042015-10-05 18:16:18 -070021#include <gtest/gtest.h>
Christopher Ferris72082ab2016-01-19 10:33:03 -080022#include <time.h>
Adam Lesinski622f3042015-10-05 18:16:18 -070023#include <memory>
Christopher Ferrisfcc081f2015-11-04 17:54:32 -080024#include <vector>
Adam Lesinski622f3042015-10-05 18:16:18 -070025
Adam Lesinski2e216de2017-03-16 13:23:51 -070026static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
27 ZipArchiveHandle handle,
28 ZipEntry* zip_entry);
29
Adam Lesinski622f3042015-10-05 18:16:18 -070030struct zipwriter : public ::testing::Test {
31 TemporaryFile* temp_file_;
32 int fd_;
33 FILE* file_;
34
35 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
42 void TearDown() override {
43 fclose(file_);
44 delete temp_file_;
45 }
46};
47
48TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
49 ZipWriter writer(file_);
50
51 const char* expected = "hello";
52
Adam Lesinskic322b432015-10-06 15:23:46 -070053 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());
Adam Lesinski622f3042015-10-05 18:16:18 -070058
Adam Lesinskic322b432015-10-06 15:23:46 -070059 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
Adam Lesinski622f3042015-10-05 18:16:18 -070060
61 ZipArchiveHandle handle;
Adam Lesinskic322b432015-10-06 15:23:46 -070062 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
Adam Lesinski622f3042015-10-05 18:16:18 -070063
64 ZipEntry data;
Adam Lesinskic322b432015-10-06 15:23:46 -070065 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
Adam Lesinskic322b432015-10-06 15:23:46 -070066 EXPECT_EQ(kCompressStored, data.method);
Adam Lesinski2e216de2017-03-16 13:23:51 -070067 EXPECT_EQ(strlen(expected), data.compressed_length);
68 ASSERT_EQ(strlen(expected), data.uncompressed_length);
69 ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
Adam Lesinski622f3042015-10-05 18:16:18 -070070
71 CloseArchive(handle);
72}
73
74TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
75 ZipWriter writer(file_);
76
Adam Lesinskic322b432015-10-06 15:23:46 -070077 ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
78 ASSERT_EQ(0, writer.WriteBytes("he", 2));
79 ASSERT_EQ(0, writer.FinishEntry());
Adam Lesinski622f3042015-10-05 18:16:18 -070080
Adam Lesinskic322b432015-10-06 15:23:46 -070081 ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
82 ASSERT_EQ(0, writer.WriteBytes("llo", 3));
83 ASSERT_EQ(0, writer.FinishEntry());
Adam Lesinski622f3042015-10-05 18:16:18 -070084
Adam Lesinskic322b432015-10-06 15:23:46 -070085 ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
86 ASSERT_EQ(0, writer.FinishEntry());
Adam Lesinski622f3042015-10-05 18:16:18 -070087
Adam Lesinskic322b432015-10-06 15:23:46 -070088 ASSERT_EQ(0, writer.Finish());
Adam Lesinski622f3042015-10-05 18:16:18 -070089
Adam Lesinskic322b432015-10-06 15:23:46 -070090 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
Adam Lesinski622f3042015-10-05 18:16:18 -070091
92 ZipArchiveHandle handle;
Adam Lesinskic322b432015-10-06 15:23:46 -070093 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
Adam Lesinski622f3042015-10-05 18:16:18 -070094
Adam Lesinski622f3042015-10-05 18:16:18 -070095 ZipEntry data;
96
Adam Lesinskic322b432015-10-06 15:23:46 -070097 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
98 EXPECT_EQ(kCompressStored, data.method);
99 EXPECT_EQ(2u, data.compressed_length);
Adam Lesinski2e216de2017-03-16 13:23:51 -0700100 ASSERT_EQ(2u, data.uncompressed_length);
101 ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
Adam Lesinski622f3042015-10-05 18:16:18 -0700102
Adam Lesinskic322b432015-10-06 15:23:46 -0700103 ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
104 EXPECT_EQ(kCompressStored, data.method);
105 EXPECT_EQ(3u, data.compressed_length);
Adam Lesinski2e216de2017-03-16 13:23:51 -0700106 ASSERT_EQ(3u, data.uncompressed_length);
107 ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
Adam Lesinski622f3042015-10-05 18:16:18 -0700108
Adam Lesinskic322b432015-10-06 15:23:46 -0700109 ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
110 EXPECT_EQ(kCompressStored, data.method);
111 EXPECT_EQ(0u, data.compressed_length);
112 EXPECT_EQ(0u, data.uncompressed_length);
Adam Lesinski622f3042015-10-05 18:16:18 -0700113
114 CloseArchive(handle);
115}
116
Christopher Ferris72082ab2016-01-19 10:33:03 -0800117TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
Adam Lesinski622f3042015-10-05 18:16:18 -0700118 ZipWriter writer(file_);
119
Adam Lesinskic322b432015-10-06 15:23:46 -0700120 ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
121 ASSERT_EQ(0, writer.WriteBytes("he", 2));
122 ASSERT_EQ(0, writer.FinishEntry());
123 ASSERT_EQ(0, writer.Finish());
Adam Lesinski622f3042015-10-05 18:16:18 -0700124
Adam Lesinskic322b432015-10-06 15:23:46 -0700125 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
Adam Lesinski622f3042015-10-05 18:16:18 -0700126
127 ZipArchiveHandle handle;
Adam Lesinskic322b432015-10-06 15:23:46 -0700128 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
Adam Lesinski622f3042015-10-05 18:16:18 -0700129
130 ZipEntry data;
Adam Lesinskic322b432015-10-06 15:23:46 -0700131 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
132 EXPECT_EQ(0, data.offset & 0x03);
133
134 CloseArchive(handle);
135}
136
Adam Lesinski2e216de2017-03-16 13:23:51 -0700137static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
Christopher Ferris72082ab2016-01-19 10:33:03 -0800138 memset(tm, 0, sizeof(struct tm));
139 tm->tm_hour = (zip_time >> 11) & 0x1f;
140 tm->tm_min = (zip_time >> 5) & 0x3f;
141 tm->tm_sec = (zip_time & 0x1f) << 1;
142
143 tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
144 tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
145 tm->tm_mday = (zip_time >> 16) & 0x1f;
146}
147
Yabin Cui6f71fb72016-02-08 16:26:33 -0800148static struct tm MakeTm() {
149 struct tm tm;
150 memset(&tm, 0, sizeof(struct tm));
151 tm.tm_year = 2001 - 1900;
152 tm.tm_mon = 1;
153 tm.tm_mday = 12;
154 tm.tm_hour = 18;
155 tm.tm_min = 30;
156 tm.tm_sec = 20;
157 return tm;
158}
159
Christopher Ferris72082ab2016-01-19 10:33:03 -0800160TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
161 ZipWriter writer(file_);
162
Yabin Cui6f71fb72016-02-08 16:26:33 -0800163 struct tm tm = MakeTm();
Christopher Ferris72082ab2016-01-19 10:33:03 -0800164 time_t time = mktime(&tm);
165 ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
166 ASSERT_EQ(0, writer.WriteBytes("he", 2));
167 ASSERT_EQ(0, writer.FinishEntry());
168 ASSERT_EQ(0, writer.Finish());
169
170 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
171
172 ZipArchiveHandle handle;
173 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
174
175 ZipEntry data;
176 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
177 EXPECT_EQ(0, data.offset & 0x03);
178
179 struct tm mod;
180 ConvertZipTimeToTm(data.mod_time, &mod);
181 EXPECT_EQ(tm.tm_sec, mod.tm_sec);
182 EXPECT_EQ(tm.tm_min, mod.tm_min);
183 EXPECT_EQ(tm.tm_hour, mod.tm_hour);
184 EXPECT_EQ(tm.tm_mday, mod.tm_mday);
185 EXPECT_EQ(tm.tm_mon, mod.tm_mon);
186 EXPECT_EQ(tm.tm_year, mod.tm_year);
187
188 CloseArchive(handle);
189}
190
191TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
192 ZipWriter writer(file_);
193
194 ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
195 ASSERT_EQ(0, writer.WriteBytes("he", 2));
196 ASSERT_EQ(0, writer.FinishEntry());
197 ASSERT_EQ(0, writer.Finish());
198
199 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
200
201 ZipArchiveHandle handle;
202 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
203
204 ZipEntry data;
205 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
206 EXPECT_EQ(0, data.offset & 0xfff);
207
208 CloseArchive(handle);
209}
210
211TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
212 ZipWriter writer(file_);
213
Yabin Cui6f71fb72016-02-08 16:26:33 -0800214 struct tm tm = MakeTm();
Christopher Ferris72082ab2016-01-19 10:33:03 -0800215 time_t time = mktime(&tm);
216 ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
217 ASSERT_EQ(0, writer.WriteBytes("he", 2));
218 ASSERT_EQ(0, writer.FinishEntry());
219 ASSERT_EQ(0, writer.Finish());
220
221 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
222
223 ZipArchiveHandle handle;
224 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
225
226 ZipEntry data;
227 ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
228 EXPECT_EQ(0, data.offset & 0xfff);
229
230 struct tm mod;
231 ConvertZipTimeToTm(data.mod_time, &mod);
232 EXPECT_EQ(tm.tm_sec, mod.tm_sec);
233 EXPECT_EQ(tm.tm_min, mod.tm_min);
234 EXPECT_EQ(tm.tm_hour, mod.tm_hour);
235 EXPECT_EQ(tm.tm_mday, mod.tm_mday);
236 EXPECT_EQ(tm.tm_mon, mod.tm_mon);
237 EXPECT_EQ(tm.tm_year, mod.tm_year);
238
239 CloseArchive(handle);
240}
241
Adam Lesinskic322b432015-10-06 15:23:46 -0700242TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
243 ZipWriter writer(file_);
244
245 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
246 ASSERT_EQ(0, writer.WriteBytes("helo", 4));
247 ASSERT_EQ(0, writer.FinishEntry());
248 ASSERT_EQ(0, writer.Finish());
249
250 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
251
252 ZipArchiveHandle handle;
253 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
254
255 ZipEntry data;
256 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
257 EXPECT_EQ(kCompressDeflated, data.method);
Adam Lesinski2e216de2017-03-16 13:23:51 -0700258 ASSERT_EQ(4u, data.uncompressed_length);
259 ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
Adam Lesinskic322b432015-10-06 15:23:46 -0700260
261 CloseArchive(handle);
Adam Lesinski622f3042015-10-05 18:16:18 -0700262}
Christopher Ferrisfcc081f2015-11-04 17:54:32 -0800263
264TEST_F(zipwriter, WriteCompressedZipFlushFull) {
265 // This exact data will cause the Finish() to require multiple calls
266 // to deflate() because the ZipWriter buffer isn't big enough to hold
267 // the entire compressed data buffer.
268 constexpr size_t kBufSize = 10000000;
269 std::vector<uint8_t> buffer(kBufSize);
270 size_t prev = 1;
271 for (size_t i = 0; i < kBufSize; i++) {
272 buffer[i] = i + prev;
273 prev = i;
274 }
275
276 ZipWriter writer(file_);
277 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
278 ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
279 ASSERT_EQ(0, writer.FinishEntry());
280 ASSERT_EQ(0, writer.Finish());
281
282 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
283
284 ZipArchiveHandle handle;
285 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
286
287 ZipEntry data;
288 ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
289 EXPECT_EQ(kCompressDeflated, data.method);
290 EXPECT_EQ(kBufSize, data.uncompressed_length);
291
292 std::vector<uint8_t> decompress(kBufSize);
293 memset(decompress.data(), 0, kBufSize);
294 ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
295 EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
296 << "Input buffer and output buffer are different.";
297
298 CloseArchive(handle);
299}
Christopher Ferris72082ab2016-01-19 10:33:03 -0800300
301TEST_F(zipwriter, CheckStartEntryErrors) {
302 ZipWriter writer(file_);
303
304 ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
305 ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
306}
Adam Lesinski2e216de2017-03-16 13:23:51 -0700307
308TEST_F(zipwriter, BackupRemovesTheLastFile) {
309 ZipWriter writer(file_);
310
311 const char* kKeepThis = "keep this";
312 const char* kDropThis = "drop this";
313 const char* kReplaceWithThis = "replace with this";
314
315 ZipWriter::FileEntry entry;
316 EXPECT_LT(writer.GetLastEntry(&entry), 0);
317
318 ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
319 ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
320 ASSERT_EQ(0, writer.FinishEntry());
321
322 ASSERT_EQ(0, writer.GetLastEntry(&entry));
323 EXPECT_EQ("keep.txt", entry.path);
324
325 ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
326 ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
327 ASSERT_EQ(0, writer.FinishEntry());
328
329 ASSERT_EQ(0, writer.GetLastEntry(&entry));
330 EXPECT_EQ("drop.txt", entry.path);
331
332 ASSERT_EQ(0, writer.DiscardLastEntry());
333
334 ASSERT_EQ(0, writer.GetLastEntry(&entry));
335 EXPECT_EQ("keep.txt", entry.path);
336
337 ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
338 ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
339 ASSERT_EQ(0, writer.FinishEntry());
340
341 ASSERT_EQ(0, writer.GetLastEntry(&entry));
342 EXPECT_EQ("replace.txt", entry.path);
343
344 ASSERT_EQ(0, writer.Finish());
345
346 // Verify that "drop.txt" does not exist.
347
348 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
349
350 ZipArchiveHandle handle;
351 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
352
353 ZipEntry data;
354 ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
355 ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
356
357 ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
358
359 ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
360 ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
361
362 CloseArchive(handle);
363}
364
365TEST_F(zipwriter, TruncateFileAfterBackup) {
366 ZipWriter writer(file_);
367
368 const char* kSmall = "small";
369
370 ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
371 ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
372 ASSERT_EQ(0, writer.FinishEntry());
373
374 ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
375 std::vector<uint8_t> data;
376 data.resize(1024*1024, 0xef);
377 ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
378 ASSERT_EQ(0, writer.FinishEntry());
379
380 off64_t before_len = ftello64(file_);
381
382 ZipWriter::FileEntry entry;
383 ASSERT_EQ(0, writer.GetLastEntry(&entry));
384 ASSERT_EQ(0, writer.DiscardLastEntry());
385
386 ASSERT_EQ(0, writer.Finish());
387
388 off64_t after_len = ftello64(file_);
389
390 ASSERT_GT(before_len, after_len);
391}
392
393static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
394 ZipArchiveHandle handle,
395 ZipEntry* zip_entry) {
396 if (expected.size() != zip_entry->uncompressed_length) {
397 return ::testing::AssertionFailure() << "uncompressed entry size "
398 << zip_entry->uncompressed_length << " does not match expected size " << expected.size();
399 }
400
401 std::string actual;
402 actual.resize(expected.size());
403
404 uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
405 if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) {
406 return ::testing::AssertionFailure() << "failed to extract entry";
407 }
408
409 if (expected != actual) {
410 return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
411 << "' does not match expected '" << expected << "'";
412 }
413 return ::testing::AssertionSuccess();
414}