1*acea8879SAndroid Build Coastguard Worker /*
2*acea8879SAndroid Build Coastguard Worker * Copyright (C) 2009 The Android Open Source Project
3*acea8879SAndroid Build Coastguard Worker *
4*acea8879SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*acea8879SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*acea8879SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*acea8879SAndroid Build Coastguard Worker *
8*acea8879SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*acea8879SAndroid Build Coastguard Worker *
10*acea8879SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*acea8879SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*acea8879SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*acea8879SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*acea8879SAndroid Build Coastguard Worker * limitations under the License.
15*acea8879SAndroid Build Coastguard Worker */
16*acea8879SAndroid Build Coastguard Worker
17*acea8879SAndroid Build Coastguard Worker // See imgdiff.cpp in this directory for a description of the patch file
18*acea8879SAndroid Build Coastguard Worker // format.
19*acea8879SAndroid Build Coastguard Worker
20*acea8879SAndroid Build Coastguard Worker #include <applypatch/imgpatch.h>
21*acea8879SAndroid Build Coastguard Worker
22*acea8879SAndroid Build Coastguard Worker #include <errno.h>
23*acea8879SAndroid Build Coastguard Worker #include <stdio.h>
24*acea8879SAndroid Build Coastguard Worker #include <string.h>
25*acea8879SAndroid Build Coastguard Worker #include <sys/cdefs.h>
26*acea8879SAndroid Build Coastguard Worker #include <sys/stat.h>
27*acea8879SAndroid Build Coastguard Worker #include <unistd.h>
28*acea8879SAndroid Build Coastguard Worker
29*acea8879SAndroid Build Coastguard Worker #include <memory>
30*acea8879SAndroid Build Coastguard Worker #include <string>
31*acea8879SAndroid Build Coastguard Worker #include <vector>
32*acea8879SAndroid Build Coastguard Worker
33*acea8879SAndroid Build Coastguard Worker #include <android-base/logging.h>
34*acea8879SAndroid Build Coastguard Worker #include <android-base/memory.h>
35*acea8879SAndroid Build Coastguard Worker #include <applypatch/applypatch.h>
36*acea8879SAndroid Build Coastguard Worker #include <applypatch/imgdiff.h>
37*acea8879SAndroid Build Coastguard Worker #include <openssl/sha.h>
38*acea8879SAndroid Build Coastguard Worker #include <zlib.h>
39*acea8879SAndroid Build Coastguard Worker
40*acea8879SAndroid Build Coastguard Worker #include "edify/expr.h"
41*acea8879SAndroid Build Coastguard Worker #include "otautil/print_sha1.h"
42*acea8879SAndroid Build Coastguard Worker
Read8(const void * address)43*acea8879SAndroid Build Coastguard Worker static inline int64_t Read8(const void *address) {
44*acea8879SAndroid Build Coastguard Worker return android::base::get_unaligned<int64_t>(address);
45*acea8879SAndroid Build Coastguard Worker }
46*acea8879SAndroid Build Coastguard Worker
Read4(const void * address)47*acea8879SAndroid Build Coastguard Worker static inline int32_t Read4(const void *address) {
48*acea8879SAndroid Build Coastguard Worker return android::base::get_unaligned<int32_t>(address);
49*acea8879SAndroid Build Coastguard Worker }
50*acea8879SAndroid Build Coastguard Worker
51*acea8879SAndroid Build Coastguard Worker // This function is a wrapper of ApplyBSDiffPatch(). It has a custom sink function to deflate the
52*acea8879SAndroid Build Coastguard Worker // patched data and stream the deflated data to output.
ApplyBSDiffPatchAndStreamOutput(const uint8_t * src_data,size_t src_len,const Value & patch,size_t patch_offset,const char * deflate_header,SinkFn sink)53*acea8879SAndroid Build Coastguard Worker static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len,
54*acea8879SAndroid Build Coastguard Worker const Value& patch, size_t patch_offset,
55*acea8879SAndroid Build Coastguard Worker const char* deflate_header, SinkFn sink) {
56*acea8879SAndroid Build Coastguard Worker size_t expected_target_length = static_cast<size_t>(Read8(deflate_header + 32));
57*acea8879SAndroid Build Coastguard Worker CHECK_GT(expected_target_length, static_cast<size_t>(0));
58*acea8879SAndroid Build Coastguard Worker int level = Read4(deflate_header + 40);
59*acea8879SAndroid Build Coastguard Worker int method = Read4(deflate_header + 44);
60*acea8879SAndroid Build Coastguard Worker int window_bits = Read4(deflate_header + 48);
61*acea8879SAndroid Build Coastguard Worker int mem_level = Read4(deflate_header + 52);
62*acea8879SAndroid Build Coastguard Worker int strategy = Read4(deflate_header + 56);
63*acea8879SAndroid Build Coastguard Worker
64*acea8879SAndroid Build Coastguard Worker z_stream strm;
65*acea8879SAndroid Build Coastguard Worker strm.zalloc = Z_NULL;
66*acea8879SAndroid Build Coastguard Worker strm.zfree = Z_NULL;
67*acea8879SAndroid Build Coastguard Worker strm.opaque = Z_NULL;
68*acea8879SAndroid Build Coastguard Worker strm.avail_in = 0;
69*acea8879SAndroid Build Coastguard Worker strm.next_in = nullptr;
70*acea8879SAndroid Build Coastguard Worker int ret = deflateInit2(&strm, level, method, window_bits, mem_level, strategy);
71*acea8879SAndroid Build Coastguard Worker if (ret != Z_OK) {
72*acea8879SAndroid Build Coastguard Worker LOG(ERROR) << "Failed to init uncompressed data deflation: " << ret;
73*acea8879SAndroid Build Coastguard Worker return false;
74*acea8879SAndroid Build Coastguard Worker }
75*acea8879SAndroid Build Coastguard Worker
76*acea8879SAndroid Build Coastguard Worker // Define a custom sink wrapper that feeds to bspatch. It deflates the available patch data on
77*acea8879SAndroid Build Coastguard Worker // the fly and outputs the compressed data to the given sink.
78*acea8879SAndroid Build Coastguard Worker size_t actual_target_length = 0;
79*acea8879SAndroid Build Coastguard Worker size_t total_written = 0;
80*acea8879SAndroid Build Coastguard Worker static constexpr size_t buffer_size = 32768;
81*acea8879SAndroid Build Coastguard Worker auto compression_sink = [&strm, &actual_target_length, &expected_target_length, &total_written,
82*acea8879SAndroid Build Coastguard Worker &ret, &sink](const uint8_t* data, size_t len) -> size_t {
83*acea8879SAndroid Build Coastguard Worker // The input patch length for an update never exceeds INT_MAX.
84*acea8879SAndroid Build Coastguard Worker strm.avail_in = len;
85*acea8879SAndroid Build Coastguard Worker strm.next_in = data;
86*acea8879SAndroid Build Coastguard Worker do {
87*acea8879SAndroid Build Coastguard Worker std::vector<uint8_t> buffer(buffer_size);
88*acea8879SAndroid Build Coastguard Worker strm.avail_out = buffer_size;
89*acea8879SAndroid Build Coastguard Worker strm.next_out = buffer.data();
90*acea8879SAndroid Build Coastguard Worker if (actual_target_length + len < expected_target_length) {
91*acea8879SAndroid Build Coastguard Worker ret = deflate(&strm, Z_NO_FLUSH);
92*acea8879SAndroid Build Coastguard Worker } else {
93*acea8879SAndroid Build Coastguard Worker ret = deflate(&strm, Z_FINISH);
94*acea8879SAndroid Build Coastguard Worker }
95*acea8879SAndroid Build Coastguard Worker if (ret != Z_OK && ret != Z_STREAM_END) {
96*acea8879SAndroid Build Coastguard Worker LOG(ERROR) << "Failed to deflate stream: " << ret;
97*acea8879SAndroid Build Coastguard Worker // zero length indicates an error in the sink function of bspatch().
98*acea8879SAndroid Build Coastguard Worker return 0;
99*acea8879SAndroid Build Coastguard Worker }
100*acea8879SAndroid Build Coastguard Worker
101*acea8879SAndroid Build Coastguard Worker size_t have = buffer_size - strm.avail_out;
102*acea8879SAndroid Build Coastguard Worker total_written += have;
103*acea8879SAndroid Build Coastguard Worker if (sink(buffer.data(), have) != have) {
104*acea8879SAndroid Build Coastguard Worker LOG(ERROR) << "Failed to write " << have << " compressed bytes to output.";
105*acea8879SAndroid Build Coastguard Worker return 0;
106*acea8879SAndroid Build Coastguard Worker }
107*acea8879SAndroid Build Coastguard Worker } while ((strm.avail_in != 0 || strm.avail_out == 0) && ret != Z_STREAM_END);
108*acea8879SAndroid Build Coastguard Worker
109*acea8879SAndroid Build Coastguard Worker actual_target_length += len;
110*acea8879SAndroid Build Coastguard Worker return len;
111*acea8879SAndroid Build Coastguard Worker };
112*acea8879SAndroid Build Coastguard Worker
113*acea8879SAndroid Build Coastguard Worker int bspatch_result = ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink);
114*acea8879SAndroid Build Coastguard Worker deflateEnd(&strm);
115*acea8879SAndroid Build Coastguard Worker
116*acea8879SAndroid Build Coastguard Worker if (bspatch_result != 0) {
117*acea8879SAndroid Build Coastguard Worker return false;
118*acea8879SAndroid Build Coastguard Worker }
119*acea8879SAndroid Build Coastguard Worker
120*acea8879SAndroid Build Coastguard Worker if (ret != Z_STREAM_END) {
121*acea8879SAndroid Build Coastguard Worker LOG(ERROR) << "ret is expected to be Z_STREAM_END, but it's " << ret;
122*acea8879SAndroid Build Coastguard Worker return false;
123*acea8879SAndroid Build Coastguard Worker }
124*acea8879SAndroid Build Coastguard Worker
125*acea8879SAndroid Build Coastguard Worker if (expected_target_length != actual_target_length) {
126*acea8879SAndroid Build Coastguard Worker LOG(ERROR) << "target length is expected to be " << expected_target_length << ", but it's "
127*acea8879SAndroid Build Coastguard Worker << actual_target_length;
128*acea8879SAndroid Build Coastguard Worker return false;
129*acea8879SAndroid Build Coastguard Worker }
130*acea8879SAndroid Build Coastguard Worker LOG(DEBUG) << "bspatch wrote " << total_written << " bytes in total to streaming output.";
131*acea8879SAndroid Build Coastguard Worker
132*acea8879SAndroid Build Coastguard Worker return true;
133*acea8879SAndroid Build Coastguard Worker }
134*acea8879SAndroid Build Coastguard Worker
ApplyImagePatch(const unsigned char * old_data,size_t old_size,const unsigned char * patch_data,size_t patch_size,SinkFn sink)135*acea8879SAndroid Build Coastguard Worker int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
136*acea8879SAndroid Build Coastguard Worker size_t patch_size, SinkFn sink) {
137*acea8879SAndroid Build Coastguard Worker Value patch(Value::Type::BLOB,
138*acea8879SAndroid Build Coastguard Worker std::string(reinterpret_cast<const char*>(patch_data), patch_size));
139*acea8879SAndroid Build Coastguard Worker return ApplyImagePatch(old_data, old_size, patch, sink, nullptr);
140*acea8879SAndroid Build Coastguard Worker }
141*acea8879SAndroid Build Coastguard Worker
ApplyImagePatch(const unsigned char * old_data,size_t old_size,const Value & patch,SinkFn sink,const Value * bonus_data)142*acea8879SAndroid Build Coastguard Worker int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
143*acea8879SAndroid Build Coastguard Worker const Value* bonus_data) {
144*acea8879SAndroid Build Coastguard Worker if (patch.data.size() < 12) {
145*acea8879SAndroid Build Coastguard Worker printf("patch too short to contain header\n");
146*acea8879SAndroid Build Coastguard Worker return -1;
147*acea8879SAndroid Build Coastguard Worker }
148*acea8879SAndroid Build Coastguard Worker
149*acea8879SAndroid Build Coastguard Worker // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW. (IMGDIFF1, which is no longer
150*acea8879SAndroid Build Coastguard Worker // supported, used CHUNK_NORMAL and CHUNK_GZIP.)
151*acea8879SAndroid Build Coastguard Worker const char* const patch_header = patch.data.data();
152*acea8879SAndroid Build Coastguard Worker if (memcmp(patch_header, "IMGDIFF2", 8) != 0) {
153*acea8879SAndroid Build Coastguard Worker printf("corrupt patch file header (magic number)\n");
154*acea8879SAndroid Build Coastguard Worker return -1;
155*acea8879SAndroid Build Coastguard Worker }
156*acea8879SAndroid Build Coastguard Worker
157*acea8879SAndroid Build Coastguard Worker int num_chunks = Read4(patch_header + 8);
158*acea8879SAndroid Build Coastguard Worker size_t pos = 12;
159*acea8879SAndroid Build Coastguard Worker for (int i = 0; i < num_chunks; ++i) {
160*acea8879SAndroid Build Coastguard Worker // each chunk's header record starts with 4 bytes.
161*acea8879SAndroid Build Coastguard Worker if (pos + 4 > patch.data.size()) {
162*acea8879SAndroid Build Coastguard Worker printf("failed to read chunk %d record\n", i);
163*acea8879SAndroid Build Coastguard Worker return -1;
164*acea8879SAndroid Build Coastguard Worker }
165*acea8879SAndroid Build Coastguard Worker int type = Read4(patch_header + pos);
166*acea8879SAndroid Build Coastguard Worker pos += 4;
167*acea8879SAndroid Build Coastguard Worker
168*acea8879SAndroid Build Coastguard Worker if (type == CHUNK_NORMAL) {
169*acea8879SAndroid Build Coastguard Worker const char* normal_header = patch_header + pos;
170*acea8879SAndroid Build Coastguard Worker pos += 24;
171*acea8879SAndroid Build Coastguard Worker if (pos > patch.data.size()) {
172*acea8879SAndroid Build Coastguard Worker printf("failed to read chunk %d normal header data\n", i);
173*acea8879SAndroid Build Coastguard Worker return -1;
174*acea8879SAndroid Build Coastguard Worker }
175*acea8879SAndroid Build Coastguard Worker
176*acea8879SAndroid Build Coastguard Worker size_t src_start = static_cast<size_t>(Read8(normal_header));
177*acea8879SAndroid Build Coastguard Worker size_t src_len = static_cast<size_t>(Read8(normal_header + 8));
178*acea8879SAndroid Build Coastguard Worker size_t patch_offset = static_cast<size_t>(Read8(normal_header + 16));
179*acea8879SAndroid Build Coastguard Worker
180*acea8879SAndroid Build Coastguard Worker if (src_start + src_len > old_size) {
181*acea8879SAndroid Build Coastguard Worker printf("source data too short\n");
182*acea8879SAndroid Build Coastguard Worker return -1;
183*acea8879SAndroid Build Coastguard Worker }
184*acea8879SAndroid Build Coastguard Worker if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink) != 0) {
185*acea8879SAndroid Build Coastguard Worker printf("Failed to apply bsdiff patch.\n");
186*acea8879SAndroid Build Coastguard Worker return -1;
187*acea8879SAndroid Build Coastguard Worker }
188*acea8879SAndroid Build Coastguard Worker
189*acea8879SAndroid Build Coastguard Worker LOG(DEBUG) << "Processed chunk type normal";
190*acea8879SAndroid Build Coastguard Worker } else if (type == CHUNK_RAW) {
191*acea8879SAndroid Build Coastguard Worker const char* raw_header = patch_header + pos;
192*acea8879SAndroid Build Coastguard Worker pos += 4;
193*acea8879SAndroid Build Coastguard Worker if (pos > patch.data.size()) {
194*acea8879SAndroid Build Coastguard Worker printf("failed to read chunk %d raw header data\n", i);
195*acea8879SAndroid Build Coastguard Worker return -1;
196*acea8879SAndroid Build Coastguard Worker }
197*acea8879SAndroid Build Coastguard Worker
198*acea8879SAndroid Build Coastguard Worker size_t data_len = static_cast<size_t>(Read4(raw_header));
199*acea8879SAndroid Build Coastguard Worker
200*acea8879SAndroid Build Coastguard Worker if (pos + data_len > patch.data.size()) {
201*acea8879SAndroid Build Coastguard Worker printf("failed to read chunk %d raw data\n", i);
202*acea8879SAndroid Build Coastguard Worker return -1;
203*acea8879SAndroid Build Coastguard Worker }
204*acea8879SAndroid Build Coastguard Worker if (sink(reinterpret_cast<const unsigned char*>(patch_header + pos), data_len) != data_len) {
205*acea8879SAndroid Build Coastguard Worker printf("failed to write chunk %d raw data\n", i);
206*acea8879SAndroid Build Coastguard Worker return -1;
207*acea8879SAndroid Build Coastguard Worker }
208*acea8879SAndroid Build Coastguard Worker pos += data_len;
209*acea8879SAndroid Build Coastguard Worker
210*acea8879SAndroid Build Coastguard Worker LOG(DEBUG) << "Processed chunk type raw";
211*acea8879SAndroid Build Coastguard Worker } else if (type == CHUNK_DEFLATE) {
212*acea8879SAndroid Build Coastguard Worker // deflate chunks have an additional 60 bytes in their chunk header.
213*acea8879SAndroid Build Coastguard Worker const char* deflate_header = patch_header + pos;
214*acea8879SAndroid Build Coastguard Worker pos += 60;
215*acea8879SAndroid Build Coastguard Worker if (pos > patch.data.size()) {
216*acea8879SAndroid Build Coastguard Worker printf("failed to read chunk %d deflate header data\n", i);
217*acea8879SAndroid Build Coastguard Worker return -1;
218*acea8879SAndroid Build Coastguard Worker }
219*acea8879SAndroid Build Coastguard Worker
220*acea8879SAndroid Build Coastguard Worker size_t src_start = static_cast<size_t>(Read8(deflate_header));
221*acea8879SAndroid Build Coastguard Worker size_t src_len = static_cast<size_t>(Read8(deflate_header + 8));
222*acea8879SAndroid Build Coastguard Worker size_t patch_offset = static_cast<size_t>(Read8(deflate_header + 16));
223*acea8879SAndroid Build Coastguard Worker size_t expanded_len = static_cast<size_t>(Read8(deflate_header + 24));
224*acea8879SAndroid Build Coastguard Worker
225*acea8879SAndroid Build Coastguard Worker if (src_start + src_len > old_size) {
226*acea8879SAndroid Build Coastguard Worker printf("source data too short\n");
227*acea8879SAndroid Build Coastguard Worker return -1;
228*acea8879SAndroid Build Coastguard Worker }
229*acea8879SAndroid Build Coastguard Worker
230*acea8879SAndroid Build Coastguard Worker // Decompress the source data; the chunk header tells us exactly
231*acea8879SAndroid Build Coastguard Worker // how big we expect it to be when decompressed.
232*acea8879SAndroid Build Coastguard Worker
233*acea8879SAndroid Build Coastguard Worker // Note: expanded_len will include the bonus data size if the patch was constructed with
234*acea8879SAndroid Build Coastguard Worker // bonus data. The deflation will come up 'bonus_size' bytes short; these must be appended
235*acea8879SAndroid Build Coastguard Worker // from the bonus_data value.
236*acea8879SAndroid Build Coastguard Worker size_t bonus_size = (i == 1 && bonus_data != nullptr) ? bonus_data->data.size() : 0;
237*acea8879SAndroid Build Coastguard Worker
238*acea8879SAndroid Build Coastguard Worker std::vector<unsigned char> expanded_source(expanded_len);
239*acea8879SAndroid Build Coastguard Worker
240*acea8879SAndroid Build Coastguard Worker // inflate() doesn't like strm.next_out being a nullptr even with
241*acea8879SAndroid Build Coastguard Worker // avail_out being zero (Z_STREAM_ERROR).
242*acea8879SAndroid Build Coastguard Worker if (expanded_len != 0) {
243*acea8879SAndroid Build Coastguard Worker z_stream strm;
244*acea8879SAndroid Build Coastguard Worker strm.zalloc = Z_NULL;
245*acea8879SAndroid Build Coastguard Worker strm.zfree = Z_NULL;
246*acea8879SAndroid Build Coastguard Worker strm.opaque = Z_NULL;
247*acea8879SAndroid Build Coastguard Worker strm.avail_in = src_len;
248*acea8879SAndroid Build Coastguard Worker strm.next_in = old_data + src_start;
249*acea8879SAndroid Build Coastguard Worker strm.avail_out = expanded_len;
250*acea8879SAndroid Build Coastguard Worker strm.next_out = expanded_source.data();
251*acea8879SAndroid Build Coastguard Worker
252*acea8879SAndroid Build Coastguard Worker int ret = inflateInit2(&strm, -15);
253*acea8879SAndroid Build Coastguard Worker if (ret != Z_OK) {
254*acea8879SAndroid Build Coastguard Worker printf("failed to init source inflation: %d\n", ret);
255*acea8879SAndroid Build Coastguard Worker return -1;
256*acea8879SAndroid Build Coastguard Worker }
257*acea8879SAndroid Build Coastguard Worker
258*acea8879SAndroid Build Coastguard Worker // Because we've provided enough room to accommodate the output
259*acea8879SAndroid Build Coastguard Worker // data, we expect one call to inflate() to suffice.
260*acea8879SAndroid Build Coastguard Worker ret = inflate(&strm, Z_SYNC_FLUSH);
261*acea8879SAndroid Build Coastguard Worker if (ret != Z_STREAM_END) {
262*acea8879SAndroid Build Coastguard Worker printf("source inflation returned %d\n", ret);
263*acea8879SAndroid Build Coastguard Worker return -1;
264*acea8879SAndroid Build Coastguard Worker }
265*acea8879SAndroid Build Coastguard Worker // We should have filled the output buffer exactly, except
266*acea8879SAndroid Build Coastguard Worker // for the bonus_size.
267*acea8879SAndroid Build Coastguard Worker if (strm.avail_out != bonus_size) {
268*acea8879SAndroid Build Coastguard Worker printf("source inflation short by %zu bytes\n", strm.avail_out - bonus_size);
269*acea8879SAndroid Build Coastguard Worker return -1;
270*acea8879SAndroid Build Coastguard Worker }
271*acea8879SAndroid Build Coastguard Worker inflateEnd(&strm);
272*acea8879SAndroid Build Coastguard Worker
273*acea8879SAndroid Build Coastguard Worker if (bonus_size) {
274*acea8879SAndroid Build Coastguard Worker memcpy(expanded_source.data() + (expanded_len - bonus_size), bonus_data->data.data(),
275*acea8879SAndroid Build Coastguard Worker bonus_size);
276*acea8879SAndroid Build Coastguard Worker }
277*acea8879SAndroid Build Coastguard Worker }
278*acea8879SAndroid Build Coastguard Worker
279*acea8879SAndroid Build Coastguard Worker if (!ApplyBSDiffPatchAndStreamOutput(expanded_source.data(), expanded_len, patch,
280*acea8879SAndroid Build Coastguard Worker patch_offset, deflate_header, sink)) {
281*acea8879SAndroid Build Coastguard Worker LOG(ERROR) << "Fail to apply streaming bspatch.";
282*acea8879SAndroid Build Coastguard Worker return -1;
283*acea8879SAndroid Build Coastguard Worker }
284*acea8879SAndroid Build Coastguard Worker
285*acea8879SAndroid Build Coastguard Worker LOG(DEBUG) << "Processed chunk type deflate";
286*acea8879SAndroid Build Coastguard Worker } else {
287*acea8879SAndroid Build Coastguard Worker printf("patch chunk %d is unknown type %d\n", i, type);
288*acea8879SAndroid Build Coastguard Worker return -1;
289*acea8879SAndroid Build Coastguard Worker }
290*acea8879SAndroid Build Coastguard Worker }
291*acea8879SAndroid Build Coastguard Worker
292*acea8879SAndroid Build Coastguard Worker return 0;
293*acea8879SAndroid Build Coastguard Worker }
294