blob: 9b77e7f6ea5b478664c6afca53f335b87d46e747 [file] [log] [blame]
Narayan Kamathe1e8f712013-11-21 13:05:04 +00001/*
2 * Copyright (C) 2013 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
Narayan Kamath960a0042013-12-13 16:06:19 +000017#include <errno.h>
Dmitriy Ivanov84485cf2014-07-15 19:33:00 -070018#include <fcntl.h>
Narayan Kamath960a0042013-12-13 16:06:19 +000019#include <getopt.h>
Narayan Kamathe1e8f712013-11-21 13:05:04 +000020#include <stdio.h>
Christopher Ferris2b7daba2015-11-10 14:55:12 -080021#include <string.h>
Narayan Kamath960a0042013-12-13 16:06:19 +000022#include <unistd.h>
Christopher Ferris2b7daba2015-11-10 14:55:12 -080023
Tianjie439a46f2020-03-18 17:44:30 -070024#include <map>
Yabin Cui6f71fb72016-02-08 16:26:33 -080025#include <memory>
Tianjie Xu89d79732020-03-15 21:23:24 -070026#include <set>
27#include <string_view>
Narayan Kamath960a0042013-12-13 16:06:19 +000028#include <vector>
29
Elliott Hughes53039d62015-12-04 22:00:26 -080030#include <android-base/file.h>
Andreas Gampe5a5ffb52019-04-05 13:48:02 -070031#include <android-base/logging.h>
Elliott Hughes51cbbaa2018-10-19 16:09:39 -070032#include <android-base/mapped_file.h>
Tianjie439a46f2020-03-18 17:44:30 -070033#include <android-base/memory.h>
Tianjie Xu89d79732020-03-15 21:23:24 -070034#include <android-base/strings.h>
Tianjie Xu484de542016-09-29 15:27:41 -070035#include <android-base/unique_fd.h>
Narayan Kamathe1e8f712013-11-21 13:05:04 +000036#include <gtest/gtest.h>
Christopher Ferris2b7daba2015-11-10 14:55:12 -080037#include <ziparchive/zip_archive.h>
38#include <ziparchive/zip_archive_stream_entry.h>
Narayan Kamathe1e8f712013-11-21 13:05:04 +000039
Tianjie439a46f2020-03-18 17:44:30 -070040#include "zip_archive_common.h"
41#include "zip_archive_private.h"
42
Elliott Hughes2ddb3ae2018-04-25 12:49:19 -070043static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
Narayan Kamathe1e8f712013-11-21 13:05:04 +000044
Narayan Kamath2eafc2f2013-12-10 16:47:14 +000045static const std::string kValidZip = "valid.zip";
Christopher Ferris2b7daba2015-11-10 14:55:12 -080046static const std::string kLargeZip = "large.zip";
47static const std::string kBadCrcZip = "bad_crc.zip";
Narayan Kamath2eafc2f2013-12-10 16:47:14 +000048
Jiyong Park6821cc82017-06-30 17:23:33 +090049static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
50 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
Narayan Kamath2eafc2f2013-12-10 16:47:14 +000051
Jiyong Park6821cc82017-06-30 17:23:33 +090052static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I', 'M', 'K',
53 207, 'H', 132, 210, '\\', '\0'};
Christopher Ferris2b7daba2015-11-10 14:55:12 -080054
Jiyong Park6821cc82017-06-30 17:23:33 +090055static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
Narayan Kamath2eafc2f2013-12-10 16:47:14 +000056
Jiyong Park6821cc82017-06-30 17:23:33 +090057static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
Narayan Kamath2eafc2f2013-12-10 16:47:14 +000058 const std::string abs_path = test_data_dir + "/" + name;
59 return OpenArchive(abs_path.c_str(), handle);
60}
61
Tianjie Xu89d79732020-03-15 21:23:24 -070062class CdEntryMapTest : public ::testing::Test {
63 protected:
64 void SetUp() override {
65 names_ = {
66 "a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt",
67 };
68 separator_ = "separator";
69 header_ = "metadata";
70 joined_names_ = header_ + android::base::Join(names_, separator_);
71 base_ptr_ = reinterpret_cast<uint8_t*>(&joined_names_[0]);
72
73 entry_maps_.emplace_back(CdEntryMapZip32::Create(static_cast<uint16_t>(names_.size())));
74 entry_maps_.emplace_back(CdEntryMapZip64::Create());
75 for (auto& cd_map : entry_maps_) {
76 ASSERT_NE(nullptr, cd_map);
77 size_t offset = header_.size();
78 for (const auto& name : names_) {
79 auto status = cd_map->AddToMap(
80 std::string_view{joined_names_.c_str() + offset, name.size()}, base_ptr_);
81 ASSERT_EQ(0, status);
82 offset += name.size() + separator_.size();
83 }
84 }
85 }
86
87 std::vector<std::string> names_;
88 // A continuous region of memory serves as a mock of the central directory.
89 std::string joined_names_;
90 // We expect some metadata at the beginning of the central directory and between filenames.
91 std::string header_;
92 std::string separator_;
93
94 std::vector<std::unique_ptr<CdEntryMapInterface>> entry_maps_;
95 uint8_t* base_ptr_{nullptr}; // Points to the start of the central directory.
96};
97
98TEST_F(CdEntryMapTest, AddDuplicatedEntry) {
99 for (auto& cd_map : entry_maps_) {
100 std::string_view name = "b.txt";
101 ASSERT_NE(0, cd_map->AddToMap(name, base_ptr_));
102 }
103}
104
105TEST_F(CdEntryMapTest, FindEntry) {
106 for (auto& cd_map : entry_maps_) {
107 uint64_t expected_offset = header_.size();
108 for (const auto& name : names_) {
109 auto [status, offset] = cd_map->GetCdEntryOffset(name, base_ptr_);
110 ASSERT_EQ(status, kSuccess);
111 ASSERT_EQ(offset, expected_offset);
112 expected_offset += name.size() + separator_.size();
113 }
114 }
115}
116
117TEST_F(CdEntryMapTest, Iteration) {
118 std::set<std::string_view> expected(names_.begin(), names_.end());
119 for (auto& cd_map : entry_maps_) {
120 cd_map->ResetIteration();
121 std::set<std::string_view> entry_set;
122 auto ret = cd_map->Next(base_ptr_);
123 while (ret != std::pair<std::string_view, uint64_t>{}) {
124 auto [it, insert_status] = entry_set.insert(ret.first);
125 ASSERT_TRUE(insert_status);
126 ret = cd_map->Next(base_ptr_);
127 }
128 ASSERT_EQ(expected, entry_set);
129 }
130}
131
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000132TEST(ziparchive, Open) {
133 ZipArchiveHandle handle;
134 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
Tianjie Xu597bbea2016-10-10 12:11:30 -0700135 CloseArchive(handle);
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000136
Elliott Hughesb59cc232019-12-16 16:16:16 -0800137 ASSERT_EQ(kInvalidEntryName, OpenArchiveWrapper("bad_filename.zip", &handle));
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000138 CloseArchive(handle);
139}
140
Tianjie Xu3e15f2d2016-09-21 14:58:11 -0700141TEST(ziparchive, OutOfBound) {
142 ZipArchiveHandle handle;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800143 ASSERT_EQ(kInvalidOffset, OpenArchiveWrapper("crash.apk", &handle));
144 CloseArchive(handle);
145}
146
147TEST(ziparchive, EmptyArchive) {
148 ZipArchiveHandle handle;
149 ASSERT_EQ(kEmptyArchive, OpenArchiveWrapper("empty.zip", &handle));
150 CloseArchive(handle);
151}
152
153TEST(ziparchive, ZeroSizeCentralDirectory) {
154 ZipArchiveHandle handle;
155 ASSERT_EQ(kInvalidFile, OpenArchiveWrapper("zero-size-cd.zip", &handle));
Tianjie Xu3e15f2d2016-09-21 14:58:11 -0700156 CloseArchive(handle);
157}
158
Neil Fuller29a43022014-07-25 14:43:04 +0100159TEST(ziparchive, OpenMissing) {
160 ZipArchiveHandle handle;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800161 ASSERT_NE(0, OpenArchiveWrapper("missing.zip", &handle));
Neil Fuller29a43022014-07-25 14:43:04 +0100162
163 // Confirm the file descriptor is not going to be mistaken for a valid one.
164 ASSERT_EQ(-1, GetFileDescriptor(handle));
165}
166
Dmitriy Ivanov84485cf2014-07-15 19:33:00 -0700167TEST(ziparchive, OpenAssumeFdOwnership) {
Yabin Cui6f71fb72016-02-08 16:26:33 -0800168 int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
Dmitriy Ivanov84485cf2014-07-15 19:33:00 -0700169 ASSERT_NE(-1, fd);
170 ZipArchiveHandle handle;
171 ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
172 CloseArchive(handle);
173 ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
174 ASSERT_EQ(EBADF, errno);
175}
176
177TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
Yabin Cui6f71fb72016-02-08 16:26:33 -0800178 int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
Dmitriy Ivanov84485cf2014-07-15 19:33:00 -0700179 ASSERT_NE(-1, fd);
180 ZipArchiveHandle handle;
181 ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
182 CloseArchive(handle);
183 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
184 close(fd);
185}
186
Ryan Mitchell0edeea92020-03-09 09:33:46 -0700187TEST(ziparchive, OpenAssumeFdRangeOwnership) {
188 int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
189 ASSERT_NE(-1, fd);
190 const off64_t length = lseek64(fd, 0, SEEK_END);
191 ASSERT_NE(-1, length);
192 ZipArchiveHandle handle;
193 ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
194 static_cast<size_t>(length), 0));
195 CloseArchive(handle);
196 ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
197 ASSERT_EQ(EBADF, errno);
198}
199
200TEST(ziparchive, OpenDoNotAssumeFdRangeOwnership) {
201 int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
202 ASSERT_NE(-1, fd);
203 const off64_t length = lseek(fd, 0, SEEK_END);
204 ASSERT_NE(-1, length);
205 ZipArchiveHandle handle;
206 ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
207 static_cast<size_t>(length), 0, false));
208 CloseArchive(handle);
209 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
210 close(fd);
211}
212
Elliott Hughes47390fe2019-06-12 12:12:47 -0700213TEST(ziparchive, Iteration_std_string_view) {
214 ZipArchiveHandle handle;
215 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
216
217 void* iteration_cookie;
218 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
219
Tianjie26ee1db2020-04-01 23:08:34 -0700220 ZipEntry64 data;
Elliott Hughes47390fe2019-06-12 12:12:47 -0700221 std::vector<std::string_view> names;
222 std::string_view name;
223 while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
224
225 // Assert that the names are as expected.
226 std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
227 std::sort(names.begin(), names.end());
228 ASSERT_EQ(expected_names, names);
229
230 CloseArchive(handle);
231}
232
Songchun Fan28840662020-03-24 09:15:51 -0700233static void AssertIterationNames(void* iteration_cookie,
234 const std::vector<std::string>& expected_names_sorted) {
Tianjie26ee1db2020-04-01 23:08:34 -0700235 ZipEntry64 data;
Songchun Fan28840662020-03-24 09:15:51 -0700236 std::vector<std::string> names;
Tianjie26ee1db2020-04-01 23:08:34 -0700237 std::string_view name;
Songchun Fan28840662020-03-24 09:15:51 -0700238 for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
239 ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
Tianjie26ee1db2020-04-01 23:08:34 -0700240 names.push_back(std::string(name));
Songchun Fan28840662020-03-24 09:15:51 -0700241 }
242 // End of iteration.
243 ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
244 // Assert that the names are as expected.
245 std::sort(names.begin(), names.end());
246 ASSERT_EQ(expected_names_sorted, names);
247}
248
Elliott Hughes4e78d052019-05-08 10:44:06 -0700249static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
Narayan Kamathced89592017-12-21 12:54:52 +0000250 const std::vector<std::string>& expected_names_sorted) {
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000251 ZipArchiveHandle handle;
252 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
253
254 void* iteration_cookie;
Narayan Kamathced89592017-12-21 12:54:52 +0000255 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
Songchun Fan28840662020-03-24 09:15:51 -0700256 AssertIterationNames(iteration_cookie, expected_names_sorted);
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000257 CloseArchive(handle);
Songchun Fan28840662020-03-24 09:15:51 -0700258}
Narayan Kamathced89592017-12-21 12:54:52 +0000259
Songchun Fan28840662020-03-24 09:15:51 -0700260static void AssertIterationOrderWithMatcher(std::function<bool(std::string_view)> matcher,
261 const std::vector<std::string>& expected_names_sorted) {
262 ZipArchiveHandle handle;
263 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
264
265 void* iteration_cookie;
266 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, matcher));
267 AssertIterationNames(iteration_cookie, expected_names_sorted);
268 CloseArchive(handle);
Narayan Kamathced89592017-12-21 12:54:52 +0000269}
270
271TEST(ziparchive, Iteration) {
272 static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
273 "b/d.txt"};
274
Elliott Hughes4e78d052019-05-08 10:44:06 -0700275 AssertIterationOrder("", "", kExpectedMatchesSorted);
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000276}
277
Yusuke Sato7514ba42015-06-25 14:09:00 -0700278TEST(ziparchive, IterationWithPrefix) {
Narayan Kamathced89592017-12-21 12:54:52 +0000279 static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
Yusuke Sato7514ba42015-06-25 14:09:00 -0700280
Elliott Hughes4e78d052019-05-08 10:44:06 -0700281 AssertIterationOrder("b/", "", kExpectedMatchesSorted);
Yusuke Sato7514ba42015-06-25 14:09:00 -0700282}
283
284TEST(ziparchive, IterationWithSuffix) {
Narayan Kamathced89592017-12-21 12:54:52 +0000285 static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
286 "b/d.txt"};
Yusuke Sato7514ba42015-06-25 14:09:00 -0700287
Elliott Hughes4e78d052019-05-08 10:44:06 -0700288 AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
Yusuke Sato7514ba42015-06-25 14:09:00 -0700289}
290
291TEST(ziparchive, IterationWithPrefixAndSuffix) {
Narayan Kamathced89592017-12-21 12:54:52 +0000292 static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
Yusuke Sato7514ba42015-06-25 14:09:00 -0700293
Elliott Hughes4e78d052019-05-08 10:44:06 -0700294 AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
Yusuke Sato7514ba42015-06-25 14:09:00 -0700295}
296
Songchun Fan28840662020-03-24 09:15:51 -0700297TEST(ziparchive, IterationWithAdditionalMatchesExactly) {
298 static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt"};
299 auto matcher = [](std::string_view name) { return name == "a.txt"; };
300 AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
301}
302
303TEST(ziparchive, IterationWithAdditionalMatchesWithSuffix) {
304 static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
305 "b/d.txt"};
306 auto matcher = [](std::string_view name) {
307 return name == "a.txt" || android::base::EndsWith(name, ".txt");
308 };
309 AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
310}
311
312TEST(ziparchive, IterationWithAdditionalMatchesWithPrefixAndSuffix) {
313 static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b/c.txt", "b/d.txt"};
314 auto matcher = [](std::string_view name) {
315 return name == "a.txt" ||
316 (android::base::EndsWith(name, ".txt") && android::base::StartsWith(name, "b/"));
317 };
318 AssertIterationOrderWithMatcher(matcher, kExpectedMatchesSorted);
319}
320
Yusuke Sato7514ba42015-06-25 14:09:00 -0700321TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
322 ZipArchiveHandle handle;
323 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
324
325 void* iteration_cookie;
Elliott Hughes4e78d052019-05-08 10:44:06 -0700326 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
Yusuke Sato7514ba42015-06-25 14:09:00 -0700327
Tianjie26ee1db2020-04-01 23:08:34 -0700328 ZipEntry64 data;
329 std::string_view name;
Yusuke Sato7514ba42015-06-25 14:09:00 -0700330
331 // End of iteration.
332 ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
333
334 CloseArchive(handle);
335}
336
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000337TEST(ziparchive, FindEntry) {
338 ZipArchiveHandle handle;
339 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
340
Tianjie26ee1db2020-04-01 23:08:34 -0700341 ZipEntry64 data;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800342 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000343
344 // Known facts about a.txt, from zipinfo -v.
345 ASSERT_EQ(63, data.offset);
346 ASSERT_EQ(kCompressDeflated, data.method);
Tianjiee3977ec2020-04-07 00:12:54 -0700347 ASSERT_EQ(17u, data.uncompressed_length);
348 ASSERT_EQ(13u, data.compressed_length);
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000349 ASSERT_EQ(0x950821c5, data.crc32);
beonit4063b242015-07-18 02:08:16 +0900350 ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000351
352 // An entry that doesn't exist. Should be a negative return code.
Elliott Hughesb59cc232019-12-16 16:16:16 -0800353 ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
Elliott Hughes0b4bf2f2019-05-03 22:38:44 -0700354
355 CloseArchive(handle);
356}
357
358TEST(ziparchive, FindEntry_empty) {
359 ZipArchiveHandle handle;
360 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
361
Tianjie26ee1db2020-04-01 23:08:34 -0700362 ZipEntry64 data;
Elliott Hughes0b4bf2f2019-05-03 22:38:44 -0700363 ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
364
365 CloseArchive(handle);
366}
367
368TEST(ziparchive, FindEntry_too_long) {
369 ZipArchiveHandle handle;
370 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
371
372 std::string very_long_name(65536, 'x');
Tianjie26ee1db2020-04-01 23:08:34 -0700373 ZipEntry64 data;
Elliott Hughes0b4bf2f2019-05-03 22:38:44 -0700374 ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000375
376 CloseArchive(handle);
377}
378
Mykola Kondratenko63fa9312014-09-08 12:46:37 +0200379TEST(ziparchive, TestInvalidDeclaredLength) {
380 ZipArchiveHandle handle;
381 ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
382
383 void* iteration_cookie;
Elliott Hughes4e78d052019-05-08 10:44:06 -0700384 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
Mykola Kondratenko63fa9312014-09-08 12:46:37 +0200385
Tianjie26ee1db2020-04-01 23:08:34 -0700386 std::string_view name;
387 ZipEntry64 data;
Mykola Kondratenko63fa9312014-09-08 12:46:37 +0200388
389 ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
390 ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
391
392 CloseArchive(handle);
393}
394
Ryan Mitchell0edeea92020-03-09 09:33:46 -0700395TEST(ziparchive, OpenArchiveFdRange) {
396 TemporaryFile tmp_file;
397 ASSERT_NE(-1, tmp_file.fd);
398
399 const std::string leading_garbage(21, 'x');
400 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
401 leading_garbage.size()));
402
403 std::string valid_content;
404 ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
405 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
406
407 const std::string ending_garbage(42, 'x');
408 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
409 ending_garbage.size()));
410
411 ZipArchiveHandle handle;
412 ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
413 ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
414 valid_content.size(),
415 static_cast<off64_t>(leading_garbage.size())));
416
417 // An entry that's deflated.
Tianjie26ee1db2020-04-01 23:08:34 -0700418 ZipEntry64 data;
Ryan Mitchell0edeea92020-03-09 09:33:46 -0700419 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
Tianjie26ee1db2020-04-01 23:08:34 -0700420 const auto a_size = static_cast<size_t>(data.uncompressed_length);
Ryan Mitchell0edeea92020-03-09 09:33:46 -0700421 ASSERT_EQ(a_size, kATxtContents.size());
422 auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
423 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
424 ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
425
426 // An entry that's stored.
427 ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
Tianjie26ee1db2020-04-01 23:08:34 -0700428 const auto b_size = static_cast<size_t>(data.uncompressed_length);
Ryan Mitchell0edeea92020-03-09 09:33:46 -0700429 ASSERT_EQ(b_size, kBTxtContents.size());
430 buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
431 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
432 ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
433
434 CloseArchive(handle);
435}
436
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000437TEST(ziparchive, ExtractToMemory) {
438 ZipArchiveHandle handle;
439 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
440
441 // An entry that's deflated.
Tianjie26ee1db2020-04-01 23:08:34 -0700442 ZipEntry64 data;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800443 ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
Tianjie26ee1db2020-04-01 23:08:34 -0700444 const auto a_size = static_cast<size_t>(data.uncompressed_length);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800445 ASSERT_EQ(a_size, kATxtContents.size());
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000446 uint8_t* buffer = new uint8_t[a_size];
447 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800448 ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000449 delete[] buffer;
450
451 // An entry that's stored.
Elliott Hughesb59cc232019-12-16 16:16:16 -0800452 ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
Tianjie26ee1db2020-04-01 23:08:34 -0700453 const auto b_size = static_cast<size_t>(data.uncompressed_length);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800454 ASSERT_EQ(b_size, kBTxtContents.size());
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000455 buffer = new uint8_t[b_size];
456 ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800457 ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
Narayan Kamath2eafc2f2013-12-10 16:47:14 +0000458 delete[] buffer;
459
460 CloseArchive(handle);
461}
462
Narayan Kamath1685af42014-06-03 13:59:23 +0100463static const uint32_t kEmptyEntriesZip[] = {
Jiyong Park6821cc82017-06-30 17:23:33 +0900464 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
465 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
466 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
467 0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
468 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
469 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
Narayan Kamath1685af42014-06-03 13:59:23 +0100470
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100471// This is a zip file containing a single entry (ab.txt) that contains
472// 90072 repetitions of the string "ab\n" and has an uncompressed length
473// of 270216 bytes.
474static const uint16_t kAbZip[] = {
Jiyong Park6821cc82017-06-30 17:23:33 +0900475 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
476 0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
477 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
478 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
479 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
480 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
481 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
482 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
483 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
484 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
485 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
486 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
487 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
488 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
489 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
490 0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
491 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
492 0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
493 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100494
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800495static const std::string kAbTxtName("ab.txt");
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100496static const size_t kAbUncompressedSize = 270216;
497
Narayan Kamath1685af42014-06-03 13:59:23 +0100498TEST(ziparchive, EmptyEntries) {
Yabin Cui6f71fb72016-02-08 16:26:33 -0800499 TemporaryFile tmp_file;
500 ASSERT_NE(-1, tmp_file.fd);
501 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
Narayan Kamath1ebe7942014-01-24 12:32:39 +0000502
503 ZipArchiveHandle handle;
Josh Gao5eae29a2018-11-15 18:06:31 -0800504 ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
Narayan Kamath1ebe7942014-01-24 12:32:39 +0000505
Tianjie26ee1db2020-04-01 23:08:34 -0700506 ZipEntry64 entry;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800507 ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
Tianjiee3977ec2020-04-07 00:12:54 -0700508 ASSERT_EQ(0u, entry.uncompressed_length);
509 // Extraction to a 1 byte buffer should succeed.
Narayan Kamath1ebe7942014-01-24 12:32:39 +0000510 uint8_t buffer[1];
511 ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
Tianjiee3977ec2020-04-07 00:12:54 -0700512 // Extraction to an empty buffer should succeed.
513 ASSERT_EQ(0, ExtractToMemory(handle, &entry, nullptr, 0));
Narayan Kamath1ebe7942014-01-24 12:32:39 +0000514
Yabin Cui6f71fb72016-02-08 16:26:33 -0800515 TemporaryFile tmp_output_file;
516 ASSERT_NE(-1, tmp_output_file.fd);
517 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
Narayan Kamath1ebe7942014-01-24 12:32:39 +0000518
519 struct stat stat_buf;
Yabin Cui6f71fb72016-02-08 16:26:33 -0800520 ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
Narayan Kamath1ebe7942014-01-24 12:32:39 +0000521 ASSERT_EQ(0, stat_buf.st_size);
Narayan Kamath1ebe7942014-01-24 12:32:39 +0000522}
523
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100524TEST(ziparchive, EntryLargerThan32K) {
Yabin Cui6f71fb72016-02-08 16:26:33 -0800525 TemporaryFile tmp_file;
526 ASSERT_NE(-1, tmp_file.fd);
527 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
Jiyong Park6821cc82017-06-30 17:23:33 +0900528 sizeof(kAbZip) - 1));
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100529 ZipArchiveHandle handle;
Josh Gao5eae29a2018-11-15 18:06:31 -0800530 ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100531
Tianjie26ee1db2020-04-01 23:08:34 -0700532 ZipEntry64 entry;
Elliott Hughes0b4bf2f2019-05-03 22:38:44 -0700533 ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100534 ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
535
536 // Extract the entry to memory.
537 std::vector<uint8_t> buffer(kAbUncompressedSize);
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700538 ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100539
540 // Extract the entry to a file.
Yabin Cui6f71fb72016-02-08 16:26:33 -0800541 TemporaryFile tmp_output_file;
542 ASSERT_NE(-1, tmp_output_file.fd);
543 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100544
545 // Make sure the extracted file size is as expected.
546 struct stat stat_buf;
Yabin Cui6f71fb72016-02-08 16:26:33 -0800547 ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100548 ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
549
550 // Read the file back to a buffer and make sure the contents are
551 // the same as the memory buffer we extracted directly to.
552 std::vector<uint8_t> file_contents(kAbUncompressedSize);
Elliott Hughesce5bd8e2018-10-26 21:27:38 -0700553 ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
Jiyong Park6821cc82017-06-30 17:23:33 +0900554 ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100555 ASSERT_EQ(file_contents, buffer);
556
557 for (int i = 0; i < 90072; ++i) {
558 const uint8_t* line = &file_contents[0] + (3 * i);
559 ASSERT_EQ('a', line[0]);
560 ASSERT_EQ('b', line[1]);
561 ASSERT_EQ('\n', line[2]);
562 }
Narayan Kamath7e80ad02015-04-17 11:53:14 +0100563}
564
Narayan Kamath1685af42014-06-03 13:59:23 +0100565TEST(ziparchive, TrailerAfterEOCD) {
Yabin Cui6f71fb72016-02-08 16:26:33 -0800566 TemporaryFile tmp_file;
567 ASSERT_NE(-1, tmp_file.fd);
Narayan Kamath1685af42014-06-03 13:59:23 +0100568
569 // Create a file with 8 bytes of random garbage.
Jiyong Park6821cc82017-06-30 17:23:33 +0900570 static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
Yabin Cui6f71fb72016-02-08 16:26:33 -0800571 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
572 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
Narayan Kamath1685af42014-06-03 13:59:23 +0100573
574 ZipArchiveHandle handle;
Josh Gao5eae29a2018-11-15 18:06:31 -0800575 ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
Narayan Kamath1685af42014-06-03 13:59:23 +0100576}
577
Narayan Kamath960a0042013-12-13 16:06:19 +0000578TEST(ziparchive, ExtractToFile) {
Yabin Cui6f71fb72016-02-08 16:26:33 -0800579 TemporaryFile tmp_file;
580 ASSERT_NE(-1, tmp_file.fd);
Jiyong Park6821cc82017-06-30 17:23:33 +0900581 const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
Yabin Cui6f71fb72016-02-08 16:26:33 -0800582 const size_t data_size = sizeof(data);
Narayan Kamath960a0042013-12-13 16:06:19 +0000583
Yabin Cui6f71fb72016-02-08 16:26:33 -0800584 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
Narayan Kamath960a0042013-12-13 16:06:19 +0000585
586 ZipArchiveHandle handle;
587 ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
588
Tianjie26ee1db2020-04-01 23:08:34 -0700589 ZipEntry64 entry;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800590 ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
Yabin Cui6f71fb72016-02-08 16:26:33 -0800591 ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
Narayan Kamath960a0042013-12-13 16:06:19 +0000592
Narayan Kamath960a0042013-12-13 16:06:19 +0000593 // Assert that the first 8 bytes of the file haven't been clobbered.
594 uint8_t read_buffer[data_size];
Elliott Hughesce5bd8e2018-10-26 21:27:38 -0700595 ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
Yabin Cui6f71fb72016-02-08 16:26:33 -0800596 ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
Narayan Kamath960a0042013-12-13 16:06:19 +0000597 ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
598
599 // Assert that the remainder of the file contains the incompressed data.
Tianjie26ee1db2020-04-01 23:08:34 -0700600 std::vector<uint8_t> uncompressed_data(static_cast<size_t>(entry.uncompressed_length));
601 ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
602 static_cast<size_t>(entry.uncompressed_length)));
Jiyong Park6821cc82017-06-30 17:23:33 +0900603 ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
Narayan Kamath960a0042013-12-13 16:06:19 +0000604
605 // Assert that the total length of the file is sane
Yabin Cui6f71fb72016-02-08 16:26:33 -0800606 ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
Elliott Hughesce5bd8e2018-10-26 21:27:38 -0700607 lseek(tmp_file.fd, 0, SEEK_END));
Narayan Kamath960a0042013-12-13 16:06:19 +0000608}
609
Tianjie Xu484de542016-09-29 15:27:41 -0700610#if !defined(_WIN32)
611TEST(ziparchive, OpenFromMemory) {
Elliott Hughesb59cc232019-12-16 16:16:16 -0800612 const std::string zip_path = test_data_dir + "/dummy-update.zip";
Tianjie Xu484de542016-09-29 15:27:41 -0700613 android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
614 ASSERT_NE(-1, fd);
615 struct stat sb;
616 ASSERT_EQ(0, fstat(fd, &sb));
617
618 // Memory map the file first and open the archive from the memory region.
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700619 auto file_map{
620 android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
Tianjie Xu484de542016-09-29 15:27:41 -0700621 ZipArchiveHandle handle;
Elliott Hughes51cbbaa2018-10-19 16:09:39 -0700622 ASSERT_EQ(0,
623 OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
Tianjie Xu484de542016-09-29 15:27:41 -0700624
625 // Assert one entry can be found and extracted correctly.
Tianjie26ee1db2020-04-01 23:08:34 -0700626 ZipEntry64 binary_entry;
Elliott Hughes0b4bf2f2019-05-03 22:38:44 -0700627 ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
Tianjie Xu484de542016-09-29 15:27:41 -0700628 TemporaryFile tmp_binary;
629 ASSERT_NE(-1, tmp_binary.fd);
630 ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
631}
632#endif
633
Jiyong Park6821cc82017-06-30 17:23:33 +0900634static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
635 bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
Elliott Hughes0b4bf2f2019-05-03 22:38:44 -0700636 ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800637 std::unique_ptr<ZipArchiveStreamEntry> stream;
638 if (raw) {
639 stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
640 if (entry->method == kCompressStored) {
Tianjie26ee1db2020-04-01 23:08:34 -0700641 read_data->resize(static_cast<size_t>(entry->uncompressed_length));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800642 } else {
Tianjie26ee1db2020-04-01 23:08:34 -0700643 read_data->resize(static_cast<size_t>(entry->compressed_length));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800644 }
645 } else {
646 stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
Tianjie26ee1db2020-04-01 23:08:34 -0700647 read_data->resize(static_cast<size_t>(entry->uncompressed_length));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800648 }
649 uint8_t* read_data_ptr = read_data->data();
650 ASSERT_TRUE(stream.get() != nullptr);
651 const std::vector<uint8_t>* data;
652 uint64_t total_size = 0;
653 while ((data = stream->Read()) != nullptr) {
654 total_size += data->size();
655 memcpy(read_data_ptr, data->data(), data->size());
656 read_data_ptr += data->size();
657 }
658 ASSERT_EQ(verified, stream->Verify());
659 ASSERT_EQ(total_size, read_data->size());
660}
661
Jiyong Park6821cc82017-06-30 17:23:33 +0900662static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
663 const std::string& entry_name,
664 const std::vector<uint8_t>& contents, bool raw) {
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800665 ZipArchiveHandle handle;
666 ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
667
668 ZipEntry entry;
669 std::vector<uint8_t> read_data;
670 ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
671
672 ASSERT_EQ(contents.size(), read_data.size());
673 ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
674
675 CloseArchive(handle);
676}
677
Jiyong Park6821cc82017-06-30 17:23:33 +0900678static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
679 const std::string& entry_name) {
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800680 ZipArchiveHandle handle;
681 ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
682
683 ZipEntry entry;
684 std::vector<uint8_t> read_data;
685 ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
686
Tianjie26ee1db2020-04-01 23:08:34 -0700687 std::vector<uint8_t> cmp_data(static_cast<size_t>(entry.uncompressed_length));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800688 ASSERT_EQ(entry.uncompressed_length, read_data.size());
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700689 ASSERT_EQ(
690 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800691 ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
692
693 CloseArchive(handle);
694}
695
696TEST(ziparchive, StreamCompressed) {
Elliott Hughesb59cc232019-12-16 16:16:16 -0800697 ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800698}
699
700TEST(ziparchive, StreamUncompressed) {
Elliott Hughesb59cc232019-12-16 16:16:16 -0800701 ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800702}
703
704TEST(ziparchive, StreamRawCompressed) {
Elliott Hughesb59cc232019-12-16 16:16:16 -0800705 ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800706}
707
708TEST(ziparchive, StreamRawUncompressed) {
Elliott Hughesb59cc232019-12-16 16:16:16 -0800709 ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800710}
711
712TEST(ziparchive, StreamLargeCompressed) {
Elliott Hughesb59cc232019-12-16 16:16:16 -0800713 ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800714}
715
716TEST(ziparchive, StreamLargeUncompressed) {
Elliott Hughesb59cc232019-12-16 16:16:16 -0800717 ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800718}
719
720TEST(ziparchive, StreamCompressedBadCrc) {
721 ZipArchiveHandle handle;
722 ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
723
724 ZipEntry entry;
725 std::vector<uint8_t> read_data;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800726 ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800727
728 CloseArchive(handle);
729}
730
731TEST(ziparchive, StreamUncompressedBadCrc) {
732 ZipArchiveHandle handle;
733 ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
734
735 ZipEntry entry;
736 std::vector<uint8_t> read_data;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800737 ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
Christopher Ferris2b7daba2015-11-10 14:55:12 -0800738
739 CloseArchive(handle);
740}
741
Narayan Kamath97754352017-06-05 13:21:12 +0100742// Generated using the following Java program:
743// public static void main(String[] foo) throws Exception {
744// FileOutputStream fos = new
745// FileOutputStream("/tmp/data_descriptor.zip");
746// ZipOutputStream zos = new ZipOutputStream(fos);
Tianjie26ee1db2020-04-01 23:08:34 -0700747// ZipEntry64 ze = new ZipEntry64("name");
748// ze.setMethod(ZipEntry64.DEFLATED);
Narayan Kamath97754352017-06-05 13:21:12 +0100749// zos.putNextEntry(ze);
750// zos.write("abdcdefghijk".getBytes());
751// zos.closeEntry();
752// zos.close();
753// }
754//
755// cat /tmp/data_descriptor.zip | xxd -i
756//
757static const std::vector<uint8_t> kDataDescriptorZipFile{
758 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
759 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
760 0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
761 //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
762 0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
763 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
764 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
765 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
766 0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
767 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
768
769// The offsets of the data descriptor in this file, so we can mess with
770// them later in the test.
771static constexpr uint32_t kDataDescriptorOffset = 48;
772static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
773static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
774
775static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
776 std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
777 TemporaryFile tmp_file;
778 ASSERT_NE(-1, tmp_file.fd);
779 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
780 ZipArchiveHandle handle;
Josh Gao5eae29a2018-11-15 18:06:31 -0800781 ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
Narayan Kamath97754352017-06-05 13:21:12 +0100782
783 // This function expects a variant of kDataDescriptorZipFile, for look for
784 // an entry whose name is "name" and whose size is 12 (contents =
785 // "abdcdefghijk").
Tianjie26ee1db2020-04-01 23:08:34 -0700786 ZipEntry64 entry;
Elliott Hughes0b4bf2f2019-05-03 22:38:44 -0700787 ASSERT_EQ(0, FindEntry(handle, "name", &entry));
Tianjiee3977ec2020-04-07 00:12:54 -0700788 ASSERT_EQ(12u, entry.uncompressed_length);
Narayan Kamath97754352017-06-05 13:21:12 +0100789
790 entry_out->resize(12);
791 (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
792
793 CloseArchive(handle);
794}
795
796TEST(ziparchive, ValidDataDescriptors) {
797 std::vector<uint8_t> entry;
798 int32_t error_code = 0;
799 ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
800
801 ASSERT_EQ(0, error_code);
802 ASSERT_EQ(12u, entry.size());
803 ASSERT_EQ('a', entry[0]);
804 ASSERT_EQ('k', entry[11]);
805}
806
Narayan Kamath98f2d6b2017-12-22 10:42:09 +0000807TEST(ziparchive, InvalidDataDescriptors_csize) {
Narayan Kamath97754352017-06-05 13:21:12 +0100808 std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
809 invalid_csize[kCSizeOffset] = 0xfe;
810
811 std::vector<uint8_t> entry;
812 int32_t error_code = 0;
813 ExtractEntryToMemory(invalid_csize, &entry, &error_code);
814
Narayan Kamath3a5a93b2017-06-15 13:58:25 +0100815 ASSERT_EQ(kInconsistentInformation, error_code);
Narayan Kamath98f2d6b2017-12-22 10:42:09 +0000816}
Narayan Kamath97754352017-06-05 13:21:12 +0100817
Narayan Kamath98f2d6b2017-12-22 10:42:09 +0000818TEST(ziparchive, InvalidDataDescriptors_size) {
Narayan Kamath97754352017-06-05 13:21:12 +0100819 std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
Narayan Kamath98f2d6b2017-12-22 10:42:09 +0000820 invalid_size[kSizeOffset] = 0xfe;
Narayan Kamath97754352017-06-05 13:21:12 +0100821
Narayan Kamath98f2d6b2017-12-22 10:42:09 +0000822 std::vector<uint8_t> entry;
823 int32_t error_code = 0;
824 ExtractEntryToMemory(invalid_size, &entry, &error_code);
Narayan Kamath97754352017-06-05 13:21:12 +0100825
Narayan Kamath3a5a93b2017-06-15 13:58:25 +0100826 ASSERT_EQ(kInconsistentInformation, error_code);
827}
828
829TEST(ziparchive, ErrorCodeString) {
830 ASSERT_STREQ("Success", ErrorCodeString(0));
831
832 // Out of bounds.
833 ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
Elliott Hughesb59cc232019-12-16 16:16:16 -0800834 ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
835 ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
Narayan Kamath3a5a93b2017-06-15 13:58:25 +0100836
837 ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
Narayan Kamath97754352017-06-05 13:21:12 +0100838}
839
Narayan Kamath33a086d2017-08-09 18:32:09 +0100840// A zip file whose local file header at offset zero is corrupted.
841//
842// ---------------
843// cat foo > a.txt
844// zip a.zip a.txt
845// cat a.zip | xxd -i
846//
847// Manual changes :
848// [2] = 0xff // Corrupt the LFH signature of entry 0.
849// [3] = 0xff // Corrupt the LFH signature of entry 0.
850static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
851 //[lfh-sig-----------], [lfh contents---------------------------------
852 0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
853 //--------------------------------------------------------------------
854 0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
855 //-------------------------------] [file-name-----------------], [---
856 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
857 // entry-contents------------------------------------------------------
858 0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
859 //--------------------------------------------------------------------
860 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
861 //-------------------------------------], [cd-record-sig-------], [---
862 0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
863 // cd-record-----------------------------------------------------------
864 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
865 //--------------------------------------------------------------------
866 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
867 //--------------------------------------------------------------------
868 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
869 //-] [lfh-file-header-off-], [file-name-----------------], [extra----
870 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
871 //--------------------------------------------------------------------
872 0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
873 //-------------------------------------------------------], [eocd-sig-
874 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
875 //-------], [---------------------------------------------------------
876 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
877 //-------------------------------------------]
878 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
879
880TEST(ziparchive, BrokenLfhSignature) {
881 TemporaryFile tmp_file;
882 ASSERT_NE(-1, tmp_file.fd);
883 ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
884 kZipFileWithBrokenLfhSignature.size()));
885 ZipArchiveHandle handle;
Elliott Hughesb59cc232019-12-16 16:16:16 -0800886 ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
Narayan Kamath33a086d2017-08-09 18:32:09 +0100887}
888
Narayan Kamath976e32f2017-10-30 11:17:28 +0000889class VectorReader : public zip_archive::Reader {
890 public:
891 VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
892
Tianjie26ee1db2020-04-01 23:08:34 -0700893 bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
Narayan Kamath976e32f2017-10-30 11:17:28 +0000894 if ((offset + len) < input_.size()) {
895 return false;
896 }
897
Tianjie26ee1db2020-04-01 23:08:34 -0700898 memcpy(buf, &input_[static_cast<size_t>(offset)], len);
Narayan Kamath976e32f2017-10-30 11:17:28 +0000899 return true;
900 }
901
902 private:
903 const std::vector<uint8_t>& input_;
904};
905
906class VectorWriter : public zip_archive::Writer {
907 public:
908 VectorWriter() : Writer() {}
909
910 bool Append(uint8_t* buf, size_t size) {
911 output_.insert(output_.end(), buf, buf + size);
912 return true;
913 }
914
915 std::vector<uint8_t>& GetOutput() { return output_; }
916
917 private:
918 std::vector<uint8_t> output_;
919};
920
921class BadReader : public zip_archive::Reader {
922 public:
923 BadReader() : Reader() {}
924
Tianjie26ee1db2020-04-01 23:08:34 -0700925 bool ReadAtOffset(uint8_t*, size_t, off64_t) const { return false; }
Narayan Kamath976e32f2017-10-30 11:17:28 +0000926};
927
928class BadWriter : public zip_archive::Writer {
929 public:
930 BadWriter() : Writer() {}
931
932 bool Append(uint8_t*, size_t) { return false; }
933};
934
935TEST(ziparchive, Inflate) {
Andreas Gampe5a5ffb52019-04-05 13:48:02 -0700936 const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
937 const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
Narayan Kamath976e32f2017-10-30 11:17:28 +0000938
939 const VectorReader reader(kATxtContentsCompressed);
940 {
941 VectorWriter writer;
942 uint64_t crc_out = 0;
943
944 int32_t ret =
945 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
946 ASSERT_EQ(0, ret);
947 ASSERT_EQ(kATxtContents, writer.GetOutput());
948 ASSERT_EQ(0x950821C5u, crc_out);
949 }
950
951 {
952 VectorWriter writer;
953 int32_t ret =
954 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
955 ASSERT_EQ(0, ret);
956 ASSERT_EQ(kATxtContents, writer.GetOutput());
957 }
958
959 {
960 BadWriter writer;
961 int32_t ret =
962 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
963 ASSERT_EQ(kIoError, ret);
964 }
965
966 {
967 BadReader reader;
968 VectorWriter writer;
969 int32_t ret =
970 zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
971 ASSERT_EQ(kIoError, ret);
972 ASSERT_EQ(0u, writer.GetOutput().size());
973 }
974}
Tianjie439a46f2020-03-18 17:44:30 -0700975
976// The class constructs a zipfile with zip64 format, and test the parsing logic.
977class Zip64ParseTest : public ::testing::Test {
978 protected:
979 struct LocalFileEntry {
980 std::vector<uint8_t> local_file_header;
981 std::string file_name;
982 std::vector<uint8_t> extended_field;
983 // Fake data to mimic the compressed bytes in the zipfile.
984 std::vector<uint8_t> compressed_bytes;
Tianjiecc924632020-03-26 12:34:44 -0700985 std::vector<uint8_t> data_descriptor;
Tianjie439a46f2020-03-18 17:44:30 -0700986
987 size_t GetSize() const {
988 return local_file_header.size() + file_name.size() + extended_field.size() +
Tianjiecc924632020-03-26 12:34:44 -0700989 compressed_bytes.size() + data_descriptor.size();
Tianjie439a46f2020-03-18 17:44:30 -0700990 }
991
992 void CopyToOutput(std::vector<uint8_t>* output) const {
993 std::copy(local_file_header.begin(), local_file_header.end(), std::back_inserter(*output));
994 std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
995 std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
996 std::copy(compressed_bytes.begin(), compressed_bytes.end(), std::back_inserter(*output));
Tianjiecc924632020-03-26 12:34:44 -0700997 std::copy(data_descriptor.begin(), data_descriptor.end(), std::back_inserter(*output));
Tianjie439a46f2020-03-18 17:44:30 -0700998 }
999 };
1000
1001 struct CdRecordEntry {
1002 std::vector<uint8_t> central_directory_record;
1003 std::string file_name;
1004 std::vector<uint8_t> extended_field;
1005
1006 size_t GetSize() const {
1007 return central_directory_record.size() + file_name.size() + extended_field.size();
1008 }
1009
1010 void CopyToOutput(std::vector<uint8_t>* output) const {
1011 std::copy(central_directory_record.begin(), central_directory_record.end(),
1012 std::back_inserter(*output));
1013 std::copy(file_name.begin(), file_name.end(), std::back_inserter(*output));
1014 std::copy(extended_field.begin(), extended_field.end(), std::back_inserter(*output));
1015 }
1016 };
1017
1018 static void ConstructLocalFileHeader(const std::string& name, std::vector<uint8_t>* output,
1019 uint32_t uncompressed_size, uint32_t compressed_size) {
1020 LocalFileHeader lfh = {};
1021 lfh.lfh_signature = LocalFileHeader::kSignature;
1022 lfh.compressed_size = compressed_size;
1023 lfh.uncompressed_size = uncompressed_size;
1024 lfh.file_name_length = static_cast<uint16_t>(name.size());
1025 lfh.extra_field_length = 20;
1026 *output = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&lfh),
1027 reinterpret_cast<uint8_t*>(&lfh) + sizeof(LocalFileHeader));
1028 }
1029
1030 // Put one zip64 extended info in the extended field.
1031 static void ConstructExtendedField(const std::vector<uint64_t>& zip64_fields,
1032 std::vector<uint8_t>* output) {
1033 ASSERT_FALSE(zip64_fields.empty());
1034 uint16_t data_size = 8 * static_cast<uint16_t>(zip64_fields.size());
1035 std::vector<uint8_t> extended_field(data_size + 4);
1036 android::base::put_unaligned(extended_field.data(), Zip64ExtendedInfo::kHeaderId);
1037 android::base::put_unaligned(extended_field.data() + 2, data_size);
1038 size_t offset = 4;
1039 for (const auto& field : zip64_fields) {
1040 android::base::put_unaligned(extended_field.data() + offset, field);
1041 offset += 8;
1042 }
1043
1044 *output = std::move(extended_field);
1045 }
1046
1047 static void ConstructCentralDirectoryRecord(const std::string& name, uint32_t uncompressed_size,
1048 uint32_t compressed_size, uint32_t local_offset,
1049 std::vector<uint8_t>* output) {
1050 CentralDirectoryRecord cdr = {};
1051 cdr.record_signature = CentralDirectoryRecord::kSignature;
1052 cdr.compressed_size = uncompressed_size;
1053 cdr.uncompressed_size = compressed_size;
1054 cdr.file_name_length = static_cast<uint16_t>(name.size());
1055 cdr.extra_field_length = local_offset == UINT32_MAX ? 28 : 20;
1056 cdr.local_file_header_offset = local_offset;
1057 *output =
1058 std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&cdr),
1059 reinterpret_cast<uint8_t*>(&cdr) + sizeof(CentralDirectoryRecord));
1060 }
1061
1062 // Add an entry to the zipfile, construct the corresponding local header and cd entry.
1063 void AddEntry(const std::string& name, const std::vector<uint8_t>& content,
1064 bool uncompressed_size_in_extended, bool compressed_size_in_extended,
Tianjiecc924632020-03-26 12:34:44 -07001065 bool local_offset_in_extended, bool include_data_descriptor = false) {
Tianjie439a46f2020-03-18 17:44:30 -07001066 auto uncompressed_size = static_cast<uint32_t>(content.size());
1067 auto compressed_size = static_cast<uint32_t>(content.size());
1068 uint32_t local_file_header_offset = 0;
1069 std::for_each(file_entries_.begin(), file_entries_.end(),
1070 [&local_file_header_offset](const LocalFileEntry& file_entry) {
1071 local_file_header_offset += file_entry.GetSize();
1072 });
1073
1074 std::vector<uint64_t> zip64_fields;
1075 if (uncompressed_size_in_extended) {
1076 zip64_fields.push_back(uncompressed_size);
1077 uncompressed_size = UINT32_MAX;
1078 }
1079 if (compressed_size_in_extended) {
1080 zip64_fields.push_back(compressed_size);
1081 compressed_size = UINT32_MAX;
1082 }
1083 LocalFileEntry local_entry = {
1084 .local_file_header = {},
1085 .file_name = name,
1086 .extended_field = {},
1087 .compressed_bytes = content,
1088 };
1089 ConstructLocalFileHeader(name, &local_entry.local_file_header, uncompressed_size,
1090 compressed_size);
1091 ConstructExtendedField(zip64_fields, &local_entry.extended_field);
Tianjiecc924632020-03-26 12:34:44 -07001092 if (include_data_descriptor) {
1093 size_t descriptor_size = compressed_size_in_extended ? 24 : 16;
1094 local_entry.data_descriptor.resize(descriptor_size);
1095 uint8_t* write_ptr = local_entry.data_descriptor.data();
1096 EmitUnaligned<uint32_t>(&write_ptr, DataDescriptor::kOptSignature);
1097 EmitUnaligned<uint32_t>(&write_ptr, 0 /* crc */);
1098 if (compressed_size_in_extended) {
1099 EmitUnaligned<uint64_t>(&write_ptr, compressed_size_in_extended);
1100 EmitUnaligned<uint64_t>(&write_ptr, uncompressed_size_in_extended);
1101 } else {
1102 EmitUnaligned<uint32_t>(&write_ptr, compressed_size_in_extended);
1103 EmitUnaligned<uint32_t>(&write_ptr, uncompressed_size_in_extended);
1104 }
1105 }
1106
Tianjie439a46f2020-03-18 17:44:30 -07001107 file_entries_.push_back(std::move(local_entry));
1108
1109 if (local_offset_in_extended) {
1110 zip64_fields.push_back(local_file_header_offset);
1111 local_file_header_offset = UINT32_MAX;
1112 }
1113 CdRecordEntry cd_entry = {
1114 .central_directory_record = {},
1115 .file_name = name,
1116 .extended_field = {},
1117 };
1118 ConstructCentralDirectoryRecord(name, uncompressed_size, compressed_size,
1119 local_file_header_offset, &cd_entry.central_directory_record);
1120 ConstructExtendedField(zip64_fields, &cd_entry.extended_field);
1121 cd_entries_.push_back(std::move(cd_entry));
1122 }
1123
1124 void ConstructEocd() {
1125 ASSERT_EQ(file_entries_.size(), cd_entries_.size());
1126 Zip64EocdRecord zip64_eocd = {};
1127 zip64_eocd.record_signature = Zip64EocdRecord::kSignature;
1128 zip64_eocd.num_records = file_entries_.size();
1129 zip64_eocd.cd_size = 0;
1130 std::for_each(
1131 cd_entries_.begin(), cd_entries_.end(),
1132 [&zip64_eocd](const CdRecordEntry& cd_entry) { zip64_eocd.cd_size += cd_entry.GetSize(); });
1133 zip64_eocd.cd_start_offset = 0;
1134 std::for_each(file_entries_.begin(), file_entries_.end(),
1135 [&zip64_eocd](const LocalFileEntry& file_entry) {
1136 zip64_eocd.cd_start_offset += file_entry.GetSize();
1137 });
1138 zip64_eocd_record_ =
1139 std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_eocd),
1140 reinterpret_cast<uint8_t*>(&zip64_eocd) + sizeof(Zip64EocdRecord));
1141
1142 Zip64EocdLocator zip64_locator = {};
1143 zip64_locator.locator_signature = Zip64EocdLocator::kSignature;
1144 zip64_locator.zip64_eocd_offset = zip64_eocd.cd_start_offset + zip64_eocd.cd_size;
1145 zip64_eocd_locator_ =
1146 std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&zip64_locator),
1147 reinterpret_cast<uint8_t*>(&zip64_locator) + sizeof(Zip64EocdLocator));
1148
1149 EocdRecord eocd = {};
1150 eocd.eocd_signature = EocdRecord::kSignature,
1151 eocd.num_records = file_entries_.size() > UINT16_MAX
1152 ? UINT16_MAX
1153 : static_cast<uint16_t>(file_entries_.size());
1154 eocd.cd_size = UINT32_MAX;
1155 eocd.cd_start_offset = UINT32_MAX;
1156 eocd_record_ = std::vector<uint8_t>(reinterpret_cast<uint8_t*>(&eocd),
1157 reinterpret_cast<uint8_t*>(&eocd) + sizeof(EocdRecord));
1158 }
1159
1160 // Concatenate all the local file entries, cd entries, and eocd metadata.
1161 void ConstructZipFile() {
1162 for (const auto& file_entry : file_entries_) {
1163 file_entry.CopyToOutput(&zip_content_);
1164 }
1165 for (const auto& cd_entry : cd_entries_) {
1166 cd_entry.CopyToOutput(&zip_content_);
1167 }
1168 std::copy(zip64_eocd_record_.begin(), zip64_eocd_record_.end(),
1169 std::back_inserter(zip_content_));
1170 std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
1171 std::back_inserter(zip_content_));
1172 std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
1173 }
1174
1175 std::vector<uint8_t> zip_content_;
1176
1177 std::vector<LocalFileEntry> file_entries_;
1178 std::vector<CdRecordEntry> cd_entries_;
1179 std::vector<uint8_t> zip64_eocd_record_;
1180 std::vector<uint8_t> zip64_eocd_locator_;
1181 std::vector<uint8_t> eocd_record_;
1182};
1183
1184TEST_F(Zip64ParseTest, openFile) {
1185 AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
1186 ConstructEocd();
1187 ConstructZipFile();
1188
1189 ZipArchiveHandle handle;
1190 ASSERT_EQ(
1191 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1192 CloseArchive(handle);
1193}
1194
1195TEST_F(Zip64ParseTest, openFilelocalOffsetInExtendedField) {
1196 AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, true);
1197 AddEntry("b.txt", std::vector<uint8_t>(200, 'b'), true, true, true);
1198 ConstructEocd();
1199 ConstructZipFile();
1200
1201 ZipArchiveHandle handle;
1202 ASSERT_EQ(
1203 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1204 CloseArchive(handle);
1205}
1206
1207TEST_F(Zip64ParseTest, openFileCompressedNotInExtendedField) {
1208 AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, false, false);
1209 ConstructEocd();
1210 ConstructZipFile();
1211
1212 ZipArchiveHandle handle;
1213 // Zip64 extended fields must include both uncompressed and compressed size.
1214 ASSERT_NE(
1215 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1216 CloseArchive(handle);
1217}
1218
1219TEST_F(Zip64ParseTest, findEntry) {
1220 AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1221 AddEntry("b.txt", std::vector<uint8_t>(300, 'b'), true, true, false);
1222 ConstructEocd();
1223 ConstructZipFile();
1224
1225 ZipArchiveHandle handle;
1226 ASSERT_EQ(
1227 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
Tianjie26ee1db2020-04-01 23:08:34 -07001228 ZipEntry64 entry;
Tianjie439a46f2020-03-18 17:44:30 -07001229 ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1230 ASSERT_EQ(200, entry.uncompressed_length);
1231 ASSERT_EQ(200, entry.compressed_length);
1232
1233 ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1234 ASSERT_EQ(300, entry.uncompressed_length);
1235 ASSERT_EQ(300, entry.compressed_length);
1236 CloseArchive(handle);
1237}
1238
Kelvin Zhangda618a32020-09-14 14:59:21 -04001239TEST_F(Zip64ParseTest, dataDescriptor) {
1240 AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true, false);
1241 AddEntry("b.txt", std::vector<uint8_t>(300, 'b'), true, true, true, false);
1242 // We want a file with compressed size in extended fields, but
1243 // data descriptor still in 32 bit values.
1244 auto& local_entry = file_entries_.back();
1245 local_entry.data_descriptor.resize(16);
1246 uint8_t* write_ptr = local_entry.data_descriptor.data();
1247 EmitUnaligned<uint32_t>(&write_ptr, DataDescriptor::kOptSignature);
1248 EmitUnaligned<uint32_t>(&write_ptr, 0 /* crc */);
1249 EmitUnaligned<uint32_t>(&write_ptr, 300);
1250 EmitUnaligned<uint32_t>(&write_ptr, 300);
1251
1252 ConstructEocd();
1253 ConstructZipFile();
1254
1255 ZipArchiveHandle handle;
1256 ASSERT_EQ(0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(),
1257 "debug_zip64", &handle));
1258 ZipEntry64 entry;
1259 ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1260 ASSERT_EQ(200, entry.uncompressed_length);
1261 ASSERT_EQ(200, entry.compressed_length);
1262
1263 ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1264 ASSERT_EQ(300, entry.uncompressed_length);
1265 ASSERT_EQ(300, entry.compressed_length);
1266 CloseArchive(handle);
1267}
1268
Tianjie439a46f2020-03-18 17:44:30 -07001269TEST_F(Zip64ParseTest, openFileIncorrectDataSizeInLocalExtendedField) {
1270 AddEntry("a.txt", std::vector<uint8_t>(100, 'a'), true, true, false);
1271 ASSERT_EQ(1, file_entries_.size());
1272 auto& extended_field = file_entries_[0].extended_field;
1273 // data size exceeds the extended field size in local header.
1274 android::base::put_unaligned<uint16_t>(extended_field.data() + 2, 30);
1275 ConstructEocd();
1276 ConstructZipFile();
1277
1278 ZipArchiveHandle handle;
1279 ASSERT_EQ(
1280 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
Tianjie26ee1db2020-04-01 23:08:34 -07001281 ZipEntry64 entry;
Tianjie439a46f2020-03-18 17:44:30 -07001282 ASSERT_NE(0, FindEntry(handle, "a.txt", &entry));
1283
1284 CloseArchive(handle);
1285}
1286
1287TEST_F(Zip64ParseTest, iterates) {
1288 std::set<std::string_view> names{"a.txt", "b.txt", "c.txt", "d.txt", "e.txt"};
1289 for (const auto& name : names) {
1290 AddEntry(std::string(name), std::vector<uint8_t>(100, name[0]), true, true, true);
1291 }
1292 ConstructEocd();
1293 ConstructZipFile();
1294
1295 ZipArchiveHandle handle;
1296 ASSERT_EQ(
1297 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1298
1299 void* iteration_cookie;
1300 ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
1301 std::set<std::string_view> result;
1302 std::string_view name;
Tianjie26ee1db2020-04-01 23:08:34 -07001303 ZipEntry64 entry;
Tianjie439a46f2020-03-18 17:44:30 -07001304 while (Next(iteration_cookie, &entry, &name) == 0) result.emplace(name);
1305 ASSERT_EQ(names, result);
1306
1307 CloseArchive(handle);
1308}
Tianjie0eaec6c2020-03-28 18:28:43 -07001309
1310TEST_F(Zip64ParseTest, zip64EocdWrongLocatorOffset) {
1311 AddEntry("a.txt", std::vector<uint8_t>(1, 'a'), true, true, true);
1312 ConstructEocd();
1313 zip_content_.resize(20, 'a');
1314 std::copy(zip64_eocd_locator_.begin(), zip64_eocd_locator_.end(),
1315 std::back_inserter(zip_content_));
1316 std::copy(eocd_record_.begin(), eocd_record_.end(), std::back_inserter(zip_content_));
1317
1318 ZipArchiveHandle handle;
1319 ASSERT_NE(
1320 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
1321 CloseArchive(handle);
1322}
Tianjiecc924632020-03-26 12:34:44 -07001323
1324TEST_F(Zip64ParseTest, extract) {
1325 std::vector<uint8_t> content(200, 'a');
1326 AddEntry("a.txt", content, true, true, true);
1327 ConstructEocd();
1328 ConstructZipFile();
1329
1330 ZipArchiveHandle handle;
1331 ASSERT_EQ(
1332 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
Tianjie26ee1db2020-04-01 23:08:34 -07001333 ZipEntry64 entry;
Tianjiecc924632020-03-26 12:34:44 -07001334 ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
1335
1336 VectorWriter writer;
1337 ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1338 ASSERT_EQ(content, writer.GetOutput());
1339}
1340
1341TEST_F(Zip64ParseTest, extractWithDataDescriptor) {
1342 std::vector<uint8_t> content(300, 'b');
1343 AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1344 AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
1345 ConstructEocd();
1346 ConstructZipFile();
1347
1348 ZipArchiveHandle handle;
1349 ASSERT_EQ(
1350 0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(), "debug_zip64", &handle));
Tianjie26ee1db2020-04-01 23:08:34 -07001351 ZipEntry64 entry;
Tianjiecc924632020-03-26 12:34:44 -07001352 ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1353
1354 VectorWriter writer;
1355 ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1356 ASSERT_EQ(content, writer.GetOutput());
1357}
Kelvin Zhang77495e52020-09-14 15:24:03 -04001358
1359TEST_F(Zip64ParseTest, extraLFHOffset) {
1360 std::vector<uint8_t> content(300, 'b');
1361 AddEntry("a.txt", std::vector<uint8_t>(200, 'a'), true, true, true);
1362 AddEntry("b.txt", content, true, true, true, true /* data descriptor */);
1363
1364 ASSERT_EQ(cd_entries_.back().extended_field.size(), 4 + 8 * 3)
1365 << "Extended field should contain 2 bytes id, 2 bytes size, and 3 "
1366 "values, each 64 bit";
1367 uint32_t local_file_header_offset = 0;
1368 std::for_each(file_entries_.begin(), file_entries_.end() - 1,
1369 [&local_file_header_offset](const LocalFileEntry& file_entry) {
1370 local_file_header_offset += file_entry.GetSize();
1371 });
1372 auto& cd_entry = cd_entries_.back();
1373 // We want to construct a central directory record with LFH < 0xFFFFFFFF
1374 // but still comes with a 64 bit LFH in extended field.
1375 ConstructCentralDirectoryRecord(
1376 "b.txt", static_cast<uint32_t>(content.size()),
1377 static_cast<uint32_t>(content.size()), local_file_header_offset,
1378 &cd_entry.central_directory_record);
1379 ConstructEocd();
1380 ConstructZipFile();
1381
1382 ZipArchiveHandle handle;
1383 ASSERT_EQ(0, OpenArchiveFromMemory(zip_content_.data(), zip_content_.size(),
1384 "debug_zip64", &handle));
1385 ZipEntry64 entry;
1386 ASSERT_EQ(0, FindEntry(handle, "b.txt", &entry));
1387
1388 VectorWriter writer;
1389 ASSERT_EQ(0, ExtractToWriter(handle, &entry, &writer));
1390 ASSERT_EQ(content, writer.GetOutput());
Elliott Hughes88d1cbf2021-02-08 16:02:20 -08001391}
1392
1393TEST(ziparchive, Bug174945959) {
1394 static const std::vector<uint8_t> zip {
1395 0x50, 0x4b, 0x03, 0x04, 0x50, 0x4b, 0x01, 0x02, 0x01, 0x53, 0x46, 0x5b,
1396 0xa4, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff,
1397 0xff, 0xff, 0xff, 0xff, 0x03, 0x12, 0x00, 0x07, 0x00, 0x00, 0x3b, 0x00,
1398 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0x00,
1399 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0xa4, 0x2e, 0x00, 0x00, 0x00,
1400 0x24, 0x24, 0xb6, 0x3f, 0xff, 0xff, 0x31, 0x51, 0x49, 0xff, 0xff, 0xff,
1401 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1402 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1403 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1404 0x4b, 0x05, 0x50, 0x4b, 0x05, 0x06, 0xc5, 0x1f, 0x4a, 0x04, 0x00, 0x21,
1405 0x01, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00};
1406 ZipArchiveHandle handle;
1407 ASSERT_EQ(0, OpenArchiveFromMemory(&zip[0], zip.size(), "name", &handle));
1408
1409 void* cookie;
1410 ASSERT_EQ(0, StartIteration(handle, &cookie));
1411 ZipEntry ze;
1412 std::string name;
1413 int result;
1414 while ((result = Next(cookie, &ze, &name)) == 0) {
1415 }
1416 EndIteration(cookie);
1417}