1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <sys/mman.h>
10 #include <mba/diff.h>
11 #include <mba/hexdump.h>
12 #include <mba/msgno.h>
13
14 /* bindiff format is as follows:
15 *
16 * struct bdiff_patch {
17 * struct bdiff_header {
18 * uint16_t magic = 0xBD1F;
19 * uint16_t version;
20 * uint32_t num_edits;
21 * };
22 * struct {
23 * unsigned short op;
24 * unsigned short hash;
25 * uint32_t off;
26 * uint32_t len;
27 * } edits[num_edits];
28 * uint8_t payload[sum of DIFF_INSERT lengths];
29 * };
30 */
31
32 #if defined(__sparc__)
33 #include <sys/inttypes.h>
34 #else
35 #include <stdint.h>
36 #endif
37
38 /* encode/decode integers
39 */
40
41 size_t
enc_uint16be(uint16_t s,unsigned char * dst)42 enc_uint16be(uint16_t s, unsigned char *dst)
43 {
44 dst[0] = (s >> 8) & 0xFF;
45 dst[1] = s & 0xFF;
46 return 2;
47 }
48 size_t
enc_uint32be(uint32_t i,unsigned char * dst)49 enc_uint32be(uint32_t i, unsigned char *dst)
50 {
51 dst[0] = (i >> 24) & 0xFF;
52 dst[1] = (i >> 16) & 0xFF;
53 dst[2] = (i >> 8) & 0xFF;
54 dst[3] = i & 0xFF;
55 return 4;
56 }
57 uint16_t
dec_uint16be(const unsigned char * src)58 dec_uint16be(const unsigned char *src)
59 {
60 return ((unsigned)src[0] << 8) | src[1];
61 }
62 uint32_t
dec_uint32be(const unsigned char * src)63 dec_uint32be(const unsigned char *src)
64 {
65 return ((unsigned)src[0] << 24) | ((unsigned)src[1] << 16) |
66 ((unsigned)src[2] << 8) | src[3];
67 }
68
69 /* mmap an entire file into memory
70 */
71
72 void *
mapfile(const char * filename,int * size)73 mapfile(const char *filename, int *size)
74 {
75 void *ret = NULL;
76 int fd;
77 struct stat st;
78
79 if ((fd = open(filename, O_RDONLY)) == -1 || fstat(fd, &st) == -1) {
80 PMNO(errno);
81 return NULL;
82 }
83 if ((ret = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
84 PMNO(errno);
85 return NULL;
86 }
87
88 *size = st.st_size;
89
90 return ret;
91 }
92 int
do_patch(unsigned char * p,int pn,unsigned char * a,int n,FILE * stream)93 do_patch(unsigned char *p, int pn, unsigned char *a, int n, FILE *stream)
94 {
95 unsigned char *start = p;
96 uint32_t hdr;
97 int sn, i;
98
99 if (pn < 8) {
100 PMSG("invalid patch file");
101 return -1;
102 }
103
104 /* decode header
105 */
106
107 hdr = dec_uint32be(p); p += 4;
108 if ((hdr & 0xFFFF0000) != 0xBD1F0000) {
109 PMSG("invalid bindiff patch file header: 0x%8x", hdr);
110 return -1;
111 } else if ((hdr & 0xFFFF) > 0x0001) {
112 PMSG("incompatible bindiff patch file version");
113 return -1;
114 }
115 sn = dec_uint32be(p); p += 4;
116
117 if (pn < (8 + sn * 12)) {
118 PMSG("bindiff patch file corrupted");
119 return -1;
120 }
121
122 /* decode/write edits
123 */
124
125 for (i = 0; i < sn; i++) {
126 int op, off, len;
127
128 op = dec_uint16be(p); p += 4;
129 off = dec_uint32be(p); p += 4;
130 len = dec_uint32be(p); p += 4;
131
132 switch (op) {
133 case DIFF_MATCH:
134 if ((off + len) > n) {
135 PMSG("file being patched is truncated or patch file corrupted");
136 return -1;
137 }
138 fwrite(a + off, 1, len, stream);
139 break;
140 case DIFF_INSERT:
141 if ((off + len) > pn) {
142 PMSG("patch file is truncated or corrupted");
143 return -1;
144 }
145 fwrite(start + off, 1, len, stream);
146 break;
147 }
148 }
149
150 return 0;
151 }
152 int
do_diff(unsigned char * a,int n,unsigned char * b,int m,FILE * stream)153 do_diff(unsigned char *a, int n, unsigned char *b, int m, FILE *stream)
154 {
155 int d;
156 int sn, i;
157 struct varray *ses = varray_new(sizeof(struct diff_edit), NULL);
158 unsigned char buf[12];
159 size_t off;
160
161 if ((d = diff(a, 0, n, b, 0, m, NULL, NULL, NULL, 0, ses, &sn, NULL)) == -1) {
162 MMNO(errno);
163 return EXIT_FAILURE;
164 }
165
166 /* encode/write header
167 */
168
169 enc_uint32be(0xBD1F0001, buf); /* magic and version */
170 enc_uint32be(sn, buf + 4); /* num_edits */
171
172 if (fwrite(buf, 8, 1, stream) != 1) {
173 PMNO(errno);
174 return -1;
175 }
176
177 /* encode/write edits
178 */
179
180 off = 8 + sn * 12;
181
182 for (i = 0; i < sn; i++) {
183 struct diff_edit *e = varray_get(ses, i);
184
185 enc_uint16be(e->op, buf);
186 enc_uint16be(0xab, buf + 2);
187
188 switch (e->op) {
189 case DIFF_MATCH:
190 enc_uint32be(e->off, buf + 4);
191 break;
192 case DIFF_INSERT:
193 enc_uint32be(off, buf + 4);
194 off += e->len;
195 break;
196 }
197
198 enc_uint32be(e->len, buf + 8);
199
200 if (fwrite(buf, 12, 1, stream) != 1) {
201 PMNO(errno);
202 return -1;
203 }
204 }
205
206 /* write payload
207 */
208
209 for (i = 0; i < sn; i++) {
210 struct diff_edit *e = varray_get(ses, i);
211
212 if (e->op == DIFF_INSERT) {
213 size_t n, len = e->len;
214
215 while ((n = fwrite(b + e->off, 1, len, stream)) < len) {
216 if (ferror(stream)) {
217 PMNO(errno);
218 return -1;
219 }
220 len -= n;
221 }
222 }
223 }
224
225 return d;
226 }
227 int
run(const char * file1,const char * file2,int patch)228 run(const char *file1, const char *file2, int patch)
229 {
230 unsigned char *a, *b;
231 int n, m;
232
233 if ((a = mapfile(file1, &n)) == NULL || (b = mapfile(file2, &m)) == NULL) {
234 AMSG("");
235 return -1;
236 }
237
238 if (patch) {
239 if (do_patch(a, n, b, m, stdout) == -1) {
240 AMSG("failed to patch %s with %s", file2, file1);
241 return -1;
242 }
243 } else if (do_diff(a, n, b, m, stdout) == -1) {
244 AMSG("failed to diff %s with %s", file1, file2);
245 return -1;
246 }
247 fflush(stdout);
248
249 return 0;
250 }
251
252 int
main(int argc,char * argv[])253 main(int argc, char *argv[])
254 {
255 char **args;
256 char *file1 = NULL, *file2 = NULL;
257 int patch = 0;
258
259 if (argc < 3) {
260 usage:
261 fprintf(stderr, "usage: %s [-p <patchfile>] <file1> [<file2>]\n", argv[0]);
262 return EXIT_FAILURE;
263 }
264
265 errno = 0;
266
267 args = argv;
268 args++; argc--;
269
270 while (argc) {
271 if (!patch && strcmp(*args, "-p") == 0) {
272 patch = 1;
273 args++; argc--;
274 if (argc == 0) {
275 fprintf(stderr, "-p requires a patch filename argument\n");
276 goto usage;
277 }
278 file1 = *args;
279 } else if (file2) {
280 fprintf(stderr, "invalid arguments\n");
281 goto usage;
282 } else if (file1) {
283 file2 = *args;
284 } else {
285 file1 = *args;
286 }
287
288 args++; argc--;
289 }
290
291 if (file1 == NULL || file2 == NULL) {
292 goto usage;
293 }
294
295 if (run(file1, file2, patch) == -1) {
296 MMSG("");
297 return EXIT_FAILURE;
298 }
299
300 return EXIT_SUCCESS;
301 }
302
303
304