• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   memset(&tm, 0, sizeof(struct tm));
190   tm.tm_year = 2001 - 1900;
191   tm.tm_mon = 1;
192   tm.tm_mday = 12;
193   tm.tm_hour = 18;
194   tm.tm_min = 30;
195   tm.tm_sec = 20;
196   return tm;
197 }
198 
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedFlagAndTime)199 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
200   ZipWriter writer(file_);
201 
202   struct tm tm = MakeTm();
203   time_t time = mktime(&tm);
204   ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
205   ASSERT_EQ(0, writer.WriteBytes("he", 2));
206   ASSERT_EQ(0, writer.FinishEntry());
207   ASSERT_EQ(0, writer.Finish());
208 
209   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
210 
211   ZipArchiveHandle handle;
212   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
213 
214   ZipEntry data;
215   ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
216   EXPECT_EQ(0, data.offset & 0x03);
217 
218   struct tm mod = data.GetModificationTime();
219   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
220   EXPECT_EQ(tm.tm_min, mod.tm_min);
221   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
222   EXPECT_EQ(tm.tm_mday, mod.tm_mday);
223   EXPECT_EQ(tm.tm_mon, mod.tm_mon);
224   EXPECT_EQ(tm.tm_year, mod.tm_year);
225 
226   CloseArchive(handle);
227 }
228 
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedValue)229 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
230   ZipWriter writer(file_);
231 
232   ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
233   ASSERT_EQ(0, writer.WriteBytes("he", 2));
234   ASSERT_EQ(0, writer.FinishEntry());
235   ASSERT_EQ(0, writer.Finish());
236 
237   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
238 
239   ZipArchiveHandle handle;
240   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
241 
242   ZipEntry data;
243   ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
244   EXPECT_EQ(0, data.offset & 0xfff);
245 
246   CloseArchive(handle);
247 }
248 
TEST_F(zipwriter,WriteUncompressedZipFileWithAlignedValueAndTime)249 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
250   ZipWriter writer(file_);
251 
252   struct tm tm = MakeTm();
253   time_t time = mktime(&tm);
254   ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
255   ASSERT_EQ(0, writer.WriteBytes("he", 2));
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, "align.txt", &data));
266   EXPECT_EQ(0, data.offset & 0xfff);
267 
268   struct tm mod = data.GetModificationTime();
269   EXPECT_EQ(tm.tm_sec, mod.tm_sec);
270   EXPECT_EQ(tm.tm_min, mod.tm_min);
271   EXPECT_EQ(tm.tm_hour, mod.tm_hour);
272   EXPECT_EQ(tm.tm_mday, mod.tm_mday);
273   EXPECT_EQ(tm.tm_mon, mod.tm_mon);
274   EXPECT_EQ(tm.tm_year, mod.tm_year);
275 
276   CloseArchive(handle);
277 }
278 
TEST_F(zipwriter,WriteCompressedZipWithOneFile)279 TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
280   ZipWriter writer(file_);
281 
282   ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
283   ASSERT_EQ(0, writer.WriteBytes("helo", 4));
284   ASSERT_EQ(0, writer.FinishEntry());
285   ASSERT_EQ(0, writer.Finish());
286 
287   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
288 
289   ZipArchiveHandle handle;
290   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
291 
292   ZipEntry data;
293   ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
294   EXPECT_EQ(kCompressDeflated, data.method);
295   EXPECT_EQ(0u, data.has_data_descriptor);
296   ASSERT_EQ(4u, data.uncompressed_length);
297   ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
298 
299   CloseArchive(handle);
300 }
301 
TEST_F(zipwriter,WriteCompressedZipFlushFull)302 TEST_F(zipwriter, WriteCompressedZipFlushFull) {
303   // This exact data will cause the Finish() to require multiple calls
304   // to deflate() because the ZipWriter buffer isn't big enough to hold
305   // the entire compressed data buffer.
306   constexpr size_t kBufSize = 10000000;
307   std::vector<uint8_t> buffer(kBufSize);
308   size_t prev = 1;
309   for (size_t i = 0; i < kBufSize; i++) {
310     buffer[i] = static_cast<uint8_t>(i + prev);
311     prev = i;
312   }
313 
314   ZipWriter writer(file_);
315   ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
316   ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
317   ASSERT_EQ(0, writer.FinishEntry());
318   ASSERT_EQ(0, writer.Finish());
319 
320   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
321 
322   ZipArchiveHandle handle;
323   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
324 
325   ZipEntry data;
326   ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
327   EXPECT_EQ(kCompressDeflated, data.method);
328   EXPECT_EQ(kBufSize, data.uncompressed_length);
329 
330   std::vector<uint8_t> decompress(kBufSize);
331   memset(decompress.data(), 0, kBufSize);
332   ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
333                                static_cast<uint32_t>(decompress.size())));
334   EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
335       << "Input buffer and output buffer are different.";
336 
337   CloseArchive(handle);
338 }
339 
TEST_F(zipwriter,CheckStartEntryErrors)340 TEST_F(zipwriter, CheckStartEntryErrors) {
341   ZipWriter writer(file_);
342 
343   ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
344   ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
345 }
346 
TEST_F(zipwriter,BackupRemovesTheLastFile)347 TEST_F(zipwriter, BackupRemovesTheLastFile) {
348   ZipWriter writer(file_);
349 
350   const char* kKeepThis = "keep this";
351   const char* kDropThis = "drop this";
352   const char* kReplaceWithThis = "replace with this";
353 
354   ZipWriter::FileEntry entry;
355   EXPECT_LT(writer.GetLastEntry(&entry), 0);
356 
357   ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
358   ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
359   ASSERT_EQ(0, writer.FinishEntry());
360 
361   ASSERT_EQ(0, writer.GetLastEntry(&entry));
362   EXPECT_EQ("keep.txt", entry.path);
363 
364   ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
365   ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
366   ASSERT_EQ(0, writer.FinishEntry());
367 
368   ASSERT_EQ(0, writer.GetLastEntry(&entry));
369   EXPECT_EQ("drop.txt", entry.path);
370 
371   ASSERT_EQ(0, writer.DiscardLastEntry());
372 
373   ASSERT_EQ(0, writer.GetLastEntry(&entry));
374   EXPECT_EQ("keep.txt", entry.path);
375 
376   ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
377   ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
378   ASSERT_EQ(0, writer.FinishEntry());
379 
380   ASSERT_EQ(0, writer.GetLastEntry(&entry));
381   EXPECT_EQ("replace.txt", entry.path);
382 
383   ASSERT_EQ(0, writer.Finish());
384 
385   // Verify that "drop.txt" does not exist.
386 
387   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
388 
389   ZipArchiveHandle handle;
390   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
391 
392   ZipEntry data;
393   ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
394   ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
395 
396   ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
397 
398   ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
399   ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
400 
401   CloseArchive(handle);
402 }
403 
TEST_F(zipwriter,WriteToUnseekableFile)404 TEST_F(zipwriter, WriteToUnseekableFile) {
405   const char* expected = "hello";
406   ZipWriter writer(file_);
407   writer.seekable_ = false;
408 
409   ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
410   ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
411   ASSERT_EQ(0, writer.FinishEntry());
412   ASSERT_EQ(0, writer.Finish());
413   ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
414 
415   ZipArchiveHandle handle;
416   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
417   ZipEntry data;
418   ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
419   EXPECT_EQ(kCompressStored, data.method);
420   EXPECT_EQ(1u, data.has_data_descriptor);
421   EXPECT_EQ(strlen(expected), data.compressed_length);
422   ASSERT_EQ(strlen(expected), data.uncompressed_length);
423   ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
424   CloseArchive(handle);
425 }
426 
TEST_F(zipwriter,TruncateFileAfterBackup)427 TEST_F(zipwriter, TruncateFileAfterBackup) {
428   ZipWriter writer(file_);
429 
430   const char* kSmall = "small";
431 
432   ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
433   ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
434   ASSERT_EQ(0, writer.FinishEntry());
435 
436   ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
437   std::vector<uint8_t> data;
438   data.resize(1024 * 1024, 0xef);
439   ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
440   ASSERT_EQ(0, writer.FinishEntry());
441 
442   off_t before_len = ftello(file_);
443 
444   ZipWriter::FileEntry entry;
445   ASSERT_EQ(0, writer.GetLastEntry(&entry));
446   ASSERT_EQ(0, writer.DiscardLastEntry());
447 
448   ASSERT_EQ(0, writer.Finish());
449 
450   off_t after_len = ftello(file_);
451 
452   ASSERT_GT(before_len, after_len);
453 }
454 
AssertFileEntryContentsEq(const std::string & expected,ZipArchiveHandle handle,ZipEntry * zip_entry)455 static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
456                                                             ZipArchiveHandle handle,
457                                                             ZipEntry* zip_entry) {
458   if (expected.size() != zip_entry->uncompressed_length) {
459     return ::testing::AssertionFailure()
460            << "uncompressed entry size " << zip_entry->uncompressed_length
461            << " does not match expected size " << expected.size();
462   }
463 
464   std::string actual;
465   actual.resize(expected.size());
466 
467   uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
468   if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
469     return ::testing::AssertionFailure() << "failed to extract entry";
470   }
471 
472   if (expected != actual) {
473     return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
474                                          << "' does not match expected '" << expected << "'";
475   }
476   return ::testing::AssertionSuccess();
477 }
478