Index: chrome/installer/mac/third_party/bsdiff/goobspatch.c |
=================================================================== |
--- chrome/installer/mac/third_party/bsdiff/goobspatch.c (revision 49280) |
+++ chrome/installer/mac/third_party/bsdiff/goobspatch.c (working copy) |
@@ -28,109 +28,226 @@ |
__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $"); |
#endif |
+#include <sys/types.h> |
+ |
#include <bzlib.h> |
+#include <err.h> |
+#include <fcntl.h> |
#include <stdlib.h> |
#include <stdio.h> |
#include <string.h> |
-#include <err.h> |
+#include <openssl/sha.h> |
#include <unistd.h> |
-#include <fcntl.h> |
+#include <zlib.h> |
-static off_t offtin(u_char *buf) |
+#if defined(__APPLE__) |
+#include <libkern/OSByteOrder.h> |
+#define le64toh(x) OSSwapLittleToHostInt64(x) |
+#elif defined(__linux__) |
+#include <endian.h> |
+#elif defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) |
+#define le64toh(x) (x) |
+#else |
+#error Provide le64toh for this platform |
+#endif |
+ |
+static inline off_t offtin(u_char *buf) |
{ |
- off_t y; |
+ return le64toh(*((off_t*)buf)); |
+} |
- y=buf[7]&0x7F; |
- y=y*256;y+=buf[6]; |
- y=y*256;y+=buf[5]; |
- y=y*256;y+=buf[4]; |
- y=y*256;y+=buf[3]; |
- y=y*256;y+=buf[2]; |
- y=y*256;y+=buf[1]; |
- y=y*256;y+=buf[0]; |
+static void sha1tostr(const u_char *sha1, char *sha1str) |
+{ |
+ int i; |
+ for (i = 0; i < SHA_DIGEST_LENGTH; ++i) |
+ sprintf(&sha1str[i * 2], "%02x", sha1[i]); |
+} |
- if(buf[7]&0x80) y=-y; |
+/* cfile is a uniform interface to read from maybe-compressed files. */ |
- return y; |
+typedef struct { |
+ FILE *f; /* method = 1, 2 */ |
+ union { |
+ BZFILE *bz2; /* method = 2 */ |
+ gzFile gz; /* method = 3 */ |
+ } u; |
+ const char *tag; |
+ unsigned char method; |
+} cfile; |
+ |
+/* Opens a file at path, seeks to offset off, and prepares for reading using |
+ * the specified method. Supported methods are plain uncompressed (1), bzip2 |
+ * (2), and gzip (3). tag is used as an identifier for error reporting. */ |
+static void cfopen(cfile *cf, const char *path, off_t off, |
+ const char *tag, unsigned char method) |
+{ |
+ int fd; |
+ int zerr; |
+ |
+ if (method == 1 || method == 2) { |
+ /* Use stdio for uncompressed files. The bzip interface also |
+ * sits on top of a stdio FILE* but does not take "ownership" |
+ * of the FILE*. */ |
+ if ((cf->f = fopen(path, "rb")) == NULL) |
+ err(1, "fdopen(%s)", tag); |
+ if ((fseeko(cf->f, off, SEEK_SET)) != 0) |
+ err(1, "fseeko(%s, %lld)", tag, off); |
+ if (method == 2) { |
+ if ((cf->u.bz2 = BZ2_bzReadOpen(&zerr, cf->f, 0, 0, |
+ NULL, 0)) == NULL) |
+ errx(1, "BZ2_bzReadOpen(%s): %d", tag, zerr); |
+ } |
+ } else if (method == 3) { |
+ if ((fd = open(path, O_RDONLY)) < 0) |
+ err(1, "open(%s)", tag); |
+ if (lseek(fd, off, SEEK_SET) != off) |
+ err(1, "lseek(%s, %lld)", tag, off); |
+ if ((cf->u.gz = gzdopen(fd, "rb")) == NULL) |
+ errx(1, "gzdopen(%s)", tag); |
+ } else { |
+ errx(1, "cfopen(%s): unknown method %d", tag, method); |
+ } |
+ |
+ cf->tag = tag; |
+ cf->method = method; |
} |
+static void cfclose(cfile *cf) |
+{ |
+ int zerr; |
+ |
+ if (cf->method == 1 || cf->method == 2) { |
+ if (cf->method == 2) { |
+ zerr = BZ_OK; |
+ BZ2_bzReadClose(&zerr, cf->u.bz2); |
+ if (zerr != BZ_OK) |
+ errx(1, "BZ2_bzReadClose(%s): %d\n", |
+ cf->tag, zerr); |
+ } |
+ if (fclose(cf->f) != 0) |
+ err(1, "fclose(%s)", cf->tag); |
+ } else if (cf->method == 3) { |
+ if ((zerr = gzclose(cf->u.gz)) != Z_OK) |
+ errx(1, "gzclose(%s): %d", cf->tag, zerr); |
+ } else { |
+ errx(1, "cfclose(%s): unknown method %d", cf->tag, cf->method); |
+ } |
+} |
+ |
+static void cfread(cfile *cf, u_char *buf, size_t len) |
+{ |
+ size_t nread; |
+ int zerr; |
+ |
+ if (cf->method == 1) { |
+ if ((nread = fread(buf, 1, len, cf->f)) != len) { |
+ if (!ferror(cf->f)) |
+ errx(1, "fread(%s, %zd): short read %zd", |
+ cf->tag, len, nread); |
+ err(1, "fread(%s, %zd)", cf->tag, len); |
+ } |
+ } else if (cf->method == 2) { |
+ zerr = BZ_OK; |
+ if ((nread = BZ2_bzRead(&zerr, cf->u.bz2, buf, len)) != len) { |
+ if (zerr == BZ_OK) |
+ errx(1, "BZ2_bzRead(%s, %zd): short read %zd", |
+ cf->tag, len, nread); |
+ errx(1, "BZ2_bzRead(%s, %zd): %d", cf->tag, len, zerr); |
+ } |
+ } else if (cf->method == 3) { |
+ if ((nread = gzread(cf->u.gz, buf, len)) != len) { |
+ zerr = Z_OK; |
+ gzerror(cf->u.gz, &zerr); |
+ if (zerr == Z_OK) |
+ errx(1, "gzread(%s, %zd): short read %zd", |
+ cf->tag, len, nread); |
+ errx(1, "gzread(%s, %zd): %d", cf->tag, len, zerr); |
+ } |
+ } else { |
+ errx(1, "cfread(%s, %zd): unknown method %d", |
+ cf->tag, len, cf->method); |
+ } |
+} |
+ |
int main(int argc,char * argv[]) |
{ |
- FILE * f, * cpf, * dpf, * epf; |
- BZFILE * cpfbz2, * dpfbz2, * epfbz2; |
- int cbz2err, dbz2err, ebz2err; |
+ FILE * f; |
+ cfile cf, df, ef; |
int fd; |
- ssize_t oldsize,newsize; |
- ssize_t bzctrllen,bzdatalen; |
- u_char header[32],buf[8]; |
+ off_t expect_oldsize, oldsize, newsize, patchsize; |
+ off_t zctrllen, zdatalen, zextralen; |
+ u_char header[96], buf[8]; |
u_char *old, *new; |
off_t oldpos,newpos; |
off_t ctrl[3]; |
- off_t lenread; |
off_t i; |
+ u_char sha1[SHA_DIGEST_LENGTH]; |
+ char sha1str[SHA_DIGEST_LENGTH * 2 + 1]; |
+ char expected_sha1str[SHA_DIGEST_LENGTH * 2 + 1]; |
- if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); |
+ if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile",argv[0]); |
/* Open patch file */ |
- if ((f = fopen(argv[3], "r")) == NULL) |
+ if ((f = fopen(argv[3], "rb")) == NULL) |
err(1, "fopen(%s)", argv[3]); |
/* |
File format: |
- 0 8 "BSDIFF40" |
- 8 8 X |
- 16 8 Y |
- 24 8 sizeof(newfile) |
- 32 X bzip2(control block) |
- 32+X Y bzip2(diff block) |
- 32+X+Y ??? bzip2(extra block) |
- with control block a set of triples (x,y,z) meaning "add x bytes |
+ 0 8 "BSDIFF4G" |
+ 8 8 length of compressed control block (x) |
+ 16 8 length of compressed diff block (y) |
+ 24 8 length of compressed extra block (z) |
+ 32 8 length of old file |
+ 40 8 length of new file |
+ 48 20 SHA1 of old file |
+ 68 20 SHA1 of new file |
+ 88 1 encoding of control block |
+ 89 1 encoding of diff block |
+ 90 1 encoding of extra block |
+ 91 5 unused |
+ 96 x compressed control block |
+ 96+x y compressed diff block |
+ 96+x+y z compressed extra block |
+ Encodings are 1 (uncompressed), 2 (bzip2), and 3 (gzip). |
+ The control block is a set of triples (x,y,z) meaning "add x bytes |
from oldfile to x bytes from the diff block; copy y bytes from the |
extra block; seek forwards in oldfile by z bytes". |
*/ |
/* Read header */ |
- if (fread(header, 1, 32, f) < 32) { |
+ if (fread(header, 1, sizeof(header), f) < sizeof(header)) { |
if (feof(f)) |
- errx(1, "Corrupt patch\n"); |
+ errx(1, "corrupt patch (header size)"); |
err(1, "fread(%s)", argv[3]); |
} |
/* Check for appropriate magic */ |
- if (memcmp(header, "BSDIFF40", 8) != 0) |
- errx(1, "Corrupt patch\n"); |
+ if (memcmp(header, "BSDIFF4G", 8) != 0) |
+ errx(1, "corrupt patch (magic)"); |
/* Read lengths from header */ |
- bzctrllen=offtin(header+8); |
- bzdatalen=offtin(header+16); |
- newsize=offtin(header+24); |
- if((bzctrllen<0) || (bzdatalen<0) || (newsize<0)) |
- errx(1,"Corrupt patch\n"); |
+ zctrllen = offtin(header + 8); |
+ zdatalen = offtin(header + 16); |
+ zextralen = offtin(header + 24); |
+ expect_oldsize = offtin(header + 32); |
+ newsize = offtin(header + 40); |
+ if (zctrllen < 0 || zdatalen < 0 || zextralen < 0) |
+ errx(1, "corrupt patch (stream sizes)"); |
+ if (expect_oldsize < 0 || newsize < 0) |
+ errx(1, "corrupt patch (file sizes)"); |
- /* Close patch file and re-open it via libbzip2 at the right places */ |
+ if (fseeko(f, 0, SEEK_END) != 0 || (patchsize = ftello(f)) < 0) |
+ err(1, "fseeko/ftello(%s)", argv[3]); |
+ if (patchsize != sizeof(header) + zctrllen + zdatalen + zextralen) |
+ errx(1, "corrupt patch (patch size)"); |
+ |
+ cfopen(&cf, argv[3], sizeof(header), "control", header[88]); |
+ cfopen(&df, argv[3], sizeof(header) + zctrllen, "diff", header[89]); |
+ cfopen(&ef, argv[3], sizeof(header) + zctrllen + zdatalen, "extra", |
+ header[90]); |
+ |
if (fclose(f)) |
err(1, "fclose(%s)", argv[3]); |
- if ((cpf = fopen(argv[3], "r")) == NULL) |
- err(1, "fopen(%s)", argv[3]); |
- if (fseeko(cpf, 32, SEEK_SET)) |
- err(1, "fseeko(%s, %lld)", argv[3], |
- (long long)32); |
- if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) |
- errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err); |
- if ((dpf = fopen(argv[3], "r")) == NULL) |
- err(1, "fopen(%s)", argv[3]); |
- if (fseeko(dpf, 32 + bzctrllen, SEEK_SET)) |
- err(1, "fseeko(%s, %lld)", argv[3], |
- (long long)(32 + bzctrllen)); |
- if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) |
- errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err); |
- if ((epf = fopen(argv[3], "r")) == NULL) |
- err(1, "fopen(%s)", argv[3]); |
- if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) |
- err(1, "fseeko(%s, %lld)", argv[3], |
- (long long)(32 + bzctrllen + bzdatalen)); |
- if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) |
- errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err); |
if(((fd=open(argv[1],O_RDONLY,0))<0) || |
((oldsize=lseek(fd,0,SEEK_END))==-1) || |
@@ -138,28 +255,32 @@ |
(lseek(fd,0,SEEK_SET)!=0) || |
(read(fd,old,oldsize)!=oldsize) || |
(close(fd)==-1)) err(1,"%s",argv[1]); |
+ if (expect_oldsize != oldsize) |
+ errx(1, "old size mismatch: %lld != %lld", |
+ oldsize, expect_oldsize); |
+ SHA1(old, oldsize, sha1); |
+ if (memcmp(sha1, header + 48, sizeof(sha1)) != 0) { |
+ sha1tostr(sha1, sha1str); |
+ sha1tostr(header + 48, expected_sha1str); |
+ errx(1, "old hash mismatch: %s != %s", |
+ sha1str, expected_sha1str); |
+ } |
if((new=malloc(newsize+1))==NULL) err(1,NULL); |
oldpos=0;newpos=0; |
while(newpos<newsize) { |
/* Read control data */ |
for(i=0;i<=2;i++) { |
- lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8); |
- if ((lenread < 8) || ((cbz2err != BZ_OK) && |
- (cbz2err != BZ_STREAM_END))) |
- errx(1, "Corrupt patch\n"); |
+ cfread(&cf, buf, 8); |
ctrl[i]=offtin(buf); |
}; |
/* Sanity-check */ |
if(newpos+ctrl[0]>newsize) |
- errx(1,"Corrupt patch\n"); |
+ errx(1,"corrupt patch (diff): overrun"); |
/* Read diff string */ |
- lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]); |
- if ((lenread < ctrl[0]) || |
- ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) |
- errx(1, "Corrupt patch\n"); |
+ cfread(&df, new + newpos, ctrl[0]); |
/* Add old data to diff string */ |
for(i=0;i<ctrl[0];i++) |
@@ -172,30 +293,33 @@ |
/* Sanity-check */ |
if(newpos+ctrl[1]>newsize) |
- errx(1,"Corrupt patch\n"); |
+ errx(1,"corrupt patch (extra): overrun"); |
/* Read extra string */ |
- lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]); |
- if ((lenread < ctrl[1]) || |
- ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) |
- errx(1, "Corrupt patch\n"); |
+ cfread(&ef, new + newpos, ctrl[1]); |
/* Adjust pointers */ |
newpos+=ctrl[1]; |
oldpos+=ctrl[2]; |
}; |
- /* Clean up the bzip2 reads */ |
- BZ2_bzReadClose(&cbz2err, cpfbz2); |
- BZ2_bzReadClose(&dbz2err, dpfbz2); |
- BZ2_bzReadClose(&ebz2err, epfbz2); |
- if (fclose(cpf) || fclose(dpf) || fclose(epf)) |
- err(1, "fclose(%s)", argv[3]); |
+ /* Clean up the readers */ |
+ cfclose(&cf); |
+ cfclose(&df); |
+ cfclose(&ef); |
+ SHA1(new, newsize, sha1); |
+ if (memcmp(sha1, header + 68, sizeof(sha1)) != 0) { |
+ sha1tostr(sha1, sha1str); |
+ sha1tostr(header + 68, expected_sha1str); |
+ errx(1, "new hash mismatch: %s != %s", |
+ sha1str, expected_sha1str); |
+ } |
+ |
/* Write the new file */ |
- if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) || |
+ if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0644))<0) || |
(write(fd,new,newsize)!=newsize) || (close(fd)==-1)) |
- err(1,"%s",argv[2]); |
+ err(1,"open/write/close(%s)",argv[2]); |
free(new); |
free(old); |