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,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, "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, "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, "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, "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, "align.txt", &data));
133   EXPECT_EQ(0, data.offset & 0x03);
134 
135   CloseArchive(handle);
136 }
137 
MakeTm()138 static struct tm MakeTm() {
139   struct tm tm;
140   memset(&tm, 0, sizeof(struct tm));
141   tm.tm_year = 2001 - 1900;
142   tm.tm_mon = 1;
143   tm.tm_mday = 12;
144   tm.tm_hour = 18;
145   tm.tm_min = 30;
146   tm.tm_sec = 20;
147   return tm;
148 }
149 
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedFlagAndTime)150 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
151   ZipWriter writer(file_);
152 
153   struct tm tm = MakeTm();
154   time_t time = mktime(&tm);
155   ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
156   ASSERT_EQ(0, writer.WriteBytes("he", 2));
157   ASSERT_EQ(0, writer.FinishEntry());
158   ASSERT_EQ(0, writer.Finish());
159 
160   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
161 
162   ZipArchiveHandle handle;
163   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
164 
165   ZipEntry data;
166   ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
167   EXPECT_EQ(0, data.offset & 0x03);
168 
169   struct tm mod = data.GetModificationTime();
170   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
171   EXPECT_EQ(tm.tm_min, mod.tm_min);
172   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
173   EXPECT_EQ(tm.tm_mday, mod.tm_mday);
174   EXPECT_EQ(tm.tm_mon, mod.tm_mon);
175   EXPECT_EQ(tm.tm_year, mod.tm_year);
176 
177   CloseArchive(handle);
178 }
179 
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedValue)180 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
181   ZipWriter writer(file_);
182 
183   ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
184   ASSERT_EQ(0, writer.WriteBytes("he", 2));
185   ASSERT_EQ(0, writer.FinishEntry());
186   ASSERT_EQ(0, writer.Finish());
187 
188   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
189 
190   ZipArchiveHandle handle;
191   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
192 
193   ZipEntry data;
194   ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
195   EXPECT_EQ(0, data.offset & 0xfff);
196 
197   CloseArchive(handle);
198 }
199 
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedValueAndTime)200 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
201   ZipWriter writer(file_);
202 
203   struct tm tm = MakeTm();
204   time_t time = mktime(&tm);
205   ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
206   ASSERT_EQ(0, writer.WriteBytes("he", 2));
207   ASSERT_EQ(0, writer.FinishEntry());
208   ASSERT_EQ(0, writer.Finish());
209 
210   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
211 
212   ZipArchiveHandle handle;
213   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
214 
215   ZipEntry data;
216   ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
217   EXPECT_EQ(0, data.offset & 0xfff);
218 
219   struct tm mod = data.GetModificationTime();
220   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
221   EXPECT_EQ(tm.tm_min, mod.tm_min);
222   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
223   EXPECT_EQ(tm.tm_mday, mod.tm_mday);
224   EXPECT_EQ(tm.tm_mon, mod.tm_mon);
225   EXPECT_EQ(tm.tm_year, mod.tm_year);
226 
227   CloseArchive(handle);
228 }
229 
TEST_F(zipwriter,WriteCompressedZipWithOneFile)230 TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
231   ZipWriter writer(file_);
232 
233   ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
234   ASSERT_EQ(0, writer.WriteBytes("helo", 4));
235   ASSERT_EQ(0, writer.FinishEntry());
236   ASSERT_EQ(0, writer.Finish());
237 
238   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
239 
240   ZipArchiveHandle handle;
241   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
242 
243   ZipEntry data;
244   ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
245   EXPECT_EQ(kCompressDeflated, data.method);
246   EXPECT_EQ(0u, data.has_data_descriptor);
247   ASSERT_EQ(4u, data.uncompressed_length);
248   ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
249 
250   CloseArchive(handle);
251 }
252 
TEST_F(zipwriter,WriteCompressedZipFlushFull)253 TEST_F(zipwriter, WriteCompressedZipFlushFull) {
254   // This exact data will cause the Finish() to require multiple calls
255   // to deflate() because the ZipWriter buffer isn't big enough to hold
256   // the entire compressed data buffer.
257   constexpr size_t kBufSize = 10000000;
258   std::vector<uint8_t> buffer(kBufSize);
259   size_t prev = 1;
260   for (size_t i = 0; i < kBufSize; i++) {
261     buffer[i] = static_cast<uint8_t>(i + prev);
262     prev = i;
263   }
264 
265   ZipWriter writer(file_);
266   ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
267   ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
268   ASSERT_EQ(0, writer.FinishEntry());
269   ASSERT_EQ(0, writer.Finish());
270 
271   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
272 
273   ZipArchiveHandle handle;
274   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
275 
276   ZipEntry data;
277   ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
278   EXPECT_EQ(kCompressDeflated, data.method);
279   EXPECT_EQ(kBufSize, data.uncompressed_length);
280 
281   std::vector<uint8_t> decompress(kBufSize);
282   memset(decompress.data(), 0, kBufSize);
283   ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
284                                static_cast<uint32_t>(decompress.size())));
285   EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
286       << "Input buffer and output buffer are different.";
287 
288   CloseArchive(handle);
289 }
290 
TEST_F(zipwriter,CheckStartEntryErrors)291 TEST_F(zipwriter, CheckStartEntryErrors) {
292   ZipWriter writer(file_);
293 
294   ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
295   ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
296 }
297 
TEST_F(zipwriter,BackupRemovesTheLastFile)298 TEST_F(zipwriter, BackupRemovesTheLastFile) {
299   ZipWriter writer(file_);
300 
301   const char* kKeepThis = "keep this";
302   const char* kDropThis = "drop this";
303   const char* kReplaceWithThis = "replace with this";
304 
305   ZipWriter::FileEntry entry;
306   EXPECT_LT(writer.GetLastEntry(&entry), 0);
307 
308   ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
309   ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
310   ASSERT_EQ(0, writer.FinishEntry());
311 
312   ASSERT_EQ(0, writer.GetLastEntry(&entry));
313   EXPECT_EQ("keep.txt", entry.path);
314 
315   ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
316   ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
317   ASSERT_EQ(0, writer.FinishEntry());
318 
319   ASSERT_EQ(0, writer.GetLastEntry(&entry));
320   EXPECT_EQ("drop.txt", entry.path);
321 
322   ASSERT_EQ(0, writer.DiscardLastEntry());
323 
324   ASSERT_EQ(0, writer.GetLastEntry(&entry));
325   EXPECT_EQ("keep.txt", entry.path);
326 
327   ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
328   ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
329   ASSERT_EQ(0, writer.FinishEntry());
330 
331   ASSERT_EQ(0, writer.GetLastEntry(&entry));
332   EXPECT_EQ("replace.txt", entry.path);
333 
334   ASSERT_EQ(0, writer.Finish());
335 
336   // Verify that "drop.txt" does not exist.
337 
338   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
339 
340   ZipArchiveHandle handle;
341   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
342 
343   ZipEntry data;
344   ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
345   ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
346 
347   ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
348 
349   ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
350   ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
351 
352   CloseArchive(handle);
353 }
354 
TEST_F(zipwriter,WriteToUnseekableFile)355 TEST_F(zipwriter, WriteToUnseekableFile) {
356   const char* expected = "hello";
357   ZipWriter writer(file_);
358   writer.seekable_ = false;
359 
360   ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
361   ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
362   ASSERT_EQ(0, writer.FinishEntry());
363   ASSERT_EQ(0, writer.Finish());
364   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
365 
366   ZipArchiveHandle handle;
367   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
368   ZipEntry data;
369   ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
370   EXPECT_EQ(kCompressStored, data.method);
371   EXPECT_EQ(1u, data.has_data_descriptor);
372   EXPECT_EQ(strlen(expected), data.compressed_length);
373   ASSERT_EQ(strlen(expected), data.uncompressed_length);
374   ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
375   CloseArchive(handle);
376 }
377 
TEST_F(zipwriter,TruncateFileAfterBackup)378 TEST_F(zipwriter, TruncateFileAfterBackup) {
379   ZipWriter writer(file_);
380 
381   const char* kSmall = "small";
382 
383   ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
384   ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
385   ASSERT_EQ(0, writer.FinishEntry());
386 
387   ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
388   std::vector<uint8_t> data;
389   data.resize(1024 * 1024, 0xef);
390   ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
391   ASSERT_EQ(0, writer.FinishEntry());
392 
393   off_t before_len = ftello(file_);
394 
395   ZipWriter::FileEntry entry;
396   ASSERT_EQ(0, writer.GetLastEntry(&entry));
397   ASSERT_EQ(0, writer.DiscardLastEntry());
398 
399   ASSERT_EQ(0, writer.Finish());
400 
401   off_t after_len = ftello(file_);
402 
403   ASSERT_GT(before_len, after_len);
404 }
405 
AssertFileEntryContentsEq(const std::string & expected,ZipArchiveHandle handle,ZipEntry * zip_entry)406 static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
407                                                             ZipArchiveHandle handle,
408                                                             ZipEntry* zip_entry) {
409   if (expected.size() != zip_entry->uncompressed_length) {
410     return ::testing::AssertionFailure()
411            << "uncompressed entry size " << zip_entry->uncompressed_length
412            << " does not match expected size " << expected.size();
413   }
414 
415   std::string actual;
416   actual.resize(expected.size());
417 
418   uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
419   if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
420     return ::testing::AssertionFailure() << "failed to extract entry";
421   }
422 
423   if (expected != actual) {
424     return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
425                                          << "' does not match expected '" << expected << "'";
426   }
427   return ::testing::AssertionSuccess();
428 }
429