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