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