blob: 8802652606afc6cabf288692ca711821d87e8e5c [file] [log] [blame]
Doug Zongker512536a2010-02-17 16:11:44 -08001/*
2 * Copyright (C) 2009 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/*
18 * This program constructs binary patches for images -- such as boot.img
19 * and recovery.img -- that consist primarily of large chunks of gzipped
20 * data interspersed with uncompressed data. Doing a naive bsdiff of
21 * these files is not useful because small changes in the data lead to
22 * large changes in the compressed bitstream; bsdiff patches of gzipped
23 * data are typically as large as the data itself.
24 *
25 * To patch these usefully, we break the source and target images up into
26 * chunks of two types: "normal" and "gzip". Normal chunks are simply
27 * patched using a plain bsdiff. Gzip chunks are first expanded, then a
28 * bsdiff is applied to the uncompressed data, then the patched data is
29 * gzipped using the same encoder parameters. Patched chunks are
30 * concatenated together to create the output file; the output image
31 * should be *exactly* the same series of bytes as the target image used
32 * originally to generate the patch.
33 *
34 * To work well with this tool, the gzipped sections of the target
35 * image must have been generated using the same deflate encoder that
36 * is available in applypatch, namely, the one in the zlib library.
37 * In practice this means that images should be compressed using the
38 * "minigzip" tool included in the zlib distribution, not the GNU gzip
39 * program.
40 *
41 * An "imgdiff" patch consists of a header describing the chunk structure
42 * of the file and any encoding parameters needed for the gzipped
43 * chunks, followed by N bsdiff patches, one per chunk.
44 *
45 * For a diff to be generated, the source and target images must have the
46 * same "chunk" structure: that is, the same number of gzipped and normal
47 * chunks in the same order. Android boot and recovery images currently
48 * consist of five chunks: a small normal header, a gzipped kernel, a
49 * small normal section, a gzipped ramdisk, and finally a small normal
50 * footer.
51 *
52 * Caveats: we locate gzipped sections within the source and target
53 * images by searching for the byte sequence 1f8b0800: 1f8b is the gzip
54 * magic number; 08 specifies the "deflate" encoding [the only encoding
55 * supported by the gzip standard]; and 00 is the flags byte. We do not
56 * currently support any extra header fields (which would be indicated by
57 * a nonzero flags byte). We also don't handle the case when that byte
58 * sequence appears spuriously in the file. (Note that it would have to
59 * occur spuriously within a normal chunk to be a problem.)
60 *
61 *
62 * The imgdiff patch header looks like this:
63 *
64 * "IMGDIFF1" (8) [magic number and version]
65 * chunk count (4)
66 * for each chunk:
67 * chunk type (4) [CHUNK_{NORMAL, GZIP, DEFLATE, RAW}]
68 * if chunk type == CHUNK_NORMAL:
69 * source start (8)
70 * source len (8)
71 * bsdiff patch offset (8) [from start of patch file]
72 * if chunk type == CHUNK_GZIP: (version 1 only)
73 * source start (8)
74 * source len (8)
75 * bsdiff patch offset (8) [from start of patch file]
76 * source expanded len (8) [size of uncompressed source]
77 * target expected len (8) [size of uncompressed target]
78 * gzip level (4)
79 * method (4)
80 * windowBits (4)
81 * memLevel (4)
82 * strategy (4)
83 * gzip header len (4)
84 * gzip header (gzip header len)
85 * gzip footer (8)
86 * if chunk type == CHUNK_DEFLATE: (version 2 only)
87 * source start (8)
88 * source len (8)
89 * bsdiff patch offset (8) [from start of patch file]
90 * source expanded len (8) [size of uncompressed source]
91 * target expected len (8) [size of uncompressed target]
92 * gzip level (4)
93 * method (4)
94 * windowBits (4)
95 * memLevel (4)
96 * strategy (4)
97 * if chunk type == RAW: (version 2 only)
98 * target len (4)
99 * data (target len)
100 *
101 * All integers are little-endian. "source start" and "source len"
102 * specify the section of the input image that comprises this chunk,
103 * including the gzip header and footer for gzip chunks. "source
104 * expanded len" is the size of the uncompressed source data. "target
105 * expected len" is the size of the uncompressed data after applying
106 * the bsdiff patch. The next five parameters specify the zlib
107 * parameters to be used when compressing the patched data, and the
108 * next three specify the header and footer to be wrapped around the
109 * compressed data to create the output chunk (so that header contents
110 * like the timestamp are recreated exactly).
111 *
112 * After the header there are 'chunk count' bsdiff patches; the offset
113 * of each from the beginning of the file is specified in the header.
Doug Zongkera3ccba62012-08-20 15:28:02 -0700114 *
115 * This tool can take an optional file of "bonus data". This is an
116 * extra file of data that is appended to chunk #1 after it is
117 * compressed (it must be a CHUNK_DEFLATE chunk). The same file must
118 * be available (and passed to applypatch with -b) when applying the
119 * patch. This is used to reduce the size of recovery-from-boot
120 * patches by combining the boot image with recovery ramdisk
121 * information that is stored on the system partition.
Doug Zongker512536a2010-02-17 16:11:44 -0800122 */
123
Tao Bao97555da2016-12-15 10:15:06 -0800124#include "applypatch/imgdiff.h"
125
Doug Zongker512536a2010-02-17 16:11:44 -0800126#include <errno.h>
Tao Baod37ce8f2016-12-17 17:10:04 -0800127#include <fcntl.h>
Doug Zongker512536a2010-02-17 16:11:44 -0800128#include <stdio.h>
129#include <stdlib.h>
130#include <string.h>
131#include <sys/stat.h>
Doug Zongker512536a2010-02-17 16:11:44 -0800132#include <sys/types.h>
Tao Bao97555da2016-12-15 10:15:06 -0800133#include <unistd.h>
Doug Zongker512536a2010-02-17 16:11:44 -0800134
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800135#include <algorithm>
136#include <string>
137#include <vector>
138
Tao Baod37ce8f2016-12-17 17:10:04 -0800139#include <android-base/file.h>
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800140#include <android-base/logging.h>
141#include <android-base/memory.h>
Tao Baod37ce8f2016-12-17 17:10:04 -0800142#include <android-base/unique_fd.h>
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800143#include <ziparchive/zip_archive.h>
Tao Baod37ce8f2016-12-17 17:10:04 -0800144
Sen Jiang2fffcb12016-05-03 15:49:10 -0700145#include <bsdiff.h>
Tao Bao97555da2016-12-15 10:15:06 -0800146#include <zlib.h>
Sen Jiang2fffcb12016-05-03 15:49:10 -0700147
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800148using android::base::get_unaligned;
Doug Zongker512536a2010-02-17 16:11:44 -0800149
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800150static constexpr auto BUFFER_SIZE = 0x8000;
Doug Zongker512536a2010-02-17 16:11:44 -0800151
Tianjie Xu12b90552017-03-07 14:44:14 -0800152// If we use this function to write the offset and length (type size_t), their values should not
153// exceed 2^63; because the signed bit will be casted away.
154static inline bool Write8(int fd, int64_t value) {
155 return android::base::WriteFully(fd, &value, sizeof(int64_t));
156}
157
158// Similarly, the value should not exceed 2^31 if we are casting from size_t (e.g. target chunk
159// size).
160static inline bool Write4(int fd, int32_t value) {
161 return android::base::WriteFully(fd, &value, sizeof(int32_t));
162}
163
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800164class ImageChunk {
165 public:
166 static constexpr auto WINDOWBITS = -15; // 32kb window; negative to indicate a raw stream.
167 static constexpr auto MEMLEVEL = 8; // the default value.
168 static constexpr auto METHOD = Z_DEFLATED;
169 static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY;
170
171 ImageChunk(int type, size_t start, const std::vector<uint8_t>* file_content, size_t raw_data_len)
172 : type_(type),
173 start_(start),
174 input_file_ptr_(file_content),
175 raw_data_len_(raw_data_len),
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800176 compress_level_(6),
177 source_start_(0),
178 source_len_(0),
Tianjie Xu12b90552017-03-07 14:44:14 -0800179 source_uncompressed_len_(0) {
180 CHECK(file_content != nullptr) << "input file container can't be nullptr";
181 }
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800182
183 int GetType() const {
184 return type_;
185 }
186 size_t GetRawDataLength() const {
187 return raw_data_len_;
188 }
189 const std::string& GetEntryName() const {
190 return entry_name_;
191 }
192
193 // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return
194 // the raw data.
195 const uint8_t * DataForPatch() const;
196 size_t DataLengthForPatch() const;
197
198 void Dump() const {
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700199 printf("type: %d, start: %zu, len: %zu, name: %s\n", type_, start_, DataLengthForPatch(),
200 entry_name_.c_str());
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800201 }
202
203 void SetSourceInfo(const ImageChunk& other);
204 void SetEntryName(std::string entryname);
205 void SetUncompressedData(std::vector<uint8_t> data);
206 bool SetBonusData(const std::vector<uint8_t>& bonus_data);
207
208 bool operator==(const ImageChunk& other) const;
209 bool operator!=(const ImageChunk& other) const {
210 return !(*this == other);
211 }
212
213 size_t GetHeaderSize(size_t patch_size) const;
Tianjie Xu12b90552017-03-07 14:44:14 -0800214 // Return the offset of the next patch into the patch data.
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700215 size_t WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset) const;
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800216
217 /*
218 * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob
219 * of uninterpreted data). The resulting patch will likely be about
220 * as big as the target file, but it lets us handle the case of images
221 * where some gzip chunks are reconstructible but others aren't (by
222 * treating the ones that aren't as normal chunks).
223 */
224 void ChangeDeflateChunkToNormal();
225 bool ChangeChunkToRaw(size_t patch_size);
226
227 /*
228 * Verify that we can reproduce exactly the same compressed data that
229 * we started with. Sets the level, method, windowBits, memLevel, and
230 * strategy fields in the chunk to the encoding parameters needed to
231 * produce the right output.
232 */
233 bool ReconstructDeflateChunk();
234 bool IsAdjacentNormal(const ImageChunk& other) const;
235 void MergeAdjacentNormal(const ImageChunk& other);
236
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700237 /*
238 * Compute a bsdiff patch between |this| and the input source chunks.
239 * Store the result in the patch_data.
240 * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk is used
241 * repeatedly, pass nullptr if not needed.
242 */
243 bool MakePatch(const ImageChunk& src, std::vector<uint8_t>* patch_data, saidx_t** bsdiff_cache);
244
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800245 private:
Tianjie Xu12b90552017-03-07 14:44:14 -0800246 int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW
247 size_t start_; // offset of chunk in the original input file
248 const std::vector<uint8_t>* input_file_ptr_; // ptr to the full content of original input file
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800249 size_t raw_data_len_;
Doug Zongker512536a2010-02-17 16:11:44 -0800250
Doug Zongker512536a2010-02-17 16:11:44 -0800251 // --- for CHUNK_DEFLATE chunks only: ---
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800252 std::vector<uint8_t> uncompressed_data_;
253 std::string entry_name_; // used for zip entries
Doug Zongker512536a2010-02-17 16:11:44 -0800254
255 // deflate encoder parameters
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800256 int compress_level_;
Doug Zongker512536a2010-02-17 16:11:44 -0800257
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800258 size_t source_start_;
259 size_t source_len_;
260 size_t source_uncompressed_len_;
Doug Zongker512536a2010-02-17 16:11:44 -0800261
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800262 const uint8_t* GetRawData() const;
263 bool TryReconstruction(int level);
264};
Doug Zongker512536a2010-02-17 16:11:44 -0800265
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800266const uint8_t* ImageChunk::GetRawData() const {
267 CHECK_LE(start_ + raw_data_len_, input_file_ptr_->size());
268 return input_file_ptr_->data() + start_;
269}
270
271const uint8_t * ImageChunk::DataForPatch() const {
272 if (type_ == CHUNK_DEFLATE) {
273 return uncompressed_data_.data();
274 }
275 return GetRawData();
276}
277
278size_t ImageChunk::DataLengthForPatch() const {
279 if (type_ == CHUNK_DEFLATE) {
280 return uncompressed_data_.size();
281 }
282 return raw_data_len_;
283}
284
285bool ImageChunk::operator==(const ImageChunk& other) const {
286 if (type_ != other.type_) {
287 return false;
288 }
289 return (raw_data_len_ == other.raw_data_len_ &&
290 memcmp(GetRawData(), other.GetRawData(), raw_data_len_) == 0);
291}
292
293void ImageChunk::SetSourceInfo(const ImageChunk& src) {
294 source_start_ = src.start_;
295 if (type_ == CHUNK_NORMAL) {
296 source_len_ = src.raw_data_len_;
297 } else if (type_ == CHUNK_DEFLATE) {
298 source_len_ = src.raw_data_len_;
299 source_uncompressed_len_ = src.uncompressed_data_.size();
Tao Baoa0c40112016-06-01 13:15:44 -0700300 }
Doug Zongker512536a2010-02-17 16:11:44 -0800301}
302
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800303void ImageChunk::SetEntryName(std::string entryname) {
Tianjie Xu12b90552017-03-07 14:44:14 -0800304 entry_name_ = std::move(entryname);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800305}
306
307void ImageChunk::SetUncompressedData(std::vector<uint8_t> data) {
Tianjie Xu12b90552017-03-07 14:44:14 -0800308 uncompressed_data_ = std::move(data);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800309}
310
311bool ImageChunk::SetBonusData(const std::vector<uint8_t>& bonus_data) {
312 if (type_ != CHUNK_DEFLATE) {
313 return false;
314 }
315 uncompressed_data_.insert(uncompressed_data_.end(), bonus_data.begin(), bonus_data.end());
316 return true;
317}
318
Tianjie Xu12b90552017-03-07 14:44:14 -0800319// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the target size is
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800320// smaller. Also take the header size into account during size comparison.
321bool ImageChunk::ChangeChunkToRaw(size_t patch_size) {
322 if (type_ == CHUNK_RAW) {
323 return true;
324 } else if (type_ == CHUNK_NORMAL && (raw_data_len_ <= 160 || raw_data_len_ < patch_size)) {
325 type_ = CHUNK_RAW;
326 return true;
327 }
328 return false;
329}
330
331void ImageChunk::ChangeDeflateChunkToNormal() {
332 if (type_ != CHUNK_DEFLATE) return;
333 type_ = CHUNK_NORMAL;
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700334 // No need to clear the entry name.
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800335 uncompressed_data_.clear();
336}
337
338// Header size:
339// header_type 4 bytes
340// CHUNK_NORMAL 8*3 = 24 bytes
341// CHUNK_DEFLATE 8*5 + 4*5 = 60 bytes
Tianjie Xu12b90552017-03-07 14:44:14 -0800342// CHUNK_RAW 4 bytes + patch_size
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800343size_t ImageChunk::GetHeaderSize(size_t patch_size) const {
344 switch (type_) {
345 case CHUNK_NORMAL:
346 return 4 + 8 * 3;
347 case CHUNK_DEFLATE:
348 return 4 + 8 * 5 + 4 * 5;
349 case CHUNK_RAW:
350 return 4 + 4 + patch_size;
351 default:
Tianjie Xu12b90552017-03-07 14:44:14 -0800352 CHECK(false) << "unexpected chunk type: " << type_; // Should not reach here.
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800353 return 0;
354 }
355}
356
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700357size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector<uint8_t>& patch, size_t offset) const {
Tianjie Xu12b90552017-03-07 14:44:14 -0800358 Write4(fd, type_);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800359 switch (type_) {
360 case CHUNK_NORMAL:
361 printf("normal (%10zu, %10zu) %10zu\n", start_, raw_data_len_, patch.size());
Tianjie Xu12b90552017-03-07 14:44:14 -0800362 Write8(fd, static_cast<int64_t>(source_start_));
363 Write8(fd, static_cast<int64_t>(source_len_));
364 Write8(fd, static_cast<int64_t>(offset));
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800365 return offset + patch.size();
366 case CHUNK_DEFLATE:
367 printf("deflate (%10zu, %10zu) %10zu %s\n", start_, raw_data_len_, patch.size(),
368 entry_name_.c_str());
Tianjie Xu12b90552017-03-07 14:44:14 -0800369 Write8(fd, static_cast<int64_t>(source_start_));
370 Write8(fd, static_cast<int64_t>(source_len_));
371 Write8(fd, static_cast<int64_t>(offset));
372 Write8(fd, static_cast<int64_t>(source_uncompressed_len_));
373 Write8(fd, static_cast<int64_t>(uncompressed_data_.size()));
374 Write4(fd, compress_level_);
375 Write4(fd, METHOD);
376 Write4(fd, WINDOWBITS);
377 Write4(fd, MEMLEVEL);
378 Write4(fd, STRATEGY);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800379 return offset + patch.size();
380 case CHUNK_RAW:
381 printf("raw (%10zu, %10zu)\n", start_, raw_data_len_);
Tianjie Xu12b90552017-03-07 14:44:14 -0800382 Write4(fd, static_cast<int32_t>(patch.size()));
383 if (!android::base::WriteFully(fd, patch.data(), patch.size())) {
384 CHECK(false) << "failed to write " << patch.size() <<" bytes patch";
385 }
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800386 return offset;
387 default:
Tianjie Xu12b90552017-03-07 14:44:14 -0800388 CHECK(false) << "unexpected chunk type: " << type_;
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800389 return offset;
390 }
391}
392
393bool ImageChunk::IsAdjacentNormal(const ImageChunk& other) const {
394 if (type_ != CHUNK_NORMAL || other.type_ != CHUNK_NORMAL) {
395 return false;
396 }
397 return (other.start_ == start_ + raw_data_len_);
398}
399
400void ImageChunk::MergeAdjacentNormal(const ImageChunk& other) {
401 CHECK(IsAdjacentNormal(other));
402 raw_data_len_ = raw_data_len_ + other.raw_data_len_;
403}
404
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700405bool ImageChunk::MakePatch(const ImageChunk& src, std::vector<uint8_t>* patch_data,
406 saidx_t** bsdiff_cache) {
407 if (ChangeChunkToRaw(0)) {
408 size_t patch_size = DataLengthForPatch();
409 patch_data->resize(patch_size);
410 std::copy(DataForPatch(), DataForPatch() + patch_size, patch_data->begin());
411 return true;
412 }
413
414#if defined(__ANDROID__)
415 char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX";
416#else
417 char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
418#endif
419
420 int fd = mkstemp(ptemp);
421 if (fd == -1) {
422 printf("MakePatch failed to create a temporary file: %s\n", strerror(errno));
423 return false;
424 }
425 close(fd);
426
427 int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), DataForPatch(),
428 DataLengthForPatch(), ptemp, bsdiff_cache);
429 if (r != 0) {
430 printf("bsdiff() failed: %d\n", r);
431 return false;
432 }
433
434 android::base::unique_fd patch_fd(open(ptemp, O_RDONLY));
435 if (patch_fd == -1) {
436 printf("failed to open %s: %s\n", ptemp, strerror(errno));
437 return false;
438 }
439 struct stat st;
440 if (fstat(patch_fd, &st) != 0) {
441 printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno));
442 return false;
443 }
444
445 size_t sz = static_cast<size_t>(st.st_size);
446 // Change the chunk type to raw if the patch takes less space that way.
447 if (ChangeChunkToRaw(sz)) {
448 unlink(ptemp);
449 size_t patch_size = DataLengthForPatch();
450 patch_data->resize(patch_size);
451 std::copy(DataForPatch(), DataForPatch() + patch_size, patch_data->begin());
452 return true;
453 }
454 patch_data->resize(sz);
455 if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) {
456 printf("failed to read \"%s\" %s\n", ptemp, strerror(errno));
457 unlink(ptemp);
458 return false;
459 }
460
461 unlink(ptemp);
462 SetSourceInfo(src);
463
464 return true;
465}
466
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800467bool ImageChunk::ReconstructDeflateChunk() {
468 if (type_ != CHUNK_DEFLATE) {
469 printf("attempt to reconstruct non-deflate chunk\n");
470 return false;
Doug Zongker512536a2010-02-17 16:11:44 -0800471 }
472
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800473 // We only check two combinations of encoder parameters: level 6
474 // (the default) and level 9 (the maximum).
475 for (int level = 6; level <= 9; level += 3) {
476 if (TryReconstruction(level)) {
477 compress_level_ = level;
478 return true;
Doug Zongker512536a2010-02-17 16:11:44 -0800479 }
480 }
Doug Zongker512536a2010-02-17 16:11:44 -0800481
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800482 return false;
Doug Zongker512536a2010-02-17 16:11:44 -0800483}
484
485/*
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800486 * Takes the uncompressed data stored in the chunk, compresses it
487 * using the zlib parameters stored in the chunk, and checks that it
488 * matches exactly the compressed data we started with (also stored in
489 * the chunk).
Doug Zongker512536a2010-02-17 16:11:44 -0800490 */
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800491bool ImageChunk::TryReconstruction(int level) {
492 z_stream strm;
493 strm.zalloc = Z_NULL;
494 strm.zfree = Z_NULL;
495 strm.opaque = Z_NULL;
496 strm.avail_in = uncompressed_data_.size();
497 strm.next_in = uncompressed_data_.data();
498 int ret = deflateInit2(&strm, level, METHOD, WINDOWBITS, MEMLEVEL, STRATEGY);
499 if (ret < 0) {
500 printf("failed to initialize deflate: %d\n", ret);
501 return false;
502 }
503
504 std::vector<uint8_t> buffer(BUFFER_SIZE);
505 size_t offset = 0;
506 do {
507 strm.avail_out = buffer.size();
508 strm.next_out = buffer.data();
509 ret = deflate(&strm, Z_FINISH);
510 if (ret < 0) {
511 printf("failed to deflate: %d\n", ret);
512 return false;
513 }
514
515 size_t compressed_size = buffer.size() - strm.avail_out;
516 if (memcmp(buffer.data(), input_file_ptr_->data() + start_ + offset, compressed_size) != 0) {
517 // mismatch; data isn't the same.
518 deflateEnd(&strm);
519 return false;
520 }
521 offset += compressed_size;
522 } while (ret != Z_STREAM_END);
523 deflateEnd(&strm);
524
525 if (offset != raw_data_len_) {
526 // mismatch; ran out of data before we should have.
527 return false;
528 }
529 return true;
530}
531
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700532// Interface for zip_mode and image_mode images. We initialize the image from an input file and
533// split the file content into a list of image chunks.
534class Image {
535 public:
536 explicit Image(bool is_source) : is_source_(is_source) {}
537
538 virtual ~Image() {}
539
540 // Create a list of image chunks from input file.
541 virtual bool Initialize(const std::string& filename) = 0;
542
543 // Look for runs of adjacent normal chunks and compress them down into a single chunk. (Such
544 // runs can be produced when deflate chunks are changed to normal chunks.)
545 void MergeAdjacentNormalChunks();
546
547 // In zip mode, find the matching deflate source chunk by entry name. Search for normal chunks
548 // also if |find_normal| is true.
549 ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false);
550
551 // Write the contents of |patch_data| to |patch_fd|.
552 bool WritePatchDataToFd(const std::vector<std::vector<uint8_t>>& patch_data, int patch_fd) const;
553
554 void DumpChunks() const;
555
556 // Non const iterators to access the stored ImageChunks.
557 std::vector<ImageChunk>::iterator begin() {
558 return chunks_.begin();
559 }
560
561 std::vector<ImageChunk>::iterator end() {
562 return chunks_.end();
563 }
564 // Return a pointer to the ith ImageChunk.
565 ImageChunk* Get(size_t i) {
566 CHECK_LT(i, chunks_.size());
567 return &chunks_[i];
568 }
569
570 size_t NumOfChunks() const {
571 return chunks_.size();
572 }
573
574 protected:
575 bool ReadFile(const std::string& filename, std::vector<uint8_t>* file_content);
576
577 bool is_source_; // True if it's for source chunks.
578 std::vector<ImageChunk> chunks_; // Internal storage of ImageChunk.
579 std::vector<uint8_t> file_content_; // Store the whole input file in memory.
580};
581
582void Image::MergeAdjacentNormalChunks() {
583 size_t merged_last = 0, cur = 0;
584 while (cur < chunks_.size()) {
585 // Look for normal chunks adjacent to the current one. If such chunk exists, extend the
586 // length of the current normal chunk.
587 size_t to_check = cur + 1;
588 while (to_check < chunks_.size() && chunks_[cur].IsAdjacentNormal(chunks_[to_check])) {
589 chunks_[cur].MergeAdjacentNormal(chunks_[to_check]);
590 to_check++;
591 }
592
593 if (merged_last != cur) {
594 chunks_[merged_last] = std::move(chunks_[cur]);
595 }
596 merged_last++;
597 cur = to_check;
598 }
599 if (merged_last < chunks_.size()) {
600 chunks_.erase(chunks_.begin() + merged_last, chunks_.end());
601 }
602}
603
604ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) {
605 if (name.empty()) {
606 return nullptr;
607 }
608 for (auto& chunk : chunks_) {
609 if ((chunk.GetType() == CHUNK_DEFLATE || find_normal) && chunk.GetEntryName() == name) {
610 return &chunk;
611 }
612 }
613 return nullptr;
614}
615
616bool Image::WritePatchDataToFd(const std::vector<std::vector<uint8_t>>& patch_data,
617 int patch_fd) const {
618 // Figure out how big the imgdiff file header is going to be, so that we can correctly compute
619 // the offset of each bsdiff patch within the file.
620 CHECK_EQ(chunks_.size(), patch_data.size());
621 size_t total_header_size = 12;
622 for (size_t i = 0; i < chunks_.size(); ++i) {
623 total_header_size += chunks_[i].GetHeaderSize(patch_data[i].size());
624 }
625
626 size_t offset = total_header_size;
627
628 // Write out the headers.
629 if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) {
630 printf("failed to write \"IMGDIFF2\": %s\n", strerror(errno));
631 return false;
632 }
633 Write4(patch_fd, static_cast<int32_t>(chunks_.size()));
634 for (size_t i = 0; i < chunks_.size(); ++i) {
635 printf("chunk %zu: ", i);
636 offset = chunks_[i].WriteHeaderToFd(patch_fd, patch_data[i], offset);
637 }
638
639 // Append each chunk's bsdiff patch, in order.
640 for (size_t i = 0; i < chunks_.size(); ++i) {
641 if (chunks_[i].GetType() != CHUNK_RAW) {
642 if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) {
643 printf("failed to write %zu bytes patch for chunk %zu\n", patch_data[i].size(), i);
644 return false;
645 }
646 }
647 }
648
649 return true;
650}
651
652void Image::DumpChunks() const {
653 std::string type = is_source_ ? "source" : "target";
654 printf("Dumping chunks for %s\n", type.c_str());
655 for (size_t i = 0; i < chunks_.size(); ++i) {
656 printf("chunk %zu: ", i);
657 chunks_[i].Dump();
658 }
659}
660
661bool Image::ReadFile(const std::string& filename, std::vector<uint8_t>* file_content) {
662 CHECK(file_content != nullptr);
663
664 android::base::unique_fd fd(open(filename.c_str(), O_RDONLY));
665 if (fd == -1) {
666 printf("failed to open \"%s\" %s\n", filename.c_str(), strerror(errno));
667 return false;
668 }
669 struct stat st;
670 if (fstat(fd, &st) != 0) {
671 printf("failed to stat \"%s\": %s\n", filename.c_str(), strerror(errno));
672 return false;
673 }
674
675 size_t sz = static_cast<size_t>(st.st_size);
676 file_content->resize(sz);
677 if (!android::base::ReadFully(fd, file_content->data(), sz)) {
678 printf("failed to read \"%s\" %s\n", filename.c_str(), strerror(errno));
679 return false;
680 }
681 fd.reset();
682
683 return true;
684}
685
686class ZipModeImage : public Image {
687 public:
688 explicit ZipModeImage(bool is_source) : Image(is_source) {}
689
690 bool Initialize(const std::string& filename) override;
691
692 const ImageChunk& PseudoSource() const {
693 CHECK(is_source_);
694 CHECK(pseudo_source_ != nullptr);
695 return *pseudo_source_;
696 }
697
698 // Verify that we can reconstruct the deflate chunks; also change the type to CHUNK_NORMAL if
699 // src and tgt are identical.
700 static bool CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image);
701
702 // Compute the patches against the input image, and write the data into |patch_name|.
703 static bool GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_image,
704 const std::string& patch_name);
705
706 private:
707 // Initialize image chunks based on the zip entries.
708 bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle);
709 // Add the a zip entry to the list.
710 bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry);
711 // Return the real size of the zip file. (omit the trailing zeros that used for alignment)
712 bool GetZipFileSize(size_t* input_file_size);
713
714 // The pesudo source chunk for bsdiff if there's no match for the given target chunk. It's in
715 // fact the whole source file.
716 std::unique_ptr<ImageChunk> pseudo_source_;
717};
718
719bool ZipModeImage::Initialize(const std::string& filename) {
720 if (!ReadFile(filename, &file_content_)) {
721 return false;
722 }
723
724 // Omit the trailing zeros before we pass the file to ziparchive handler.
725 size_t zipfile_size;
726 if (!GetZipFileSize(&zipfile_size)) {
727 printf("failed to parse the actual size of %s\n", filename.c_str());
728 return false;
729 }
730 ZipArchiveHandle handle;
731 int err = OpenArchiveFromMemory(const_cast<uint8_t*>(file_content_.data()), zipfile_size,
732 filename.c_str(), &handle);
733 if (err != 0) {
734 printf("failed to open zip file %s: %s\n", filename.c_str(), ErrorCodeString(err));
735 CloseArchive(handle);
736 return false;
737 }
738
739 if (is_source_) {
740 pseudo_source_ = std::make_unique<ImageChunk>(CHUNK_NORMAL, 0, &file_content_, zipfile_size);
741 }
742 if (!InitializeChunks(filename, handle)) {
743 CloseArchive(handle);
744 return false;
745 }
746
747 CloseArchive(handle);
748 return true;
749}
750
751// Iterate the zip entries and compose the image chunks accordingly.
752bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandle handle) {
753 void* cookie;
754 int ret = StartIteration(handle, &cookie, nullptr, nullptr);
755 if (ret != 0) {
756 printf("failed to iterate over entries in %s: %s\n", filename.c_str(), ErrorCodeString(ret));
757 return false;
758 }
759
760 // Create a list of deflated zip entries, sorted by offset.
761 std::vector<std::pair<std::string, ZipEntry>> temp_entries;
762 ZipString name;
763 ZipEntry entry;
764 while ((ret = Next(cookie, &entry, &name)) == 0) {
765 if (entry.method == kCompressDeflated) {
766 std::string entry_name(name.name, name.name + name.name_length);
767 temp_entries.emplace_back(entry_name, entry);
768 }
769 }
770
771 if (ret != -1) {
772 printf("Error while iterating over zip entries: %s\n", ErrorCodeString(ret));
773 return false;
774 }
775 std::sort(temp_entries.begin(), temp_entries.end(),
776 [](auto& entry1, auto& entry2) { return entry1.second.offset < entry2.second.offset; });
777
778 EndIteration(cookie);
779
780 // For source chunks, we don't need to compose chunks for the metadata.
781 if (is_source_) {
782 for (auto& entry : temp_entries) {
783 if (!AddZipEntryToChunks(handle, entry.first, &entry.second)) {
784 printf("Failed to add %s to source chunks\n", entry.first.c_str());
785 return false;
786 }
787 }
788 return true;
789 }
790
791 // For target chunks, add the deflate entries as CHUNK_DEFLATE and the contents between two
792 // deflate entries as CHUNK_NORMAL.
793 size_t pos = 0;
794 size_t nextentry = 0;
795 while (pos < file_content_.size()) {
796 if (nextentry < temp_entries.size() &&
797 static_cast<off64_t>(pos) == temp_entries[nextentry].second.offset) {
798 // Add the next zip entry.
799 std::string entry_name = temp_entries[nextentry].first;
800 if (!AddZipEntryToChunks(handle, entry_name, &temp_entries[nextentry].second)) {
801 printf("Failed to add %s to target chunks\n", entry_name.c_str());
802 return false;
803 }
804
805 pos += temp_entries[nextentry].second.compressed_length;
806 ++nextentry;
807 continue;
808 }
809
810 // Use a normal chunk to take all the data up to the start of the next entry.
811 size_t raw_data_len;
812 if (nextentry < temp_entries.size()) {
813 raw_data_len = temp_entries[nextentry].second.offset - pos;
814 } else {
815 raw_data_len = file_content_.size() - pos;
816 }
817 chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, raw_data_len);
818
819 pos += raw_data_len;
820 }
821
822 return true;
823}
824
825bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name,
826 ZipEntry* entry) {
827 size_t compressed_len = entry->compressed_length;
828 if (entry->method == kCompressDeflated) {
829 size_t uncompressed_len = entry->uncompressed_length;
830 std::vector<uint8_t> uncompressed_data(uncompressed_len);
831 int ret = ExtractToMemory(handle, entry, uncompressed_data.data(), uncompressed_len);
832 if (ret != 0) {
833 printf("failed to extract %s with size %zu: %s\n", entry_name.c_str(), uncompressed_len,
834 ErrorCodeString(ret));
835 return false;
836 }
837 ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len);
838 curr.SetEntryName(entry_name);
839 curr.SetUncompressedData(std::move(uncompressed_data));
840 chunks_.push_back(curr);
841 } else {
842 ImageChunk curr(CHUNK_NORMAL, entry->offset, &file_content_, compressed_len);
843 curr.SetEntryName(entry_name);
844 chunks_.push_back(curr);
845 }
846
847 return true;
848}
849
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800850// EOCD record
851// offset 0: signature 0x06054b50, 4 bytes
852// offset 4: number of this disk, 2 bytes
853// ...
854// offset 20: comment length, 2 bytes
855// offset 22: comment, n bytes
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700856bool ZipModeImage::GetZipFileSize(size_t* input_file_size) {
857 if (file_content_.size() < 22) {
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800858 printf("file is too small to be a zip file\n");
859 return false;
860 }
861
862 // Look for End of central directory record of the zip file, and calculate the actual
863 // zip_file size.
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700864 for (int i = file_content_.size() - 22; i >= 0; i--) {
865 if (file_content_[i] == 0x50) {
866 if (get_unaligned<uint32_t>(&file_content_[i]) == 0x06054b50) {
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800867 // double-check: this archive consists of a single "disk".
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700868 CHECK_EQ(get_unaligned<uint16_t>(&file_content_[i + 4]), 0);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800869
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700870 uint16_t comment_length = get_unaligned<uint16_t>(&file_content_[i + 20]);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800871 size_t file_size = i + 22 + comment_length;
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700872 CHECK_LE(file_size, file_content_.size());
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800873 *input_file_size = file_size;
874 return true;
875 }
876 }
877 }
878
879 // EOCD not found, this file is likely not a valid zip file.
880 return false;
881}
882
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700883bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image) {
884 for (auto& tgt_chunk : *tgt_image) {
885 if (tgt_chunk.GetType() != CHUNK_DEFLATE) {
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800886 continue;
887 }
888
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700889 ImageChunk* src_chunk = src_image->FindChunkByName(tgt_chunk.GetEntryName());
890 if (src_chunk == nullptr) {
891 tgt_chunk.ChangeDeflateChunkToNormal();
892 } else if (tgt_chunk == *src_chunk) {
893 // If two deflate chunks are identical (eg, the kernel has not changed between two builds),
894 // treat them as normal chunks. This makes applypatch much faster -- it can apply a trivial
895 // patch to the compressed data, rather than uncompressing and recompressing to apply the
896 // trivial patch to the uncompressed data.
897 tgt_chunk.ChangeDeflateChunkToNormal();
898 src_chunk->ChangeDeflateChunkToNormal();
899 } else if (!tgt_chunk.ReconstructDeflateChunk()) {
900 // We cannot recompress the data and get exactly the same bits as are in the input target
901 // image. Treat the chunk as a normal non-deflated chunk.
902 printf("failed to reconstruct target deflate chunk [%s]; treating as normal\n",
903 tgt_chunk.GetEntryName().c_str());
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800904
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700905 tgt_chunk.ChangeDeflateChunkToNormal();
906 src_chunk->ChangeDeflateChunkToNormal();
907 }
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800908 }
909
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800910 return true;
911}
912
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700913bool ZipModeImage::GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_image,
914 const std::string& patch_name) {
915 // For zips, we only need merge normal chunks for the target: deflated chunks are matched via
916 // filename, and normal chunks are patched using the entire source file as the source.
917 tgt_image->MergeAdjacentNormalChunks();
918 tgt_image->DumpChunks();
Tianjie Xu12b90552017-03-07 14:44:14 -0800919
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700920 printf("Construct patches for %zu chunks...\n", tgt_image->NumOfChunks());
921 std::vector<std::vector<uint8_t>> patch_data(tgt_image->NumOfChunks());
922
923 saidx_t* bsdiff_cache = nullptr;
924 size_t i = 0;
925 for (auto& tgt_chunk : *tgt_image) {
926 ImageChunk* src_chunk = (tgt_chunk.GetType() != CHUNK_DEFLATE)
927 ? nullptr
928 : src_image->FindChunkByName(tgt_chunk.GetEntryName());
929
930 const auto& src_ref = (src_chunk == nullptr) ? src_image->PseudoSource() : *src_chunk;
931 saidx_t** bsdiff_cache_ptr = (src_chunk == nullptr) ? &bsdiff_cache : nullptr;
932
933 if (!tgt_chunk.MakePatch(src_ref, &patch_data[i], bsdiff_cache_ptr)) {
934 printf("Failed to generate patch, name: %s\n", tgt_chunk.GetEntryName().c_str());
935 return false;
936 }
937
938 printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(),
939 tgt_chunk.GetRawDataLength());
940 i++;
Tianjie Xu12b90552017-03-07 14:44:14 -0800941 }
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700942 free(bsdiff_cache);
943
944 android::base::unique_fd patch_fd(
945 open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
946 if (patch_fd == -1) {
947 printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno));
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800948 return false;
949 }
950
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700951 return tgt_image->WritePatchDataToFd(patch_data, patch_fd);
952}
953
954class ImageModeImage : public Image {
955 public:
956 explicit ImageModeImage(bool is_source) : Image(is_source) {}
957
958 // Initialize the image chunks list by searching the magic numbers in an image file.
959 bool Initialize(const std::string& filename) override;
960
961 // In Image Mode, verify that the source and target images have the same chunk structure (ie, the
962 // same sequence of deflate and normal chunks).
963 static bool CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image);
964
965 // In image mode, generate patches against the given source chunks and bonus_data; write the
966 // result to |patch_name|.
967 static bool GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* src_image,
968 const std::vector<uint8_t>& bonus_data, const std::string& patch_name);
969};
970
971bool ImageModeImage::Initialize(const std::string& filename) {
972 if (!ReadFile(filename, &file_content_)) {
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800973 return false;
Doug Zongker512536a2010-02-17 16:11:44 -0800974 }
Doug Zongker512536a2010-02-17 16:11:44 -0800975
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700976 size_t sz = file_content_.size();
Doug Zongker512536a2010-02-17 16:11:44 -0800977 size_t pos = 0;
Tao Baoba9a42a2015-06-23 23:23:33 -0700978 while (pos < sz) {
Tianjie Xu12b90552017-03-07 14:44:14 -0800979 // 0x00 no header flags, 0x08 deflate compression, 0x1f8b gzip magic number
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700980 if (sz - pos >= 4 && get_unaligned<uint32_t>(file_content_.data() + pos) == 0x00088b1f) {
Doug Zongker512536a2010-02-17 16:11:44 -0800981 // 'pos' is the offset of the start of a gzip chunk.
Johan Redestigc68bd342015-04-14 21:20:06 +0200982 size_t chunk_offset = pos;
Doug Zongker512536a2010-02-17 16:11:44 -0800983
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800984 // The remaining data is too small to be a gzip chunk; treat them as a normal chunk.
985 if (sz - pos < GZIP_HEADER_LEN + GZIP_FOOTER_LEN) {
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700986 chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, sz - pos);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800987 break;
988 }
Doug Zongker512536a2010-02-17 16:11:44 -0800989
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800990 // We need three chunks for the deflated image in total, one normal chunk for the header,
991 // one deflated chunk for the body, and another normal chunk for the footer.
Tianjie Xu6b03ba72017-07-19 14:16:30 -0700992 chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, GZIP_HEADER_LEN);
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800993 pos += GZIP_HEADER_LEN;
Doug Zongker512536a2010-02-17 16:11:44 -0800994
Tianjie Xu1ea84d62017-02-22 18:23:58 -0800995 // We must decompress this chunk in order to discover where it ends, and so we can update
996 // the uncompressed_data of the image body and its length.
Doug Zongker512536a2010-02-17 16:11:44 -0800997
998 z_stream strm;
999 strm.zalloc = Z_NULL;
1000 strm.zfree = Z_NULL;
1001 strm.opaque = Z_NULL;
Tao Baoba9a42a2015-06-23 23:23:33 -07001002 strm.avail_in = sz - pos;
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001003 strm.next_in = file_content_.data() + pos;
Doug Zongker512536a2010-02-17 16:11:44 -08001004
1005 // -15 means we are decoding a 'raw' deflate stream; zlib will
1006 // not expect zlib headers.
1007 int ret = inflateInit2(&strm, -15);
Rahul Chaudhrya793c582016-11-29 17:10:14 -08001008 if (ret < 0) {
1009 printf("failed to initialize inflate: %d\n", ret);
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001010 return false;
Rahul Chaudhrya793c582016-11-29 17:10:14 -08001011 }
Doug Zongker512536a2010-02-17 16:11:44 -08001012
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001013 size_t allocated = BUFFER_SIZE;
1014 std::vector<uint8_t> uncompressed_data(allocated);
1015 size_t uncompressed_len = 0, raw_data_len = 0;
Doug Zongker512536a2010-02-17 16:11:44 -08001016 do {
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001017 strm.avail_out = allocated - uncompressed_len;
1018 strm.next_out = uncompressed_data.data() + uncompressed_len;
Doug Zongker512536a2010-02-17 16:11:44 -08001019 ret = inflate(&strm, Z_NO_FLUSH);
Johan Redestigc68bd342015-04-14 21:20:06 +02001020 if (ret < 0) {
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001021 printf("Warning: inflate failed [%s] at offset [%zu], treating as a normal chunk\n",
David Riley0779fc92015-12-10 10:18:25 -08001022 strm.msg, chunk_offset);
Sen Jiangfa4f1b72016-02-11 16:14:23 -08001023 break;
Johan Redestigc68bd342015-04-14 21:20:06 +02001024 }
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001025 uncompressed_len = allocated - strm.avail_out;
Doug Zongker512536a2010-02-17 16:11:44 -08001026 if (strm.avail_out == 0) {
1027 allocated *= 2;
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001028 uncompressed_data.resize(allocated);
Doug Zongker512536a2010-02-17 16:11:44 -08001029 }
1030 } while (ret != Z_STREAM_END);
1031
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001032 raw_data_len = sz - strm.avail_in - pos;
Doug Zongker512536a2010-02-17 16:11:44 -08001033 inflateEnd(&strm);
Sen Jiangfa4f1b72016-02-11 16:14:23 -08001034
1035 if (ret < 0) {
Sen Jiangfa4f1b72016-02-11 16:14:23 -08001036 continue;
1037 }
1038
Tianjie Xu14ebc1e2017-07-05 12:04:07 -07001039 // The footer contains the size of the uncompressed data. Double-check to make sure that it
1040 // matches the size of the data we got when we actually did the decompression.
1041 size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4;
1042 if (sz - footer_index < 4) {
1043 printf("Warning: invalid footer position; treating as a nomal chunk\n");
1044 continue;
1045 }
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001046 size_t footer_size = get_unaligned<uint32_t>(file_content_.data() + footer_index);
Tianjie Xu14ebc1e2017-07-05 12:04:07 -07001047 if (footer_size != uncompressed_len) {
1048 printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n",
1049 footer_size, uncompressed_len);
1050 continue;
1051 }
1052
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001053 ImageChunk body(CHUNK_DEFLATE, pos, &file_content_, raw_data_len);
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001054 uncompressed_data.resize(uncompressed_len);
1055 body.SetUncompressedData(std::move(uncompressed_data));
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001056 chunks_.push_back(body);
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001057
1058 pos += raw_data_len;
Doug Zongker512536a2010-02-17 16:11:44 -08001059
1060 // create a normal chunk for the footer
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001061 chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, GZIP_FOOTER_LEN);
Doug Zongker512536a2010-02-17 16:11:44 -08001062
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001063 pos += GZIP_FOOTER_LEN;
Doug Zongker512536a2010-02-17 16:11:44 -08001064 } else {
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001065 // Use a normal chunk to take all the contents until the next gzip chunk (or EOF); we expect
1066 // the number of chunks to be small (5 for typical boot and recovery images).
Doug Zongker512536a2010-02-17 16:11:44 -08001067
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001068 // Scan forward until we find a gzip header.
1069 size_t data_len = 0;
1070 while (data_len + pos < sz) {
Tianjie Xu12b90552017-03-07 14:44:14 -08001071 if (data_len + pos + 4 <= sz &&
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001072 get_unaligned<uint32_t>(file_content_.data() + pos + data_len) == 0x00088b1f) {
Doug Zongker512536a2010-02-17 16:11:44 -08001073 break;
1074 }
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001075 data_len++;
Doug Zongker512536a2010-02-17 16:11:44 -08001076 }
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001077 chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, data_len);
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001078
1079 pos += data_len;
Doug Zongker512536a2010-02-17 16:11:44 -08001080 }
1081 }
1082
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001083 return true;
Doug Zongker512536a2010-02-17 16:11:44 -08001084}
1085
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001086// In Image Mode, verify that the source and target images have the same chunk structure (ie, the
1087// same sequence of deflate and normal chunks).
1088bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image) {
1089 // In image mode, merge the gzip header and footer in with any adjacent normal chunks.
1090 tgt_image->MergeAdjacentNormalChunks();
1091 src_image->MergeAdjacentNormalChunks();
Doug Zongker512536a2010-02-17 16:11:44 -08001092
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001093 if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) {
1094 printf("source and target don't have same number of chunks!\n");
1095 tgt_image->DumpChunks();
1096 src_image->DumpChunks();
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001097 return false;
Jeremy Compostellaa91c66d2015-09-08 19:15:09 +02001098 }
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001099 for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) {
1100 if (tgt_image->Get(i)->GetType() != src_image->Get(i)->GetType()) {
1101 printf("source and target don't have same chunk structure! (chunk %zu)\n", i);
1102 tgt_image->DumpChunks();
1103 src_image->DumpChunks();
1104 return false;
1105 }
Doug Zongker512536a2010-02-17 16:11:44 -08001106 }
1107
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001108 for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) {
1109 auto& tgt_chunk = *tgt_image->Get(i);
1110 auto& src_chunk = *src_image->Get(i);
1111 if (tgt_chunk.GetType() != CHUNK_DEFLATE) {
1112 continue;
1113 }
1114
1115 // Confirm that we can recompress the data and get exactly the same bits as are in the
1116 // input target image.
1117 if (!tgt_chunk.ReconstructDeflateChunk()) {
1118 printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i,
1119 tgt_chunk.GetEntryName().c_str());
1120 tgt_chunk.ChangeDeflateChunkToNormal();
1121 src_chunk.ChangeDeflateChunkToNormal();
1122 continue;
1123 }
1124
1125 // If two deflate chunks are identical treat them as normal chunks.
1126 if (tgt_chunk == src_chunk) {
1127 tgt_chunk.ChangeDeflateChunkToNormal();
1128 src_chunk.ChangeDeflateChunkToNormal();
1129 }
Doug Zongker512536a2010-02-17 16:11:44 -08001130 }
1131
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001132 // For images, we need to maintain the parallel structure of the chunk lists, so do the merging
1133 // in both the source and target lists.
1134 tgt_image->MergeAdjacentNormalChunks();
1135 src_image->MergeAdjacentNormalChunks();
1136 if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) {
1137 // This shouldn't happen.
1138 printf("merging normal chunks went awry\n");
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001139 return false;
Doug Zongker512536a2010-02-17 16:11:44 -08001140 }
Doug Zongker512536a2010-02-17 16:11:44 -08001141
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001142 return true;
Doug Zongker512536a2010-02-17 16:11:44 -08001143}
1144
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001145// In image mode, generate patches against the given source chunks and bonus_data; write the
1146// result to |patch_name|.
1147bool ImageModeImage::GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* src_image,
1148 const std::vector<uint8_t>& bonus_data,
1149 const std::string& patch_name) {
1150 printf("Construct patches for %zu chunks...\n", tgt_image->NumOfChunks());
1151 std::vector<std::vector<uint8_t>> patch_data(tgt_image->NumOfChunks());
1152
1153 for (size_t i = 0; i < tgt_image->NumOfChunks(); i++) {
1154 auto& tgt_chunk = *tgt_image->Get(i);
1155 auto& src_chunk = *src_image->Get(i);
1156
1157 if (i == 1 && !bonus_data.empty()) {
1158 printf(" using %zu bytes of bonus data for chunk %zu\n", bonus_data.size(), i);
1159 src_chunk.SetBonusData(bonus_data);
Doug Zongker512536a2010-02-17 16:11:44 -08001160 }
1161
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001162 if (!tgt_chunk.MakePatch(src_chunk, &patch_data[i], nullptr)) {
1163 printf("Failed to generate patch for target chunk %zu: ", i);
1164 return false;
Doug Zongker512536a2010-02-17 16:11:44 -08001165 }
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001166 printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(),
1167 tgt_chunk.GetRawDataLength());
Doug Zongker512536a2010-02-17 16:11:44 -08001168 }
Doug Zongker512536a2010-02-17 16:11:44 -08001169
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001170 android::base::unique_fd patch_fd(
1171 open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR));
1172 if (patch_fd == -1) {
1173 printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno));
1174 return false;
Doug Zongker512536a2010-02-17 16:11:44 -08001175 }
Doug Zongker512536a2010-02-17 16:11:44 -08001176
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001177 return tgt_image->WritePatchDataToFd(patch_data, patch_fd);
Doug Zongker512536a2010-02-17 16:11:44 -08001178}
1179
Tao Bao97555da2016-12-15 10:15:06 -08001180int imgdiff(int argc, const char** argv) {
1181 bool zip_mode = false;
Tianjie Xu1ea84d62017-02-22 18:23:58 -08001182 std::vector<uint8_t> bonus_data;
Tianjie Xu12b90552017-03-07 14:44:14 -08001183
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001184 int opt;
1185 optind = 1; // Reset the getopt state so that we can call it multiple times for test.
Doug Zongkera3ccba62012-08-20 15:28:02 -07001186
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001187 while ((opt = getopt(argc, const_cast<char**>(argv), "zb:")) != -1) {
1188 switch (opt) {
1189 case 'z':
1190 zip_mode = true;
1191 break;
1192 case 'b': {
1193 android::base::unique_fd fd(open(optarg, O_RDONLY));
1194 if (fd == -1) {
1195 printf("failed to open bonus file %s: %s\n", optarg, strerror(errno));
1196 return 1;
1197 }
1198 struct stat st;
1199 if (fstat(fd, &st) != 0) {
1200 printf("failed to stat bonus file %s: %s\n", optarg, strerror(errno));
1201 return 1;
1202 }
1203
1204 size_t bonus_size = st.st_size;
1205 bonus_data.resize(bonus_size);
1206 if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) {
1207 printf("failed to read bonus file %s: %s\n", optarg, strerror(errno));
1208 return 1;
1209 }
1210 break;
1211 }
1212 default:
1213 printf("unexpected opt: %s\n", optarg);
1214 return 2;
1215 }
Doug Zongkera3ccba62012-08-20 15:28:02 -07001216 }
1217
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001218 if (argc - optind != 3) {
1219 printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n", argv[0]);
Doug Zongkera3ccba62012-08-20 15:28:02 -07001220 return 2;
1221 }
Doug Zongker512536a2010-02-17 16:11:44 -08001222
Doug Zongker512536a2010-02-17 16:11:44 -08001223 if (zip_mode) {
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001224 ZipModeImage src_image(true);
1225 ZipModeImage tgt_image(false);
1226
1227 if (!src_image.Initialize(argv[optind])) {
Doug Zongker512536a2010-02-17 16:11:44 -08001228 return 1;
1229 }
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001230 if (!tgt_image.Initialize(argv[optind + 1])) {
1231 return 1;
1232 }
1233
1234 if (!ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) {
1235 return 1;
1236 }
1237 // Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of
1238 // deflate chunks).
1239 if (!ZipModeImage::GeneratePatches(&tgt_image, &src_image, argv[optind + 2])) {
Doug Zongker512536a2010-02-17 16:11:44 -08001240 return 1;
1241 }
1242 } else {
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001243 ImageModeImage src_image(true);
1244 ImageModeImage tgt_image(false);
1245
1246 if (!src_image.Initialize(argv[optind])) {
Doug Zongker512536a2010-02-17 16:11:44 -08001247 return 1;
1248 }
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001249 if (!tgt_image.Initialize(argv[optind + 1])) {
Doug Zongker512536a2010-02-17 16:11:44 -08001250 return 1;
1251 }
1252
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001253 if (!ImageModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) {
Doug Zongker512536a2010-02-17 16:11:44 -08001254 return 1;
1255 }
Tianjie Xu6b03ba72017-07-19 14:16:30 -07001256 if (!ImageModeImage::GeneratePatches(&tgt_image, &src_image, bonus_data, argv[optind + 2])) {
Doug Zongker512536a2010-02-17 16:11:44 -08001257 return 1;
1258 }
1259 }
1260
Doug Zongker512536a2010-02-17 16:11:44 -08001261 return 0;
1262}