1 /*
2  * Copyright (C) 2016 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 <stdio.h>
18 
19 #include <algorithm>
20 #include <string>
21 #include <tuple>
22 #include <vector>
23 
24 #include <android-base/file.h>
25 #include <android-base/memory.h>
26 #include <android-base/stringprintf.h>
27 #include <android-base/strings.h>
28 #include <applypatch/imgdiff.h>
29 #include <applypatch/imgdiff_image.h>
30 #include <applypatch/imgpatch.h>
31 #include <gtest/gtest.h>
32 #include <ziparchive/zip_writer.h>
33 
34 #include "common/test_constants.h"
35 
36 using android::base::get_unaligned;
37 
38 // Sanity check for the given imgdiff patch header.
verify_patch_header(const std::string & patch,size_t * num_normal,size_t * num_raw,size_t * num_deflate)39 static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw,
40                                 size_t* num_deflate) {
41   const size_t size = patch.size();
42   const char* data = patch.data();
43 
44   ASSERT_GE(size, 12U);
45   ASSERT_EQ("IMGDIFF2", std::string(data, 8));
46 
47   const int num_chunks = get_unaligned<int32_t>(data + 8);
48   ASSERT_GE(num_chunks, 0);
49 
50   size_t normal = 0;
51   size_t raw = 0;
52   size_t deflate = 0;
53 
54   size_t pos = 12;
55   for (int i = 0; i < num_chunks; ++i) {
56     ASSERT_LE(pos + 4, size);
57     int type = get_unaligned<int32_t>(data + pos);
58     pos += 4;
59     if (type == CHUNK_NORMAL) {
60       pos += 24;
61       ASSERT_LE(pos, size);
62       normal++;
63     } else if (type == CHUNK_RAW) {
64       ASSERT_LE(pos + 4, size);
65       ssize_t data_len = get_unaligned<int32_t>(data + pos);
66       ASSERT_GT(data_len, 0);
67       pos += 4 + data_len;
68       ASSERT_LE(pos, size);
69       raw++;
70     } else if (type == CHUNK_DEFLATE) {
71       pos += 60;
72       ASSERT_LE(pos, size);
73       deflate++;
74     } else {
75       FAIL() << "Invalid patch type: " << type;
76     }
77   }
78 
79   if (num_normal != nullptr) *num_normal = normal;
80   if (num_raw != nullptr) *num_raw = raw;
81   if (num_deflate != nullptr) *num_deflate = deflate;
82 }
83 
GenerateTarget(const std::string & src,const std::string & patch,std::string * patched)84 static void GenerateTarget(const std::string& src, const std::string& patch, std::string* patched) {
85   patched->clear();
86   ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
87                                reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
88                                [&](const unsigned char* data, size_t len) {
89                                  patched->append(reinterpret_cast<const char*>(data), len);
90                                  return len;
91                                }));
92 }
93 
verify_patched_image(const std::string & src,const std::string & patch,const std::string & tgt)94 static void verify_patched_image(const std::string& src, const std::string& patch,
95                                  const std::string& tgt) {
96   std::string patched;
97   GenerateTarget(src, patch, &patched);
98   ASSERT_EQ(tgt, patched);
99 }
100 
TEST(ImgdiffTest,invalid_args)101 TEST(ImgdiffTest, invalid_args) {
102   // Insufficient inputs.
103   ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" }));
104   ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-z" }));
105   ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-b" }));
106   ASSERT_EQ(2, imgdiff(3, (const char* []){ "imgdiff", "-z", "-b" }));
107 
108   // Failed to read bonus file.
109   ASSERT_EQ(1, imgdiff(3, (const char* []){ "imgdiff", "-b", "doesntexist" }));
110 
111   // Failed to read input files.
112   ASSERT_EQ(1, imgdiff(4, (const char* []){ "imgdiff", "doesntexist", "doesntexist", "output" }));
113   ASSERT_EQ(
114       1, imgdiff(5, (const char* []){ "imgdiff", "-z", "doesntexist", "doesntexist", "output" }));
115 }
116 
TEST(ImgdiffTest,image_mode_smoke)117 TEST(ImgdiffTest, image_mode_smoke) {
118   // Random bytes.
119   const std::string src("abcdefg");
120   TemporaryFile src_file;
121   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
122 
123   const std::string tgt("abcdefgxyz");
124   TemporaryFile tgt_file;
125   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
126 
127   TemporaryFile patch_file;
128   std::vector<const char*> args = {
129     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
130   };
131   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
132 
133   // Verify.
134   std::string patch;
135   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
136 
137   // Expect one CHUNK_RAW entry.
138   size_t num_normal;
139   size_t num_raw;
140   size_t num_deflate;
141   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
142   ASSERT_EQ(0U, num_normal);
143   ASSERT_EQ(0U, num_deflate);
144   ASSERT_EQ(1U, num_raw);
145 
146   verify_patched_image(src, patch, tgt);
147 }
148 
TEST(ImgdiffTest,zip_mode_smoke_store)149 TEST(ImgdiffTest, zip_mode_smoke_store) {
150   // Construct src and tgt zip files.
151   TemporaryFile src_file;
152   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
153   ZipWriter src_writer(src_file_ptr);
154   ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0));  // Store mode.
155   const std::string src_content("abcdefg");
156   ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
157   ASSERT_EQ(0, src_writer.FinishEntry());
158   ASSERT_EQ(0, src_writer.Finish());
159   ASSERT_EQ(0, fclose(src_file_ptr));
160 
161   TemporaryFile tgt_file;
162   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
163   ZipWriter tgt_writer(tgt_file_ptr);
164   ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0));  // Store mode.
165   const std::string tgt_content("abcdefgxyz");
166   ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
167   ASSERT_EQ(0, tgt_writer.FinishEntry());
168   ASSERT_EQ(0, tgt_writer.Finish());
169   ASSERT_EQ(0, fclose(tgt_file_ptr));
170 
171   // Compute patch.
172   TemporaryFile patch_file;
173   std::vector<const char*> args = {
174     "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
175   };
176   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
177 
178   // Verify.
179   std::string tgt;
180   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
181   std::string src;
182   ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
183   std::string patch;
184   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
185 
186   // Expect one CHUNK_RAW entry.
187   size_t num_normal;
188   size_t num_raw;
189   size_t num_deflate;
190   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
191   ASSERT_EQ(0U, num_normal);
192   ASSERT_EQ(0U, num_deflate);
193   ASSERT_EQ(1U, num_raw);
194 
195   verify_patched_image(src, patch, tgt);
196 }
197 
TEST(ImgdiffTest,zip_mode_smoke_compressed)198 TEST(ImgdiffTest, zip_mode_smoke_compressed) {
199   // Generate 1 block of random data.
200   std::string random_data;
201   random_data.reserve(4096);
202   generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; });
203 
204   // Construct src and tgt zip files.
205   TemporaryFile src_file;
206   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
207   ZipWriter src_writer(src_file_ptr);
208   ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
209   const std::string src_content = random_data;
210   ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
211   ASSERT_EQ(0, src_writer.FinishEntry());
212   ASSERT_EQ(0, src_writer.Finish());
213   ASSERT_EQ(0, fclose(src_file_ptr));
214 
215   TemporaryFile tgt_file;
216   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
217   ZipWriter tgt_writer(tgt_file_ptr);
218   ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
219   const std::string tgt_content = random_data + "extra contents";
220   ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
221   ASSERT_EQ(0, tgt_writer.FinishEntry());
222   ASSERT_EQ(0, tgt_writer.Finish());
223   ASSERT_EQ(0, fclose(tgt_file_ptr));
224 
225   // Compute patch.
226   TemporaryFile patch_file;
227   std::vector<const char*> args = {
228     "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
229   };
230   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
231 
232   // Verify.
233   std::string tgt;
234   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
235   std::string src;
236   ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
237   std::string patch;
238   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
239 
240   // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
241   size_t num_normal;
242   size_t num_raw;
243   size_t num_deflate;
244   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
245   ASSERT_EQ(0U, num_normal);
246   ASSERT_EQ(1U, num_deflate);
247   ASSERT_EQ(2U, num_raw);
248 
249   verify_patched_image(src, patch, tgt);
250 }
251 
TEST(ImgdiffTest,zip_mode_empty_target)252 TEST(ImgdiffTest, zip_mode_empty_target) {
253   TemporaryFile src_file;
254   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
255   ZipWriter src_writer(src_file_ptr);
256   ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
257   const std::string src_content = "abcdefg";
258   ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
259   ASSERT_EQ(0, src_writer.FinishEntry());
260   ASSERT_EQ(0, src_writer.Finish());
261   ASSERT_EQ(0, fclose(src_file_ptr));
262 
263   // Construct a empty entry in the target zip.
264   TemporaryFile tgt_file;
265   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
266   ZipWriter tgt_writer(tgt_file_ptr);
267   ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
268   const std::string tgt_content;
269   ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
270   ASSERT_EQ(0, tgt_writer.FinishEntry());
271   ASSERT_EQ(0, tgt_writer.Finish());
272 
273   // Compute patch.
274   TemporaryFile patch_file;
275   std::vector<const char*> args = {
276     "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
277   };
278   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
279 
280   // Verify.
281   std::string tgt;
282   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
283   std::string src;
284   ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
285   std::string patch;
286   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
287 
288   verify_patched_image(src, patch, tgt);
289 }
290 
TEST(ImgdiffTest,zip_mode_smoke_trailer_zeros)291 TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) {
292   // Generate 1 block of random data.
293   std::string random_data;
294   random_data.reserve(4096);
295   generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; });
296 
297   // Construct src and tgt zip files.
298   TemporaryFile src_file;
299   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
300   ZipWriter src_writer(src_file_ptr);
301   ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
302   const std::string src_content = random_data;
303   ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
304   ASSERT_EQ(0, src_writer.FinishEntry());
305   ASSERT_EQ(0, src_writer.Finish());
306   ASSERT_EQ(0, fclose(src_file_ptr));
307 
308   TemporaryFile tgt_file;
309   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
310   ZipWriter tgt_writer(tgt_file_ptr);
311   ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
312   const std::string tgt_content = random_data + "abcdefg";
313   ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
314   ASSERT_EQ(0, tgt_writer.FinishEntry());
315   ASSERT_EQ(0, tgt_writer.Finish());
316   // Add trailing zeros to the target zip file.
317   std::vector<uint8_t> zeros(10);
318   ASSERT_EQ(zeros.size(), fwrite(zeros.data(), sizeof(uint8_t), zeros.size(), tgt_file_ptr));
319   ASSERT_EQ(0, fclose(tgt_file_ptr));
320 
321   // Compute patch.
322   TemporaryFile patch_file;
323   std::vector<const char*> args = {
324     "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
325   };
326   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
327 
328   // Verify.
329   std::string tgt;
330   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
331   std::string src;
332   ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
333   std::string patch;
334   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
335 
336   // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
337   size_t num_normal;
338   size_t num_raw;
339   size_t num_deflate;
340   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
341   ASSERT_EQ(0U, num_normal);
342   ASSERT_EQ(1U, num_deflate);
343   ASSERT_EQ(2U, num_raw);
344 
345   verify_patched_image(src, patch, tgt);
346 }
347 
TEST(ImgdiffTest,image_mode_simple)348 TEST(ImgdiffTest, image_mode_simple) {
349   std::string gzipped_source_path = from_testdata_base("gzipped_source");
350   std::string gzipped_source;
351   ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source));
352 
353   const std::string src = "abcdefg" + gzipped_source;
354   TemporaryFile src_file;
355   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
356 
357   std::string gzipped_target_path = from_testdata_base("gzipped_target");
358   std::string gzipped_target;
359   ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target));
360   const std::string tgt = "abcdefgxyz" + gzipped_target;
361 
362   TemporaryFile tgt_file;
363   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
364 
365   TemporaryFile patch_file;
366   std::vector<const char*> args = {
367     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
368   };
369   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
370 
371   // Verify.
372   std::string patch;
373   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
374 
375   // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
376   size_t num_normal;
377   size_t num_raw;
378   size_t num_deflate;
379   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
380   ASSERT_EQ(0U, num_normal);
381   ASSERT_EQ(1U, num_deflate);
382   ASSERT_EQ(2U, num_raw);
383 
384   verify_patched_image(src, patch, tgt);
385 }
386 
TEST(ImgdiffTest,image_mode_bad_gzip)387 TEST(ImgdiffTest, image_mode_bad_gzip) {
388   // Modify the uncompressed length in the gzip footer.
389   const std::vector<char> src_data = { 'a',    'b',    'c',    'd',    'e',    'f',    'g',
390                                        'h',    '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
391                                        '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
392                                        '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
393                                        '\xff', '\xff', '\xff' };
394   const std::string src(src_data.cbegin(), src_data.cend());
395   TemporaryFile src_file;
396   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
397 
398   // Modify the uncompressed length in the gzip footer.
399   const std::vector<char> tgt_data = {
400     'a',    'b',    'c',    'd',    'e',    'f',    'g',    'x',    'y',    'z',    '\x1f', '\x8b',
401     '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
402     '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\xff', '\xff', '\xff'
403   };
404   const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
405   TemporaryFile tgt_file;
406   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
407 
408   TemporaryFile patch_file;
409   std::vector<const char*> args = {
410     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
411   };
412   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
413 
414   // Verify.
415   std::string patch;
416   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
417   verify_patched_image(src, patch, tgt);
418 }
419 
TEST(ImgdiffTest,image_mode_different_num_chunks)420 TEST(ImgdiffTest, image_mode_different_num_chunks) {
421   // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test".
422   const std::vector<char> src_data = {
423     'a',    'b',    'c',    'd',    'e',    'f',    'g',    'h',    '\x1f', '\x8b', '\x08',
424     '\x00', '\xc4', '\x1e', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', '\x02',
425     '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', '\x00', '\x00', '\x00', '\x1f', '\x8b',
426     '\x08', '\x00', '\xb2', '\x3a', '\x53', '\x58', '\x00', '\x03', '\x2b', '\x49', '\x2d',
427     '\x2e', '\x01', '\x00', '\x0c', '\x7e', '\x7f', '\xd8', '\x04', '\x00', '\x00', '\x00'
428   };
429   const std::string src(src_data.cbegin(), src_data.cend());
430   TemporaryFile src_file;
431   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
432 
433   // tgt: "abcdefgxyz" + gzipped "xxyyzz".
434   const std::vector<char> tgt_data = {
435     'a',    'b',    'c',    'd',    'e',    'f',    'g',    'x',    'y',    'z',    '\x1f', '\x8b',
436     '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
437     '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
438   };
439   const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
440   TemporaryFile tgt_file;
441   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
442 
443   TemporaryFile patch_file;
444   std::vector<const char*> args = {
445     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
446   };
447   ASSERT_EQ(1, imgdiff(args.size(), args.data()));
448 }
449 
TEST(ImgdiffTest,image_mode_merge_chunks)450 TEST(ImgdiffTest, image_mode_merge_chunks) {
451   // src: "abcdefg" + gzipped_source.
452   std::string gzipped_source_path = from_testdata_base("gzipped_source");
453   std::string gzipped_source;
454   ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source));
455 
456   const std::string src = "abcdefg" + gzipped_source;
457   TemporaryFile src_file;
458   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
459 
460   // tgt: gzipped_target + "abcdefgxyz".
461   std::string gzipped_target_path = from_testdata_base("gzipped_target");
462   std::string gzipped_target;
463   ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target));
464 
465   const std::string tgt = gzipped_target + "abcdefgxyz";
466   TemporaryFile tgt_file;
467   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
468 
469   // Since a gzipped entry will become CHUNK_RAW (header) + CHUNK_DEFLATE (data) +
470   // CHUNK_RAW (footer), they both should contain the same chunk types after merging.
471 
472   TemporaryFile patch_file;
473   std::vector<const char*> args = {
474     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
475   };
476   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
477 
478   // Verify.
479   std::string patch;
480   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
481 
482   // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
483   size_t num_normal;
484   size_t num_raw;
485   size_t num_deflate;
486   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
487   ASSERT_EQ(0U, num_normal);
488   ASSERT_EQ(1U, num_deflate);
489   ASSERT_EQ(2U, num_raw);
490 
491   verify_patched_image(src, patch, tgt);
492 }
493 
TEST(ImgdiffTest,image_mode_spurious_magic)494 TEST(ImgdiffTest, image_mode_spurious_magic) {
495   // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
496   const std::vector<char> src_data = { 'a',    'b',    'c',    'd',    'e',    'f',    'g',
497                                        'h',    '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
498                                        '\x53', '\x58', 't',    'e',    's',    't' };
499   const std::string src(src_data.cbegin(), src_data.cend());
500   TemporaryFile src_file;
501   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
502 
503   // tgt: "abcdefgxyz".
504   const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
505   const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
506   TemporaryFile tgt_file;
507   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
508 
509   TemporaryFile patch_file;
510   std::vector<const char*> args = {
511     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
512   };
513   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
514 
515   // Verify.
516   std::string patch;
517   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
518 
519   // Expect one CHUNK_RAW (header) entry.
520   size_t num_normal;
521   size_t num_raw;
522   size_t num_deflate;
523   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
524   ASSERT_EQ(0U, num_normal);
525   ASSERT_EQ(0U, num_deflate);
526   ASSERT_EQ(1U, num_raw);
527 
528   verify_patched_image(src, patch, tgt);
529 }
530 
TEST(ImgdiffTest,image_mode_short_input1)531 TEST(ImgdiffTest, image_mode_short_input1) {
532   // src: "abcdefgh" + '0x1f8b0b'.
533   const std::vector<char> src_data = { 'a', 'b', 'c',    'd',    'e',   'f',
534                                        'g', 'h', '\x1f', '\x8b', '\x08' };
535   const std::string src(src_data.cbegin(), src_data.cend());
536   TemporaryFile src_file;
537   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
538 
539   // tgt: "abcdefgxyz".
540   const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
541   const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
542   TemporaryFile tgt_file;
543   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
544 
545   TemporaryFile patch_file;
546   std::vector<const char*> args = {
547     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
548   };
549   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
550 
551   // Verify.
552   std::string patch;
553   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
554 
555   // Expect one CHUNK_RAW (header) entry.
556   size_t num_normal;
557   size_t num_raw;
558   size_t num_deflate;
559   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
560   ASSERT_EQ(0U, num_normal);
561   ASSERT_EQ(0U, num_deflate);
562   ASSERT_EQ(1U, num_raw);
563 
564   verify_patched_image(src, patch, tgt);
565 }
566 
TEST(ImgdiffTest,image_mode_short_input2)567 TEST(ImgdiffTest, image_mode_short_input2) {
568   // src: "abcdefgh" + '0x1f8b0b00'.
569   const std::vector<char> src_data = { 'a', 'b', 'c',    'd',    'e',    'f',
570                                        'g', 'h', '\x1f', '\x8b', '\x08', '\x00' };
571   const std::string src(src_data.cbegin(), src_data.cend());
572   TemporaryFile src_file;
573   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
574 
575   // tgt: "abcdefgxyz".
576   const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
577   const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
578   TemporaryFile tgt_file;
579   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
580 
581   TemporaryFile patch_file;
582   std::vector<const char*> args = {
583     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
584   };
585   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
586 
587   // Verify.
588   std::string patch;
589   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
590 
591   // Expect one CHUNK_RAW (header) entry.
592   size_t num_normal;
593   size_t num_raw;
594   size_t num_deflate;
595   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
596   ASSERT_EQ(0U, num_normal);
597   ASSERT_EQ(0U, num_deflate);
598   ASSERT_EQ(1U, num_raw);
599 
600   verify_patched_image(src, patch, tgt);
601 }
602 
TEST(ImgdiffTest,image_mode_single_entry_long)603 TEST(ImgdiffTest, image_mode_single_entry_long) {
604   // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
605   const std::vector<char> src_data = { 'a',    'b',    'c',    'd',    'e',    'f',    'g',
606                                        'h',    '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
607                                        '\x53', '\x58', 't',    'e',    's',    't' };
608   const std::string src(src_data.cbegin(), src_data.cend());
609   TemporaryFile src_file;
610   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
611 
612   // tgt: "abcdefgxyz" + 200 bytes.
613   std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
614   tgt_data.resize(tgt_data.size() + 200);
615 
616   const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
617   TemporaryFile tgt_file;
618   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
619 
620   TemporaryFile patch_file;
621   std::vector<const char*> args = {
622     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
623   };
624   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
625 
626   // Verify.
627   std::string patch;
628   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
629 
630   // Expect one CHUNK_NORMAL entry, since it's exceeding the 160-byte limit for RAW.
631   size_t num_normal;
632   size_t num_raw;
633   size_t num_deflate;
634   verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
635   ASSERT_EQ(1U, num_normal);
636   ASSERT_EQ(0U, num_deflate);
637   ASSERT_EQ(0U, num_raw);
638 
639   verify_patched_image(src, patch, tgt);
640 }
641 
TEST(ImgpatchTest,image_mode_patch_corruption)642 TEST(ImgpatchTest, image_mode_patch_corruption) {
643   // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
644   const std::vector<char> src_data = { 'a',    'b',    'c',    'd',    'e',    'f',    'g',
645                                        'h',    '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
646                                        '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
647                                        '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
648                                        '\x00', '\x00', '\x00' };
649   const std::string src(src_data.cbegin(), src_data.cend());
650   TemporaryFile src_file;
651   ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
652 
653   // tgt: "abcdefgxyz" + gzipped "xxyyzz".
654   const std::vector<char> tgt_data = {
655     'a',    'b',    'c',    'd',    'e',    'f',    'g',    'x',    'y',    'z',    '\x1f', '\x8b',
656     '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
657     '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
658   };
659   const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
660   TemporaryFile tgt_file;
661   ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
662 
663   TemporaryFile patch_file;
664   std::vector<const char*> args = {
665     "imgdiff", src_file.path, tgt_file.path, patch_file.path,
666   };
667   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
668 
669   // Verify.
670   std::string patch;
671   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
672   verify_patched_image(src, patch, tgt);
673 
674   // Corrupt the end of the patch and expect the ApplyImagePatch to fail.
675   patch.insert(patch.end() - 10, 10, '0');
676   ASSERT_EQ(-1, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
677                                 reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
678                                 [](const unsigned char* /*data*/, size_t len) { return len; }));
679 }
680 
construct_store_entry(const std::vector<std::tuple<std::string,size_t,char>> & info,ZipWriter * writer)681 static void construct_store_entry(const std::vector<std::tuple<std::string, size_t, char>>& info,
682                                   ZipWriter* writer) {
683   for (auto& t : info) {
684     // Create t(1) blocks of t(2), and write the data to t(0)
685     ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), 0));
686     const std::string content(std::get<1>(t) * 4096, std::get<2>(t));
687     ASSERT_EQ(0, writer->WriteBytes(content.data(), content.size()));
688     ASSERT_EQ(0, writer->FinishEntry());
689   }
690 }
691 
construct_deflate_entry(const std::vector<std::tuple<std::string,size_t,size_t>> & info,ZipWriter * writer,const std::string & data)692 static void construct_deflate_entry(const std::vector<std::tuple<std::string, size_t, size_t>>& info,
693                                     ZipWriter* writer, const std::string& data) {
694   for (auto& t : info) {
695     // t(0): entry_name; t(1): block offset; t(2) length in blocks.
696     ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), ZipWriter::kCompress));
697     ASSERT_EQ(0, writer->WriteBytes(data.data() + std::get<1>(t) * 4096, std::get<2>(t) * 4096));
698     ASSERT_EQ(0, writer->FinishEntry());
699   }
700 }
701 
702 // Look for the source and patch pieces in debug_dir. Generate a target piece from each pair.
703 // Concatenate all the target pieces and match against the orignal one. Used pieces in debug_dir
704 // will be cleaned up.
GenerateAndCheckSplitTarget(const std::string & debug_dir,size_t count,const std::string & tgt)705 static void GenerateAndCheckSplitTarget(const std::string& debug_dir, size_t count,
706                                         const std::string& tgt) {
707   std::string patched;
708   for (size_t i = 0; i < count; i++) {
709     std::string split_src_path = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i);
710     std::string split_src;
711     ASSERT_TRUE(android::base::ReadFileToString(split_src_path, &split_src));
712     ASSERT_EQ(0, unlink(split_src_path.c_str()));
713 
714     std::string split_patch_path =
715         android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i);
716     std::string split_patch;
717     ASSERT_TRUE(android::base::ReadFileToString(split_patch_path, &split_patch));
718     ASSERT_EQ(0, unlink(split_patch_path.c_str()));
719 
720     std::string split_tgt;
721     GenerateTarget(split_src, split_patch, &split_tgt);
722     patched += split_tgt;
723   }
724 
725   // Verify we can get back the original target image.
726   ASSERT_EQ(tgt, patched);
727 }
728 
ConstructImageChunks(const std::vector<uint8_t> & content,const std::vector<std::tuple<std::string,size_t>> & info)729 std::vector<ImageChunk> ConstructImageChunks(
730     const std::vector<uint8_t>& content, const std::vector<std::tuple<std::string, size_t>>& info) {
731   std::vector<ImageChunk> chunks;
732   size_t start = 0;
733   for (const auto& t : info) {
734     size_t length = std::get<1>(t);
735     chunks.emplace_back(CHUNK_NORMAL, start, &content, length, std::get<0>(t));
736     start += length;
737   }
738 
739   return chunks;
740 }
741 
TEST(ImgdiffTest,zip_mode_split_image_smoke)742 TEST(ImgdiffTest, zip_mode_split_image_smoke) {
743   std::vector<uint8_t> content;
744   content.reserve(4096 * 50);
745   uint8_t n = 0;
746   generate_n(back_inserter(content), 4096 * 50, [&n]() { return n++ / 4096; });
747 
748   ZipModeImage tgt_image(false, 4096 * 10);
749   std::vector<ImageChunk> tgt_chunks = ConstructImageChunks(content, { { "a", 100 },
750                                                                        { "b", 4096 * 2 },
751                                                                        { "c", 4096 * 3 },
752                                                                        { "d", 300 },
753                                                                        { "e-0", 4096 * 10 },
754                                                                        { "e-1", 4096 * 5 },
755                                                                        { "CD", 200 } });
756   tgt_image.Initialize(std::move(tgt_chunks),
757                        std::vector<uint8_t>(content.begin(), content.begin() + 82520));
758 
759   tgt_image.DumpChunks();
760 
761   ZipModeImage src_image(true, 4096 * 10);
762   std::vector<ImageChunk> src_chunks = ConstructImageChunks(content, { { "b", 4096 * 3 },
763                                                                        { "c-0", 4096 * 10 },
764                                                                        { "c-1", 4096 * 2 },
765                                                                        { "a", 4096 * 5 },
766                                                                        { "e-0", 4096 * 10 },
767                                                                        { "e-1", 10000 },
768                                                                        { "CD", 5000 } });
769   src_image.Initialize(std::move(src_chunks),
770                        std::vector<uint8_t>(content.begin(), content.begin() + 137880));
771 
772   std::vector<ZipModeImage> split_tgt_images;
773   std::vector<ZipModeImage> split_src_images;
774   std::vector<SortedRangeSet> split_src_ranges;
775 
776   ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
777                                            &split_src_images, &split_src_ranges);
778 
779   // src_piece 1: a 5 blocks, b 3 blocks
780   // src_piece 2: c-0 10 blocks
781   // src_piece 3: d 0 block, e-0 10 blocks
782   // src_piece 4: e-1 2 blocks; CD 2 blocks
783   ASSERT_EQ(split_tgt_images.size(), split_src_images.size());
784   ASSERT_EQ(static_cast<size_t>(4), split_tgt_images.size());
785 
786   ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[0].NumOfChunks());
787   ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[0][0].DataLengthForPatch());
788   ASSERT_EQ("4,0,3,15,20", split_src_ranges[0].ToString());
789 
790   ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[1].NumOfChunks());
791   ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[1][0].DataLengthForPatch());
792   ASSERT_EQ("2,3,13", split_src_ranges[1].ToString());
793 
794   ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[2].NumOfChunks());
795   ASSERT_EQ(static_cast<size_t>(40960), split_tgt_images[2][0].DataLengthForPatch());
796   ASSERT_EQ("2,20,30", split_src_ranges[2].ToString());
797 
798   ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[3].NumOfChunks());
799   ASSERT_EQ(static_cast<size_t>(16984), split_tgt_images[3][0].DataLengthForPatch());
800   ASSERT_EQ("2,30,34", split_src_ranges[3].ToString());
801 }
802 
TEST(ImgdiffTest,zip_mode_store_large_apk)803 TEST(ImgdiffTest, zip_mode_store_large_apk) {
804   // Construct src and tgt zip files with limit = 10 blocks.
805   //     src              tgt
806   //  12 blocks 'd'    3 blocks  'a'
807   //  8 blocks  'c'    3 blocks  'b'
808   //  3 blocks  'b'    8 blocks  'c' (exceeds limit)
809   //  3 blocks  'a'    12 blocks 'd' (exceeds limit)
810   //                   3 blocks  'e'
811   TemporaryFile tgt_file;
812   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
813   ZipWriter tgt_writer(tgt_file_ptr);
814   construct_store_entry(
815       { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } },
816       &tgt_writer);
817   ASSERT_EQ(0, tgt_writer.Finish());
818   ASSERT_EQ(0, fclose(tgt_file_ptr));
819 
820   TemporaryFile src_file;
821   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
822   ZipWriter src_writer(src_file_ptr);
823   construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } },
824                         &src_writer);
825   ASSERT_EQ(0, src_writer.Finish());
826   ASSERT_EQ(0, fclose(src_file_ptr));
827 
828   // Compute patch.
829   TemporaryFile patch_file;
830   TemporaryFile split_info_file;
831   TemporaryDir debug_dir;
832   std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
833   std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
834   std::vector<const char*> args = {
835     "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
836     src_file.path, tgt_file.path, patch_file.path,
837   };
838   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
839 
840   std::string tgt;
841   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
842 
843   // Expect 4 pieces of patch. (Roughly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e')
844   GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt);
845 }
846 
TEST(ImgdiffTest,zip_mode_deflate_large_apk)847 TEST(ImgdiffTest, zip_mode_deflate_large_apk) {
848   // Src and tgt zip files are constructed as follows.
849   //     src               tgt
850   //  22 blocks, "d"    4  blocks,  "a"
851   //  5 blocks,  "b"    4  blocks,  "b"
852   //  3 blocks,  "a"    8  blocks,  "c" (exceeds limit)
853   //  1 block,   "g"    20 blocks,  "d" (exceeds limit)
854   //  8 blocks,  "c"    2  blocks,  "e"
855   //  1 block,   "f"    1  block ,  "f"
856   std::string tgt_path = from_testdata_base("deflate_tgt.zip");
857   std::string src_path = from_testdata_base("deflate_src.zip");
858 
859   ZipModeImage src_image(true, 10 * 4096);
860   ZipModeImage tgt_image(false, 10 * 4096);
861   ASSERT_TRUE(src_image.Initialize(src_path));
862   ASSERT_TRUE(tgt_image.Initialize(tgt_path));
863   ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image));
864 
865   src_image.DumpChunks();
866   tgt_image.DumpChunks();
867 
868   std::vector<ZipModeImage> split_tgt_images;
869   std::vector<ZipModeImage> split_src_images;
870   std::vector<SortedRangeSet> split_src_ranges;
871   ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
872                                            &split_src_images, &split_src_ranges);
873 
874   // Expected split images with limit = 10 blocks.
875   // src_piece 0: a 3 blocks, b 5 blocks
876   // src_piece 1: c 8 blocks
877   // src_piece 2: d-0 10 block
878   // src_piece 3: d-1 10 blocks
879   // src_piece 4: e 1 block, CD
880   ASSERT_EQ(split_tgt_images.size(), split_src_images.size());
881   ASSERT_EQ(static_cast<size_t>(5), split_tgt_images.size());
882 
883   ASSERT_EQ(static_cast<size_t>(2), split_src_images[0].NumOfChunks());
884   ASSERT_EQ("a", split_src_images[0][0].GetEntryName());
885   ASSERT_EQ("b", split_src_images[0][1].GetEntryName());
886 
887   ASSERT_EQ(static_cast<size_t>(1), split_src_images[1].NumOfChunks());
888   ASSERT_EQ("c", split_src_images[1][0].GetEntryName());
889 
890   ASSERT_EQ(static_cast<size_t>(0), split_src_images[2].NumOfChunks());
891   ASSERT_EQ(static_cast<size_t>(0), split_src_images[3].NumOfChunks());
892   ASSERT_EQ(static_cast<size_t>(0), split_src_images[4].NumOfChunks());
893 
894   // Compute patch.
895   TemporaryFile patch_file;
896   TemporaryFile split_info_file;
897   TemporaryDir debug_dir;
898   ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges,
899                                             patch_file.path, split_info_file.path, debug_dir.path));
900 
901   // Verify the content of split info.
902   // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"]
903   std::string split_info_string;
904   android::base::ReadFileToString(split_info_file.path, &split_info_string);
905   std::vector<std::string> info_list =
906       android::base::Split(android::base::Trim(split_info_string), "\n");
907 
908   ASSERT_EQ(static_cast<size_t>(7), info_list.size());
909   ASSERT_EQ("2", android::base::Trim(info_list[0]));
910   ASSERT_EQ("5", android::base::Trim(info_list[1]));
911 
912   std::string tgt;
913   ASSERT_TRUE(android::base::ReadFileToString(tgt_path, &tgt));
914   ASSERT_EQ(static_cast<size_t>(160385), tgt.size());
915   std::vector<std::string> tgt_file_ranges = {
916     "36864 2,22,31", "32768 2,31,40", "40960 2,0,11", "40960 2,11,21", "8833 4,21,22,40,41",
917   };
918 
919   for (size_t i = 0; i < 5; i++) {
920     struct stat st;
921     std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i);
922     ASSERT_EQ(0, stat(path.c_str(), &st));
923     ASSERT_EQ(std::to_string(st.st_size) + " " + tgt_file_ranges[i],
924               android::base::Trim(info_list[i + 2]));
925   }
926 
927   GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt);
928 }
929 
TEST(ImgdiffTest,zip_mode_no_match_source)930 TEST(ImgdiffTest, zip_mode_no_match_source) {
931   // Generate 20 blocks of random data.
932   std::string random_data;
933   random_data.reserve(4096 * 20);
934   generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; });
935 
936   TemporaryFile tgt_file;
937   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
938   ZipWriter tgt_writer(tgt_file_ptr);
939 
940   construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer,
941                           random_data);
942 
943   ASSERT_EQ(0, tgt_writer.Finish());
944   ASSERT_EQ(0, fclose(tgt_file_ptr));
945 
946   // We don't have a matching source entry.
947   TemporaryFile src_file;
948   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
949   ZipWriter src_writer(src_file_ptr);
950   construct_store_entry({ { "d", 1, 'd' } }, &src_writer);
951   ASSERT_EQ(0, src_writer.Finish());
952   ASSERT_EQ(0, fclose(src_file_ptr));
953 
954   // Compute patch.
955   TemporaryFile patch_file;
956   TemporaryFile split_info_file;
957   TemporaryDir debug_dir;
958   std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
959   std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
960   std::vector<const char*> args = {
961     "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), split_info_arg.c_str(),
962     src_file.path, tgt_file.path, patch_file.path,
963   };
964   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
965 
966   std::string tgt;
967   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
968 
969   // Expect 1 pieces of patch due to no matching source entry.
970   GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt);
971 }
972 
TEST(ImgdiffTest,zip_mode_large_enough_limit)973 TEST(ImgdiffTest, zip_mode_large_enough_limit) {
974   // Generate 20 blocks of random data.
975   std::string random_data;
976   random_data.reserve(4096 * 20);
977   generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; });
978 
979   TemporaryFile tgt_file;
980   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
981   ZipWriter tgt_writer(tgt_file_ptr);
982 
983   construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data);
984 
985   ASSERT_EQ(0, tgt_writer.Finish());
986   ASSERT_EQ(0, fclose(tgt_file_ptr));
987 
988   // Construct 10 blocks of source.
989   TemporaryFile src_file;
990   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
991   ZipWriter src_writer(src_file_ptr);
992   construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data);
993   ASSERT_EQ(0, src_writer.Finish());
994   ASSERT_EQ(0, fclose(src_file_ptr));
995 
996   // Compute patch with a limit of 20 blocks.
997   TemporaryFile patch_file;
998   TemporaryFile split_info_file;
999   TemporaryDir debug_dir;
1000   std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
1001   std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
1002   std::vector<const char*> args = {
1003     "imgdiff", "-z", "--block-limit=20", split_info_arg.c_str(), debug_dir_arg.c_str(),
1004     src_file.path, tgt_file.path, patch_file.path,
1005   };
1006   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1007 
1008   std::string tgt;
1009   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1010 
1011   // Expect 1 piece of patch since limit is larger than the zip file size.
1012   GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt);
1013 }
1014 
TEST(ImgdiffTest,zip_mode_large_apk_small_target_chunk)1015 TEST(ImgdiffTest, zip_mode_large_apk_small_target_chunk) {
1016   TemporaryFile tgt_file;
1017   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
1018   ZipWriter tgt_writer(tgt_file_ptr);
1019 
1020   // The first entry is less than 4096 bytes, followed immediately by an entry that has a very
1021   // large counterpart in the source file. Therefore the first entry will be patched separately.
1022   std::string small_chunk("a", 2000);
1023   ASSERT_EQ(0, tgt_writer.StartEntry("a", 0));
1024   ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size()));
1025   ASSERT_EQ(0, tgt_writer.FinishEntry());
1026   construct_store_entry(
1027       {
1028           { "b", 12, 'b' }, { "c", 3, 'c' },
1029       },
1030       &tgt_writer);
1031   ASSERT_EQ(0, tgt_writer.Finish());
1032   ASSERT_EQ(0, fclose(tgt_file_ptr));
1033 
1034   TemporaryFile src_file;
1035   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
1036   ZipWriter src_writer(src_file_ptr);
1037   construct_store_entry({ { "a", 1, 'a' }, { "b", 13, 'b' }, { "c", 1, 'c' } }, &src_writer);
1038   ASSERT_EQ(0, src_writer.Finish());
1039   ASSERT_EQ(0, fclose(src_file_ptr));
1040 
1041   // Compute patch.
1042   TemporaryFile patch_file;
1043   TemporaryFile split_info_file;
1044   TemporaryDir debug_dir;
1045   std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
1046   std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
1047   std::vector<const char*> args = {
1048     "imgdiff",     "-z",          "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
1049     src_file.path, tgt_file.path, patch_file.path,
1050   };
1051   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1052 
1053   std::string tgt;
1054   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1055 
1056   // Expect three split src images:
1057   // src_piece 0: a 1 blocks
1058   // src_piece 1: b-0 10 blocks
1059   // src_piece 2: b-1 3 blocks, c 1 blocks, CD
1060   GenerateAndCheckSplitTarget(debug_dir.path, 3, tgt);
1061 }
1062 
TEST(ImgdiffTest,zip_mode_large_apk_skipped_small_target_chunk)1063 TEST(ImgdiffTest, zip_mode_large_apk_skipped_small_target_chunk) {
1064   TemporaryFile tgt_file;
1065   FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb");
1066   ZipWriter tgt_writer(tgt_file_ptr);
1067 
1068   construct_store_entry(
1069       {
1070           { "a", 11, 'a' },
1071       },
1072       &tgt_writer);
1073 
1074   // Construct a tiny target entry of 1 byte, which will be skipped due to the tail alignment of
1075   // the previous entry.
1076   std::string small_chunk("b", 1);
1077   ASSERT_EQ(0, tgt_writer.StartEntry("b", 0));
1078   ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size()));
1079   ASSERT_EQ(0, tgt_writer.FinishEntry());
1080 
1081   ASSERT_EQ(0, tgt_writer.Finish());
1082   ASSERT_EQ(0, fclose(tgt_file_ptr));
1083 
1084   TemporaryFile src_file;
1085   FILE* src_file_ptr = fdopen(src_file.release(), "wb");
1086   ZipWriter src_writer(src_file_ptr);
1087   construct_store_entry(
1088       {
1089           { "a", 11, 'a' }, { "b", 11, 'b' },
1090       },
1091       &src_writer);
1092   ASSERT_EQ(0, src_writer.Finish());
1093   ASSERT_EQ(0, fclose(src_file_ptr));
1094 
1095   // Compute patch.
1096   TemporaryFile patch_file;
1097   TemporaryFile split_info_file;
1098   TemporaryDir debug_dir;
1099   std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path);
1100   std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path);
1101   std::vector<const char*> args = {
1102     "imgdiff",     "-z",          "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(),
1103     src_file.path, tgt_file.path, patch_file.path,
1104   };
1105   ASSERT_EQ(0, imgdiff(args.size(), args.data()));
1106 
1107   std::string tgt;
1108   ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
1109 
1110   // Expect two split src images:
1111   // src_piece 0: a-0 10 blocks
1112   // src_piece 1: a-0 1 block, CD
1113   GenerateAndCheckSplitTarget(debug_dir.path, 2, tgt);
1114 }
1115