1148771Scperciva/*-
2148771Scperciva * Copyright 2003-2005 Colin Percival
3148771Scperciva * All rights reserved
4148771Scperciva *
5148771Scperciva * Redistribution and use in source and binary forms, with or without
6148771Scperciva * modification, are permitted providing that the following conditions
7148771Scperciva * are met:
8148771Scperciva * 1. Redistributions of source code must retain the above copyright
9148771Scperciva *    notice, this list of conditions and the following disclaimer.
10148771Scperciva * 2. Redistributions in binary form must reproduce the above copyright
11148771Scperciva *    notice, this list of conditions and the following disclaimer in the
12148771Scperciva *    documentation and/or other materials provided with the distribution.
13148771Scperciva *
14148771Scperciva * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15148771Scperciva * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16148771Scperciva * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17148771Scperciva * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18148771Scperciva * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19148771Scperciva * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20148771Scperciva * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21148771Scperciva * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22148771Scperciva * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23148771Scperciva * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24148771Scperciva * POSSIBILITY OF SUCH DAMAGE.
25148771Scperciva */
26148771Scperciva
27148771Scperciva#include <sys/cdefs.h>
28148771Scperciva__FBSDID("$FreeBSD: releng/10.1/usr.bin/bsdiff/bspatch/bspatch.c 306941 2016-10-10 07:18:54Z delphij $");
29148771Scperciva
30306941Sdelphij#if defined(__FreeBSD__)
31306941Sdelphij#include <sys/param.h>
32306941Sdelphij#if __FreeBSD_version >= 1001511
33306941Sdelphij#include <sys/capsicum.h>
34306941Sdelphij#define HAVE_CAPSICUM
35306941Sdelphij#endif
36306941Sdelphij#endif
37306941Sdelphij
38148771Scperciva#include <bzlib.h>
39306941Sdelphij#include <err.h>
40306941Sdelphij#include <errno.h>
41306941Sdelphij#include <fcntl.h>
42306941Sdelphij#include <libgen.h>
43306941Sdelphij#include <limits.h>
44306941Sdelphij#include <stdint.h>
45306941Sdelphij#include <stdio.h>
46148771Scperciva#include <stdlib.h>
47148771Scperciva#include <string.h>
48148771Scperciva#include <unistd.h>
49148771Scperciva
50164922Scperciva#ifndef O_BINARY
51164922Scperciva#define O_BINARY 0
52164922Scperciva#endif
53306941Sdelphij#define HEADER_SIZE 32
54164922Scperciva
55306941Sdelphijstatic char *newfile;
56306941Sdelphijstatic int dirfd = -1;
57306941Sdelphij
58306941Sdelphijstatic void
59306941Sdelphijexit_cleanup(void)
60306941Sdelphij{
61306941Sdelphij
62306941Sdelphij	if (dirfd != -1 && newfile != NULL)
63306941Sdelphij		if (unlinkat(dirfd, newfile, 0))
64306941Sdelphij			warn("unlinkat");
65306941Sdelphij}
66306941Sdelphij
67148771Scpercivastatic off_t offtin(u_char *buf)
68148771Scperciva{
69148771Scperciva	off_t y;
70148771Scperciva
71306941Sdelphij	y = buf[7] & 0x7F;
72306941Sdelphij	y = y * 256; y += buf[6];
73306941Sdelphij	y = y * 256; y += buf[5];
74306941Sdelphij	y = y * 256; y += buf[4];
75306941Sdelphij	y = y * 256; y += buf[3];
76306941Sdelphij	y = y * 256; y += buf[2];
77306941Sdelphij	y = y * 256; y += buf[1];
78306941Sdelphij	y = y * 256; y += buf[0];
79148771Scperciva
80306941Sdelphij	if (buf[7] & 0x80)
81306941Sdelphij		y = -y;
82148771Scperciva
83306941Sdelphij	return (y);
84148771Scperciva}
85148771Scperciva
86306941Sdelphijint main(int argc, char *argv[])
87148771Scperciva{
88306941Sdelphij	FILE *f, *cpf, *dpf, *epf;
89306941Sdelphij	BZFILE *cpfbz2, *dpfbz2, *epfbz2;
90306941Sdelphij	char *directory, *namebuf;
91148771Scperciva	int cbz2err, dbz2err, ebz2err;
92306941Sdelphij	int newfd, oldfd;
93306941Sdelphij	off_t oldsize, newsize;
94306941Sdelphij	off_t bzctrllen, bzdatalen;
95306941Sdelphij	u_char header[HEADER_SIZE], buf[8];
96148771Scperciva	u_char *old, *new;
97306941Sdelphij	off_t oldpos, newpos;
98148771Scperciva	off_t ctrl[3];
99306941Sdelphij	off_t i, lenread, offset;
100306941Sdelphij#ifdef HAVE_CAPSICUM
101306941Sdelphij	cap_rights_t rights_dir, rights_ro, rights_wr;
102306941Sdelphij#endif
103148771Scperciva
104148771Scperciva	if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
105148771Scperciva
106148771Scperciva	/* Open patch file */
107164922Scperciva	if ((f = fopen(argv[3], "rb")) == NULL)
108148771Scperciva		err(1, "fopen(%s)", argv[3]);
109306941Sdelphij	/* Open patch file for control block */
110306941Sdelphij	if ((cpf = fopen(argv[3], "rb")) == NULL)
111306941Sdelphij		err(1, "fopen(%s)", argv[3]);
112306941Sdelphij	/* open patch file for diff block */
113306941Sdelphij	if ((dpf = fopen(argv[3], "rb")) == NULL)
114306941Sdelphij		err(1, "fopen(%s)", argv[3]);
115306941Sdelphij	/* open patch file for extra block */
116306941Sdelphij	if ((epf = fopen(argv[3], "rb")) == NULL)
117306941Sdelphij		err(1, "fopen(%s)", argv[3]);
118306941Sdelphij	/* open oldfile */
119306941Sdelphij	if ((oldfd = open(argv[1], O_RDONLY | O_BINARY, 0)) < 0)
120306941Sdelphij		err(1, "open(%s)", argv[1]);
121306941Sdelphij	/* open directory where we'll write newfile */
122306941Sdelphij	if ((namebuf = strdup(argv[2])) == NULL ||
123306941Sdelphij	    (directory = dirname(namebuf)) == NULL ||
124306941Sdelphij	    (dirfd = open(directory, O_DIRECTORY)) < 0)
125306941Sdelphij		err(1, "open %s", argv[2]);
126306941Sdelphij	free(namebuf);
127306941Sdelphij	if ((newfile = basename(argv[2])) == NULL)
128306941Sdelphij		err(1, "basename");
129306941Sdelphij	/* open newfile */
130306941Sdelphij	if ((newfd = openat(dirfd, newfile,
131306941Sdelphij	    O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0)
132306941Sdelphij		err(1, "open(%s)", argv[2]);
133306941Sdelphij	atexit(exit_cleanup);
134148771Scperciva
135306941Sdelphij#ifdef HAVE_CAPSICUM
136306941Sdelphij	if (cap_enter() < 0) {
137306941Sdelphij		/* Failed to sandbox, fatal if CAPABILITY_MODE enabled */
138306941Sdelphij		if (errno != ENOSYS)
139306941Sdelphij			err(1, "failed to enter security sandbox");
140306941Sdelphij	} else {
141306941Sdelphij		/* Capsicum Available */
142306941Sdelphij		cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK);
143306941Sdelphij		cap_rights_init(&rights_wr, CAP_WRITE);
144306941Sdelphij		cap_rights_init(&rights_dir, CAP_UNLINKAT);
145306941Sdelphij
146306941Sdelphij		if (cap_rights_limit(fileno(f), &rights_ro) < 0 ||
147306941Sdelphij		    cap_rights_limit(fileno(cpf), &rights_ro) < 0 ||
148306941Sdelphij		    cap_rights_limit(fileno(dpf), &rights_ro) < 0 ||
149306941Sdelphij		    cap_rights_limit(fileno(epf), &rights_ro) < 0 ||
150306941Sdelphij		    cap_rights_limit(oldfd, &rights_ro) < 0 ||
151306941Sdelphij		    cap_rights_limit(newfd, &rights_wr) < 0 ||
152306941Sdelphij		    cap_rights_limit(dirfd, &rights_dir) < 0)
153306941Sdelphij			err(1, "cap_rights_limit() failed, could not restrict"
154306941Sdelphij			    " capabilities");
155306941Sdelphij	}
156306941Sdelphij#endif
157306941Sdelphij
158148771Scperciva	/*
159148771Scperciva	File format:
160148771Scperciva		0	8	"BSDIFF40"
161148771Scperciva		8	8	X
162148771Scperciva		16	8	Y
163148771Scperciva		24	8	sizeof(newfile)
164148771Scperciva		32	X	bzip2(control block)
165148771Scperciva		32+X	Y	bzip2(diff block)
166148771Scperciva		32+X+Y	???	bzip2(extra block)
167148771Scperciva	with control block a set of triples (x,y,z) meaning "add x bytes
168148771Scperciva	from oldfile to x bytes from the diff block; copy y bytes from the
169148771Scperciva	extra block; seek forwards in oldfile by z bytes".
170148771Scperciva	*/
171148771Scperciva
172148771Scperciva	/* Read header */
173306941Sdelphij	if (fread(header, 1, HEADER_SIZE, f) < HEADER_SIZE) {
174148771Scperciva		if (feof(f))
175306941Sdelphij			errx(1, "Corrupt patch");
176148771Scperciva		err(1, "fread(%s)", argv[3]);
177148771Scperciva	}
178148771Scperciva
179148771Scperciva	/* Check for appropriate magic */
180148771Scperciva	if (memcmp(header, "BSDIFF40", 8) != 0)
181306941Sdelphij		errx(1, "Corrupt patch");
182148771Scperciva
183148771Scperciva	/* Read lengths from header */
184306941Sdelphij	bzctrllen = offtin(header + 8);
185306941Sdelphij	bzdatalen = offtin(header + 16);
186306941Sdelphij	newsize = offtin(header + 24);
187306941Sdelphij	if (bzctrllen < 0 || bzctrllen > OFF_MAX - HEADER_SIZE ||
188306941Sdelphij	    bzdatalen < 0 || bzctrllen + HEADER_SIZE > OFF_MAX - bzdatalen ||
189306941Sdelphij	    newsize < 0 || newsize > SSIZE_MAX)
190306941Sdelphij		errx(1, "Corrupt patch");
191148771Scperciva
192148771Scperciva	/* Close patch file and re-open it via libbzip2 at the right places */
193148771Scperciva	if (fclose(f))
194148771Scperciva		err(1, "fclose(%s)", argv[3]);
195306941Sdelphij	offset = HEADER_SIZE;
196306941Sdelphij	if (fseeko(cpf, offset, SEEK_SET))
197306941Sdelphij		err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset);
198148771Scperciva	if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
199148771Scperciva		errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
200306941Sdelphij	offset += bzctrllen;
201306941Sdelphij	if (fseeko(dpf, offset, SEEK_SET))
202306941Sdelphij		err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset);
203148771Scperciva	if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
204148771Scperciva		errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
205306941Sdelphij	offset += bzdatalen;
206306941Sdelphij	if (fseeko(epf, offset, SEEK_SET))
207306941Sdelphij		err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset);
208148771Scperciva	if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
209148771Scperciva		errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
210148771Scperciva
211306941Sdelphij	if ((oldsize = lseek(oldfd, 0, SEEK_END)) == -1 ||
212306941Sdelphij	    oldsize > SSIZE_MAX ||
213306941Sdelphij	    (old = malloc(oldsize)) == NULL ||
214306941Sdelphij	    lseek(oldfd, 0, SEEK_SET) != 0 ||
215306941Sdelphij	    read(oldfd, old, oldsize) != oldsize ||
216306941Sdelphij	    close(oldfd) == -1)
217306941Sdelphij		err(1, "%s", argv[1]);
218306941Sdelphij	if ((new = malloc(newsize)) == NULL)
219306941Sdelphij		err(1, NULL);
220148771Scperciva
221306941Sdelphij	oldpos = 0;
222306941Sdelphij	newpos = 0;
223306941Sdelphij	while (newpos < newsize) {
224148771Scperciva		/* Read control data */
225306941Sdelphij		for (i = 0; i <= 2; i++) {
226148771Scperciva			lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
227148771Scperciva			if ((lenread < 8) || ((cbz2err != BZ_OK) &&
228148771Scperciva			    (cbz2err != BZ_STREAM_END)))
229306941Sdelphij				errx(1, "Corrupt patch");
230306941Sdelphij			ctrl[i] = offtin(buf);
231148771Scperciva		};
232148771Scperciva
233148771Scperciva		/* Sanity-check */
234306941Sdelphij		if (ctrl[0] < 0 || ctrl[0] > INT_MAX ||
235306941Sdelphij		    ctrl[1] < 0 || ctrl[1] > INT_MAX)
236306941Sdelphij			errx(1, "Corrupt patch");
237303304Sdelphij
238303304Sdelphij		/* Sanity-check */
239306941Sdelphij		if (newpos + ctrl[0] > newsize)
240306941Sdelphij			errx(1, "Corrupt patch");
241148771Scperciva
242148771Scperciva		/* Read diff string */
243148771Scperciva		lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
244148771Scperciva		if ((lenread < ctrl[0]) ||
245148771Scperciva		    ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
246306941Sdelphij			errx(1, "Corrupt patch");
247148771Scperciva
248148771Scperciva		/* Add old data to diff string */
249306941Sdelphij		for (i = 0; i < ctrl[0]; i++)
250306941Sdelphij			if ((oldpos + i >= 0) && (oldpos + i < oldsize))
251306941Sdelphij				new[newpos + i] += old[oldpos + i];
252148771Scperciva
253148771Scperciva		/* Adjust pointers */
254306941Sdelphij		newpos += ctrl[0];
255306941Sdelphij		oldpos += ctrl[0];
256148771Scperciva
257148771Scperciva		/* Sanity-check */
258306941Sdelphij		if (newpos + ctrl[1] > newsize)
259306941Sdelphij			errx(1, "Corrupt patch");
260148771Scperciva
261148771Scperciva		/* Read extra string */
262148771Scperciva		lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
263148771Scperciva		if ((lenread < ctrl[1]) ||
264148771Scperciva		    ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
265306941Sdelphij			errx(1, "Corrupt patch");
266148771Scperciva
267148771Scperciva		/* Adjust pointers */
268148771Scperciva		newpos+=ctrl[1];
269148771Scperciva		oldpos+=ctrl[2];
270148771Scperciva	};
271148771Scperciva
272148771Scperciva	/* Clean up the bzip2 reads */
273148771Scperciva	BZ2_bzReadClose(&cbz2err, cpfbz2);
274148771Scperciva	BZ2_bzReadClose(&dbz2err, dpfbz2);
275148771Scperciva	BZ2_bzReadClose(&ebz2err, epfbz2);
276148771Scperciva	if (fclose(cpf) || fclose(dpf) || fclose(epf))
277148771Scperciva		err(1, "fclose(%s)", argv[3]);
278148771Scperciva
279148771Scperciva	/* Write the new file */
280306941Sdelphij	if (write(newfd, new, newsize) != newsize || close(newfd) == -1)
281306941Sdelphij		err(1, "%s", argv[2]);
282306941Sdelphij	/* Disable atexit cleanup */
283306941Sdelphij	newfile = NULL;
284148771Scperciva
285148771Scperciva	free(new);
286148771Scperciva	free(old);
287148771Scperciva
288306941Sdelphij	return (0);
289148771Scperciva}
290