1 /*-
2 * Copyright 2003-2005 Colin Percival
3 * All rights reserved
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #if 0
28 __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
29 #endif
30
31 #include <bzlib.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <err.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/mman.h>
40 #include <sys/stat.h>
41 #include <limits.h>
42 #include <errno.h>
43 #include <stdbool.h>
44 #include "md5sum.h"
45 #include "log.h"
46
offtin(unsigned char * buf)47 static off_t offtin(unsigned char *buf)
48 {
49 off_t y;
50
51 y = buf[7] & 0x7F;
52 y = y * 256; y += buf[6];
53 y = y * 256; y += buf[5];
54 y = y * 256; y += buf[4];
55 y = y * 256; y += buf[3];
56 y = y * 256; y += buf[2];
57 y = y * 256; y += buf[1];
58 y = y * 256; y += buf[0];
59
60 if (buf[7] & 0x80) y = -y;
61
62 return y;
63 }
64
65 /* Return:
66 * -1 patching error,
67 * 0 not a diff img,
68 * >0 the new image size
69 * dst_file size if patch successfully
70 */
do_patch_rkimg(const char * img,ssize_t offset,ssize_t size,const char * blk_dev,const char * dst_file)71 int do_patch_rkimg(const char *img, ssize_t offset, ssize_t size,
72 const char *blk_dev, const char *dst_file)
73 {
74 #define TAIL_SIZE 80
75 #define TID_HEAD 0
76 #define TID_NAME 1
77 #define TID_OLD_SIZE 2
78 #define TID_NEW_SIZE 3
79 #define TID_MD5SUM 4
80 #define TOKENS 5
81 #define MAGIC_TAIL "DIFF"
82 #define MD5SUM_LEN 32
83
84 // For tail parsing.
85 // Tail size is 80 bytes as like,
86 // "DIFF:%-15s:%-12s:%-12s:%-32s:"
87 // $name $old_size $new_size $md5sum
88 int fd_img;
89 const char *split = ": "; //space is a split char too
90 char tail[TAIL_SIZE];
91 ssize_t len, ret;
92 ssize_t oldsize, newsize;
93 char *saveptr, *str, *name, *md5sum, *token[TOKENS];
94 int j;
95
96 // For patching
97 FILE * f = NULL, * cpf = NULL, * dpf = NULL, * epf = NULL;
98 BZFILE * cpfbz2, * dpfbz2, * epfbz2;
99 int cbz2err, dbz2err, ebz2err;
100 int fd;
101 ssize_t bzctrllen, bzdatalen;
102 unsigned char header[32], buf[8];
103 unsigned char *old = NULL, *new_ptr = NULL;
104 off_t oldpos, newpos;
105 off_t ctrl[3];
106 off_t lenread;
107 off_t i;
108 struct stat old_stat, dst_stat;
109
110 if ((fd_img = open(img, O_RDONLY, 0)) < 0) {
111 LOGE("open %s failed\n", img);
112 return -1;
113 }
114 if (lseek(fd_img, offset + size - TAIL_SIZE, SEEK_SET) < 0) {
115 LOGE("%s: lseek to: %ld failed: %s\n", img,
116 offset + size - TAIL_SIZE, strerror(errno));
117 close(fd_img);
118 return -1;
119 }
120 len = 0;
121 while (len != TAIL_SIZE) {
122 ret = read(fd_img, tail + len, TAIL_SIZE - len);
123 if (ret < 0) {
124 LOGE("read %s tail err\n", img);
125 close(fd_img);
126 return -1;
127 }
128 len += ret;
129 }
130 close(fd_img);
131
132 tail[TAIL_SIZE - 1] = '\0';
133 for (j = 0, str = tail; j < TOKENS; j++, str = NULL) {
134 token[j] = strtok_r(str, split, &saveptr);
135 if (token[j] == NULL)
136 break;
137 }
138 name = token[TID_NAME];
139 md5sum = token[TID_MD5SUM];
140
141 /* When unexpected reboot during patching/writing happened,
142 * if dst_file is in correct state, then old image may already broken
143 */
144 if (stat(dst_file, &dst_stat) == 0 &&
145 compareMd5sum(dst_file, (unsigned char *)md5sum, 0, dst_stat.st_size)) {
146 LOGI("Recovery from unecptected reboot successfully.");
147 return dst_stat.st_size;
148 }
149 /* If dst_file exist but md5sum is wrong, old image file is clean, hopefully */
150
151 //check tail magic, return 0 if not exist
152 if (j == 0 || strncmp(MAGIC_TAIL, token[TID_HEAD], strlen(MAGIC_TAIL)) != 0) {
153 LOGW("Not a diff image, ret = %ld\n", ret);
154 return 0;
155 }
156 LOGI("This is a diff image, patching...\n");
157 if (j != TOKENS ||
158 (oldsize = strtol(token[TID_OLD_SIZE], &saveptr, 10)) == 0 ||
159 (errno == ERANGE && (oldsize == LONG_MAX || oldsize == LONG_MIN)) ||
160 saveptr == token[TID_OLD_SIZE] ||
161 (newsize = strtol(token[TID_NEW_SIZE], &saveptr, 10)) == 0 ||
162 (errno == ERANGE && (newsize == LONG_MAX || newsize == LONG_MIN)) ||
163 saveptr == token[TID_NEW_SIZE] ||
164 strlen(token[TID_MD5SUM]) != MD5SUM_LEN) {
165 LOGE("Bad Tail header of bsdiff patch\n");
166 return -1;
167 }
168
169 //TODO: check dst_file dir size, return -1 if space too small.
170
171 /* Open patch file */
172 if ((f = fopen(img, "r")) == NULL) {
173 LOGE("fopen %s err\n", img);
174 return -1;
175 }
176 if (fseeko(f, offset, SEEK_SET)) {
177 LOGE("fseeko %s err\n", img);
178 fclose(f);
179 return -1;
180 }
181
182 /*
183 File format:
184 0 8 "BSDIFF40"
185 8 8 X
186 16 8 Y
187 24 8 sizeof(newfile)
188 32 X bzip2(control block)
189 32+X Y bzip2(diff block)
190 32+X+Y ??? bzip2(extra block)
191 with control block a set of triples (x,y,z) meaning "add x bytes
192 from oldfile to x bytes from the diff block; copy y bytes from the
193 extra block; seek forwards in oldfile by z bytes".
194 */
195
196 /* Read header */
197 if (fread(header, 1, 32, f) < 32) {
198 LOGE("Read header err\n");
199 fclose(f);
200 return -1;
201 }
202 fclose(f);
203 f = NULL;
204
205 /* Check for appropriate magic */
206 if (memcmp(header, "BSDIFF40", 8) != 0) {
207 LOGE("Bad header, Corrupt patch\n");
208 return -1;
209 }
210
211 /* Read lengths from header */
212 bzctrllen = offtin(header + 8);
213 bzdatalen = offtin(header + 16);
214 newsize = offtin(header + 24);
215 if ((bzctrllen < 0) || (bzdatalen < 0) || (newsize <= 0)) {
216 LOGE("Bad header len, Corrupt patch\n");
217 return -1;
218 }
219
220 /* re-open patch file via libbzip2 at the right places */
221 if ((cpf = fopen(img, "r")) == NULL)
222 return -1;
223 if (fseeko(cpf, offset + 32, SEEK_SET)) {
224 LOGE("fseeko(%s, %lld) err\n", img, (long long)(32 + offset));
225 goto cleanup;
226 }
227 if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) {
228 LOGE("BZ2_bzReadOpen, bz2err = %d, err\n", cbz2err);
229 goto cleanup;
230 }
231 if ((dpf = fopen(img, "r")) == NULL)
232 goto cleanup;
233 if (fseeko(dpf, offset + 32 + bzctrllen, SEEK_SET)) {
234 LOGE("fseeko(%s, %lld) err\n", img,
235 (long long)(offset + 32 + bzctrllen));
236 goto cleanup;
237 }
238 if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) {
239 LOGE("BZ2_bzReadOpen, bz2err = %d, err\n", dbz2err);
240 goto cleanup;
241 }
242 if ((epf = fopen(img, "r")) == NULL) {
243 LOGE("fopen(%s) err\n", img);
244 goto cleanup;
245 }
246 if (fseeko(epf, offset + 32 + bzctrllen + bzdatalen, SEEK_SET)) {
247 LOGE("fseeko(%s, %lld) err\n", img,
248 (long long)(offset + 32 + bzctrllen + bzdatalen));
249 goto cleanup;
250 }
251 if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) {
252 LOGE("BZ2_bzReadOpen, bz2err = %d\n", ebz2err);
253 goto cleanup;
254 }
255
256 if (((fd = open(blk_dev, O_RDONLY, 0)) < 0) ||
257 ((old = (unsigned char *)mmap(NULL, oldsize, PROT_READ,
258 MAP_SHARED | MAP_POPULATE, fd, 0)) == MAP_FAILED)) {
259 LOGE("open %s err\n", blk_dev);
260 goto cleanup;
261 }
262 close(fd);
263 fd = -1;
264
265 /* mmap the new file */
266 if (((fd = open(dst_file, O_CREAT | O_TRUNC | O_RDWR, 0666)) < 0) ||
267 (lseek(fd, newsize - 1, SEEK_SET) != (newsize - 1)) ||
268 (write(fd, "E", 1) != 1) ||
269 (lseek(fd, 0, SEEK_SET) != 0) ||
270 ((new_ptr = (unsigned char *)mmap(NULL, newsize, PROT_READ | PROT_WRITE,
271 MAP_SHARED, fd, 0)) == MAP_FAILED)) {
272 LOGE("mmap %s err\n", dst_file);
273 goto cleanup;
274 }
275 close(fd);
276 fd = -1;
277
278 oldpos = 0; newpos = 0;
279 while (newpos < newsize) {
280 /* Read control data */
281 for (i = 0; i <= 2; i++) {
282 lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
283 if ((lenread < 8) || ((cbz2err != BZ_OK) &&
284 (cbz2err != BZ_STREAM_END))) {
285 LOGE("Read control data: Corrupt patch\n");
286 goto cleanup;
287 }
288 ctrl[i] = offtin(buf);
289 };
290
291 /* Sanity-check */
292 if (newpos + ctrl[0] > newsize) {
293 LOGE("Sanity-check: Corrupt patch\n");
294 goto cleanup;
295 }
296
297 /* Read diff string */
298 lenread = BZ2_bzRead(&dbz2err, dpfbz2, new_ptr + newpos, ctrl[0]);
299 if ((lenread < ctrl[0]) ||
300 ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) {
301 LOGE("Read diff string: Corrupt patch\n");
302 goto cleanup;
303 }
304
305 /* Add old data to diff string */
306 for (i = 0; i < ctrl[0]; i++)
307 if ((oldpos + i >= 0) && (oldpos + i < oldsize))
308 new_ptr[newpos + i] += old[oldpos + i];
309
310 /* Adjust pointers */
311 newpos += ctrl[0];
312 oldpos += ctrl[0];
313
314 /* Sanity-check */
315 if (newpos + ctrl[1] > newsize) {
316 LOGE("Sanity-check: Corrupt patch\n");
317 goto cleanup;
318 }
319
320 /* Read extra string */
321 lenread = BZ2_bzRead(&ebz2err, epfbz2, new_ptr + newpos, ctrl[1]);
322 if ((lenread < ctrl[1]) ||
323 ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) {
324 LOGE("Read extra string: Corrupt patch\n");
325 goto cleanup;
326 }
327
328 /* Adjust pointers */
329 newpos += ctrl[1];
330 oldpos += ctrl[2];
331 };
332
333 /* Clean up the bzip2 reads */
334 BZ2_bzReadClose(&cbz2err, cpfbz2);
335 BZ2_bzReadClose(&dbz2err, dpfbz2);
336 BZ2_bzReadClose(&ebz2err, epfbz2);
337 fclose(cpf);
338 fclose(dpf);
339 fclose(epf);
340
341 munmap(new_ptr, newsize);
342 munmap(old, oldsize);
343 sync();
344
345 //check md5sum
346 if (!compareMd5sum(dst_file, (unsigned char *)md5sum, 0, newsize))
347 return -1;
348
349 LOGI("Diff patch apply successfully for %s, size: %ld\n",
350 name, newsize);
351 return newsize;
352 cleanup:
353 if (new_ptr != NULL)
354 munmap(new_ptr, newsize);
355 if (old != NULL)
356 munmap(old, oldsize);
357 if (fd >= 0)
358 close(fd);
359 if (cpf != NULL)
360 fclose(cpf);
361 if (dpf != NULL)
362 fclose(dpf);
363 if (epf != NULL)
364 fclose(epf);
365 return -1;
366 }
367
368 #if 0
369 int main(int argc, char *argv[])
370 {
371 do_patch_rkimg("./update.img", 676276, 16964, "OLD/Image/uboot.img", "uboot.img");
372 do_patch_rkimg("./update.img", 672180, 3589, "OLD/Image/trust.img", "trust.img");
373 do_patch_rkimg("./update.img", 743860, 532546, "OLD/Image/boot.img", "boot.img");
374 do_patch_rkimg("./update.img", 15630772, 88186412, "OLD/Image/rootfs.img", "rootfs.img");
375
376 return 0;
377 }
378 #endif
379