1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <unistd.h>
6
7 #include <vector>
8
9 #include "gtest/gtest.h"
10
11 #include "puffin/file_stream.h"
12 #include "puffin/memory_stream.h"
13 #include "puffin/src/include/puffin/common.h"
14 #include "puffin/src/include/puffin/utils.h"
15 #include "puffin/src/unittest_common.h"
16
17 using std::string;
18 using std::vector;
19
20 namespace puffin {
21
22 namespace {
23 const uint8_t kZipEntries[] = {
24 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0xfc, 0x88,
25 0x28, 0x4c, 0xcb, 0x86, 0xe1, 0x80, 0x06, 0x00, 0x00, 0x00, 0x09, 0x00,
26 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x31, 0x55, 0x54, 0x09, 0x00, 0x03,
27 0xec, 0x15, 0x54, 0x5a, 0x49, 0x10, 0x54, 0x5a, 0x75, 0x78, 0x0b, 0x00,
28 0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33,
29 0x34, 0x84, 0x00, 0x2e, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02,
30 0x00, 0x08, 0x00, 0x01, 0x89, 0x28, 0x4c, 0xe0, 0xe8, 0x6f, 0x6d, 0x06,
31 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x32,
32 0x55, 0x54, 0x09, 0x00, 0x03, 0xf1, 0x15, 0x54, 0x5a, 0x38, 0x10, 0x54,
33 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04,
34 0x88, 0x13, 0x00, 0x00, 0x33, 0x32, 0x82, 0x01, 0x2e, 0x00};
35
36 // (echo "666666" > 2 && zip -fd test.zip 2 &&
37 // cat test.zip | hexdump -v -e '10/1 "0x%02x, " "\n"')
38 const uint8_t kZipEntryWithDataDescriptor[] = {
39 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b, 0x74,
40 0x2b, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
41 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x32, 0x55, 0x54, 0x09, 0x00, 0x03,
42 0xf5, 0xe5, 0x57, 0x5a, 0xf2, 0xe5, 0x57, 0x5a, 0x75, 0x78, 0x0b, 0x00,
43 0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33,
44 0x33, 0x03, 0x01, 0x2e, 0x00, 0x50, 0x4b, 0x07, 0x08, 0xb4, 0xa0, 0xf2,
45 0x36, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03,
46 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b, 0x74, 0x2b, 0x4c, 0x00,
47 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01,
48 0x00, 0x1c, 0x00, 0x32, 0x55, 0x54, 0x09, 0x00, 0x03, 0xf5, 0xe5, 0x57,
49 0x5a, 0xf2, 0xe5, 0x57, 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x8f,
50 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33, 0x33, 0x03, 0x01,
51 0x2e, 0x00, 0xb4, 0xa0, 0xf2, 0x36, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00,
52 0x00, 0x00};
53
54 // echo "0123456789" > test1.txt && echo "9876543210" > test2.txt &&
55 // gzip -kf test1.txt test2.txt && cat test1.txt.gz test2.txt.gz |
56 // hexdump -v -e '12/1 "0x%02x, " "\n"'
57 const uint8_t kGzipEntryWithMultipleMembers[] = {
58 0x1f, 0x8b, 0x08, 0x08, 0x77, 0xd5, 0x84, 0x5a, 0x00, 0x03, 0x74, 0x65,
59 0x73, 0x74, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x33, 0x30, 0x34, 0x32,
60 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02, 0x00, 0xd1, 0xe5, 0x76,
61 0x40, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x08, 0x77, 0xd5, 0x84,
62 0x5a, 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x32, 0x2e, 0x74, 0x78, 0x74,
63 0x00, 0xb3, 0xb4, 0x30, 0x37, 0x33, 0x35, 0x31, 0x36, 0x32, 0x34, 0xe0,
64 0x02, 0x00, 0x20, 0x9c, 0x5f, 0x89, 0x0b, 0x00, 0x00, 0x00};
65
66 // echo "0123456789" > test1.txt && gzip -kf test1.txt && cat test1.txt.gz |
67 // hexdump -v -e '12/1 "0x%02x, " "\n"'
68 // And manually insert extra field with two byte length (10) followed by:
69 // echo "extrafield" | hexdump -v -e '12/1 "0x%02x, " "\n"'
70 // Then change the forth byte of array to -x0c to enable the extra field.
71 const uint8_t kGzipEntryWithExtraField[] = {
72 0x1f, 0x8b, 0x08, 0x0c, 0xcf, 0x0e, 0x86, 0x5a, 0x00, 0x03,
73 // Extra field begin
74 0x0A, 0x00, 0x65, 0x78, 0x74, 0x72, 0x61, 0x66, 0x69, 0x65, 0x6c, 0x64,
75 // Extra field end
76 0x74, 0x65, 0x73, 0x74, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x33, 0x30,
77 0x34, 0x32, 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02, 0x00, 0xd1,
78 0xe5, 0x76, 0x40, 0x0b, 0x00, 0x00, 0x00};
79
80 // echo "0123456789" | zlib-flate -compress |
81 // hexdump -v -e '12/1 "0x%02x, " "\n"'
82 const uint8_t kZlibEntry[] = {0x78, 0x9c, 0x33, 0x30, 0x34, 0x32, 0x36,
83 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02,
84 0x00, 0x0d, 0x17, 0x02, 0x18};
85
FindDeflatesInZlibBlocks(const Buffer & src,const vector<ByteExtent> & zlibs,const vector<BitExtent> & deflates)86 void FindDeflatesInZlibBlocks(const Buffer& src,
87 const vector<ByteExtent>& zlibs,
88 const vector<BitExtent>& deflates) {
89 string tmp_file;
90 ASSERT_TRUE(MakeTempFile(&tmp_file, nullptr));
91 ScopedPathUnlinker unlinker(tmp_file);
92 auto src_stream = FileStream::Open(tmp_file, false, true);
93 ASSERT_TRUE(src_stream);
94 ASSERT_TRUE(src_stream->Write(src.data(), src.size()));
95 ASSERT_TRUE(src_stream->Close());
96
97 vector<BitExtent> deflates_out;
98 ASSERT_TRUE(LocateDeflatesInZlibBlocks(tmp_file, zlibs, &deflates_out));
99 ASSERT_EQ(deflates, deflates_out);
100 }
101
CheckFindPuffLocation(const Buffer & compressed,const vector<BitExtent> & deflates,const vector<ByteExtent> & expected_puffs,uint64_t expected_puff_size)102 void CheckFindPuffLocation(const Buffer& compressed,
103 const vector<BitExtent>& deflates,
104 const vector<ByteExtent>& expected_puffs,
105 uint64_t expected_puff_size) {
106 auto src = MemoryStream::CreateForRead(compressed);
107 vector<ByteExtent> puffs;
108 uint64_t puff_size;
109 ASSERT_TRUE(FindPuffLocations(src, deflates, &puffs, &puff_size));
110 EXPECT_EQ(puffs, expected_puffs);
111 EXPECT_EQ(puff_size, expected_puff_size);
112 }
113 } // namespace
114
115 // Test Simple Puffing of the source.
TEST(UtilsTest,FindPuffLocations1Test)116 TEST(UtilsTest, FindPuffLocations1Test) {
117 CheckFindPuffLocation(kDeflatesSample1, kSubblockDeflateExtentsSample1,
118 kPuffExtentsSample1, kPuffsSample1.size());
119 }
120
TEST(UtilsTest,FindPuffLocations2Test)121 TEST(UtilsTest, FindPuffLocations2Test) {
122 CheckFindPuffLocation(kDeflatesSample2, kSubblockDeflateExtentsSample2,
123 kPuffExtentsSample2, kPuffsSample2.size());
124 }
125
TEST(UtilsTest,LocateDeflatesInZlib)126 TEST(UtilsTest, LocateDeflatesInZlib) {
127 Buffer zlib_data(kZlibEntry, std::end(kZlibEntry));
128 vector<BitExtent> deflates;
129 vector<BitExtent> expected_deflates = {{16, 98}};
130 EXPECT_TRUE(LocateDeflatesInZlib(zlib_data, &deflates));
131 EXPECT_EQ(deflates, expected_deflates);
132 }
133
TEST(UtilsTest,LocateDeflatesInEmptyZlib)134 TEST(UtilsTest, LocateDeflatesInEmptyZlib) {
135 Buffer empty;
136 vector<ByteExtent> empty_zlibs;
137 vector<BitExtent> empty_deflates;
138 FindDeflatesInZlibBlocks(empty, empty_zlibs, empty_deflates);
139 }
140
TEST(UtilsTest,LocateDeflatesInZlibWithInvalidFields)141 TEST(UtilsTest, LocateDeflatesInZlibWithInvalidFields) {
142 Buffer zlib_data(kZlibEntry, std::end(kZlibEntry));
143 auto cmf = zlib_data[0];
144 auto flag = zlib_data[1];
145
146 vector<BitExtent> deflates;
147 zlib_data[0] = cmf & 0xF0;
148 EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
149 zlib_data[0] = cmf | (8 << 4);
150 EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
151 zlib_data[0] = cmf; // Correct it.
152
153 zlib_data[1] = flag & 0xF0;
154 EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates));
155 }
156
TEST(UtilsTest,LocateDeflatesInZipArchiveSmoke)157 TEST(UtilsTest, LocateDeflatesInZipArchiveSmoke) {
158 Buffer zip_entries(kZipEntries, std::end(kZipEntries));
159 vector<BitExtent> deflates;
160 vector<BitExtent> expected_deflates = {{472, 46}, {992, 46}};
161 EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates));
162 EXPECT_EQ(deflates, expected_deflates);
163 }
164
TEST(UtilsTest,LocateDeflatesInZipArchiveWithDataDescriptor)165 TEST(UtilsTest, LocateDeflatesInZipArchiveWithDataDescriptor) {
166 Buffer zip_entries(kZipEntryWithDataDescriptor,
167 std::end(kZipEntryWithDataDescriptor));
168 vector<BitExtent> deflates;
169 vector<BitExtent> expected_deflates = {{472, 46}, {1120, 46}};
170 EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates));
171 EXPECT_EQ(deflates, expected_deflates);
172 }
173
TEST(UtilsTest,LocateDeflatesInZipArchiveErrorChecks)174 TEST(UtilsTest, LocateDeflatesInZipArchiveErrorChecks) {
175 Buffer zip_entries(kZipEntries, std::end(kZipEntries));
176 // Construct a invalid zip entry whose size overflows.
177 zip_entries[29] = 0xff;
178 vector<BitExtent> deflates_overflow;
179 vector<BitExtent> expected_deflates = {{992, 46}};
180 EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates_overflow));
181 EXPECT_EQ(deflates_overflow, expected_deflates);
182
183 zip_entries.resize(128);
184 vector<BitExtent> deflates_incomplete;
185 EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates_incomplete));
186 EXPECT_TRUE(deflates_incomplete.empty());
187 }
188
TEST(UtilsTest,LocateDeflatesInGzip)189 TEST(UtilsTest, LocateDeflatesInGzip) {
190 Buffer gzip_data(kGzipEntryWithMultipleMembers,
191 std::end(kGzipEntryWithMultipleMembers));
192 vector<BitExtent> deflates;
193 vector<BitExtent> expected_deflates = {{160, 98}, {488, 98}};
194 EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates));
195 EXPECT_EQ(deflates, expected_deflates);
196 }
197
TEST(UtilsTest,LocateDeflatesInGzipFail)198 TEST(UtilsTest, LocateDeflatesInGzipFail) {
199 Buffer gzip_data(kGzipEntryWithMultipleMembers,
200 std::end(kGzipEntryWithMultipleMembers));
201 gzip_data[0] ^= 1;
202 vector<BitExtent> deflates;
203 EXPECT_FALSE(LocateDeflatesInGzip(gzip_data, &deflates));
204 }
205
TEST(UtilsTest,LocateDeflatesInGzipWithPadding)206 TEST(UtilsTest, LocateDeflatesInGzipWithPadding) {
207 Buffer gzip_data(kGzipEntryWithMultipleMembers,
208 std::end(kGzipEntryWithMultipleMembers));
209 gzip_data.resize(gzip_data.size() + 100);
210 vector<BitExtent> deflates;
211 vector<BitExtent> expected_deflates = {{160, 98}, {488, 98}};
212 EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates));
213 EXPECT_EQ(deflates, expected_deflates);
214 }
215
TEST(UtilsTest,LocateDeflatesInGzipWithExtraField)216 TEST(UtilsTest, LocateDeflatesInGzipWithExtraField) {
217 Buffer gzip_data(kGzipEntryWithExtraField,
218 std::end(kGzipEntryWithExtraField));
219 vector<BitExtent> deflates;
220 vector<BitExtent> expected_deflates = {{256, 98}};
221 EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates));
222 EXPECT_EQ(deflates, expected_deflates);
223 }
224
TEST(UtilsTest,RemoveEqualBitExtents)225 TEST(UtilsTest, RemoveEqualBitExtents) {
226 Buffer data1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
227 Buffer data2 = {1, 2, 3, 4, 5, 5, 6, 7, 8, 9};
228 vector<BitExtent> ext1 = {{0, 10}, {10, 14}, {25, 15}, {40, 8}, {50, 23}};
229 vector<BitExtent> ext2 = {{0, 10}, {17, 15}, {32, 8}, {40, 8}, {50, 23}};
230 RemoveEqualBitExtents(data1, data2, &ext1, &ext2);
231 vector<BitExtent> expected_ext1 = {{0, 10}, {10, 14}};
232 EXPECT_EQ(expected_ext1, ext1);
233 vector<BitExtent> expected_ext2 = {{0, 10}};
234 EXPECT_EQ(expected_ext2, ext2);
235 RemoveEqualBitExtents(data1, data2, &ext1, &ext1);
236 EXPECT_EQ(expected_ext1, ext1);
237 RemoveEqualBitExtents(data1, data1, &ext1, &ext1);
238 EXPECT_TRUE(ext1.empty());
239 expected_ext1 = ext1 = {{0, 0}, {1, 1}, {2, 7}};
240 RemoveEqualBitExtents(data1, data2, &ext1, &ext2);
241 EXPECT_EQ(expected_ext1, ext1);
242 EXPECT_EQ(expected_ext2, ext2);
243 }
244
TEST(UtilsTest,RemoveDeflatesWithBadDistanceCaches)245 TEST(UtilsTest, RemoveDeflatesWithBadDistanceCaches) {
246 vector<BitExtent> deflates(kProblematicCacheDeflateExtents), empty;
247 EXPECT_TRUE(
248 RemoveDeflatesWithBadDistanceCaches(kProblematicCache, &deflates));
249 EXPECT_EQ(deflates, empty);
250
251 // Just a sanity check to make sure this function is not removing anything
252 // else.
253 deflates = kSubblockDeflateExtentsSample1;
254 EXPECT_TRUE(RemoveDeflatesWithBadDistanceCaches(kDeflatesSample1, &deflates));
255 EXPECT_EQ(deflates, kSubblockDeflateExtentsSample1);
256
257 // Now combine three deflates and make sure it is doing the right job.
258 Buffer data;
259 data.insert(data.end(), kDeflatesSample1.begin(), kDeflatesSample1.end());
260 data.insert(data.end(), kProblematicCache.begin(), kProblematicCache.end());
261 data.insert(data.end(), kDeflatesSample1.begin(), kDeflatesSample1.end());
262
263 deflates = kSubblockDeflateExtentsSample1;
264 size_t offset = kDeflatesSample1.size() * 8;
265 for (const auto& deflate : kProblematicCacheDeflateExtents) {
266 deflates.emplace_back(deflate.offset + offset, deflate.length);
267 }
268 offset += kProblematicCache.size() * 8;
269 for (const auto& deflate : kSubblockDeflateExtentsSample1) {
270 deflates.emplace_back(deflate.offset + offset, deflate.length);
271 }
272
273 auto expected_deflates(deflates);
274 expected_deflates.erase(expected_deflates.begin() +
275 kSubblockDeflateExtentsSample1.size());
276
277 EXPECT_TRUE(RemoveDeflatesWithBadDistanceCaches(data, &deflates));
278 EXPECT_EQ(deflates, expected_deflates);
279 }
280
281 } // namespace puffin
282