bsdiff-portable

A more portable version of Colin Percival's bsdiff
git clone git://git.sgregoratto.me/bsdiff-portable
Log | Files | Refs | README | LICENSE

bspatch.c (6779B)


      1 /*-
      2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
      3  *
      4  * Copyright 2003-2005 Colin Percival
      5  * Copyright 2012      Matthew Endsley
      6  * Copyright 2020      Stephen Gregoratto
      7  * All rights reserved
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted providing that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     28  * POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #if 0
     32 __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
     33 #endif
     34 
     35 #include "config.h"
     36 #include <sys/stat.h>
     37 
     38 #include <bzlib.h>
     39 #if HAVE_ERR
     40 #include <err.h>
     41 #endif
     42 #include <fcntl.h>
     43 #include <stdarg.h>
     44 #include <stdio.h>
     45 #include <stdlib.h>
     46 #include <string.h>
     47 #include <unistd.h>
     48 
     49 #include "bsdiff.h"
     50 
     51 char	*newpath;
     52 FILE	*newfile;
     53 
     54 void
     55 cleanup_newfile(void)
     56 {
     57 	if (fclose(newfile) == EOF)
     58 		warn("fclose(%s)", newpath);
     59 	if (remove(newpath) == -1)
     60 		warn("remove(%s)", newpath);
     61 
     62 	exit(1);
     63 }
     64 
     65 void
     66 err_rm(const char *fmt, ...)
     67 {
     68 	va_list ap;
     69 	va_start(ap, fmt);
     70 	vwarn(fmt, ap);
     71 	va_end(ap);
     72 
     73 	cleanup_newfile();
     74 }
     75 
     76 void
     77 errx_rm(const char *fmt, ...)
     78 {
     79 	va_list ap;
     80 	va_start(ap, fmt);
     81 	vwarnx(fmt, ap);
     82 	va_end(ap);
     83 
     84 	cleanup_newfile();
     85 }
     86 
     87 static BZFILE *
     88 bz_openat(const char *path, FILE **f, off_t o)
     89 {
     90 	int bzerr;
     91 	BZFILE *bzf;
     92 
     93 	if ((*f = fopen(path, "r")) == NULL)
     94 		err_rm("fopen(%s)", path);
     95 	if (fseeko(*f, o, SEEK_SET) == -1)
     96 		err_rm("fseeko(%s, %lld)", path, (long long)o);
     97 	if ((bzf = BZ2_bzReadOpen(&bzerr, *f, 0, 0, NULL, 0)) == NULL)
     98 		errx_rm("BZ2_bzReadOpen returned %d", bzerr);
     99 
    100 	return bzf;
    101 }
    102 
    103 int
    104 main(int argc, char **argv)
    105 {
    106 	FILE *f, *cpf, *dpf, *epf;
    107 	BZFILE *cpfbz2, *dpfbz2, *epfbz2;
    108 	int bzerr;
    109 	struct stat st;
    110 	off_t oldsize, newsize;
    111 	off_t bzctrllen, bzdatalen;
    112 	uint8_t header[32], buf[8];
    113 	uint8_t *old, *new;
    114 	off_t oldpos, newpos;
    115 	off_t ctrl[3];
    116 	off_t lenread;
    117 	off_t i;
    118 
    119 	if (argc != 4)
    120 		usage();
    121 	newpath = argv[2];
    122 
    123 #if HAVE_PLEDGE && HAVE_UNVEIL
    124 	if (pledge("stdio rpath wpath cpath fattr unveil", NULL) == -1)
    125 		err(1, "pledge");
    126 	if (unveil(argv[1], "r") == -1)
    127 		err(1, "unveil(%s, r)", argv[1]);
    128 	if (unveil(newpath, "wc") == -1)
    129 		err(1, "unveil(%s, wc)", newpath);
    130 	if (unveil(argv[3], "r") == -1)
    131 		err(1, "unveil(%s, r)", argv[3]);
    132 	if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
    133 		err(1, "pledge");
    134 #endif
    135 	/* Open patch file */
    136 	if ((f = fopen(newpath, "r")) == NULL)
    137 		err_rm("fopen(%s)", newpath);
    138 	/* Open the new file */
    139 	if ((newfile = fopen(newpath, "w")) == NULL)
    140 		err_rm("fopen(%s)", newpath);
    141 #if HAVE_PLEDGE && HAVE_UNVEIL
    142 	if (pledge("stdio rpath cpath fattr", NULL) == -1)
    143 		err_rm("pledge");
    144 #endif
    145 
    146 	/* Read header */
    147 	if (fread(header, 1, 32, f) != 32) {
    148 		if (feof(f))
    149 			errx_rm("Corrupt patch");
    150 		err_rm("fread(%s)", newpath);
    151 	}
    152 
    153 	/* Check for appropriate magic */
    154 	if (memcmp(header, "BSDIFF40", 8) != 0)
    155 		errx_rm("Corrupt patch");
    156 
    157 	/* Read lengths from header */
    158 	bzctrllen = offtin(header + 8);
    159 	bzdatalen = offtin(header + 16);
    160 	newsize = offtin(header + 24);
    161 	if (bzctrllen < 0 || bzdatalen < 0 || newsize < 0)
    162 		errx_rm("Corrupt patch");
    163 
    164 	/* Close patch file and re-open it via libbzip2 at the right places */
    165 	if (fclose(f))
    166 		err_rm("fclose(%s)", newpath);
    167 	cpfbz2 = bz_openat(newpath, &cpf, 32);
    168 	dpfbz2 = bz_openat(newpath, &dpf, 32 + bzctrllen);
    169 	epfbz2 = bz_openat(newpath, &epf, 32 + bzctrllen + bzdatalen);
    170 
    171 	old = readfile(argv[1], &oldsize, &st);
    172 	if (fchmod(fileno(newfile), st.st_mode) == -1)
    173 		err_rm("fchmod(%s, %04o)", newpath, st.st_mode);
    174 #if HAVE_PLEDGE && HAVE_UNVEIL
    175 	if (pledge("stdio cpath", NULL) == -1)
    176 		err_rm("pledge");
    177 #endif
    178 	if ((new = reallocarray(NULL, newsize + 1, 1)) == NULL)
    179 		err_rm("reallocarray");
    180 
    181 	oldpos = newpos = 0;
    182 	while (newpos < newsize) {
    183 		/* Read control data */
    184 		for (i = 0; i <= 2; i++) {
    185 			lenread = BZ2_bzRead(&bzerr, cpfbz2, buf, 8);
    186 			if (lenread < 8 || (bzerr != BZ_OK && bzerr != BZ_STREAM_END))
    187 				errx_rm("Corrupt patch");
    188 			ctrl[i] = offtin(buf);
    189 		}
    190 
    191 		/* Sanity-check */
    192 		if (ctrl[0] < 0 || ctrl[1] < 0)
    193 			errx_rm("Corrupt patch");
    194 		if (newpos + ctrl[0] > newsize)
    195 			errx_rm("Corrupt patch");
    196 
    197 		/* Read diff string */
    198 		lenread = BZ2_bzRead(&bzerr, dpfbz2, new + newpos, ctrl[0]);
    199 		if (lenread < ctrl[0] || (bzerr != BZ_OK && bzerr != BZ_STREAM_END))
    200 			errx_rm("Corrupt patch");
    201 
    202 		/* Add old data to diff string */
    203 		for (i = 0; i < ctrl[0]; i++)
    204 			if (oldpos + i >= 0 && oldpos + i < oldsize)
    205 				new[newpos + i] += old[oldpos + i];
    206 
    207 		/* Adjust pointers */
    208 		newpos += ctrl[0];
    209 		oldpos += ctrl[0];
    210 
    211 		/* Sanity-check */
    212 		if (newpos + ctrl[1] > newsize)
    213 			errx_rm("Corrupt patch");
    214 
    215 		/* Read extra string */
    216 		lenread = BZ2_bzRead(&bzerr, epfbz2, new + newpos, ctrl[1]);
    217 		if (lenread < ctrl[1] || (bzerr != BZ_OK && bzerr != BZ_STREAM_END))
    218 			errx_rm("Corrupt patch");
    219 
    220 		/* Adjust pointers */
    221 		newpos += ctrl[1];
    222 		oldpos += ctrl[2];
    223 	}
    224 
    225 	/* Clean up the bzip2 reads */
    226 #define BZ_CLOSE(f, bzf)                                                                           \
    227 	BZ2_bzReadClose(&bzerr, (bzf));                                                            \
    228 	if (bzerr != BZ_OK)                                                                        \
    229 		err_rm("BZ2_bzReadClose(%s) returned %d", newpath, bzerr);                         \
    230 	if (fclose((f)) == EOF)                                                                    \
    231 		err_rm("fclose(%s)", newpath);
    232 	BZ_CLOSE(cpf, cpfbz2)
    233 	BZ_CLOSE(dpf, dpfbz2)
    234 	BZ_CLOSE(epf, epfbz2)
    235 
    236 	/* Write the new file */
    237 	if (fwrite(new, 1, newsize, newfile) != (size_t)newsize)
    238 		err_rm("fwrite(%s, %lld)", newpath, (long long)newsize);
    239 	if (fclose(newfile) == -1)
    240 		err_rm("fclose(%s)", newpath);
    241 
    242 	free(new);
    243 	free(old);
    244 
    245 	return 0;
    246 }