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 }