OLD | NEW |
1 /*- | 1 /*- |
2 * Copyright 2003-2005 Colin Percival | 2 * Copyright 2003-2005 Colin Percival |
3 * All rights reserved | 3 * All rights reserved |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted providing that the following conditions | 6 * modification, are permitted providing that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 10 matching lines...) Expand all Loading... |
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | 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 | 23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
24 * POSSIBILITY OF SUCH DAMAGE. | 24 * POSSIBILITY OF SUCH DAMAGE. |
25 */ | 25 */ |
26 | 26 |
27 #if 0 | 27 #if 0 |
28 __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:
06 cperciva Exp $"); | 28 __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:
06 cperciva Exp $"); |
29 #endif | 29 #endif |
30 | 30 |
| 31 #include <sys/types.h> |
| 32 |
31 #include <bzlib.h> | 33 #include <bzlib.h> |
| 34 #include <err.h> |
| 35 #include <fcntl.h> |
32 #include <stdlib.h> | 36 #include <stdlib.h> |
33 #include <stdio.h> | 37 #include <stdio.h> |
34 #include <string.h> | 38 #include <string.h> |
35 #include <err.h> | 39 #include <openssl/sha.h> |
36 #include <unistd.h> | 40 #include <unistd.h> |
37 #include <fcntl.h> | 41 #include <zlib.h> |
38 | 42 |
39 static off_t offtin(u_char *buf) | 43 #if defined(__APPLE__) |
40 { | 44 #include <libkern/OSByteOrder.h> |
41 » off_t y; | 45 #define le64toh(x) OSSwapLittleToHostInt64(x) |
42 | 46 #elif defined(__linux__) |
43 » y=buf[7]&0x7F; | 47 #include <endian.h> |
44 » y=y*256;y+=buf[6]; | 48 #elif defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) |
45 » y=y*256;y+=buf[5]; | 49 #define le64toh(x) (x) |
46 » y=y*256;y+=buf[4]; | 50 #else |
47 » y=y*256;y+=buf[3]; | 51 #error Provide le64toh for this platform |
48 » y=y*256;y+=buf[2]; | 52 #endif |
49 » y=y*256;y+=buf[1]; | 53 |
50 » y=y*256;y+=buf[0]; | 54 static inline off_t offtin(u_char *buf) |
51 | 55 { |
52 » if(buf[7]&0x80) y=-y; | 56 » return le64toh(*((off_t*)buf)); |
53 | 57 } |
54 » return y; | 58 |
| 59 static void sha1tostr(const u_char *sha1, char *sha1str) |
| 60 { |
| 61 » int i; |
| 62 » for (i = 0; i < SHA_DIGEST_LENGTH; ++i) |
| 63 » » sprintf(&sha1str[i * 2], "%02x", sha1[i]); |
| 64 } |
| 65 |
| 66 /* cfile is a uniform interface to read from maybe-compressed files. */ |
| 67 |
| 68 typedef struct { |
| 69 » FILE *f; /* method = 1, 2 */ |
| 70 » union { |
| 71 » » BZFILE *bz2; /* method = 2 */ |
| 72 » » gzFile gz; /* method = 3 */ |
| 73 » } u; |
| 74 » const char *tag; |
| 75 » unsigned char method; |
| 76 } cfile; |
| 77 |
| 78 /* Opens a file at path, seeks to offset off, and prepares for reading using |
| 79 * the specified method. Supported methods are plain uncompressed (1), bzip2 |
| 80 * (2), and gzip (3). tag is used as an identifier for error reporting. */ |
| 81 static void cfopen(cfile *cf, const char *path, off_t off, |
| 82 const char *tag, unsigned char method) |
| 83 { |
| 84 » int fd; |
| 85 » int zerr; |
| 86 |
| 87 » if (method == 1 || method == 2) { |
| 88 » » /* Use stdio for uncompressed files. The bzip interface also |
| 89 » » * sits on top of a stdio FILE* but does not take "ownership" |
| 90 » » * of the FILE*. */ |
| 91 » » if ((cf->f = fopen(path, "rb")) == NULL) |
| 92 » » » err(1, "fdopen(%s)", tag); |
| 93 » » if ((fseeko(cf->f, off, SEEK_SET)) != 0) |
| 94 » » » err(1, "fseeko(%s, %lld)", tag, off); |
| 95 » » if (method == 2) { |
| 96 » » » if ((cf->u.bz2 = BZ2_bzReadOpen(&zerr, cf->f, 0, 0, |
| 97 » » » NULL, 0)) == NULL) |
| 98 » » » » errx(1, "BZ2_bzReadOpen(%s): %d", tag, zerr); |
| 99 » » } |
| 100 » } else if (method == 3) { |
| 101 » » if ((fd = open(path, O_RDONLY)) < 0) |
| 102 » » » err(1, "open(%s)", tag); |
| 103 » » if (lseek(fd, off, SEEK_SET) != off) |
| 104 » » » err(1, "lseek(%s, %lld)", tag, off); |
| 105 » » if ((cf->u.gz = gzdopen(fd, "rb")) == NULL) |
| 106 » » » errx(1, "gzdopen(%s)", tag); |
| 107 » } else { |
| 108 » » errx(1, "cfopen(%s): unknown method %d", tag, method); |
| 109 » } |
| 110 |
| 111 » cf->tag = tag; |
| 112 » cf->method = method; |
| 113 } |
| 114 |
| 115 static void cfclose(cfile *cf) |
| 116 { |
| 117 » int zerr; |
| 118 |
| 119 » if (cf->method == 1 || cf->method == 2) { |
| 120 » » if (cf->method == 2) { |
| 121 » » » zerr = BZ_OK; |
| 122 » » » BZ2_bzReadClose(&zerr, cf->u.bz2); |
| 123 » » » if (zerr != BZ_OK) |
| 124 » » » » errx(1, "BZ2_bzReadClose(%s): %d\n", |
| 125 » » » » cf->tag, zerr); |
| 126 » » } |
| 127 » » if (fclose(cf->f) != 0) |
| 128 » » » err(1, "fclose(%s)", cf->tag); |
| 129 » } else if (cf->method == 3) { |
| 130 » » if ((zerr = gzclose(cf->u.gz)) != Z_OK) |
| 131 » » » errx(1, "gzclose(%s): %d", cf->tag, zerr); |
| 132 » } else { |
| 133 » » errx(1, "cfclose(%s): unknown method %d", cf->tag, cf->method); |
| 134 » } |
| 135 } |
| 136 |
| 137 static void cfread(cfile *cf, u_char *buf, size_t len) |
| 138 { |
| 139 » size_t nread; |
| 140 » int zerr; |
| 141 |
| 142 » if (cf->method == 1) { |
| 143 » » if ((nread = fread(buf, 1, len, cf->f)) != len) { |
| 144 » » » if (!ferror(cf->f)) |
| 145 » » » » errx(1, "fread(%s, %zd): short read %zd", |
| 146 » » » » cf->tag, len, nread); |
| 147 » » » err(1, "fread(%s, %zd)", cf->tag, len); |
| 148 » » } |
| 149 » } else if (cf->method == 2) { |
| 150 » » zerr = BZ_OK; |
| 151 » » if ((nread = BZ2_bzRead(&zerr, cf->u.bz2, buf, len)) != len) { |
| 152 » » » if (zerr == BZ_OK) |
| 153 » » » » errx(1, "BZ2_bzRead(%s, %zd): short read %zd", |
| 154 » » » » cf->tag, len, nread); |
| 155 » » » errx(1, "BZ2_bzRead(%s, %zd): %d", cf->tag, len, zerr); |
| 156 » » } |
| 157 » } else if (cf->method == 3) { |
| 158 » » if ((nread = gzread(cf->u.gz, buf, len)) != len) { |
| 159 » » » zerr = Z_OK; |
| 160 » » » gzerror(cf->u.gz, &zerr); |
| 161 » » » if (zerr == Z_OK) |
| 162 » » » » errx(1, "gzread(%s, %zd): short read %zd", |
| 163 » » » » cf->tag, len, nread); |
| 164 » » » errx(1, "gzread(%s, %zd): %d", cf->tag, len, zerr); |
| 165 » » } |
| 166 » } else { |
| 167 » » errx(1, "cfread(%s, %zd): unknown method %d", |
| 168 » » cf->tag, len, cf->method); |
| 169 » } |
55 } | 170 } |
56 | 171 |
57 int main(int argc,char * argv[]) | 172 int main(int argc,char * argv[]) |
58 { | 173 { |
59 » FILE * f, * cpf, * dpf, * epf; | 174 » FILE * f; |
60 » BZFILE * cpfbz2, * dpfbz2, * epfbz2; | 175 » cfile cf, df, ef; |
61 » int cbz2err, dbz2err, ebz2err; | |
62 int fd; | 176 int fd; |
63 » ssize_t oldsize,newsize; | 177 » off_t expect_oldsize, oldsize, newsize, patchsize; |
64 » ssize_t bzctrllen,bzdatalen; | 178 » off_t zctrllen, zdatalen, zextralen; |
65 » u_char header[32],buf[8]; | 179 » u_char header[96], buf[8]; |
66 u_char *old, *new; | 180 u_char *old, *new; |
67 off_t oldpos,newpos; | 181 off_t oldpos,newpos; |
68 off_t ctrl[3]; | 182 off_t ctrl[3]; |
69 off_t lenread; | |
70 off_t i; | 183 off_t i; |
71 | 184 » u_char sha1[SHA_DIGEST_LENGTH]; |
72 » if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); | 185 » char sha1str[SHA_DIGEST_LENGTH * 2 + 1]; |
| 186 » char expected_sha1str[SHA_DIGEST_LENGTH * 2 + 1]; |
| 187 |
| 188 » if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile",argv[0]); |
73 | 189 |
74 /* Open patch file */ | 190 /* Open patch file */ |
75 » if ((f = fopen(argv[3], "r")) == NULL) | 191 » if ((f = fopen(argv[3], "rb")) == NULL) |
76 err(1, "fopen(%s)", argv[3]); | 192 err(1, "fopen(%s)", argv[3]); |
77 | 193 |
78 /* | 194 /* |
79 File format: | 195 File format: |
80 » » 0» 8» "BSDIFF40" | 196 » » 0» 8» "BSDIFF4G" |
81 » » 8» 8» X | 197 » » 8» 8» length of compressed control block (x) |
82 » » 16» 8» Y | 198 » » 16» 8» length of compressed diff block (y) |
83 » » 24» 8» sizeof(newfile) | 199 » » 24» 8» length of compressed extra block (z) |
84 » » 32» X» bzip2(control block) | 200 » » 32» 8» length of old file |
85 » » 32+X» Y» bzip2(diff block) | 201 » » 40» 8» length of new file |
86 » » 32+X+Y» ???» bzip2(extra block) | 202 » » 48» 20» SHA1 of old file |
87 » with control block a set of triples (x,y,z) meaning "add x bytes | 203 » » 68» 20» SHA1 of new file |
| 204 » » 88» 1» encoding of control block |
| 205 » » 89» 1» encoding of diff block |
| 206 » » 90» 1» encoding of extra block |
| 207 » » 91» 5» unused |
| 208 » » 96» x» compressed control block |
| 209 » » 96+x» y» compressed diff block |
| 210 » » 96+x+y» z» compressed extra block |
| 211 » Encodings are 1 (uncompressed), 2 (bzip2), and 3 (gzip). |
| 212 » The control block is a set of triples (x,y,z) meaning "add x bytes |
88 from oldfile to x bytes from the diff block; copy y bytes from the | 213 from oldfile to x bytes from the diff block; copy y bytes from the |
89 extra block; seek forwards in oldfile by z bytes". | 214 extra block; seek forwards in oldfile by z bytes". |
90 */ | 215 */ |
91 | 216 |
92 /* Read header */ | 217 /* Read header */ |
93 » if (fread(header, 1, 32, f) < 32) { | 218 » if (fread(header, 1, sizeof(header), f) < sizeof(header)) { |
94 if (feof(f)) | 219 if (feof(f)) |
95 » » » errx(1, "Corrupt patch\n"); | 220 » » » errx(1, "corrupt patch (header size)"); |
96 err(1, "fread(%s)", argv[3]); | 221 err(1, "fread(%s)", argv[3]); |
97 } | 222 } |
98 | 223 |
99 /* Check for appropriate magic */ | 224 /* Check for appropriate magic */ |
100 » if (memcmp(header, "BSDIFF40", 8) != 0) | 225 » if (memcmp(header, "BSDIFF4G", 8) != 0) |
101 » » errx(1, "Corrupt patch\n"); | 226 » » errx(1, "corrupt patch (magic)"); |
102 | 227 |
103 /* Read lengths from header */ | 228 /* Read lengths from header */ |
104 » bzctrllen=offtin(header+8); | 229 » zctrllen = offtin(header + 8); |
105 » bzdatalen=offtin(header+16); | 230 » zdatalen = offtin(header + 16); |
106 » newsize=offtin(header+24); | 231 » zextralen = offtin(header + 24); |
107 » if((bzctrllen<0) || (bzdatalen<0) || (newsize<0)) | 232 » expect_oldsize = offtin(header + 32); |
108 » » errx(1,"Corrupt patch\n"); | 233 » newsize = offtin(header + 40); |
109 | 234 » if (zctrllen < 0 || zdatalen < 0 || zextralen < 0) |
110 » /* Close patch file and re-open it via libbzip2 at the right places */ | 235 » » errx(1, "corrupt patch (stream sizes)"); |
| 236 » if (expect_oldsize < 0 || newsize < 0) |
| 237 » » errx(1, "corrupt patch (file sizes)"); |
| 238 |
| 239 » if (fseeko(f, 0, SEEK_END) != 0 || (patchsize = ftello(f)) < 0) |
| 240 » » err(1, "fseeko/ftello(%s)", argv[3]); |
| 241 » if (patchsize != sizeof(header) + zctrllen + zdatalen + zextralen) |
| 242 » » errx(1, "corrupt patch (patch size)"); |
| 243 |
| 244 » cfopen(&cf, argv[3], sizeof(header), "control", header[88]); |
| 245 » cfopen(&df, argv[3], sizeof(header) + zctrllen, "diff", header[89]); |
| 246 » cfopen(&ef, argv[3], sizeof(header) + zctrllen + zdatalen, "extra", |
| 247 » header[90]); |
| 248 |
111 if (fclose(f)) | 249 if (fclose(f)) |
112 err(1, "fclose(%s)", argv[3]); | 250 err(1, "fclose(%s)", argv[3]); |
113 if ((cpf = fopen(argv[3], "r")) == NULL) | |
114 err(1, "fopen(%s)", argv[3]); | |
115 if (fseeko(cpf, 32, SEEK_SET)) | |
116 err(1, "fseeko(%s, %lld)", argv[3], | |
117 (long long)32); | |
118 if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) | |
119 errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err); | |
120 if ((dpf = fopen(argv[3], "r")) == NULL) | |
121 err(1, "fopen(%s)", argv[3]); | |
122 if (fseeko(dpf, 32 + bzctrllen, SEEK_SET)) | |
123 err(1, "fseeko(%s, %lld)", argv[3], | |
124 (long long)(32 + bzctrllen)); | |
125 if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) | |
126 errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err); | |
127 if ((epf = fopen(argv[3], "r")) == NULL) | |
128 err(1, "fopen(%s)", argv[3]); | |
129 if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) | |
130 err(1, "fseeko(%s, %lld)", argv[3], | |
131 (long long)(32 + bzctrllen + bzdatalen)); | |
132 if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) | |
133 errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err); | |
134 | 251 |
135 if(((fd=open(argv[1],O_RDONLY,0))<0) || | 252 if(((fd=open(argv[1],O_RDONLY,0))<0) || |
136 ((oldsize=lseek(fd,0,SEEK_END))==-1) || | 253 ((oldsize=lseek(fd,0,SEEK_END))==-1) || |
137 ((old=malloc(oldsize+1))==NULL) || | 254 ((old=malloc(oldsize+1))==NULL) || |
138 (lseek(fd,0,SEEK_SET)!=0) || | 255 (lseek(fd,0,SEEK_SET)!=0) || |
139 (read(fd,old,oldsize)!=oldsize) || | 256 (read(fd,old,oldsize)!=oldsize) || |
140 (close(fd)==-1)) err(1,"%s",argv[1]); | 257 (close(fd)==-1)) err(1,"%s",argv[1]); |
| 258 if (expect_oldsize != oldsize) |
| 259 errx(1, "old size mismatch: %lld != %lld", |
| 260 oldsize, expect_oldsize); |
| 261 SHA1(old, oldsize, sha1); |
| 262 if (memcmp(sha1, header + 48, sizeof(sha1)) != 0) { |
| 263 sha1tostr(sha1, sha1str); |
| 264 sha1tostr(header + 48, expected_sha1str); |
| 265 errx(1, "old hash mismatch: %s != %s", |
| 266 sha1str, expected_sha1str); |
| 267 } |
141 if((new=malloc(newsize+1))==NULL) err(1,NULL); | 268 if((new=malloc(newsize+1))==NULL) err(1,NULL); |
142 | 269 |
143 oldpos=0;newpos=0; | 270 oldpos=0;newpos=0; |
144 while(newpos<newsize) { | 271 while(newpos<newsize) { |
145 /* Read control data */ | 272 /* Read control data */ |
146 for(i=0;i<=2;i++) { | 273 for(i=0;i<=2;i++) { |
147 » » » lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8); | 274 » » » cfread(&cf, buf, 8); |
148 » » » if ((lenread < 8) || ((cbz2err != BZ_OK) && | |
149 » » » (cbz2err != BZ_STREAM_END))) | |
150 » » » » errx(1, "Corrupt patch\n"); | |
151 ctrl[i]=offtin(buf); | 275 ctrl[i]=offtin(buf); |
152 }; | 276 }; |
153 | 277 |
154 /* Sanity-check */ | 278 /* Sanity-check */ |
155 if(newpos+ctrl[0]>newsize) | 279 if(newpos+ctrl[0]>newsize) |
156 » » » errx(1,"Corrupt patch\n"); | 280 » » » errx(1,"corrupt patch (diff): overrun"); |
157 | 281 |
158 /* Read diff string */ | 282 /* Read diff string */ |
159 » » lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]); | 283 » » cfread(&df, new + newpos, ctrl[0]); |
160 » » if ((lenread < ctrl[0]) || | |
161 » » ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) | |
162 » » » errx(1, "Corrupt patch\n"); | |
163 | 284 |
164 /* Add old data to diff string */ | 285 /* Add old data to diff string */ |
165 for(i=0;i<ctrl[0];i++) | 286 for(i=0;i<ctrl[0];i++) |
166 if((oldpos+i>=0) && (oldpos+i<oldsize)) | 287 if((oldpos+i>=0) && (oldpos+i<oldsize)) |
167 new[newpos+i]+=old[oldpos+i]; | 288 new[newpos+i]+=old[oldpos+i]; |
168 | 289 |
169 /* Adjust pointers */ | 290 /* Adjust pointers */ |
170 newpos+=ctrl[0]; | 291 newpos+=ctrl[0]; |
171 oldpos+=ctrl[0]; | 292 oldpos+=ctrl[0]; |
172 | 293 |
173 /* Sanity-check */ | 294 /* Sanity-check */ |
174 if(newpos+ctrl[1]>newsize) | 295 if(newpos+ctrl[1]>newsize) |
175 » » » errx(1,"Corrupt patch\n"); | 296 » » » errx(1,"corrupt patch (extra): overrun"); |
176 | 297 |
177 /* Read extra string */ | 298 /* Read extra string */ |
178 » » lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]); | 299 » » cfread(&ef, new + newpos, ctrl[1]); |
179 » » if ((lenread < ctrl[1]) || | |
180 » » ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) | |
181 » » » errx(1, "Corrupt patch\n"); | |
182 | 300 |
183 /* Adjust pointers */ | 301 /* Adjust pointers */ |
184 newpos+=ctrl[1]; | 302 newpos+=ctrl[1]; |
185 oldpos+=ctrl[2]; | 303 oldpos+=ctrl[2]; |
186 }; | 304 }; |
187 | 305 |
188 » /* Clean up the bzip2 reads */ | 306 » /* Clean up the readers */ |
189 » BZ2_bzReadClose(&cbz2err, cpfbz2); | 307 » cfclose(&cf); |
190 » BZ2_bzReadClose(&dbz2err, dpfbz2); | 308 » cfclose(&df); |
191 » BZ2_bzReadClose(&ebz2err, epfbz2); | 309 » cfclose(&ef); |
192 » if (fclose(cpf) || fclose(dpf) || fclose(epf)) | 310 |
193 » » err(1, "fclose(%s)", argv[3]); | 311 » SHA1(new, newsize, sha1); |
| 312 » if (memcmp(sha1, header + 68, sizeof(sha1)) != 0) { |
| 313 » » sha1tostr(sha1, sha1str); |
| 314 » » sha1tostr(header + 68, expected_sha1str); |
| 315 » » errx(1, "new hash mismatch: %s != %s", |
| 316 » » sha1str, expected_sha1str); |
| 317 » } |
194 | 318 |
195 /* Write the new file */ | 319 /* Write the new file */ |
196 » if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) || | 320 » if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0644))<0) || |
197 (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) | 321 (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) |
198 » » err(1,"%s",argv[2]); | 322 » » err(1,"open/write/close(%s)",argv[2]); |
199 | 323 |
200 free(new); | 324 free(new); |
201 free(old); | 325 free(old); |
202 | 326 |
203 return 0; | 327 return 0; |
204 } | 328 } |
OLD | NEW |