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