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