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