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