blob: 4be99c7ac55620a0f120fc30bfd9bc0e0b426e2c [file] [log] [blame]
The Android Open Source Projectc285fea2009-03-03 19:29:20 -08001/*-
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>
Zdenek Behan339e0ee2010-06-14 12:50:53 -070032#include <errno.h>
33#include <inttypes.h>
34#include <stdint.h>
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080035#include <stdlib.h>
36#include <stdio.h>
37#include <string.h>
38#include <err.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <sys/types.h> // android
42
Zdenek Behan339e0ee2010-06-14 12:50:53 -070043#define JOIN(a, b) __JOIN(a, b)
44#define __JOIN(a, b) a ## b
45#define COMPILE_ASSERT(expr, message) \
46 typedef char JOIN(message, JOIN(_, __LINE__)) [(expr) ? 1 : -1]
47
48COMPILE_ASSERT(sizeof(int64_t) == 8, int64_t_64_bit);
Andrew de los Reyes11a4ecc2010-08-09 15:52:37 -070049COMPILE_ASSERT(sizeof(off_t) == 8, off_t_64_bit);
Zdenek Behan339e0ee2010-06-14 12:50:53 -070050
51#define MIN(a, b) \
52 ((a) < (b) ? (a) : (b))
53
Andrew de los Reyes3ebed9c2010-10-12 16:15:26 -070054static const char kFdFilePrefix[] = "/dev/fd/";
55
Zdenek Behan339e0ee2010-06-14 12:50:53 -070056// Reads next int from *ints. The int should be terminated with a comma
57// or NULL char. *ints will be updated to the space right after the comma
58// or set to NULL if this was the last number. This assumes the input is
59// a valid string, as validated with PositionsStringIsValid().
60// Returns 1 on success.
61int NextInt64(const char** ints, int64_t *out) {
62 if (!ints[0])
63 return 0;
64 int r = sscanf(*ints, "%" PRIi64, out);
65 if (r == 1) {
66 const char* next_comma = strchr(*ints, ',');
67 const char* next_colon = strchr(*ints, ':');
68 if (!next_comma && !next_colon)
69 *ints = NULL;
70 else if (!next_comma)
71 *ints = next_colon + 1;
72 else if (!next_colon)
73 *ints = next_comma + 1;
74 else
75 *ints = MIN(next_comma, next_colon) + 1;
76 return 1;
77 }
78 return 0;
79}
80
81COMPILE_ASSERT(sizeof(intmax_t) == 8, intmax_t_not_64_bit);
82
83// Returns 1 if str can be converted to int64_t without over/underflowing.
84// str is assumed to point to an optional negative sign followed by numbers,
85// optionally followed by non-numeric characters, followed by '\0'.
86int IsValidInt64(const char* str) {
Andrew de los Reyes3ebed9c2010-10-12 16:15:26 -070087 char* end_ptr = 0;
Zdenek Behan339e0ee2010-06-14 12:50:53 -070088 errno = 0;
89 intmax_t result = strtoimax(str, &end_ptr, /* base: */ 10);
90 return errno == 0;
91}
92
93// Input validator. Make sure the positions string is well formatted.
94// All numerical values are checked to make sure they don't over/underflow
95// int64_t. Returns 1 if valid.
96int PositionsStringIsValid(const char* positions) {
97 if (positions == NULL)
98 errx(1, "bad string");
99
100 // Special case: empty string is valid
101 if (!positions[0])
102 return 1;
103
104 // Use a state machine to determine if the string is valid.
105 // Key: (s): state, ((s)) valid end state.
106 // n (negative_valid) is a boolean that starts out as true.
107 // If n is true, ':' is the delimiter, otherwise ','.
108 //
109 // .--------------------------.
110 // | | n ? ':' : ',' ; n = !n
111 // V '-'&&n 0-9 |
112 // start->(0)------------->(1)----->((2))---.
113 // `---------------------> <--' 0-9
114 // 0-9
115 int state = 0;
116 int negative_valid = 1;
117 const char* number_start = positions;
118 for (;; positions++) {
119 char c = *positions;
120 switch (state) {
121 case 0:
122 if (c == '-' && negative_valid) {
123 state = 1;
124 continue;
125 }
126 if (isdigit(c)) {
127 state = 2;
128 continue;
129 }
130 return 0;
131 case 1:
132 if (isdigit(c)) {
133 state = 2;
134 continue;
135 }
136 return 0;
137 case 2:
138 if (isdigit(c))
139 continue;
140 // number_start must point to a valid number
141 if (!IsValidInt64(number_start)) {
142 return 0;
143 }
144 if ((negative_valid && c == ':') ||
145 (!negative_valid && c == ',')) {
146 state = 0;
147 number_start = positions + 1;
148 negative_valid = !negative_valid;
149 continue;
150 }
151 return (c == '\0');
152 }
153 }
154}
155
Andrew de los Reyes11a4ecc2010-08-09 15:52:37 -0700156static const int64_t kMaxLength = sizeof(size_t) > 4 ? INT64_MAX : SIZE_MAX;
157
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700158// Reads into a buffer a series of byte ranges from filename.
159// Each range is a pair of comma-separated ints from positions.
160// -1 as an offset means a sparse-hole.
161// E.g. If positions were "1,5:23,4:-1,8:3,7", then we would return a buffer
162// consisting of 5 bytes from offset 1 of the file, followed by
163// 4 bytes from offset 23, then 8 bytes of all zeros, then 7 bytes from
164// offset 3 in the file.
165// Returns NULL on error.
166static char* PositionedRead(const char* filename,
167 const char* positions,
168 ssize_t* old_size) {
169 if (!PositionsStringIsValid(positions)) {
170 errx(1, "invalid positions string for read\n");
171 }
172
173 // Get length
174 const char* p = positions;
175 int64_t length = 0;
176 for (;;) {
177 int64_t value;
178 if (0 == NextInt64(&p, &value)) {
179 break;
180 }
181 int r = NextInt64(&p, &value);
182 if (r == 0) {
183 errx(1, "bad length parse\n");
184 }
185 if (value < 0) {
186 errx(1, "length can't be negative\n");
187 }
188 length += value;
189 }
190
191 // Malloc
192 if (length > 0x40000000) { // 1 GiB; sanity check
193 errx(1, "Read length too long (exceeds 1 GiB)");
194 }
195 // Following bsdiff convention, allocate length + 1 to avoid malloc(0)
196 char* buf = malloc(length + 1);
197 if (buf == NULL) {
198 errx(1, "malloc failed\n");
199 }
200 char* buf_tail = buf;
201
Andrew de los Reyes3ebed9c2010-10-12 16:15:26 -0700202 int fd = -1;
203 int should_close = 1;
204 if (strlen(filename) > strlen(kFdFilePrefix) &&
205 !strncmp(filename, kFdFilePrefix, strlen(kFdFilePrefix))) {
206 sscanf(filename + strlen(kFdFilePrefix), "%d", &fd);
207 should_close = 0;
208 } else {
209 fd = open(filename, O_RDONLY);
210 }
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700211 if (fd < 0) {
212 errx(1, "open failed for read\n");
213 }
214
215 // Read bytes
216 p = positions;
217 for (;;) {
218 int64_t offset, read_length;
219 if (NextInt64(&p, &offset) == 0) {
220 break;
221 }
222 if (offset < 0) {
223 errx(1, "no support for sparse positions "
224 "yet during read\n");
225 }
226 if (NextInt64(&p, &read_length) == 0) {
227 errx(1, "bad length parse (should never happen)\n");
228 }
229 if (read_length < 0) {
230 errx(1, "length can't be negative "
231 "(should never happen)\n");
232 }
Andrew de los Reyes11a4ecc2010-08-09 15:52:37 -0700233 if (read_length > kMaxLength) {
234 errx(1, "read length too large\n");
235 }
236 ssize_t rc = pread(fd, buf_tail, (size_t)read_length, (off_t)offset);
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700237 if (rc != read_length) {
238 errx(1, "read failed\n");
239 }
240 buf_tail += rc;
241 }
Andrew de los Reyes3ebed9c2010-10-12 16:15:26 -0700242 if (should_close)
243 close(fd);
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700244 *old_size = length;
245 return buf;
246}
247
248static void PositionedWrite(const char* filename,
249 const char* positions,
250 const char* buf,
251 ssize_t new_size) {
252 if (!PositionsStringIsValid(positions)) {
253 errx(1, "invalid positions string for write\n");
254 }
Andrew de los Reyes3ebed9c2010-10-12 16:15:26 -0700255 int fd = -1;
256 int should_close = 1;
257 if (strlen(filename) > strlen(kFdFilePrefix) &&
258 !strncmp(filename, kFdFilePrefix, strlen(kFdFilePrefix))) {
259 sscanf(filename + strlen(kFdFilePrefix), "%d", &fd);
260 should_close = 0;
261 } else {
262 fd = open(filename, O_WRONLY | O_CREAT, 0666);
263 }
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700264 if (fd < 0) {
265 errx(1, "open failed for write\n");
266 }
267
268 for (;;) {
269 int64_t offset, length;
270 if (NextInt64(&positions, &offset) == 0) {
271 break;
272 }
273 if (NextInt64(&positions, &length) == 0) {
274 errx(1, "bad length parse for write\n");
275 }
276 if (length < 0) {
277 errx(1, "length can't be negative for write\n");
278 }
Andrew de los Reyes11a4ecc2010-08-09 15:52:37 -0700279 if (length > kMaxLength) {
280 errx(1, "write length too large\n");
281 }
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700282
283 if (offset < 0) {
284 // Sparse hole. Skip.
285 } else {
Andrew de los Reyes11a4ecc2010-08-09 15:52:37 -0700286 ssize_t rc = pwrite(fd, buf, (size_t)length, (off_t)offset);
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700287 if (rc != length) {
288 errx(1, "write failed\n");
289 }
290 }
291 buf += length;
292 new_size -= length;
293 }
294 if (new_size != 0) {
295 errx(1, "output position length doesn't match new size\n");
296 }
Andrew de los Reyes3ebed9c2010-10-12 16:15:26 -0700297 if (should_close)
298 close(fd);
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700299}
300
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800301static off_t offtin(u_char *buf)
302{
303 off_t y;
304
305 y=buf[7]&0x7F;
306 y=y*256;y+=buf[6];
307 y=y*256;y+=buf[5];
308 y=y*256;y+=buf[4];
309 y=y*256;y+=buf[3];
310 y=y*256;y+=buf[2];
311 y=y*256;y+=buf[1];
312 y=y*256;y+=buf[0];
313
314 if(buf[7]&0x80) y=-y;
315
316 return y;
317}
318
319int main(int argc,char * argv[])
320{
321 FILE * f, * cpf, * dpf, * epf;
322 BZFILE * cpfbz2, * dpfbz2, * epfbz2;
323 int cbz2err, dbz2err, ebz2err;
324 int fd;
325 ssize_t oldsize,newsize;
326 ssize_t bzctrllen,bzdatalen;
327 u_char header[32],buf[8];
328 u_char *old, *new;
329 off_t oldpos,newpos;
330 off_t ctrl[3];
331 off_t lenread;
332 off_t i;
333
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700334 if ((argc != 6) && (argc != 4)) {
335 errx(1,"usage: %s oldfile newfile patchfile \\\n"
336 " [in_offset,in_length,in_offset,in_length,... \\\n"
337 " out_offset,out_length,"
338 "out_offset,out_length,...]\n",argv[0]);
339 }
340 int using_positioning = (argc == 6);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800341
342 /* Open patch file */
343 if ((f = fopen(argv[3], "r")) == NULL)
344 err(1, "fopen(%s)", argv[3]);
345
346 /*
347 File format:
348 0 8 "BSDIFF40"
349 8 8 X
350 16 8 Y
351 24 8 sizeof(newfile)
352 32 X bzip2(control block)
353 32+X Y bzip2(diff block)
354 32+X+Y ??? bzip2(extra block)
355 with control block a set of triples (x,y,z) meaning "add x bytes
356 from oldfile to x bytes from the diff block; copy y bytes from the
357 extra block; seek forwards in oldfile by z bytes".
358 */
359
360 /* Read header */
361 if (fread(header, 1, 32, f) < 32) {
362 if (feof(f))
363 errx(1, "Corrupt patch\n");
364 err(1, "fread(%s)", argv[3]);
365 }
366
367 /* Check for appropriate magic */
368 if (memcmp(header, "BSDIFF40", 8) != 0)
369 errx(1, "Corrupt patch\n");
370
371 /* Read lengths from header */
372 bzctrllen=offtin(header+8);
373 bzdatalen=offtin(header+16);
374 newsize=offtin(header+24);
375 if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
376 errx(1,"Corrupt patch\n");
377
378 /* Close patch file and re-open it via libbzip2 at the right places */
379 if (fclose(f))
380 err(1, "fclose(%s)", argv[3]);
381 if ((cpf = fopen(argv[3], "r")) == NULL)
382 err(1, "fopen(%s)", argv[3]);
383 if (fseeko(cpf, 32, SEEK_SET))
384 err(1, "fseeko(%s, %lld)", argv[3],
385 (long long)32);
386 if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
387 errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
388 if ((dpf = fopen(argv[3], "r")) == NULL)
389 err(1, "fopen(%s)", argv[3]);
390 if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
391 err(1, "fseeko(%s, %lld)", argv[3],
392 (long long)(32 + bzctrllen));
393 if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
394 errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
395 if ((epf = fopen(argv[3], "r")) == NULL)
396 err(1, "fopen(%s)", argv[3]);
397 if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
398 err(1, "fseeko(%s, %lld)", argv[3],
399 (long long)(32 + bzctrllen + bzdatalen));
400 if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
401 errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
402
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700403 // Read
404
405 if (!using_positioning) {
406 if(((fd=open(argv[1],O_RDONLY,0))<0) ||
407 ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
408 ((old=malloc(oldsize+1))==NULL) ||
409 (lseek(fd,0,SEEK_SET)!=0) ||
410 (read(fd,old,oldsize)!=oldsize) ||
411 (close(fd)==-1)) err(1,"%s",argv[1]);
412 } else {
413 old = PositionedRead(argv[1], argv[4], &oldsize);
414 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800415 if((new=malloc(newsize+1))==NULL) err(1,NULL);
416
417 oldpos=0;newpos=0;
418 while(newpos<newsize) {
419 /* Read control data */
420 for(i=0;i<=2;i++) {
421 lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
422 if ((lenread < 8) || ((cbz2err != BZ_OK) &&
423 (cbz2err != BZ_STREAM_END)))
424 errx(1, "Corrupt patch\n");
425 ctrl[i]=offtin(buf);
426 };
427
Doug Zongker4d054792014-05-13 08:37:06 -0700428 // android local change (start)
429 if (ctrl[0]<0||ctrl[1]<0)
430 errx(1,"Corrupt patch\n");
431 // android local change (end)
432
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800433 /* Sanity-check */
434 if(newpos+ctrl[0]>newsize)
435 errx(1,"Corrupt patch\n");
436
437 /* Read diff string */
438 lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
439 if ((lenread < ctrl[0]) ||
440 ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
441 errx(1, "Corrupt patch\n");
442
443 /* Add old data to diff string */
444 for(i=0;i<ctrl[0];i++)
445 if((oldpos+i>=0) && (oldpos+i<oldsize))
446 new[newpos+i]+=old[oldpos+i];
447
448 /* Adjust pointers */
449 newpos+=ctrl[0];
450 oldpos+=ctrl[0];
451
452 /* Sanity-check */
453 if(newpos+ctrl[1]>newsize)
454 errx(1,"Corrupt patch\n");
455
456 /* Read extra string */
457 lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
458 if ((lenread < ctrl[1]) ||
459 ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
460 errx(1, "Corrupt patch\n");
461
462 /* Adjust pointers */
463 newpos+=ctrl[1];
464 oldpos+=ctrl[2];
465 };
466
467 /* Clean up the bzip2 reads */
468 BZ2_bzReadClose(&cbz2err, cpfbz2);
469 BZ2_bzReadClose(&dbz2err, dpfbz2);
470 BZ2_bzReadClose(&ebz2err, epfbz2);
471 if (fclose(cpf) || fclose(dpf) || fclose(epf))
472 err(1, "fclose(%s)", argv[3]);
473
474 /* Write the new file */
Zdenek Behan339e0ee2010-06-14 12:50:53 -0700475 if (!using_positioning) {
476 if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
477 (write(fd,new,newsize)!=newsize) || (close(fd)==-1))
478 err(1,"%s",argv[2]);
479 } else {
480 PositionedWrite(argv[2], argv[5], new, newsize);
481 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800482
483 free(new);
484 free(old);
485
486 return 0;
487}