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