├── bzip2 └── .gitkeep ├── Makefile ├── .gitignore ├── Makefile.mak ├── LICENSE ├── bsdifflib.h ├── bsdiff.c ├── bspatch.c ├── README.md ├── bspatchlib.h ├── bspatchlib.c └── bsdifflib.c /bzip2/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS += -O2 -Wall 3 | LDFLAGS += -lbz2 4 | 5 | SOURCES = bsdiff.c bsdifflib.c bspatch.c bspatchlib.c 6 | OBJECTS = $(SOURCES:.c=.o) 7 | EXECUTABLES = bsdiff bspatch 8 | 9 | .PHONY: all clean 10 | 11 | all: $(EXECUTABLES) 12 | 13 | bsdiff: bsdiff.o bsdifflib.o 14 | bspatch: bspatch.o bspatchlib.o 15 | 16 | $(EXECUTABLES): %: %.o 17 | $(CC) $^ $(LDFLAGS) -o $@ 18 | 19 | %.o: %.c 20 | $(CC) $(CFLAGS) -c -o $@ $< 21 | 22 | clean: 23 | rm -f $(EXECUTABLES) $(OBJECTS) 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Manifest files 16 | *.manifest 17 | 18 | # Precompiled Headers 19 | *.gch 20 | *.pch 21 | 22 | # Libraries 23 | *.lib 24 | *.a 25 | *.la 26 | *.lo 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | # Debug files 43 | *.dSYM/ 44 | *.su 45 | *.idb 46 | *.pdb 47 | 48 | # Kernel Module Compile Results 49 | *.mod* 50 | *.cmd 51 | .tmp_versions/ 52 | modules.order 53 | Module.symvers 54 | Mkfile.old 55 | dkms.conf 56 | 57 | # Linux bsdiff/bspatch binaries 58 | bsdiff 59 | bspatch 60 | 61 | # bzip2 files 62 | bzip2/* 63 | !bzip2/.gitkeep 64 | -------------------------------------------------------------------------------- /Makefile.mak: -------------------------------------------------------------------------------- 1 | # NMake makefile 2 | 3 | # Compiler 4 | CC = cl 5 | 6 | # Compiler flags 7 | CFLAGS = /nologo /O2 /W3 /Ibzip2 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS 8 | 9 | # Linker 10 | LINK = link 11 | 12 | # Linker flags 13 | LFLAGS = /nologo /MANIFEST:EMBED /MANIFESTUAC:"level='asInvoker' uiAccess='false'" 14 | 15 | # Source files 16 | BSDIFF_SRCS = bsdiff.c bsdifflib.c 17 | BSPATCH_SRCS = bspatch.c bspatchlib.c 18 | BZIP2_SRCS = bzip2\blocksort.c bzip2\bzlib.c bzip2\compress.c bzip2\crctable.c bzip2\decompress.c bzip2\huffman.c bzip2\randtable.c 19 | 20 | # Object files 21 | BSDIFF_OBJS = $(BSDIFF_SRCS:.c=.obj) 22 | BSPATCH_OBJS = $(BSPATCH_SRCS:.c=.obj) 23 | BZIP2_OBJS = $(BZIP2_SRCS:.c=.obj) 24 | 25 | # Target executables 26 | BSDIFF_TARGET = bsdiff.exe 27 | BSPATCH_TARGET = bspatch.exe 28 | 29 | # Default target 30 | all: $(BSDIFF_TARGET) $(BSPATCH_TARGET) 31 | 32 | # Compile C source files 33 | .c.obj: 34 | $(CC) $(CFLAGS) /c /Fo$(@D)\ $< 35 | 36 | # Link object files 37 | $(BSDIFF_TARGET): $(BSDIFF_OBJS) $(BZIP2_OBJS) 38 | $(LINK) $(LFLAGS) -out:$@ $(BSDIFF_OBJS) $(BZIP2_OBJS) 39 | 40 | $(BSPATCH_TARGET): $(BSPATCH_OBJS) $(BZIP2_OBJS) 41 | $(LINK) $(LFLAGS) -out:$@ $(BSPATCH_OBJS) $(BZIP2_OBJS) 42 | 43 | # Clean target 44 | clean: 45 | del /Q /F $(BSDIFF_OBJS) $(BSPATCH_OBJS) $(BZIP2_OBJS) $(BSDIFF_TARGET) $(BSPATCH_TARGET) 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2003-2005 Colin Percival 2 | Copyright 2007 Andreas John 3 | Copyright 2023 Peter Vaskovic 4 | All rights reserved 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted providing that the following conditions 8 | are met: 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /bsdifflib.h: -------------------------------------------------------------------------------- 1 | #ifndef BSDIFFLIBH__ 2 | #define BSDIFFLIBH__ 3 | /*- 4 | * Copyright 2023 Peter Vaskovic 5 | * All rights reserved 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted providing that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifdef __cplusplus 30 | extern "C" 31 | { 32 | #endif 33 | 34 | /* Returns NULL if creating the patch succeeded, and an error message otherwise. */ 35 | char *bsdiff(const char *oldfile, const char *newfile, 36 | const char *patchfile); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif /* BSDIFFLIBH__ */ 43 | -------------------------------------------------------------------------------- /bsdiff.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2023 Peter Vaskovic 3 | * All rights reserved 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted providing that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 23 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include "bsdifflib.h" 29 | 30 | int main(int argc, char *argv[]) 31 | { 32 | char *errmsg; 33 | 34 | if (argc != 4) 35 | { 36 | printf("usage: %s oldfile newfile patchfile\n", argv[0]); 37 | return 1; 38 | } 39 | 40 | if ((errmsg = bsdiff(argv[1], argv[2], argv[3])) != NULL) 41 | { 42 | printf("%s\n", errmsg); 43 | return 1; 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /bspatch.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2023 Peter Vaskovic 3 | * All rights reserved 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted providing that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 23 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include "bspatchlib.h" 29 | 30 | int main(int argc, char *argv[]) 31 | { 32 | char *errmsg; 33 | 34 | if (argc == 2) 35 | { 36 | /* Call bspatch_info with the single argument (patchfile) */ 37 | if ((errmsg = bspatch_info(argv[1])) != NULL) 38 | { 39 | printf("%s\n", errmsg); 40 | return 1; 41 | } 42 | } 43 | else if (argc == 4) 44 | { 45 | /* Call bspatch with three arguments (oldfile, newfile, patchfile) */ 46 | if ((errmsg = bspatch(argv[1], argv[2], argv[3])) != NULL) 47 | { 48 | printf("%s\n", errmsg); 49 | return 1; 50 | } 51 | } 52 | else 53 | { 54 | printf("usage:\n"); 55 | printf(" for patching: %s oldfile newfile patchfile\n", argv[0]); 56 | printf(" for info: %s patchfile\n", argv[0]); 57 | return 1; 58 | } 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Binary diff/patch library (bsdifflib/bspatchlib) 1.2 is based on the original 2 | binary diff/patch utility (bsdiff/bspatch) by Colin Percival and the Win32 3 | port by Andreas John. 4 | 5 | Binary diff/patch library adds an API to make it usable as a cross-platform 6 | C/C++ library. This library generates patches that are compatible with the 7 | original bsdiff tool. The patch routine now works on memory instead of files 8 | so multiple patches in succession can be applied without disk access. 9 | 10 | See bsdiff.c/bspatch.c for example usage. 11 | 12 | Error handling was inspired by GetWAD, written by 13 | Copyright 2003-2024 Hippocrates Sendoukas 14 | available at http://getwad.zdaemon.org/ 15 | 16 | I would like to thank Hippocrates Sendoukas for helping me out with some 17 | C related questions and giving me helpful coding suggestions in general. 18 | You can visit his site at http://hs.zdaemon.org/ 19 | 20 | Compile: 21 | 22 | Linux: Just run make 23 | Windows: Unpack bzip2 source code to the bzip2 folder. 24 | Run nmake -f Makefile.mak 25 | 26 | Changelog: 27 | 28 | bsdifflib/bspatchlib 1.3 (21 September 2024) 29 | 30 | Restored compatibility with older Visual Studio versions. 31 | Fixed manifest handling. No more UAC in Windows. 32 | 33 | bsdifflib/bspatchlib 1.2 (9 September 2023) 34 | 35 | Removed Visual Studio Project files. 36 | Removed bzip2 source code. 37 | Added nmake file for Windows. 38 | Updated Makefile. 39 | Minor code cleanups. 40 | Pass patch file only to bspatch to print decompressed ctrl/diff/extra sizes. 41 | Fixed CVE-2014-9862. 42 | Fixed CVE-2020-14315. 43 | Fixed memory leaks and add more checks in decompress_block. 44 | Fixed handling of empty source/destination files. 45 | 46 | bsdifflib/bspatchlib 1.1 (9 November 2010) 47 | 48 | Included own header in source files. 49 | Hippocrates Sendoukas optimized the patching process to 50 | take place in memory. New functions have been added to the bspatchlib 51 | API to make use of the optimizations for certain use cases. 52 | Updated libbzip2 to 1.0.6. 53 | 54 | bsdifflib/bspatchlib 1.0 (26 May 2010) 55 | 56 | Initial release. 57 | -------------------------------------------------------------------------------- /bspatchlib.h: -------------------------------------------------------------------------------- 1 | #ifndef BSPATCHLIBH__ 2 | #define BSPATCHLIBH__ 3 | /*- 4 | * Copyright 2023 Peter Vaskovic 5 | * All rights reserved 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted providing that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifdef __cplusplus 30 | extern "C" 31 | { 32 | #endif 33 | 34 | /* Returns NULL on success, and an error message otherwise. */ 35 | char *file_to_mem(const char *fname, unsigned char **buf, int *buf_sz); 36 | 37 | /* Same */ 38 | char *mem_to_file(const unsigned char *buf, int buf_sz, const char *fname); 39 | 40 | /* Same */ 41 | char *bspatch_mem(const unsigned char *old_buf, int old_size, 42 | unsigned char **new_buf, int *new_size, 43 | const unsigned char *compr_patch_buf, int compr_patch_buf_sz, 44 | int uncompr_ctrl_sz, int uncompr_diff_sz, int uncompr_xtra_sz); 45 | 46 | /* Same */ 47 | char *bspatch(const char *oldfile, const char *newfile, 48 | const char *patchfile); 49 | 50 | /* Same */ 51 | char *bspatch_info(const char *patchfile); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | #endif /* BSPATCHLIBH__ */ 58 | -------------------------------------------------------------------------------- /bspatchlib.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2003-2005 Colin Percival 3 | * Copyright 2007 Andreas John 4 | * Copyright 2023 Peter Vaskovic 5 | * All rights reserved 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted providing that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "bspatchlib.h" 36 | 37 | #define ERRSTR_MAX_LEN 1024 38 | #define HEADER_SIZE 32 39 | 40 | /* TYPE_MINIMUM and TYPE_MAXIMUM taken from coreutils */ 41 | #ifndef TYPE_MINIMUM 42 | #define TYPE_MINIMUM(t) \ 43 | ((t)((t)0 < (t)-1 ? (t)0 : ~TYPE_MAXIMUM(t))) 44 | #endif 45 | #ifndef TYPE_MAXIMUM 46 | #define TYPE_MAXIMUM(t) \ 47 | ((t)((t)0 < (t)-1 \ 48 | ? (t)-1 \ 49 | : ((((t)1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1))) 50 | #endif 51 | 52 | #ifndef OFF_MAX 53 | #define OFF_MAX TYPE_MAXIMUM(off_t) 54 | #endif 55 | 56 | #ifndef OFF_MIN 57 | #define OFF_MIN TYPE_MINIMUM(off_t) 58 | #endif 59 | 60 | #if defined(_MSC_VER) && _MSC_VER < 1900 //VS before 2015 61 | #define snprintf _snprintf 62 | #endif 63 | 64 | #if defined(_MSC_VER) && _MSC_VER < 1800 //VS before 2013 65 | #if defined(_WIN64) 66 | #define SIZE_T_FMT "%llu" 67 | #else 68 | #define SIZE_T_FMT "%lu" 69 | #endif 70 | #else 71 | #define SIZE_T_FMT "%zu" 72 | #endif 73 | 74 | #ifdef _WIN32 75 | #define LONG_LONG_FMT "%I64d" 76 | #else 77 | #define LONG_LONG_FMT "%lld" 78 | #endif 79 | 80 | /***************************************************************************/ 81 | 82 | char *file_to_mem(const char *fname, unsigned char **buf, int *buf_sz) 83 | { 84 | FILE *f; 85 | long f_sz; 86 | size_t l_sz; 87 | unsigned char *l_buf; 88 | static char errstr[ERRSTR_MAX_LEN]; 89 | 90 | *buf = NULL; 91 | *buf_sz = 0; 92 | 93 | if ((f = fopen(fname, "rb")) == NULL) 94 | { 95 | snprintf(errstr, sizeof(errstr), "Cannot open file \"%s\" for reading.", fname); 96 | return errstr; 97 | } 98 | if (fseek(f, 0, SEEK_END) || (f_sz = ftell(f)) < 0) 99 | { 100 | fclose(f); 101 | snprintf(errstr, sizeof(errstr), "Seek failure on file \"%s\".", fname); 102 | return errstr; 103 | } 104 | 105 | if (f_sz >= INT_MAX) 106 | { 107 | fclose(f); 108 | snprintf(errstr, sizeof(errstr), "File size (" LONG_LONG_FMT " bytes) exceeds maximum limit for an int.", (long long)f_sz); 109 | return errstr; 110 | } 111 | l_sz = (size_t)f_sz; 112 | 113 | if ((l_buf = (unsigned char *)malloc(l_sz + 1)) == NULL) 114 | { 115 | fclose(f); 116 | snprintf(errstr, sizeof(errstr), "Cannot allocate " SIZE_T_FMT " bytes to read file \"%s\" into memory.", l_sz + 1, fname); 117 | return errstr; 118 | } 119 | 120 | fseek(f, 0, SEEK_SET); 121 | 122 | if (fread(l_buf, 1, l_sz, f) != l_sz) 123 | { 124 | free(l_buf); 125 | fclose(f); 126 | snprintf(errstr, sizeof(errstr), "Cannot read from file \"%s\".", fname); 127 | return errstr; 128 | } 129 | fclose(f); 130 | *buf = l_buf; 131 | *buf_sz = (int)l_sz; 132 | return NULL; 133 | } 134 | 135 | /***************************************************************************/ 136 | 137 | char *mem_to_file(const unsigned char *buf, int buf_sz, const char *fname) 138 | { 139 | FILE *f; 140 | static char errstr[ERRSTR_MAX_LEN]; 141 | 142 | if ((f = fopen(fname, "wb")) == NULL) 143 | { 144 | snprintf(errstr, sizeof(errstr), "Cannot open file \"%s\" for writing.", fname); 145 | return errstr; 146 | } 147 | if ((int)fwrite(buf, 1, buf_sz, f) != buf_sz) 148 | { 149 | fclose(f); 150 | snprintf(errstr, sizeof(errstr), "Cannot write to file \"%s\".", fname); 151 | return errstr; 152 | } 153 | fclose(f); 154 | return NULL; 155 | } 156 | 157 | /***************************************************************************/ 158 | 159 | static int add_off_t_overflow(off_t a, off_t b, off_t *res) 160 | { 161 | 162 | if ((b > 0 && a > OFF_MAX - b) || (b < 0 && a < OFF_MIN - b)) 163 | return -1; 164 | 165 | *res = a + b; 166 | return 0; 167 | } 168 | 169 | /***************************************************************************/ 170 | 171 | static off_t offtin(const unsigned char *buf) 172 | { 173 | off_t y; 174 | y = buf[7] & 0x7F; 175 | y = y * 256; 176 | y += buf[6]; 177 | y = y * 256; 178 | y += buf[5]; 179 | y = y * 256; 180 | y += buf[4]; 181 | y = y * 256; 182 | y += buf[3]; 183 | y = y * 256; 184 | y += buf[2]; 185 | y = y * 256; 186 | y += buf[1]; 187 | y = y * 256; 188 | y += buf[0]; 189 | 190 | if (buf[7] & 0x80) 191 | { 192 | y = -y; 193 | } 194 | 195 | return y; 196 | } 197 | 198 | /***************************************************************************/ 199 | 200 | static char *decompress_block(const unsigned char *in_buf, unsigned in_sz, unsigned char **out_buf, int *out_sz) 201 | { 202 | int rc, known_size; 203 | unsigned sz; 204 | unsigned char *buf; 205 | unsigned char *new_buf; 206 | static char errstr[ERRSTR_MAX_LEN]; 207 | 208 | known_size = *out_sz; 209 | *out_buf = NULL; 210 | *out_sz = 0; 211 | 212 | if (in_sz == 0) /* BZ2 file cannot be empty. It must at least have a header */ 213 | { 214 | snprintf(errstr, sizeof(errstr), "decompress_block: compressed file cannot be empty"); 215 | return errstr; 216 | } 217 | 218 | if (in_sz >= INT_MAX) /* Compressed input file is bigger than 2 GB */ 219 | { 220 | snprintf(errstr, sizeof(errstr), "decompress_block: compressed file is too big"); 221 | return errstr; 222 | } 223 | 224 | if (known_size >= INT_MAX) /* Uncompressed output file is bigger than 2 GB */ 225 | { 226 | snprintf(errstr, sizeof(errstr), "decompress_block: uncompressed file is too big"); 227 | return errstr; 228 | } 229 | 230 | sz = (known_size >= 0) ? ((unsigned)known_size + 16) : 2 * in_sz; 231 | if ((buf = (unsigned char *)malloc(sz)) == NULL) 232 | { 233 | snprintf(errstr, sizeof(errstr), "decompress_block: cannot allocate %u bytes for patch", sz); 234 | return errstr; 235 | } 236 | 237 | for (;;) 238 | { 239 | rc = BZ2_bzBuffToBuffDecompress((char *)buf, &sz, (char *)in_buf, in_sz, 0, 0); 240 | if (rc == BZ_OK) 241 | { 242 | *out_buf = buf; 243 | *out_sz = (int)sz; 244 | return NULL; 245 | } 246 | if (rc != BZ_OUTBUFF_FULL) 247 | { 248 | free(buf); /* Release memory if decompression failed for some other reason */ 249 | snprintf(errstr, sizeof(errstr), "decompress_block: BZ2_bzBuffToBuffDecompress() returned %d", rc); 250 | return errstr; 251 | } 252 | 253 | if (sz >= INT_MAX) /* Seems to me that 2 GB should be more than enough for a patch */ 254 | break; 255 | 256 | /* 257 | * We come to this point if the decompression failed because 258 | * we have not allocated enough memory 259 | */ 260 | if (known_size >= 0) /* This should not happen, but don't break if it does */ 261 | { 262 | if (sz < 2 * in_sz) 263 | sz = 2 * in_sz; 264 | else 265 | sz *= 2; 266 | known_size = -1; 267 | } 268 | else /* Double the buffer size */ 269 | { 270 | sz *= 2; 271 | } 272 | 273 | if ((new_buf = (unsigned char *)realloc(buf, sz)) == NULL) /* Allocate more memory */ 274 | { 275 | free(buf); 276 | snprintf(errstr, sizeof(errstr), "decompress_block: cannot allocate %u bytes for patch", sz); 277 | return errstr; 278 | } 279 | buf = new_buf; 280 | } 281 | free(buf); 282 | snprintf(errstr, sizeof(errstr), "decompress_block: even %u bytes are not enough for the patch", sz); 283 | return errstr; 284 | } 285 | 286 | /***************************************************************************/ 287 | 288 | char *bspatch_mem(const unsigned char *old_buf, int old_size, 289 | unsigned char **new_buf, int *new_size, 290 | const unsigned char *compr_patch_buf, int compr_patch_buf_sz, 291 | int uncompr_ctrl_sz, int uncompr_diff_sz, int uncompr_xtra_sz) 292 | { 293 | off_t l_new_size, res_add_off_t; 294 | int dec_ctrl_sz, dec_diff_sz, dec_xtra_sz; 295 | off_t bzctrllen, bzdifflen, bzextralen; 296 | unsigned char *l_new_buf, *dec_ctrl_buf, *dec_diff_buf, *dec_xtra_buf, 297 | *pctrl, *pdiff, *pxtra, *pctrl_end, *pdiff_end, *pxtra_end; 298 | off_t oldpos, newpos, i, ctrl[3]; 299 | char *errmsg; 300 | static char errstr[ERRSTR_MAX_LEN]; 301 | 302 | *new_buf = NULL; 303 | *new_size = 0; 304 | 305 | /* 306 | * File format: 307 | * 0 8 "BSDIFF40" 308 | * 8 8 X 309 | * 16 8 Y 310 | * 24 8 sizeof(newfile) 311 | * 32 X bzip2(control block) 312 | * 32+X Y bzip2(diff block) 313 | * 32+X+Y ??? bzip2(extra block) 314 | * with control block a set of triples (x,y,z) meaning "add x bytes 315 | * from oldfile to x bytes from the diff block; copy y bytes from the 316 | * extra block; seek forwards in oldfile by z bytes". 317 | */ 318 | if (compr_patch_buf_sz < HEADER_SIZE) 319 | { 320 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Too short patch size."); 321 | return errstr; 322 | } 323 | 324 | /* Check for appropriate magic */ 325 | if (memcmp(compr_patch_buf, "BSDIFF40", 8) != 0) 326 | { 327 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Bad header signature."); 328 | return errstr; 329 | } 330 | 331 | /* Read lengths from header */ 332 | bzctrllen = offtin(compr_patch_buf + 8); 333 | bzdifflen = offtin(compr_patch_buf + 16); 334 | l_new_size = offtin(compr_patch_buf + 24); 335 | 336 | /* Overflow-check */ 337 | if (add_off_t_overflow(HEADER_SIZE, bzctrllen, &res_add_off_t) || 338 | add_off_t_overflow(HEADER_SIZE + bzctrllen, bzdifflen, &res_add_off_t)) 339 | { 340 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Overflow 1."); 341 | return errstr; 342 | } 343 | 344 | if (bzctrllen <= 0 || bzctrllen > OFF_MAX - HEADER_SIZE || 345 | bzdifflen <= 0 || bzctrllen + HEADER_SIZE > OFF_MAX - bzdifflen || 346 | l_new_size < 0 || l_new_size > INT_MAX || 347 | compr_patch_buf_sz <= HEADER_SIZE + bzctrllen + bzdifflen) 348 | { 349 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Bad header lengths."); 350 | return errstr; 351 | } 352 | bzextralen = compr_patch_buf_sz - HEADER_SIZE - bzctrllen - bzdifflen; 353 | 354 | /* Limiting the size to INT_MAX should be good enough */ 355 | if (bzctrllen > INT_MAX || bzdifflen > INT_MAX || bzextralen > INT_MAX) 356 | { 357 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Patch is too big."); 358 | return errstr; 359 | } 360 | 361 | dec_ctrl_sz = uncompr_ctrl_sz; 362 | if ((errmsg = decompress_block(compr_patch_buf + HEADER_SIZE, bzctrllen, 363 | &dec_ctrl_buf, &dec_ctrl_sz)) != NULL) 364 | return errmsg; 365 | 366 | dec_diff_sz = uncompr_diff_sz; 367 | if ((errmsg = decompress_block(compr_patch_buf + HEADER_SIZE + bzctrllen, bzdifflen, 368 | &dec_diff_buf, &dec_diff_sz)) != NULL) 369 | { 370 | free(dec_ctrl_buf); 371 | return errmsg; 372 | } 373 | 374 | dec_xtra_sz = uncompr_xtra_sz; 375 | if ((errmsg = decompress_block(compr_patch_buf + HEADER_SIZE + bzctrllen + bzdifflen, bzextralen, 376 | &dec_xtra_buf, &dec_xtra_sz)) != NULL) 377 | { 378 | free(dec_diff_buf); 379 | free(dec_ctrl_buf); 380 | return errmsg; 381 | } 382 | 383 | /* Print decompressed ctrl/diff/xtra sizes */ 384 | if (old_buf == NULL) 385 | { 386 | printf("Decompressed ctrl/diff/extra sizes are: %d/%d/%d.\n", dec_ctrl_sz, dec_diff_sz, dec_xtra_sz); 387 | free(dec_xtra_buf); 388 | free(dec_diff_buf); 389 | free(dec_ctrl_buf); 390 | return NULL; 391 | } 392 | 393 | if ((l_new_buf = (unsigned char *)malloc(l_new_size + 1)) == NULL) 394 | { 395 | snprintf(errstr, sizeof(errstr), "Cannot allocate %ld bytes to create the patch.", l_new_size + 1); 396 | free(dec_xtra_buf); 397 | free(dec_diff_buf); 398 | free(dec_ctrl_buf); 399 | return errstr; 400 | } 401 | 402 | pctrl_end = (pctrl = dec_ctrl_buf) + dec_ctrl_sz; 403 | pdiff_end = (pdiff = dec_diff_buf) + dec_diff_sz; 404 | pxtra_end = (pxtra = dec_xtra_buf) + dec_xtra_sz; 405 | 406 | oldpos = newpos = 0; 407 | 408 | while (newpos < l_new_size) 409 | { 410 | /* Read control data */ 411 | if (pctrl > pctrl_end - 2 * 8) 412 | { 413 | snprintf(errstr, sizeof(errstr), "Corrupt patch 1."); 414 | GETOUT: 415 | free(dec_xtra_buf); 416 | free(dec_diff_buf); 417 | free(dec_ctrl_buf); 418 | free(l_new_buf); 419 | return errstr; 420 | } 421 | for (i = 0; i <= 2; i++) 422 | { 423 | ctrl[i] = offtin(pctrl); 424 | pctrl += 8; 425 | } 426 | 427 | /* Sanity-check */ 428 | if (ctrl[0] < 0 || ctrl[0] > INT_MAX || 429 | (ctrl[1] < 0) || ctrl[1] > INT_MAX) 430 | { 431 | snprintf(errstr, sizeof(errstr), "Corrupt patch 2."); 432 | goto GETOUT; 433 | } 434 | 435 | /* Overflow-check */ 436 | if (add_off_t_overflow(newpos, ctrl[0], &res_add_off_t)) 437 | { 438 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Overflow 2."); 439 | goto GETOUT; 440 | } 441 | 442 | /* Sanity-check */ 443 | if (res_add_off_t > l_new_size) 444 | { 445 | snprintf(errstr, sizeof(errstr), "Corrupt patch 3."); 446 | goto GETOUT; 447 | } 448 | 449 | /* Read diff string */ 450 | if (pdiff > pdiff_end - ctrl[0]) 451 | { 452 | snprintf(errstr, sizeof(errstr), "Corrupt patch 4."); 453 | goto GETOUT; 454 | } 455 | memcpy(l_new_buf + newpos, pdiff, ctrl[0]); 456 | pdiff += ctrl[0]; 457 | 458 | /* Overflow-check */ 459 | if (add_off_t_overflow(oldpos, ctrl[0], &res_add_off_t)) 460 | { 461 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Overflow 3."); 462 | goto GETOUT; 463 | } 464 | 465 | /* Add old data to diff string */ 466 | for (i = 0; i < ctrl[0]; i++) 467 | { 468 | if (oldpos + i < old_size) 469 | l_new_buf[newpos + i] += old_buf[oldpos + i]; 470 | } 471 | 472 | /* Adjust pointers */ 473 | oldpos = res_add_off_t; 474 | if (add_off_t_overflow(newpos, ctrl[0], &res_add_off_t)) 475 | { 476 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Overflow 4."); 477 | goto GETOUT; 478 | } 479 | newpos = res_add_off_t; 480 | 481 | /* Overflow-check */ 482 | if (add_off_t_overflow(newpos, ctrl[1], &res_add_off_t)) 483 | { 484 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Overflow 5."); 485 | goto GETOUT; 486 | } 487 | 488 | /* Sanity-check */ 489 | if (res_add_off_t > l_new_size) 490 | { 491 | snprintf(errstr, sizeof(errstr), "Corrupt patch 5."); 492 | goto GETOUT; 493 | } 494 | 495 | /* Read extra string */ 496 | if (pxtra > pxtra_end - ctrl[1]) 497 | { 498 | snprintf(errstr, sizeof(errstr), "Corrupt patch 6."); 499 | goto GETOUT; 500 | } 501 | memcpy(l_new_buf + newpos, pxtra, ctrl[1]); 502 | pxtra += ctrl[1]; 503 | 504 | /* Adjust pointers */ 505 | newpos = res_add_off_t; 506 | if (add_off_t_overflow(oldpos, ctrl[2], &res_add_off_t)) 507 | { 508 | snprintf(errstr, sizeof(errstr), "Corrupt patch. Overflow 6."); 509 | goto GETOUT; 510 | } 511 | oldpos = res_add_off_t; 512 | } 513 | 514 | /* Clean up the bzip2 reads */ 515 | free(dec_xtra_buf); 516 | free(dec_diff_buf); 517 | free(dec_ctrl_buf); 518 | 519 | *new_buf = l_new_buf; 520 | *new_size = l_new_size; 521 | return NULL; 522 | } 523 | 524 | /***************************************************************************/ 525 | 526 | char *bspatch(const char *oldfile, const char *newfile, const char *patchfile) 527 | { 528 | unsigned char *in_buf, *out_buf, *patch_buf; 529 | int in_sz, out_sz, patch_sz; 530 | char *errmsg; 531 | 532 | errmsg = file_to_mem(oldfile, &in_buf, &in_sz); 533 | if (errmsg) 534 | return errmsg; 535 | 536 | errmsg = file_to_mem(patchfile, &patch_buf, &patch_sz); 537 | if (errmsg) 538 | { 539 | free(in_buf); 540 | return errmsg; 541 | } 542 | 543 | errmsg = bspatch_mem(in_buf, in_sz, &out_buf, &out_sz, patch_buf, patch_sz, -1, -1, -1); 544 | free(in_buf); 545 | free(patch_buf); 546 | if (errmsg) 547 | return errmsg; 548 | 549 | errmsg = mem_to_file(out_buf, out_sz, newfile); 550 | free(out_buf); 551 | return (errmsg) ? errmsg : NULL; 552 | } 553 | 554 | /***************************************************************************/ 555 | 556 | char *bspatch_info(const char *patchfile) 557 | { 558 | unsigned char *dummy_buf, *patch_buf; 559 | int dummy_sz, patch_sz; 560 | char *errmsg; 561 | 562 | errmsg = file_to_mem(patchfile, &patch_buf, &patch_sz); 563 | if (errmsg) 564 | { 565 | return errmsg; 566 | } 567 | 568 | /* Passing NULL as the first argument to bspatch_mem will make it print 569 | the decompressed ctrl/diff/xtra sizes of the patch file to the console. 570 | These can then be used when calling bspatch_mem to speed up the memory 571 | allocation */ 572 | errmsg = bspatch_mem(NULL, 0, &dummy_buf, &dummy_sz, patch_buf, patch_sz, -1, -1, -1); 573 | free(patch_buf); 574 | return (errmsg) ? errmsg : NULL; 575 | } 576 | 577 | /***************************************************************************/ 578 | -------------------------------------------------------------------------------- /bsdifflib.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2003-2005 Colin Percival 3 | * Copyright 2007 Andreas John 4 | * Copyright 2023 Peter Vaskovic 5 | * All rights reserved 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted providing that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "bsdifflib.h" 35 | 36 | #define ERRSTR_MAX_LEN 1024 37 | #define HEADER_SIZE 32 38 | 39 | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) 40 | 41 | #if defined(_MSC_VER) && _MSC_VER < 1900 //VS before 2015 42 | #define snprintf _snprintf 43 | #endif 44 | 45 | /***************************************************************************/ 46 | 47 | static void split(off_t *I, off_t *V, off_t start, off_t len, off_t h) 48 | { 49 | off_t i, j, k, x, tmp, jj, kk; 50 | 51 | if (len < 16) 52 | { 53 | for (k = start; k < start + len; k += j) 54 | { 55 | j = 1; 56 | x = V[I[k] + h]; 57 | 58 | for (i = 1; k + i < start + len; i++) 59 | { 60 | if (V[I[k + i] + h] < x) 61 | { 62 | x = V[I[k + i] + h]; 63 | j = 0; 64 | }; 65 | 66 | if (V[I[k + i] + h] == x) 67 | { 68 | tmp = I[k + j]; 69 | I[k + j] = I[k + i]; 70 | I[k + i] = tmp; 71 | j++; 72 | }; 73 | }; 74 | 75 | for (i = 0; i < j; i++) 76 | { 77 | V[I[k + i]] = k + j - 1; 78 | } 79 | 80 | if (j == 1) 81 | { 82 | I[k] = -1; 83 | } 84 | }; 85 | 86 | return; 87 | }; 88 | 89 | x = V[I[start + len / 2] + h]; 90 | 91 | jj = 0; 92 | 93 | kk = 0; 94 | 95 | for (i = start; i < start + len; i++) 96 | { 97 | if (V[I[i] + h] < x) 98 | { 99 | jj++; 100 | } 101 | 102 | if (V[I[i] + h] == x) 103 | { 104 | kk++; 105 | } 106 | }; 107 | 108 | jj += start; 109 | 110 | kk += jj; 111 | 112 | i = start; 113 | 114 | j = 0; 115 | 116 | k = 0; 117 | 118 | while (i < jj) 119 | { 120 | if (V[I[i] + h] < x) 121 | { 122 | i++; 123 | } 124 | else if (V[I[i] + h] == x) 125 | { 126 | tmp = I[i]; 127 | I[i] = I[jj + j]; 128 | I[jj + j] = tmp; 129 | j++; 130 | } 131 | else 132 | { 133 | tmp = I[i]; 134 | I[i] = I[kk + k]; 135 | I[kk + k] = tmp; 136 | k++; 137 | }; 138 | }; 139 | 140 | while (jj + j < kk) 141 | { 142 | if (V[I[jj + j] + h] == x) 143 | { 144 | j++; 145 | } 146 | else 147 | { 148 | tmp = I[jj + j]; 149 | I[jj + j] = I[kk + k]; 150 | I[kk + k] = tmp; 151 | k++; 152 | }; 153 | }; 154 | 155 | if (jj > start) 156 | { 157 | split(I, V, start, jj - start, h); 158 | } 159 | 160 | for (i = 0; i < kk - jj; i++) 161 | { 162 | V[I[jj + i]] = kk - 1; 163 | } 164 | 165 | if (jj == kk - 1) 166 | { 167 | I[jj] = -1; 168 | } 169 | 170 | if (start + len > kk) 171 | { 172 | split(I, V, kk, start + len - kk, h); 173 | } 174 | } 175 | 176 | /***************************************************************************/ 177 | 178 | static void qsufsort(off_t *I, off_t *V, unsigned char *old, off_t oldsize) 179 | { 180 | off_t buckets[256]; 181 | off_t i, h, len; 182 | 183 | for (i = 0; i < 256; i++) 184 | { 185 | buckets[i] = 0; 186 | } 187 | 188 | for (i = 0; i < oldsize; i++) 189 | { 190 | buckets[old[i]]++; 191 | } 192 | 193 | for (i = 1; i < 256; i++) 194 | { 195 | buckets[i] += buckets[i - 1]; 196 | } 197 | 198 | for (i = 255; i > 0; i--) 199 | { 200 | buckets[i] = buckets[i - 1]; 201 | } 202 | 203 | buckets[0] = 0; 204 | 205 | for (i = 0; i < oldsize; i++) 206 | { 207 | I[++buckets[old[i]]] = i; 208 | } 209 | 210 | I[0] = oldsize; 211 | 212 | for (i = 0; i < oldsize; i++) 213 | { 214 | V[i] = buckets[old[i]]; 215 | } 216 | 217 | V[oldsize] = 0; 218 | 219 | for (i = 1; i < 256; i++) 220 | if (buckets[i] == buckets[i - 1] + 1) 221 | { 222 | I[buckets[i]] = -1; 223 | } 224 | 225 | I[0] = -1; 226 | 227 | for (h = 1; I[0] != -(oldsize + 1); h += h) 228 | { 229 | len = 0; 230 | 231 | for (i = 0; i < oldsize + 1;) 232 | { 233 | if (I[i] < 0) 234 | { 235 | len -= I[i]; 236 | i -= I[i]; 237 | } 238 | else 239 | { 240 | if (len) 241 | { 242 | I[i - len] = -len; 243 | } 244 | 245 | len = V[I[i]] + 1 - i; 246 | split(I, V, i, len, h); 247 | i += len; 248 | len = 0; 249 | }; 250 | }; 251 | 252 | if (len) 253 | { 254 | I[i - len] = -len; 255 | } 256 | }; 257 | 258 | for (i = 0; i < oldsize + 1; i++) 259 | { 260 | I[V[i]] = i; 261 | } 262 | } 263 | 264 | /***************************************************************************/ 265 | 266 | static off_t matchlen(unsigned char *old, off_t oldsize, unsigned char *_new, off_t newsize) 267 | { 268 | off_t i; 269 | 270 | for (i = 0; (i < oldsize) && (i < newsize); i++) 271 | if (old[i] != _new[i]) 272 | { 273 | break; 274 | } 275 | 276 | return i; 277 | } 278 | 279 | /***************************************************************************/ 280 | 281 | static off_t search(off_t *I, unsigned char *old, off_t oldsize, 282 | unsigned char *_new, off_t newsize, off_t st, off_t en, off_t *pos) 283 | { 284 | off_t x, y; 285 | 286 | if (en - st < 2) 287 | { 288 | x = matchlen(old + I[st], oldsize - I[st], _new, newsize); 289 | y = matchlen(old + I[en], oldsize - I[en], _new, newsize); 290 | 291 | if (x > y) 292 | { 293 | *pos = I[st]; 294 | return x; 295 | } 296 | else 297 | { 298 | *pos = I[en]; 299 | return y; 300 | } 301 | }; 302 | 303 | x = st + (en - st) / 2; 304 | 305 | if (memcmp(old + I[x], _new, MIN(oldsize - I[x], newsize)) <= 0) 306 | { 307 | return search(I, old, oldsize, _new, newsize, x, en, pos); 308 | } 309 | else 310 | { 311 | return search(I, old, oldsize, _new, newsize, st, x, pos); 312 | }; 313 | } 314 | 315 | /***************************************************************************/ 316 | 317 | static void offtout(off_t x, unsigned char *buf) 318 | { 319 | off_t y; 320 | 321 | if (x < 0) 322 | { 323 | y = -x; 324 | } 325 | else 326 | { 327 | y = x; 328 | } 329 | 330 | buf[0] = y % 256; 331 | y -= buf[0]; 332 | y = y / 256; 333 | buf[1] = y % 256; 334 | y -= buf[1]; 335 | y = y / 256; 336 | buf[2] = y % 256; 337 | y -= buf[2]; 338 | y = y / 256; 339 | buf[3] = y % 256; 340 | y -= buf[3]; 341 | y = y / 256; 342 | buf[4] = y % 256; 343 | y -= buf[4]; 344 | y = y / 256; 345 | buf[5] = y % 256; 346 | y -= buf[5]; 347 | y = y / 256; 348 | buf[6] = y % 256; 349 | y -= buf[6]; 350 | y = y / 256; 351 | buf[7] = y % 256; 352 | 353 | if (x < 0) 354 | { 355 | buf[7] |= 0x80; 356 | } 357 | } 358 | 359 | /***************************************************************************/ 360 | 361 | char *bsdiff(const char *oldfile, const char *newfile, 362 | const char *patchfile) 363 | { 364 | unsigned char *old = NULL; 365 | unsigned char *_new = NULL; 366 | off_t oldsize, newsize; 367 | off_t *I = NULL; 368 | off_t *V = NULL; 369 | off_t scan, len; 370 | off_t pos = 0; 371 | off_t lastscan, lastpos, lastoffset; 372 | off_t oldscore, scsc; 373 | off_t s, Sf, lenf, Sb, lenb; 374 | off_t overlap, Ss, lens; 375 | off_t i; 376 | off_t dblen, eblen; 377 | unsigned char *db = NULL; 378 | unsigned char *eb = NULL; 379 | unsigned char buf[8]; 380 | unsigned char header[HEADER_SIZE]; 381 | FILE *pf, *fp; 382 | BZFILE *pfbz2; 383 | int bz2err; 384 | static char errstr[ERRSTR_MAX_LEN]; 385 | 386 | /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure 387 | that we never try to malloc(0) and get a NULL pointer */ 388 | if (((fp = fopen(oldfile, "rb")) == NULL) || 389 | (fseek(fp, 0, SEEK_END) != 0) || 390 | ((oldsize = ftell(fp)) == -1) || 391 | ((old = (unsigned char *)malloc(oldsize + 1)) == NULL) || 392 | (fseek(fp, 0, SEEK_SET) != 0) || 393 | ((off_t)fread(old, 1, oldsize, fp) != oldsize)) 394 | { 395 | snprintf(errstr, sizeof(errstr), "Cannot process file \"%s\".", oldfile); 396 | free(old); 397 | if (fp) 398 | fclose(fp); 399 | return errstr; 400 | } 401 | 402 | if (fclose(fp) != 0) 403 | { 404 | snprintf(errstr, sizeof(errstr), "Cannot close file \"%s\".", oldfile); 405 | free(old); 406 | return errstr; 407 | } 408 | 409 | if (((I = (off_t *)malloc((oldsize + 1) * sizeof(off_t))) == NULL) || 410 | ((V = (off_t *)malloc((oldsize + 1) * sizeof(off_t))) == NULL)) 411 | { 412 | snprintf(errstr, sizeof(errstr), "Cannot allocate memory."); 413 | free(I); 414 | free(V); 415 | free(old); 416 | return errstr; 417 | } 418 | 419 | qsufsort(I, V, old, oldsize); 420 | free(V); 421 | 422 | /* Allocate newsize+1 bytes instead of newsize bytes to ensure 423 | that we never try to malloc(0) and get a NULL pointer */ 424 | if (((fp = fopen(newfile, "rb")) == NULL) || 425 | (fseek(fp, 0, SEEK_END) != 0) || 426 | ((newsize = ftell(fp)) == -1) || 427 | ((_new = (unsigned char *)malloc(newsize + 1)) == NULL) || 428 | (fseek(fp, 0, SEEK_SET) != 0) || 429 | ((off_t)fread(_new, 1, newsize, fp) != newsize)) 430 | { 431 | snprintf(errstr, sizeof(errstr), "Cannot process file \"%s\".", newfile); 432 | if (fp) 433 | fclose(fp); 434 | goto GETOUT; 435 | } 436 | 437 | if (fclose(fp) != 0) 438 | { 439 | snprintf(errstr, sizeof(errstr), "Cannot process file \"%s\".", newfile); 440 | goto GETOUT; 441 | } 442 | 443 | if (((db = (unsigned char *)malloc(newsize + 1)) == NULL) || 444 | ((eb = (unsigned char *)malloc(newsize + 1)) == NULL)) 445 | { 446 | snprintf(errstr, sizeof(errstr), "Cannot allocate memory."); 447 | goto GETOUT_DB; 448 | } 449 | 450 | dblen = 0; 451 | eblen = 0; 452 | 453 | /* Create the patch file */ 454 | if ((pf = fopen(patchfile, "wb")) == NULL) 455 | { 456 | snprintf(errstr, sizeof(errstr), "Cannot open file \"%s\".", patchfile); 457 | goto GETOUT_DB; 458 | } 459 | 460 | /* Header is 461 | 0 8 "BSDIFF40" 462 | 8 8 length of bzip2ed ctrl block 463 | 16 8 length of bzip2ed diff block 464 | 24 8 length of new file */ 465 | /* File is 466 | 0 32 Header 467 | 32 ?? Bzip2ed ctrl block 468 | ?? ?? Bzip2ed diff block 469 | ?? ?? Bzip2ed extra block */ 470 | memcpy(header, "BSDIFF40", 8); 471 | offtout(0, header + 8); 472 | offtout(0, header + 16); 473 | offtout(newsize, header + 24); 474 | 475 | if (fwrite(header, HEADER_SIZE, 1, pf) != 1) 476 | { 477 | snprintf(errstr, sizeof(errstr), "Cannot write file \"%s\".", patchfile); 478 | goto GETOUT_PF; 479 | } 480 | 481 | /* Compute the differences, writing ctrl as we go */ 482 | if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) 483 | { 484 | snprintf(errstr, sizeof(errstr), "BZ2_bzWriteOpen, bz2err = %d", bz2err); 485 | goto GETOUT_PF; 486 | } 487 | 488 | scan = 0; 489 | len = 0; 490 | lastscan = 0; 491 | lastpos = 0; 492 | lastoffset = 0; 493 | 494 | while (scan < newsize) 495 | { 496 | oldscore = 0; 497 | 498 | /* If we come across a large block of data that only differs 499 | * by less than 8 bytes, this loop will take a long time to 500 | * go past that block of data. We need to track the number of 501 | * times we're stuck in the block and break out of it. */ 502 | int num_less_than_eight = 0; 503 | off_t prev_len, prev_oldscore, prev_pos; 504 | for (scsc = scan += len; scan < newsize; scan++) 505 | { 506 | prev_len = len; 507 | prev_oldscore = oldscore; 508 | prev_pos = pos; 509 | 510 | len = search(I, old, oldsize, _new + scan, newsize - scan, 511 | 0, oldsize, &pos); 512 | 513 | for (; scsc < scan + len; scsc++) 514 | if ((scsc + lastoffset < oldsize) && 515 | (old[scsc + lastoffset] == _new[scsc])) 516 | { 517 | oldscore++; 518 | } 519 | 520 | if (((len == oldscore) && (len != 0)) || 521 | (len > oldscore + 8)) 522 | { 523 | break; 524 | } 525 | 526 | if ((scan + lastoffset < oldsize) && 527 | (old[scan + lastoffset] == _new[scan])) 528 | { 529 | oldscore--; 530 | } 531 | 532 | const off_t fuzz = 8; 533 | if (prev_len-fuzz <= len && len <= prev_len && 534 | prev_oldscore-fuzz <= oldscore && 535 | oldscore <= prev_oldscore && 536 | prev_pos <= pos && pos <= prev_pos + fuzz && 537 | oldscore <= len && len <= oldscore + fuzz) 538 | { 539 | ++num_less_than_eight; 540 | } 541 | else 542 | { 543 | num_less_than_eight = 0; 544 | } 545 | 546 | if (num_less_than_eight > 100) break; 547 | }; 548 | 549 | if ((len != oldscore) || (scan == newsize)) 550 | { 551 | s = 0; 552 | Sf = 0; 553 | lenf = 0; 554 | 555 | for (i = 0; (lastscan + i < scan) && (lastpos + i < oldsize);) 556 | { 557 | if (old[lastpos + i] == _new[lastscan + i]) 558 | { 559 | s++; 560 | } 561 | 562 | i++; 563 | 564 | if (s * 2 - i > Sf * 2 - lenf) 565 | { 566 | Sf = s; 567 | lenf = i; 568 | }; 569 | }; 570 | 571 | lenb = 0; 572 | 573 | if (scan < newsize) 574 | { 575 | s = 0; 576 | Sb = 0; 577 | 578 | for (i = 1; (scan >= lastscan + i) && (pos >= i); i++) 579 | { 580 | if (old[pos - i] == _new[scan - i]) 581 | { 582 | s++; 583 | } 584 | 585 | if (s * 2 - i > Sb * 2 - lenb) 586 | { 587 | Sb = s; 588 | lenb = i; 589 | }; 590 | }; 591 | }; 592 | 593 | if (lastscan + lenf > scan - lenb) 594 | { 595 | overlap = (lastscan + lenf) - (scan - lenb); 596 | s = 0; 597 | Ss = 0; 598 | lens = 0; 599 | 600 | for (i = 0; i < overlap; i++) 601 | { 602 | if (_new[lastscan + lenf - overlap + i] == 603 | old[lastpos + lenf - overlap + i]) 604 | { 605 | s++; 606 | } 607 | 608 | if (_new[scan - lenb + i] == 609 | old[pos - lenb + i]) 610 | { 611 | s--; 612 | } 613 | 614 | if (s > Ss) 615 | { 616 | Ss = s; 617 | lens = i + 1; 618 | }; 619 | }; 620 | 621 | lenf += lens - overlap; 622 | lenb -= lens; 623 | }; 624 | 625 | for (i = 0; i < lenf; i++) 626 | { 627 | db[dblen + i] = _new[lastscan + i] - old[lastpos + i]; 628 | } 629 | 630 | for (i = 0; i < (scan - lenb) - (lastscan + lenf); i++) 631 | { 632 | eb[eblen + i] = _new[lastscan + lenf + i]; 633 | } 634 | 635 | dblen += lenf; 636 | eblen += (scan - lenb) - (lastscan + lenf); 637 | offtout(lenf, buf); 638 | BZ2_bzWrite(&bz2err, pfbz2, buf, 8); 639 | 640 | if (bz2err != BZ_OK) 641 | { 642 | snprintf(errstr, sizeof(errstr), "BZ2_bzWrite, bz2err = %d", bz2err); 643 | goto GETOUT_PFBZ; 644 | } 645 | 646 | offtout((scan - lenb) - (lastscan + lenf), buf); 647 | BZ2_bzWrite(&bz2err, pfbz2, buf, 8); 648 | 649 | if (bz2err != BZ_OK) 650 | { 651 | snprintf(errstr, sizeof(errstr), "BZ2_bzWrite, bz2err = %d", bz2err); 652 | goto GETOUT_PFBZ; 653 | } 654 | 655 | offtout((pos - lenb) - (lastpos + lenf), buf); 656 | BZ2_bzWrite(&bz2err, pfbz2, buf, 8); 657 | 658 | if (bz2err != BZ_OK) 659 | { 660 | snprintf(errstr, sizeof(errstr), "BZ2_bzWrite, bz2err = %d", bz2err); 661 | goto GETOUT_PFBZ; 662 | } 663 | 664 | lastscan = scan - lenb; 665 | lastpos = pos - lenb; 666 | lastoffset = pos - scan; 667 | }; 668 | }; 669 | 670 | BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); 671 | 672 | if (bz2err != BZ_OK) 673 | { 674 | snprintf(errstr, sizeof(errstr), "BZ2_bzWriteClose, bz2err = %d", bz2err); 675 | goto GETOUT_PF; 676 | } 677 | 678 | /* Compute size of compressed ctrl data */ 679 | if ((len = ftell(pf)) == -1) 680 | { 681 | snprintf(errstr, sizeof(errstr), "Cannot return current value of the position indicator in file \"%s\".", patchfile); 682 | goto GETOUT_PF; 683 | } 684 | 685 | offtout(len - HEADER_SIZE, header + 8); 686 | 687 | /* Write compressed diff data */ 688 | if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) 689 | { 690 | snprintf(errstr, sizeof(errstr), "BZ2_bzWriteOpen, bz2err = %d", bz2err); 691 | goto GETOUT_PF; 692 | } 693 | 694 | BZ2_bzWrite(&bz2err, pfbz2, db, dblen); 695 | 696 | if (bz2err != BZ_OK) 697 | { 698 | snprintf(errstr, sizeof(errstr), "BZ2_bzWrite, bz2err = %d", bz2err); 699 | goto GETOUT_PFBZ; 700 | } 701 | 702 | BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); 703 | 704 | if (bz2err != BZ_OK) 705 | { 706 | snprintf(errstr, sizeof(errstr), "BZ2_bzWriteClose, bz2err = %d", bz2err); 707 | goto GETOUT_PF; 708 | } 709 | 710 | /* Compute size of compressed diff data */ 711 | if ((newsize = ftell(pf)) == -1) 712 | { 713 | snprintf(errstr, sizeof(errstr), "Cannot return current value of the position indicator in file \"%s\".", patchfile); 714 | goto GETOUT_PF; 715 | } 716 | 717 | offtout(newsize - len, header + 16); 718 | 719 | /* Write compressed extra data */ 720 | if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) 721 | { 722 | snprintf(errstr, sizeof(errstr), "BZ2_bzWriteOpen, bz2err = %d", bz2err); 723 | goto GETOUT_PF; 724 | } 725 | 726 | BZ2_bzWrite(&bz2err, pfbz2, eb, eblen); 727 | 728 | if (bz2err != BZ_OK) 729 | { 730 | snprintf(errstr, sizeof(errstr), "BZ2_bzWrite, bz2err = %d", bz2err); 731 | GETOUT_PFBZ: 732 | BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); 733 | GETOUT_PF: 734 | fclose(pf); 735 | GETOUT_DB: 736 | free(db); 737 | free(eb); 738 | GETOUT: 739 | free(I); 740 | free(_new); 741 | free(old); 742 | return errstr; 743 | } 744 | 745 | BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); 746 | 747 | if (bz2err != BZ_OK) 748 | { 749 | snprintf(errstr, sizeof(errstr), "BZ2_bzWriteClose, bz2err = %d", bz2err); 750 | goto GETOUT_PF; 751 | } 752 | 753 | /* Seek to the beginning, write the header, and close the file */ 754 | if (fseek(pf, 0, SEEK_SET)) 755 | { 756 | snprintf(errstr, sizeof(errstr), "Cannot change the file position indicator in file \"%s\".", patchfile); 757 | goto GETOUT_PF; 758 | } 759 | 760 | if (fwrite(header, HEADER_SIZE, 1, pf) != 1) 761 | { 762 | snprintf(errstr, sizeof(errstr), "Cannot write file \"%s\".", patchfile); 763 | goto GETOUT_PF; 764 | } 765 | 766 | if (fclose(pf)) 767 | { 768 | snprintf(errstr, sizeof(errstr), "Cannot close file \"%s\".", patchfile); 769 | goto GETOUT_DB; 770 | } 771 | 772 | /* Free the memory we used */ 773 | free(db); 774 | free(eb); 775 | free(I); 776 | free(old); 777 | free(_new); 778 | return NULL; 779 | } 780 | --------------------------------------------------------------------------------