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