xref: /OK3568_Linux_fs/external/recovery/update_engine/do_patch.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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