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_writer.h"
18 #include "ziparchive/zip_archive.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,WriteEmptyUncompressedZipWithOneFile)48 TEST_F(zipwriter, WriteEmptyUncompressedZipWithOneFile) {
49 ZipWriter writer(file_);
50
51 const char* expected = "";
52
53 ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
54 ASSERT_EQ(0, writer.FinishEntry());
55 ASSERT_EQ(0, writer.Finish());
56
57 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
58
59 ZipArchiveHandle handle;
60 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
61
62 ZipEntry data;
63 ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
64 EXPECT_EQ(kCompressStored, data.method);
65 EXPECT_EQ(0u, data.has_data_descriptor);
66 EXPECT_EQ(strlen(expected), data.compressed_length);
67 ASSERT_EQ(strlen(expected), data.uncompressed_length);
68 ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
69
70 CloseArchive(handle);
71 }
72
TEST_F(zipwriter,WriteEmptyCompressedZipWithOneFile)73 TEST_F(zipwriter, WriteEmptyCompressedZipWithOneFile) {
74 ZipWriter writer(file_);
75
76 const char* expected = "";
77
78 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
79 ASSERT_EQ(0, writer.FinishEntry());
80 ASSERT_EQ(0, writer.Finish());
81
82 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
83
84 ZipArchiveHandle handle;
85 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
86
87 ZipEntry data;
88 ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
89 EXPECT_EQ(kCompressDeflated, data.method);
90 EXPECT_EQ(0u, data.has_data_descriptor);
91 ASSERT_EQ(strlen(expected), data.uncompressed_length);
92 ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
93
94 CloseArchive(handle);
95 }
96
TEST_F(zipwriter,WriteUncompressedZipWithOneFile)97 TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
98 ZipWriter writer(file_);
99
100 const char* expected = "hello";
101
102 ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
103 ASSERT_EQ(0, writer.WriteBytes("he", 2));
104 ASSERT_EQ(0, writer.WriteBytes("llo", 3));
105 ASSERT_EQ(0, writer.FinishEntry());
106 ASSERT_EQ(0, writer.Finish());
107
108 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
109
110 ZipArchiveHandle handle;
111 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
112
113 ZipEntry data;
114 ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
115 EXPECT_EQ(kCompressStored, data.method);
116 EXPECT_EQ(0u, data.has_data_descriptor);
117 EXPECT_EQ(strlen(expected), data.compressed_length);
118 ASSERT_EQ(strlen(expected), data.uncompressed_length);
119 ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
120
121 CloseArchive(handle);
122 }
123
TEST_F(zipwriter,WriteUncompressedZipWithMultipleFiles)124 TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
125 ZipWriter writer(file_);
126
127 ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
128 ASSERT_EQ(0, writer.WriteBytes("he", 2));
129 ASSERT_EQ(0, writer.FinishEntry());
130
131 ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
132 ASSERT_EQ(0, writer.WriteBytes("llo", 3));
133 ASSERT_EQ(0, writer.FinishEntry());
134
135 ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
136 ASSERT_EQ(0, writer.FinishEntry());
137
138 ASSERT_EQ(0, writer.Finish());
139
140 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
141
142 ZipArchiveHandle handle;
143 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
144
145 ZipEntry data;
146
147 ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
148 EXPECT_EQ(kCompressStored, data.method);
149 EXPECT_EQ(2u, data.compressed_length);
150 ASSERT_EQ(2u, data.uncompressed_length);
151 ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
152
153 ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
154 EXPECT_EQ(kCompressStored, data.method);
155 EXPECT_EQ(3u, data.compressed_length);
156 ASSERT_EQ(3u, data.uncompressed_length);
157 ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
158
159 ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
160 EXPECT_EQ(kCompressStored, data.method);
161 EXPECT_EQ(0u, data.compressed_length);
162 EXPECT_EQ(0u, data.uncompressed_length);
163
164 CloseArchive(handle);
165 }
166
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedFlag)167 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
168 ZipWriter writer(file_);
169
170 ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
171 ASSERT_EQ(0, writer.WriteBytes("he", 2));
172 ASSERT_EQ(0, writer.FinishEntry());
173 ASSERT_EQ(0, writer.Finish());
174
175 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
176
177 ZipArchiveHandle handle;
178 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
179
180 ZipEntry data;
181 ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
182 EXPECT_EQ(0, data.offset & 0x03);
183
184 CloseArchive(handle);
185 }
186
MakeTm()187 static struct tm MakeTm() {
188 struct tm tm = {};
189 tm.tm_year = 2001 - 1900;
190 tm.tm_mon = 1;
191 tm.tm_mday = 12;
192 tm.tm_hour = 18;
193 tm.tm_min = 30;
194 tm.tm_sec = 20;
195 return tm;
196 }
197
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedFlagAndTime)198 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
199 ZipWriter writer(file_);
200
201 struct tm tm = MakeTm();
202 time_t time = mktime(&tm);
203 ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
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, "align.txt", &data));
215 EXPECT_EQ(0, data.offset & 0x03);
216
217 struct tm mod = data.GetModificationTime();
218 EXPECT_EQ(tm.tm_sec, mod.tm_sec);
219 EXPECT_EQ(tm.tm_min, mod.tm_min);
220 EXPECT_EQ(tm.tm_hour, mod.tm_hour);
221 EXPECT_EQ(tm.tm_mday, mod.tm_mday);
222 EXPECT_EQ(tm.tm_mon, mod.tm_mon);
223 EXPECT_EQ(tm.tm_year, mod.tm_year);
224
225 CloseArchive(handle);
226 }
227
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedValue)228 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
229 ZipWriter writer(file_);
230
231 ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
232 ASSERT_EQ(0, writer.WriteBytes("he", 2));
233 ASSERT_EQ(0, writer.FinishEntry());
234 ASSERT_EQ(0, writer.Finish());
235
236 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
237
238 ZipArchiveHandle handle;
239 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
240
241 ZipEntry data;
242 ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
243 EXPECT_EQ(0, data.offset & 0xfff);
244
245 CloseArchive(handle);
246 }
247
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedValueAndTime)248 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
249 ZipWriter writer(file_);
250
251 struct tm tm = MakeTm();
252 time_t time = mktime(&tm);
253 ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
254 ASSERT_EQ(0, writer.WriteBytes("he", 2));
255 ASSERT_EQ(0, writer.FinishEntry());
256 ASSERT_EQ(0, writer.Finish());
257
258 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
259
260 ZipArchiveHandle handle;
261 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
262
263 ZipEntry data;
264 ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
265 EXPECT_EQ(0, data.offset & 0xfff);
266
267 struct tm mod = data.GetModificationTime();
268 EXPECT_EQ(tm.tm_sec, mod.tm_sec);
269 EXPECT_EQ(tm.tm_min, mod.tm_min);
270 EXPECT_EQ(tm.tm_hour, mod.tm_hour);
271 EXPECT_EQ(tm.tm_mday, mod.tm_mday);
272 EXPECT_EQ(tm.tm_mon, mod.tm_mon);
273 EXPECT_EQ(tm.tm_year, mod.tm_year);
274
275 CloseArchive(handle);
276 }
277
TEST_F(zipwriter,WriteCompressedZipWithOneFile)278 TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
279 ZipWriter writer(file_);
280
281 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
282 ASSERT_EQ(0, writer.WriteBytes("helo", 4));
283 ASSERT_EQ(0, writer.FinishEntry());
284 ASSERT_EQ(0, writer.Finish());
285
286 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
287
288 ZipArchiveHandle handle;
289 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
290
291 ZipEntry data;
292 ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
293 EXPECT_EQ(kCompressDeflated, data.method);
294 EXPECT_EQ(0u, data.has_data_descriptor);
295 ASSERT_EQ(4u, data.uncompressed_length);
296 ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
297
298 CloseArchive(handle);
299 }
300
TEST_F(zipwriter,WriteCompressedZipFlushFull)301 TEST_F(zipwriter, WriteCompressedZipFlushFull) {
302 // This exact data will cause the Finish() to require multiple calls
303 // to deflate() because the ZipWriter buffer isn't big enough to hold
304 // the entire compressed data buffer.
305 constexpr size_t kBufSize = 10000000;
306 std::vector<uint8_t> buffer(kBufSize);
307 size_t prev = 1;
308 for (size_t i = 0; i < kBufSize; i++) {
309 buffer[i] = static_cast<uint8_t>(i + prev);
310 prev = i;
311 }
312
313 ZipWriter writer(file_);
314 ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
315 ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
316 ASSERT_EQ(0, writer.FinishEntry());
317 ASSERT_EQ(0, writer.Finish());
318
319 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
320
321 ZipArchiveHandle handle;
322 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
323
324 ZipEntry data;
325 ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
326 EXPECT_EQ(kCompressDeflated, data.method);
327 EXPECT_EQ(kBufSize, data.uncompressed_length);
328
329 std::vector<uint8_t> decompress(kBufSize);
330 memset(decompress.data(), 0, kBufSize);
331 ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
332 static_cast<uint32_t>(decompress.size())));
333 EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
334 << "Input buffer and output buffer are different.";
335
336 CloseArchive(handle);
337 }
338
TEST_F(zipwriter,WriteZipWithEntryTimeBefore1980)339 TEST_F(zipwriter, WriteZipWithEntryTimeBefore1980) {
340 ZipWriter writer(file_);
341
342 struct tm tm = {};
343 tm.tm_year = 77; // 1977
344 tm.tm_mon = 7; // August (0-based)
345 tm.tm_mday = 16; // 16th
346 tm.tm_hour = 15;
347 tm.tm_min = 30;
348 tm.tm_sec = 20;
349 time_t time = mktime(&tm);
350 ASSERT_EQ(0, writer.StartEntryWithTime("file.txt", 0, time));
351 ASSERT_EQ(0, writer.WriteBytes("king", 4));
352 ASSERT_EQ(0, writer.FinishEntry());
353 ASSERT_EQ(0, writer.Finish());
354
355 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
356
357 ZipArchiveHandle handle;
358 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
359
360 ZipEntry data;
361 ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
362
363 // We expect an entry time before 1980 to be set to 1980-01-01.
364 struct tm mod = data.GetModificationTime();
365 EXPECT_EQ(80, mod.tm_year);
366 EXPECT_EQ(0, mod.tm_mon);
367 EXPECT_EQ(1, mod.tm_mday);
368 EXPECT_EQ(0, mod.tm_hour);
369 EXPECT_EQ(0, mod.tm_min);
370 EXPECT_EQ(0, mod.tm_sec);
371
372 CloseArchive(handle);
373 }
374
TEST_F(zipwriter,CheckStartEntryErrors)375 TEST_F(zipwriter, CheckStartEntryErrors) {
376 ZipWriter writer(file_);
377
378 ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
379 ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
380 }
381
TEST_F(zipwriter,BackupRemovesTheLastFile)382 TEST_F(zipwriter, BackupRemovesTheLastFile) {
383 ZipWriter writer(file_);
384
385 const char* kKeepThis = "keep this";
386 const char* kDropThis = "drop this";
387 const char* kReplaceWithThis = "replace with this";
388
389 ZipWriter::FileEntry entry;
390 EXPECT_LT(writer.GetLastEntry(&entry), 0);
391
392 ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
393 ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
394 ASSERT_EQ(0, writer.FinishEntry());
395
396 ASSERT_EQ(0, writer.GetLastEntry(&entry));
397 EXPECT_EQ("keep.txt", entry.path);
398
399 ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
400 ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
401 ASSERT_EQ(0, writer.FinishEntry());
402
403 ASSERT_EQ(0, writer.GetLastEntry(&entry));
404 EXPECT_EQ("drop.txt", entry.path);
405
406 ASSERT_EQ(0, writer.DiscardLastEntry());
407
408 ASSERT_EQ(0, writer.GetLastEntry(&entry));
409 EXPECT_EQ("keep.txt", entry.path);
410
411 ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
412 ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
413 ASSERT_EQ(0, writer.FinishEntry());
414
415 ASSERT_EQ(0, writer.GetLastEntry(&entry));
416 EXPECT_EQ("replace.txt", entry.path);
417
418 ASSERT_EQ(0, writer.Finish());
419
420 // Verify that "drop.txt" does not exist.
421
422 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
423
424 ZipArchiveHandle handle;
425 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
426
427 ZipEntry data;
428 ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
429 ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
430
431 ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
432
433 ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
434 ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
435
436 CloseArchive(handle);
437 }
438
TEST_F(zipwriter,WriteToUnseekableFile)439 TEST_F(zipwriter, WriteToUnseekableFile) {
440 const char* expected = "hello";
441 ZipWriter writer(file_);
442 writer.seekable_ = false;
443
444 ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
445 ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
446 ASSERT_EQ(0, writer.FinishEntry());
447 ASSERT_EQ(0, writer.Finish());
448 ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
449
450 ZipArchiveHandle handle;
451 ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
452 ZipEntry data;
453 ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
454 EXPECT_EQ(kCompressStored, data.method);
455 EXPECT_EQ(1u, data.has_data_descriptor);
456 EXPECT_EQ(strlen(expected), data.compressed_length);
457 ASSERT_EQ(strlen(expected), data.uncompressed_length);
458 ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
459 CloseArchive(handle);
460 }
461
TEST_F(zipwriter,TruncateFileAfterBackup)462 TEST_F(zipwriter, TruncateFileAfterBackup) {
463 ZipWriter writer(file_);
464
465 const char* kSmall = "small";
466
467 ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
468 ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
469 ASSERT_EQ(0, writer.FinishEntry());
470
471 ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
472 std::vector<uint8_t> data;
473 data.resize(1024 * 1024, 0xef);
474 ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
475 ASSERT_EQ(0, writer.FinishEntry());
476
477 off_t before_len = ftello(file_);
478
479 ZipWriter::FileEntry entry;
480 ASSERT_EQ(0, writer.GetLastEntry(&entry));
481 ASSERT_EQ(0, writer.DiscardLastEntry());
482
483 ASSERT_EQ(0, writer.Finish());
484
485 off_t after_len = ftello(file_);
486
487 ASSERT_GT(before_len, after_len);
488 }
489
AssertFileEntryContentsEq(const std::string & expected,ZipArchiveHandle handle,ZipEntry * zip_entry)490 static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
491 ZipArchiveHandle handle,
492 ZipEntry* zip_entry) {
493 if (expected.size() != zip_entry->uncompressed_length) {
494 return ::testing::AssertionFailure()
495 << "uncompressed entry size " << zip_entry->uncompressed_length
496 << " does not match expected size " << expected.size();
497 }
498
499 std::string actual;
500 actual.resize(expected.size());
501
502 uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
503 if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
504 return ::testing::AssertionFailure() << "failed to extract entry";
505 }
506
507 if (expected != actual) {
508 return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
509 << "' does not match expected '" << expected << "'";
510 }
511 return ::testing::AssertionSuccess();
512 }
513