• 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