├── m4 └── .gitignore ├── test ├── data │ ├── 5.bspatch.diff │ ├── 6.bspatch.diff │ ├── 7.bspatch.diff │ ├── 8.bspatch.diff │ ├── 9.bspatch.diff │ ├── 10.bspatch.diff │ ├── 11.bspatch.diff │ ├── 12.bspatch.diff │ ├── 16.bspatch.diff │ ├── 10.bspatch.modified │ ├── 10.bspatch.original │ ├── 14.bspatch.modified │ ├── 14.bspatch.original │ ├── 8.bspatch.original │ ├── 16.bspatch.original │ ├── 15.bspatch.modified │ ├── 15.bspatch.original │ ├── 6.bspatch.original │ ├── 13.bspatch.modified │ ├── 12.bspatch.modified │ ├── 12.bspatch.original │ ├── 5.bspatch.original │ ├── 9.bspatch.modified │ ├── 9.bspatch.original │ └── 7.bspatch.original └── run.sh ├── src ├── bsdiff.sym ├── patch_main.c ├── diff_main.c ├── sufsort.c ├── bsheader.h ├── dump_main.c ├── patch.c └── diff.c ├── autogen.sh ├── data └── bsdiff.pc.in ├── include └── bsdiff.h ├── README.md ├── .gitignore ├── README ├── .travis.yml ├── COPYING ├── findstatic.pl ├── .clang-format ├── check-lib-version.sh ├── configure.ac ├── README.chromium └── Makefile.am /m4/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/data/5.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/5.bspatch.diff -------------------------------------------------------------------------------- /test/data/6.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/6.bspatch.diff -------------------------------------------------------------------------------- /test/data/7.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/7.bspatch.diff -------------------------------------------------------------------------------- /test/data/8.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/8.bspatch.diff -------------------------------------------------------------------------------- /test/data/9.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/9.bspatch.diff -------------------------------------------------------------------------------- /test/data/10.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/10.bspatch.diff -------------------------------------------------------------------------------- /test/data/11.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/11.bspatch.diff -------------------------------------------------------------------------------- /test/data/12.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/12.bspatch.diff -------------------------------------------------------------------------------- /test/data/16.bspatch.diff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/16.bspatch.diff -------------------------------------------------------------------------------- /test/data/10.bspatch.modified: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/10.bspatch.modified -------------------------------------------------------------------------------- /test/data/10.bspatch.original: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/10.bspatch.original -------------------------------------------------------------------------------- /test/data/14.bspatch.modified: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/14.bspatch.modified -------------------------------------------------------------------------------- /test/data/14.bspatch.original: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/14.bspatch.original -------------------------------------------------------------------------------- /test/data/8.bspatch.original: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearlinux/bsdiff/master/test/data/8.bspatch.original -------------------------------------------------------------------------------- /src/bsdiff.sym: -------------------------------------------------------------------------------- 1 | BSDIFF_1_0_0 { 2 | global: 3 | make_bsdiff_delta; 4 | apply_bsdiff_delta; 5 | local: 6 | *; 7 | }; 8 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | autoreconf --force --install --symlink --warnings=all 6 | 7 | args="\ 8 | --prefix=/usr" 9 | 10 | if test -z "${NOCONFIGURE}"; then 11 | ./configure $args "$@" 12 | make clean 13 | fi 14 | -------------------------------------------------------------------------------- /data/bsdiff.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: bsdiff 7 | Description: Library for bsdiff 8 | Version: @PACKAGE_VERSION@ 9 | Libs: -L${libdir} -lbsdiff 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /test/data/16.bspatch.original: -------------------------------------------------------------------------------- 1 | ................................................................................................................................................................................................................................................................ -------------------------------------------------------------------------------- /test/data/15.bspatch.modified: -------------------------------------------------------------------------------- 1 | adm:!!:: 2 | wheel:!!:: 3 | systemd-journal:!!:: 4 | systemd-resolve:!!:: 5 | systemd-network:!!:: 6 | systemd-bus-proxy:!!:: 7 | audio:!!:: 8 | systemd-timesync:!!:: 9 | nobody:!!:: 10 | lp:!!:: 11 | log:!!:: 12 | systemd-journal-gateway:!!:: 13 | input:!!:: 14 | -------------------------------------------------------------------------------- /test/data/15.bspatch.original: -------------------------------------------------------------------------------- 1 | wheel:!!:: 2 | input:!!:: 3 | systemd-journal-gateway:!!:: 4 | systemd-network:!!:: 5 | lp:!!:: 6 | systemd-bus-proxy:!!:: 7 | systemd-journal:!!:: 8 | systemd-timesync:!!:: 9 | log:!!:: 10 | systemd-resolve:!!:: 11 | adm:!!:: 12 | nobody:!!:: 13 | audio:!!:: 14 | -------------------------------------------------------------------------------- /test/data/6.bspatch.original: -------------------------------------------------------------------------------- 1 | prefix=/usr 2 | exec_prefix=/usr 3 | libdir=/usr/lib64 4 | includedir=/usr/include 5 | target=x11 6 | 7 | gtk_binary_version=2.10.0 8 | gtk_host=x86_64-pc-linux-gnu 9 | 10 | Name: GTK+ 11 | Description: GTK+ Unix print support 12 | Version: 2.24.10 13 | Requires: gtk+-${target}-2.0 atk cairo gdk-pixbuf-2.0 gio-2.0 pangoft2 14 | Cflags: -I${includedir}/gtk-unix-print-2.0 15 | -------------------------------------------------------------------------------- /include/bsdiff.h: -------------------------------------------------------------------------------- 1 | #ifndef __INCLUDE_GUARD_BSDIFF_H 2 | #define __INCLUDE_GUARD_BSDIFF_H 3 | 4 | /* encodings */ 5 | enum BSDIFF_ENCODINGS { 6 | BSDIFF_ENC_ANY, 7 | BSDIFF_ENC_NONE, 8 | BSDIFF_ENC_BZIP2, 9 | BSDIFF_ENC_GZIP, 10 | BSDIFF_ENC_XZ, 11 | BSDIFF_ENC_ZEROS, 12 | BSDIFF_ENC_LAST 13 | }; 14 | 15 | /* API definition */ 16 | int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filename, int enc); 17 | int apply_bsdiff_delta(char *oldfile, char *newfile, char *deltafile); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DISCONTINUATION OF PROJECT. 2 | 3 | This project will no longer be maintained by Intel. 4 | 5 | Intel will not provide or guarantee development of or support for this project, including but not limited to, maintenance, bug fixes, new releases or updates. Patches to this project are no longer accepted by Intel. If you have an ongoing need to use this project, are interested in independently developing it, or would like to maintain patches for the community, please create your own fork of the project. 6 | 7 | Contact: webadmin@linux.intel.com 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.o 4 | *.i 5 | *.s 6 | .libs/ 7 | *.lo 8 | *.pc 9 | config.* 10 | .deps/ 11 | aclocal.m4 12 | autom4te.cache/ 13 | Makefile 14 | Makefile.in 15 | bsdiff 16 | bsdump 17 | bspatch 18 | compile 19 | configure 20 | coverage/ 21 | depcomp 22 | install-sh 23 | libbsdiff.la 24 | libtool 25 | ltmain.sh 26 | m4/* 27 | !m4/.gitignore 28 | missing 29 | src/.dirstamp 30 | src/*.gcda 31 | src/*.gcno 32 | stamp-h1 33 | /bsdiff-*.tar.xz 34 | /bsdiff-*/ 35 | test/*.diff 36 | test/*.out 37 | test/*.log 38 | test/*.trs 39 | tap-driver.sh 40 | test-suite.log 41 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This project is a forked version of BSDiff, as taken from the Chromium project 2 | at SVN Revision 122769. See README.chromium for background on its origins. 3 | 4 | Significant changes after the fork include: 5 | 6 | - Changing the BSDiff file format to support file owner, group, and permission 7 | information. 8 | - Adding more robust error handling. 9 | - Adding a new program, bsdump, that outputs BSDiff header information. 10 | - Converting the core functionality into a library, libbsdiff, with public API 11 | functions make_bsdiff_delta() and apply_bsdiff_delta(). 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: c 4 | 5 | # Pre-install missing build dependencies: 6 | # - valgrind (from the repo) 7 | # - libcheck 0.9.10 is slightly too old, since 0.9.12 adds TAP support 8 | before_install: 9 | - sudo apt-get -qq update 10 | - sudo apt-get install -y valgrind 11 | 12 | install: 13 | - wget http://downloads.sourceforge.net/project/check/check/0.10.0/check-0.10.0.tar.gz 14 | - tar -xvf check-0.10.0.tar.gz 15 | - pushd check-0.10.0 && ./configure --prefix=/usr && make -j48 && sudo make install && popd 16 | 17 | # Ubuntu's default umask is 0002, but tests are written with the expectation of a 0022 default. 18 | script: 19 | - autoreconf --verbose --warnings=none --install --force && ./configure && make -j48 && sudo sh -c 'umask 0022 && make -j48 check' 20 | after_failure: cat test-suite.log 21 | -------------------------------------------------------------------------------- /test/data/13.bspatch.modified: -------------------------------------------------------------------------------- 1 | 2 | Network Error 3 | 4 | 5 | 6 |
7 |
8 |
9 | 10 | 17 | 22 | 27 | 33 |
11 | 12 | Network Error (tcp_error) 13 |
14 |
15 |
16 |
18 | 19 | A communication error occurred: "" 20 | 21 |
23 | 24 | The Web Server may be down, too busy, or experiencing other problems preventing it from responding to requests. You may wish to try again at a later time. 25 | 26 |
28 | 29 |
30 | For assistance, contact your network support team. 31 |
32 |
34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /test/data/12.bspatch.modified: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # If using normal root, avoid changing anything. 4 | if [ -z "$RPM_BUILD_ROOT" -o "$RPM_BUILD_ROOT" = "/" ]; then 5 | exit 0 6 | fi 7 | 8 | cd $RPM_BUILD_ROOT 9 | 10 | # Compress man pages 11 | COMPRESS="gzip -9 -n" 12 | COMPRESS_EXT= 13 | 14 | for d in ./usr/man/man* ./usr/man/*/man* ./usr/info \ 15 | ./usr/share/man/man* ./usr/share/man/*/man* ./usr/share/info \ 16 | ./usr/kerberos/man ./usr/X11R6/man/man* ./usr/lib/perl5/man/man* \ 17 | ./usr/share/doc/*/man/man* ./usr/lib/*/man/man* 18 | do 19 | [ -d $d ] || continue 20 | for f in `find $d -type f` 21 | do 22 | [ -f "$f" ] || continue 23 | [ "`basename $f`" = "dir" ] && continue 24 | 25 | case "$f" in 26 | *.Z) gunzip -f $f; b=`echo $f | sed -e 's/\.Z$//'`;; 27 | *.gz) gunzip -f $f; b=`echo $f | sed -e 's/\.gz$//'`;; 28 | *.bz2) bunzip2 -f $f; b=`echo $f | sed -e 's/\.bz2$//'`;; 29 | *) b=$f;; 30 | esac 31 | done 32 | 33 | for f in `find $d -type l` 34 | do 35 | l=`ls -l $f | sed -e 's/.* -> //' -e 's/\.gz$//' -e 's/\.bz2$//' -e 's/\.Z$//'` 36 | rm -f $f 37 | b=`echo $f | sed -e 's/\.gz$//' -e 's/\.bz2$//' -e 's/\.Z$//'` 38 | ln -sf $l$COMPRESS_EXT $b$COMPRESS_EXT 39 | done 40 | done 41 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2003-2005 Colin Percival 2 | All rights reserved 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted providing that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 21 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 22 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /test/data/12.bspatch.original: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # If using normal root, avoid changing anything. 4 | if [ -z "$RPM_BUILD_ROOT" -o "$RPM_BUILD_ROOT" = "/" ]; then 5 | exit 0 6 | fi 7 | 8 | cd $RPM_BUILD_ROOT 9 | 10 | # Compress man pages 11 | COMPRESS="gzip -9 -n" 12 | COMPRESS_EXT=.gz 13 | 14 | for d in ./usr/man/man* ./usr/man/*/man* ./usr/info \ 15 | ./usr/share/man/man* ./usr/share/man/*/man* ./usr/share/info \ 16 | ./usr/kerberos/man ./usr/X11R6/man/man* ./usr/lib/perl5/man/man* \ 17 | ./usr/share/doc/*/man/man* ./usr/lib/*/man/man* 18 | do 19 | [ -d $d ] || continue 20 | for f in `find $d -type f` 21 | do 22 | [ -f "$f" ] || continue 23 | [ "`basename $f`" = "dir" ] && continue 24 | 25 | case "$f" in 26 | *.Z) gunzip -f $f; b=`echo $f | sed -e 's/\.Z$//'`;; 27 | *.gz) gunzip -f $f; b=`echo $f | sed -e 's/\.gz$//'`;; 28 | *.bz2) bunzip2 -f $f; b=`echo $f | sed -e 's/\.bz2$//'`;; 29 | *) b=$f;; 30 | esac 31 | 32 | $COMPRESS $b /dev/null || { 33 | inode=`ls -i $b | awk '{ print $1 }'` 34 | others=`find $d -type f -inum $inode` 35 | if [ -n "$others" ]; then 36 | for afile in $others ; do 37 | [ "$afile" != "$b" ] && rm -f $afile 38 | done 39 | $COMPRESS -f $b 40 | for afile in $others ; do 41 | [ "$afile" != "$b" ] && ln $b$COMPRESS_EXT $afile$COMPRESS_EXT 42 | done 43 | else 44 | $COMPRESS -f $b 45 | fi 46 | } 47 | done 48 | 49 | for f in `find $d -type l` 50 | do 51 | l=`ls -l $f | sed -e 's/.* -> //' -e 's/\.gz$//' -e 's/\.bz2$//' -e 's/\.Z$//'` 52 | rm -f $f 53 | b=`echo $f | sed -e 's/\.gz$//' -e 's/\.bz2$//' -e 's/\.Z$//'` 54 | ln -sf $l$COMPRESS_EXT $b$COMPRESS_EXT 55 | done 56 | done 57 | -------------------------------------------------------------------------------- /findstatic.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # find a list of fns and variables in the code that could be static 3 | # usually called with something like this: 4 | # findstatic.pl `find . -name "*.o"` 5 | # Andrew Tridgell 6 | 7 | use strict; 8 | 9 | # use nm to find the symbols 10 | my($saved_delim) = $/; 11 | undef $/; 12 | my($syms) = `nm -o @ARGV`; 13 | $/ = $saved_delim; 14 | 15 | my(@lines) = split(/\n/s, $syms); 16 | 17 | my(%def); 18 | my(%undef); 19 | my(%stype); 20 | 21 | my(%typemap) = ( 22 | "T" => "function", 23 | "C" => "uninitialised variable", 24 | "D" => "initialised variable" 25 | ); 26 | 27 | 28 | # parse the symbols into defined and undefined 29 | for (my($i)=0; $i <= $#lines; $i++) { 30 | my($line) = $lines[$i]; 31 | if ($line =~ /(.*):[a-f0-9]* ([TCD]) (.*)/) { 32 | my($fname) = $1; 33 | my($symbol) = $3; 34 | push(@{$def{$fname}}, $symbol); 35 | $stype{$symbol} = $2; 36 | } 37 | if ($line =~ /(.*):\s* U (.*)/) { 38 | my($fname) = $1; 39 | my($symbol) = $2; 40 | push(@{$undef{$fname}}, $symbol); 41 | } 42 | } 43 | 44 | # look for defined symbols that are never referenced outside the place they 45 | # are defined 46 | foreach my $f (keys %def) { 47 | print "Checking $f\n"; 48 | my($found_one) = 0; 49 | foreach my $s (@{$def{$f}}) { 50 | my($found) = 0; 51 | foreach my $f2 (keys %undef) { 52 | if ($f2 ne $f) { 53 | foreach my $s2 (@{$undef{$f2}}) { 54 | if ($s2 eq $s) { 55 | $found = 1; 56 | $found_one = 1; 57 | } 58 | } 59 | } 60 | } 61 | if ($found == 0) { 62 | my($t) = $typemap{$stype{$s}}; 63 | if ($s eq 'main') { 64 | # special case: main program 65 | $found_one = 1; 66 | } else { 67 | print " '$s' is unique to $f, should be static? ($t)\n"; 68 | } 69 | } 70 | } 71 | if ($found_one == 0) { 72 | print " all symbols in '$f' are unused (main program?)\n"; 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /test/data/5.bspatch.original: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 | 15 | 16 | 17 | 19 | 21 | 26 | 27 | 28 | 31 | 32 | 33 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 51 | 52 | 53 | 57 | 58 | 59 | 62 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/patch_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bsdiff. 3 | * 4 | * Copyright © 2012-2016 Intel Corporation. 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 | * 27 | * Authors: 28 | * Tim Pepper 29 | * 30 | */ 31 | 32 | #define _GNU_SOURCE 33 | #include 34 | #include 35 | 36 | #include "bsdiff.h" 37 | 38 | int main(int argc, char **argv) 39 | { 40 | int ret; 41 | 42 | if (argc != 4) { 43 | printf("Usage: %s oldfile newfile deltafile\n\n", argv[0]); 44 | printf("Applies the binary diff DELTAFILE to OLDFILE."); 45 | printf(" The resulting file will be named NEWFILE.\n"); 46 | return -EXIT_FAILURE; 47 | } 48 | 49 | ret = apply_bsdiff_delta(argv[1], argv[2], argv[3]); 50 | 51 | if (ret != 0) { 52 | printf("Failed to apply delta (%d)\n", ret); 53 | return ret; 54 | } 55 | 56 | return EXIT_SUCCESS; 57 | } 58 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: true 6 | AlignConsecutiveAssignments: false 7 | AlignEscapedNewlinesLeft: false 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: false 18 | AlwaysBreakTemplateDeclarations: false 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | BreakBeforeBinaryOperators: None 22 | BreakBeforeBraces: Linux 23 | BreakBeforeTernaryOperators: true 24 | BreakConstructorInitializersBeforeComma: false 25 | ColumnLimit: 0 26 | CommentPragmas: '^ IWYU pragma:' 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 28 | ConstructorInitializerIndentWidth: 4 29 | ContinuationIndentWidth: 4 30 | Cpp11BracedListStyle: true 31 | DerivePointerAlignment: false 32 | DisableFormat: false 33 | ExperimentalAutoDetectBinPacking: false 34 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 35 | IndentCaseLabels: false 36 | IndentWidth: 8 37 | IndentWrappedFunctionNames: false 38 | KeepEmptyLinesAtTheStartOfBlocks: true 39 | MacroBlockBegin: '' 40 | MacroBlockEnd: '' 41 | MaxEmptyLinesToKeep: 1 42 | NamespaceIndentation: None 43 | ObjCBlockIndentWidth: 2 44 | ObjCSpaceAfterProperty: false 45 | ObjCSpaceBeforeProtocolList: true 46 | PenaltyBreakBeforeFirstCallParameter: 19 47 | PenaltyBreakComment: 300 48 | PenaltyBreakFirstLessLess: 120 49 | PenaltyBreakString: 1000 50 | PenaltyExcessCharacter: 1000000 51 | PenaltyReturnTypeOnItsOwnLine: 60 52 | PointerAlignment: Right 53 | SpaceAfterCStyleCast: false 54 | SpaceBeforeAssignmentOperators: true 55 | SpaceBeforeParens: ControlStatements 56 | SpaceInEmptyParentheses: false 57 | SpacesBeforeTrailingComments: 1 58 | SpacesInAngles: false 59 | SpacesInContainerLiterals: true 60 | SpacesInCStyleCastParentheses: false 61 | SpacesInParentheses: false 62 | SpacesInSquareBrackets: false 63 | Standard: Cpp11 64 | TabWidth: 8 65 | UseTab: Always 66 | ... 67 | 68 | -------------------------------------------------------------------------------- /check-lib-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TMPDIR=$(mktemp -d) 4 | PKG="bsdiff" 5 | LIB="libbsdiff" 6 | 7 | OLDVERSION=$(git tag -l 'v*' | sort -t . -k 1.2,1n -k 2,2n -k 3,3n | tail -1) 8 | NEWVERSION="HEAD" 9 | 10 | OLDCOMMIT=$(git rev-list -n 1 --abbrev-commit ${OLDVERSION}) 11 | NEWCOMMIT=$(git rev-list -n 1 --abbrev-commit ${NEWVERSION}) 12 | 13 | do_build() { 14 | local commit=$1 15 | echo "Building ${PKG} at commit ${commit}..." 16 | git archive \ 17 | -o "${TMPDIR}/${PKG}-${commit}.tar.gz" \ 18 | --prefix="${PKG}-${commit}/" ${commit} 19 | ( 20 | pushd "${TMPDIR}" 21 | tar xf ${PKG}-${commit}.tar.gz 22 | pushd ${PKG}-${commit} 23 | ./autogen.sh 24 | make 25 | popd 26 | popd 27 | ) &> /dev/null 28 | } 29 | 30 | create_xml_desc() { 31 | local commit=$1 32 | cat > ${TMPDIR}/${PKG}-${commit}.xml << EOF 33 | 34 | ${commit} 35 | 36 | 37 | ${TMPDIR}/${PKG}-${commit}/include/bsdiff.h 38 | 39 | 40 | ${TMPDIR}/${PKG}-${commit}/.libs 41 | 42 | EOF 43 | } 44 | 45 | bump_current() { 46 | local current=$(grep '^LIBBSDIFF_CURRENT' Makefile.am | cut -d'=' -f2) 47 | current=$(expr $current + 1) 48 | sed -i "s/^\(LIBBSDIFF_CURRENT=\).*$/\1$current/" Makefile.am 49 | # Note: the REVISION and AGE fields are not modified at all right now, but 50 | # when logic is implemented in the future according to the libtool algorithm, 51 | # they must be reset to zero, as below. 52 | sed -i "s/^\(LIBBSDIFF_REVISION=\).*$/\10/" Makefile.am 53 | sed -i "s/^\(LIBBSDIFF_AGE=\).*$/\10/" Makefile.am 54 | git add Makefile.am 55 | git commit -s -m "Bump $LIB major version to $current" 56 | } 57 | 58 | do_build ${OLDCOMMIT} 59 | do_build ${NEWCOMMIT} 60 | 61 | create_xml_desc ${OLDCOMMIT} 62 | create_xml_desc ${NEWCOMMIT} 63 | 64 | abi-compliance-checker \ 65 | -lib ${LIB} \ 66 | -report-path ${TMPDIR}/reports/${OLDCOMMIT}_to_${NEWCOMMIT}/report.xml \ 67 | -log1-path ${TMPDIR}/logs/${OLDCOMMIT}.log \ 68 | -log2-path ${TMPDIR}/logs/${NEWCOMMIT}.log \ 69 | -old ${TMPDIR}/${PKG}-${OLDCOMMIT}.xml \ 70 | -new ${TMPDIR}/${PKG}-${NEWCOMMIT}.xml \ 71 | -xml 72 | 73 | ret=$? 74 | 75 | if [ $ret -eq 0 ]; then 76 | # TODO: should bump AGE and CURRENT for backward-compatible library changes, 77 | # and REVISION for any library code changes. 78 | echo "No compatibility errors found!" 79 | elif [ $ret -eq 1 ]; then 80 | echo "Incompatibilities found; bumping lib version." 81 | bump_current 82 | elif [ $ret -gt 1 ]; then 83 | echo "Problem running the ABI check tool: error code ${ret}." 84 | fi 85 | 86 | exit $ret 87 | 88 | # vi: ts=8 sw=2 sts=2 et tw=80 89 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.66]) 2 | AC_INIT([bsdiff],[1.0.4],[patrick.mccarty@intel.com]) 3 | AC_CONFIG_MACRO_DIR([m4]) 4 | AC_PROG_CC 5 | AC_LANG(C) 6 | AC_CONFIG_HEADERS([config.h]) 7 | AC_PREFIX_DEFAULT(/usr/local) 8 | AC_CHECK_LIB([pthread], [pthread_create]) 9 | 10 | AM_INIT_AUTOMAKE([-Wall -Wno-portability no-dist-gzip dist-xz foreign subdir-objects]) 11 | AM_SILENT_RULES([yes]) 12 | 13 | LT_INIT 14 | 15 | PKG_CHECK_MODULES([zlib], [zlib]) 16 | 17 | AC_ARG_ENABLE([bzip2], 18 | [AS_HELP_STRING([--disable-bzip2],[Do not use bzip2 compression (uses bzip2 by default)])]) 19 | 20 | AC_ARG_ENABLE([lzma], 21 | [AS_HELP_STRING([--disable-lzma],[Do not use lzma compression (uses lzma by default)])]) 22 | 23 | AS_IF([test "$enable_bzip2" != "no"], [ 24 | AC_CHECK_LIB([bz2], [BZ2_bzBuffToBuffCompress], [], [AC_MSG_ERROR([the libbz2 library is missing])]) 25 | AC_DEFINE(BSDIFF_WITH_BZIP2,1,[Use bzip2 compression]) 26 | ]) 27 | 28 | AS_IF([test "$enable_lzma" != "no"], [ 29 | PKG_CHECK_MODULES([lzma], [liblzma]) 30 | AC_DEFINE(BSDIFF_WITH_LZMA,1,[Use lzma compression]) 31 | ]) 32 | AM_CONDITIONAL([ENABLE_LZMA], [test "$enable_lzma" != "no"]) 33 | 34 | AC_ARG_ENABLE( 35 | [tests], 36 | [AS_HELP_STRING([--disable-tests], [Do not enable functional tests (enabled by default)])] 37 | ) 38 | 39 | have_coverage=no 40 | AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage], [enable test coverage])) 41 | if test "x$enable_coverage" = "xyes" ; then 42 | AC_CHECK_PROG(lcov_found, [lcov], [yes], [no]) 43 | if test "x$lcov_found" = xno ; then 44 | AC_MSG_ERROR([*** lcov support requested but the program was not found]) 45 | else 46 | lcov_version_major="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 1`" 47 | lcov_version_minor="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 2`" 48 | if test "$lcov_version_major" -eq 1 -a "$lcov_version_minor" -lt 10; then 49 | AC_MSG_ERROR([*** lcov version is too old. 1.10 required]) 50 | else 51 | have_coverage=yes 52 | AC_DEFINE([COVERAGE], [1], [Coverage enabled]) 53 | fi 54 | fi 55 | fi 56 | AM_CONDITIONAL([COVERAGE], [test "$have_coverage" = "yes"]) 57 | 58 | AS_IF([test "$enable_tests" != "no"], [ 59 | PKG_CHECK_MODULES([CHECK], [check >= 0.9.12]) 60 | AC_PATH_PROG([have_valgrind], [valgrind]) 61 | AS_IF([test -z "${have_valgrind}"], [ 62 | AC_MSG_ERROR([Must have valgrind installed to run functional tests]) 63 | ]) 64 | ]) 65 | AM_CONDITIONAL([ENABLE_TESTS], [test "$enable_tests" != "no"]) 66 | 67 | AC_CONFIG_FILES([Makefile data/bsdiff.pc]) 68 | AC_REQUIRE_AUX_FILE([tap-driver.sh]) 69 | AC_OUTPUT 70 | 71 | AC_MSG_RESULT([ 72 | bsdiff $VERSION 73 | ======== 74 | 75 | prefix: ${prefix} 76 | libdir: ${libdir} 77 | exec_prefix: ${exec_prefix} 78 | bindir: ${bindir} 79 | 80 | compiler: ${CC} 81 | cflags: ${CFLAGS} 82 | ldflags: ${LDFLAGS} 83 | ]) 84 | -------------------------------------------------------------------------------- /src/diff_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bsdiff. 3 | * 4 | * Copyright © 2012-2016 Intel Corporation. 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 | * 27 | * Authors: 28 | * Tim Pepper 29 | * 30 | */ 31 | 32 | #define _GNU_SOURCE 33 | #include 34 | #include 35 | #include 36 | 37 | #include "bsdiff.h" 38 | 39 | /* parse encoding as string and return value as enum 40 | */ 41 | 42 | static int get_encoding(char *encoding) 43 | { 44 | if (strcmp(encoding, "raw") == 0) { 45 | return BSDIFF_ENC_NONE; 46 | } else if (strcmp(encoding, "bzip2") == 0) { 47 | return BSDIFF_ENC_BZIP2; 48 | } else if (strcmp(encoding, "gzip") == 0) { 49 | return BSDIFF_ENC_GZIP; 50 | } else if (strcmp(encoding, "xz") == 0) { 51 | return BSDIFF_ENC_XZ; 52 | } else if (strcmp(encoding, "zeros") == 0) { 53 | return BSDIFF_ENC_ZEROS; 54 | } else if (strcmp(encoding, "any") == 0) { 55 | return BSDIFF_ENC_ANY; 56 | } else { 57 | return -1; 58 | } 59 | } 60 | 61 | int main(int argc, char **argv) 62 | { 63 | int ret, enc = BSDIFF_ENC_ANY; 64 | 65 | if (argc < 4) { 66 | printf("Usage: %s oldfile newfile deltafile [encoding]\n\n", argv[0]); 67 | printf("Creates a binary diff DELTAFILE from OLDFILE to NEWFILE."); 68 | printf(" If ENCODING is specified, accepted values are 'raw', 'bzip2',"); 69 | printf(" 'gzip', 'xz', 'zeros', or 'any'. The 'raw' value will force"); 70 | printf(" no compression.\n"); 71 | return -EXIT_FAILURE; 72 | } 73 | 74 | if (argc > 4) { 75 | if ((enc = get_encoding(argv[4])) < 0) { 76 | printf("Unknown encoding algorithm\n"); 77 | return -EXIT_FAILURE; 78 | } 79 | } 80 | 81 | ret = make_bsdiff_delta(argv[1], argv[2], argv[3], enc); 82 | 83 | if (ret != 0) { 84 | printf("Failed to create delta (%d)\n", ret); 85 | return ret; 86 | } 87 | 88 | return EXIT_SUCCESS; 89 | } 90 | -------------------------------------------------------------------------------- /README.chromium: -------------------------------------------------------------------------------- 1 | The README below was taken from a snapshot of the Chromium project at: 2 | svn - Revision 122769: /trunk/src/chrome/installer/mac/third_party/bsdiff 3 | 4 | For information specific to the fork as implemented in this project, see the 5 | main README file. 6 | 7 | --- 8 | 9 | Name: BSDiff 10 | URL: http://www.daemonology.net/bsdiff/ 11 | Source URL: http://www.daemonology.net/bsdiff/bsdiff-4.3.tar.gz 12 | Version: 4.3 13 | License: BSD 14 | License File: LICENSE 15 | 16 | Description: 17 | Binary diff/patch utility. There are other copies of BSDiff in the Chromium 18 | repository, but they're all different. The other copies are based on Mozilla's 19 | fork of BSDiff, which serves a different set of needs. Relative to upstream 20 | BSDiff, Mozilla's version removes all compression, adds a CRC-32 check of the 21 | original file, replaces the custom off_t encoding with signed 32-bit 22 | big-endian integers, and contains a total reorganization of the code. The 23 | version in this directory contains no Mozilla code and builds directly on the 24 | upstream version. It retains and enhances the compression, uses SHA1 to check 25 | both the original file and the patched file, uses a different off_t encoding 26 | more compatible with the original, and involves minimal changes to the 27 | original code. 28 | 29 | Theoretically, a hash of the original file should be enough to guarantee data 30 | integrity, but in the event of algorithmic or programming bugs or other 31 | unexpected conditions, a hash of the patched file provides a better guarantee. 32 | This implementation now checks the integrity of both the original and the 33 | patched files. SHA1, rather than CRC-32, is used to minimize the likelihood 34 | that an original file that has been intentionally tampered with will produce 35 | an altered patched file without being detected. 36 | 37 | Local Modifications: 38 | - Added LICENSE file by copying the license block from bsdiff.c and 39 | bspatch.c. 40 | - The following modifications are relative to the original unpatched version, 41 | checked in to the Chromium repository at r49280. 42 | - Created goobsdiff.gyp for GYP build system integration. 43 | - Renamed bsdiff.c to goobsdiff.c and bspatch.c to goobspatch.c. 44 | - Added sha1_adapter.cc, sha1_adapter.h, and empty.cc to facilitate hashing. 45 | - Added #include to goobspatch.c so that it compiles. (Oops!) 46 | - Changed the magic number in the header from BSDIFF40 to BSDIFF4G. 47 | - Expanded the header to include SHA1 hashes of the original and new files, 48 | and added hash checks to the patcher. 49 | - Expanded the header to include the lengths of the control, diff, and extra 50 | blocks in the patch file, and added patch file length validation to the 51 | patcher. 52 | - Replaced custom off_t encoding with signed 64-bit little-endian integers. 53 | - The control, diff, and extra blocks can be compressed with bzip2, gzip, or 54 | xz/lzma2, or left uncompressed, independently of one another, depending on 55 | which is smallest. This often results in a net reduction in patch size of 56 | about 3%-5%. 57 | - Error messages in the patcher are slightly more descriptive. 58 | - The patcher treats a few more unexpected read cases as errors than it did 59 | previously. This will theoretically cause it to exit with an error instead 60 | of sitting in a busy loop or crashing when certain malformatted patch files 61 | are provided. 62 | -------------------------------------------------------------------------------- /test/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # $srcdir variable is set by automake environment 4 | cd $srcdir/test 5 | 6 | # number is incremented after running every test 7 | testnum=0 8 | 9 | sudo rm -f *.diff *.out 10 | 11 | VALGRIND="valgrind -q" 12 | if [ -n "$SKIP_VALGRIND" ]; then 13 | VALGRIND="" 14 | fi 15 | 16 | libdir="$abs_builddir/.libs" 17 | ldpath="LD_LIBRARY_PATH=$libdir" 18 | BSDIFF="sudo $ldpath $VALGRIND $libdir/bsdiff" 19 | BSPATCH="sudo $ldpath $VALGRIND $libdir/bspatch" 20 | 21 | # If exit status is 0, the test succeeded. Else it failed. 22 | check_success() { 23 | res=$? 24 | [ -n "$1" ] && msg="$1" || msg="" 25 | testnum=$(expr $testnum + 1) 26 | if [ $res -ne 0 ]; then 27 | echo "not ok $testnum - $msg" 28 | else 29 | echo "ok $testnum" 30 | fi 31 | } 32 | 33 | # If exit status is 255, the test succeeded. Else it failed. 34 | check_failure() { 35 | res=$? 36 | [ -n "$1" ] && msg="$1" || msg="" 37 | testnum=$(expr $testnum + 1) 38 | if [ $res -ne 255 ]; then 39 | echo "not ok $testnum - $msg" 40 | else 41 | echo "ok $testnum" 42 | fi 43 | } 44 | 45 | echo "Running test #5 ..." 46 | $BSPATCH data/5.bspatch.original 5.out data/5.bspatch.diff 47 | check_success 48 | 49 | echo "Running test #6 ..." 50 | $BSPATCH data/6.bspatch.original 6.out data/6.bspatch.diff 51 | check_success 52 | 53 | echo "Running test #7 ..." 54 | $BSPATCH data/7.bspatch.original 7.out data/7.bspatch.diff 55 | check_success 56 | 57 | echo "Running test #8 ..." 58 | $BSPATCH data/8.bspatch.original 8.out data/8.bspatch.diff 59 | check_success 60 | 61 | echo "Running test #9 ..." 62 | $BSPATCH data/9.bspatch.original 9.out data/9.bspatch.diff 63 | diff data/9.bspatch.modified 9.out 64 | check_success "output does not match expected!!" 65 | 66 | echo "Running test #10 ..." 67 | $BSPATCH data/10.bspatch.original 10.out data/10.bspatch.diff 68 | diff data/10.bspatch.modified 10.out 69 | check_success "output does not match expected!!" 70 | 71 | #same as 9 but with zeros encoding 72 | echo "Running test #11 ..." 73 | $BSPATCH data/9.bspatch.original 11.out data/11.bspatch.diff 74 | diff data/9.bspatch.modified 11.out 75 | check_success "output does not match expected!!" 76 | 77 | echo "Running test #12 ..." 78 | $BSPATCH data/12.bspatch.original 12.out data/12.bspatch.diff 79 | diff data/12.bspatch.modified 12.out 80 | check_success "output does not match expected!!" 81 | 82 | echo "Running test #13 ..." 83 | $BSDIFF data/13.bspatch.original data/13.bspatch.modified 13.diff any 84 | $BSPATCH data/13.bspatch.original 13.out 13.diff 85 | diff data/13.bspatch.modified 13.out 86 | check_success "output does not match expected!!" 87 | 88 | # Next a very loooong running test, but one which successfully condenses the 2MB 89 | # original file pair into a 26kB bsdiff. The bsdiff computation alone (ie: 90 | # non-valgrind'd) takes ~20minutes on a decent build machine. Running it 91 | # through valgrind takes many many hours to run to completion. Therefore leave 92 | # filepair #14 as one for only occasional use in long-running regression 93 | # testing. The other file pairs can be check quickly enough that they can be 94 | # used in a regression test run at every check-in of code changes to the bsdiff 95 | # implementation. 96 | # 97 | #echo "Running test #14 ..." 98 | #$BSDIFF data/14.bspatch.original data/14.bspatch.modified 14.diff any 99 | #$BSPATCH data/14.bspatch.original 14.out 14.diff 100 | #diff data/14.bspatch.modified 14.out 101 | #check_success "output does not match expected!!" 102 | 103 | echo "Running test #15 ..." 104 | $BSDIFF data/15.bspatch.original data/15.bspatch.modified 15.diff any 105 | # expected output: "Failed to create delta (-1)" 106 | check_failure "patch creation has memory management issue!" 107 | 108 | echo "Running test #16 ..." 109 | # any valgrind errors may indicate a buffer overflow 110 | $BSPATCH data/16.bspatch.original 16.out data/16.bspatch.diff 111 | check_success 112 | 113 | # For TAP support, output the plan 114 | echo "1..${testnum}" 115 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | 3 | AM_CFLAGS = \ 4 | -fno-common \ 5 | -fstack-protector \ 6 | -std=gnu99 \ 7 | -Wall \ 8 | -Wformat \ 9 | -Wformat-security \ 10 | -Wimplicit-function-declaration \ 11 | -Wno-conversion \ 12 | -Wstrict-prototypes \ 13 | -Wundef \ 14 | -Wunreachable-code \ 15 | -Wunused-variable 16 | 17 | AM_CPPFLAGS = \ 18 | $(AM_CFLAGS) \ 19 | -D_FORTIFY_SOURCE=2 \ 20 | -I$(top_srcdir)/include 21 | 22 | EXTRA_DIST = \ 23 | COPYING \ 24 | findstatic.pl \ 25 | README.chromium \ 26 | src/bsdiff.sym 27 | 28 | AUTOMAKE_OPTIONS = color-tests parallel-tests 29 | 30 | if COVERAGE 31 | AM_CFLAGS += --coverage 32 | 33 | coverage: coverage-clean 34 | mkdir -p coverage 35 | lcov --compat-libtool --directory . --capture --output-file coverage/report 36 | genhtml -o coverage/ coverage/report 37 | 38 | coverage-clean: 39 | rm -rf coverage 40 | endif 41 | 42 | bin_PROGRAMS = \ 43 | bsdiff \ 44 | bsdump \ 45 | bspatch 46 | 47 | bsdump_SOURCES = \ 48 | src/dump_main.c 49 | 50 | bsdump_LDADD = \ 51 | libbsdiff.la 52 | 53 | bsdiff_SOURCES = \ 54 | src/diff_main.c 55 | 56 | bsdiff_LDADD = \ 57 | libbsdiff.la 58 | 59 | bspatch_SOURCES = \ 60 | src/patch_main.c 61 | 62 | bspatch_LDADD = \ 63 | libbsdiff.la 64 | 65 | lib_LTLIBRARIES = \ 66 | libbsdiff.la 67 | 68 | libbsdiff_la_SOURCES = \ 69 | src/diff.c \ 70 | src/patch.c \ 71 | src/sufsort.c 72 | 73 | libbsdiff_la_LIBADD = \ 74 | $(zlib_LIBS) 75 | 76 | if ENABLE_LZMA 77 | libbsdiff_la_LIBADD += \ 78 | $(lzma_LIBS) 79 | endif 80 | 81 | pkgconfiglibdir=$(libdir)/pkgconfig 82 | pkgconfiglib_DATA = \ 83 | data/bsdiff.pc 84 | 85 | include_HEADERS = \ 86 | include/bsdiff.h 87 | 88 | noinst_HEADERS = \ 89 | src/bsheader.h 90 | 91 | # Library version changes according to the libtool convention: 92 | # http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info 93 | LIBBSDIFF_CURRENT=1 94 | LIBBSDIFF_REVISION=0 95 | LIBBSDIFF_AGE=0 96 | libbsdiff_la_LDFLAGS = \ 97 | -version-info $(LIBBSDIFF_CURRENT):$(LIBBSDIFF_REVISION):$(LIBBSDIFF_AGE) \ 98 | -Wl,--version-script=$(top_srcdir)/src/bsdiff.sym 99 | 100 | mostlyclean-local: 101 | -rm -f *.i 102 | -rm -f *.s 103 | 104 | distclean-local: 105 | -rm -f config.guess~ 106 | -rm -f config.h.in~ 107 | -rm -f config.sub~ 108 | -rm -f configure~ 109 | 110 | install-exec-hook: 111 | perl $(top_srcdir)/findstatic.pl $(top_builddir)/src/*.o | grep -v Checking || : 112 | 113 | TEST_EXTENSIONS = .sh 114 | 115 | EXTRA_DIST += \ 116 | test/data/5.bspatch.diff \ 117 | test/data/5.bspatch.original \ 118 | test/data/6.bspatch.diff \ 119 | test/data/6.bspatch.original \ 120 | test/data/7.bspatch.diff \ 121 | test/data/7.bspatch.original \ 122 | test/data/8.bspatch.diff \ 123 | test/data/8.bspatch.original \ 124 | test/data/9.bspatch.diff \ 125 | test/data/9.bspatch.modified \ 126 | test/data/9.bspatch.original \ 127 | test/data/10.bspatch.diff \ 128 | test/data/10.bspatch.modified \ 129 | test/data/10.bspatch.original \ 130 | test/data/11.bspatch.diff \ 131 | test/data/12.bspatch.diff \ 132 | test/data/12.bspatch.modified \ 133 | test/data/12.bspatch.original \ 134 | test/data/13.bspatch.modified \ 135 | test/data/13.bspatch.original \ 136 | test/data/14.bspatch.modified \ 137 | test/data/14.bspatch.original \ 138 | test/data/15.bspatch.modified \ 139 | test/data/15.bspatch.original \ 140 | test/data/16.bspatch.diff \ 141 | test/data/16.bspatch.original 142 | 143 | if ENABLE_TESTS 144 | AM_TESTS_ENVIRONMENT = \ 145 | abs_builddir=$(abs_builddir); export abs_builddir; 146 | 147 | tap_driver = env AM_TAP_AWK='$(AWK)' $(SHELL) \ 148 | $(top_srcdir)/tap-driver.sh 149 | 150 | LOG_DRIVER = $(tap_driver) 151 | SH_LOG_DRIVER = $(tap_driver) 152 | TESTS = $(dist_check_SCRIPTS) 153 | dist_check_SCRIPTS = \ 154 | test/run.sh 155 | endif 156 | 157 | compliant: 158 | @git diff --quiet --exit-code include src; ret=$$?; \ 159 | if [ $$ret -eq 1 ]; then \ 160 | echo "Error: can only check code style when include/ and src/ are clean."; \ 161 | echo "Stash or commit your changes and try again."; \ 162 | exit $$ret; \ 163 | elif [ $$ret -gt 1 ]; then \ 164 | exit $$ret; \ 165 | fi; \ 166 | clang-format -i -style=file include/*.h src/*.c; ret=$$?; \ 167 | if [ $$ret -ne 0 ]; then \ 168 | exit $$ret; \ 169 | fi; \ 170 | git diff --quiet --exit-code include src; ret=$$?; \ 171 | if [ $$ret -eq 1 ]; then \ 172 | echo "Code style issues found. Run 'git diff' to view issues."; \ 173 | elif [ $$ret -eq 0 ]; then \ 174 | echo "No code style issues found."; \ 175 | fi; \ 176 | exit $$ret 177 | 178 | release: 179 | @git rev-parse v$(PACKAGE_VERSION) &> /dev/null; \ 180 | if [ "$$?" -eq 0 ]; then \ 181 | echo "Error: Release $(PACKAGE_VERSION) already exists."; \ 182 | echo "Bump version in configure.ac before releasing."; \ 183 | exit 1; \ 184 | fi 185 | @git tag -a -m "$(PACKAGE_NAME) release $(PACKAGE_VERSION)" v$(PACKAGE_VERSION) 186 | @printf "\nNew release $(PACKAGE_VERSION) tagged!\n\n" 187 | -------------------------------------------------------------------------------- /src/sufsort.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2003-2005 Colin Percival 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 "bsheader.h" 28 | 29 | /* NOTES: 30 | * I and V are chunks of memory (arrays) with length = (oldfile size +1) * sizeof(int64_t). 31 | * Additionally, we pass in arraylen now. The parent function qsufsort receives it, so it 32 | * should be available here as well for error checking. 33 | * start: is actually the point in the array sent in during the suffix sort, which sorts by 34 | * small blocks/chunks. 35 | * len: refers to the length of the current chunk being processed - NOT the array length(s). 36 | * h: will never be more than 8, and increases by *2 during suffix sort (h += h) */ 37 | static void split(int64_t *I, int64_t *V, int64_t arraylen, int64_t start, int64_t len, 38 | int64_t h) 39 | { 40 | int64_t i, j, k, x, tmp, jj, kk; 41 | 42 | if (len < 16) { 43 | for (k = start; k < start + len; k += j) { 44 | j = 1; 45 | x = V[I[k] + h]; 46 | for (i = 1; k + i < start + len; i++) { 47 | if (V[I[k + i] + h] < x) { 48 | x = V[I[k + i] + h]; 49 | j = 0; 50 | } 51 | if (V[I[k + i] + h] == x) { 52 | tmp = I[k + j]; 53 | I[k + j] = I[k + i]; 54 | I[k + i] = tmp; 55 | j++; 56 | } 57 | } 58 | for (i = 0; i < j; i++) { 59 | V[I[k + i]] = k + j - 1; 60 | } 61 | if (j == 1) { 62 | I[k] = -1; 63 | } 64 | } 65 | return; 66 | } 67 | 68 | x = V[I[start + len / 2] + h]; 69 | jj = 0; 70 | kk = 0; 71 | for (i = start; i < start + len; i++) { 72 | if (V[I[i] + h] < x) { 73 | jj++; 74 | } 75 | if (V[I[i] + h] == x) { 76 | kk++; 77 | } 78 | } 79 | jj += start; 80 | kk += jj; 81 | 82 | i = start; 83 | j = 0; 84 | k = 0; 85 | while (i < jj) { 86 | if (V[I[i] + h] < x) { 87 | i++; 88 | } else if (V[I[i] + h] == x) { 89 | tmp = I[i]; 90 | I[i] = I[jj + j]; 91 | I[jj + j] = tmp; 92 | j++; 93 | } else { 94 | tmp = I[i]; 95 | I[i] = I[kk + k]; 96 | I[kk + k] = tmp; 97 | k++; 98 | } 99 | } 100 | 101 | while (jj + j < kk) { 102 | if (V[I[jj + j] + h] == x) { 103 | j++; 104 | } else { 105 | tmp = I[jj + j]; 106 | I[jj + j] = I[kk + k]; 107 | I[kk + k] = tmp; 108 | k++; 109 | } 110 | } 111 | 112 | if (jj > start) { 113 | split(I, V, arraylen, start, jj - start, h); 114 | } 115 | 116 | for (i = 0; i < kk - jj; i++) { 117 | V[I[jj + i]] = kk - 1; 118 | } 119 | if (jj == kk - 1) { 120 | I[jj] = -1; 121 | } 122 | 123 | if (start + len > kk) { 124 | split(I, V, arraylen, kk, start + len - kk, h); 125 | } 126 | } 127 | 128 | /* The old_data (previous file data) is passed into this suffix sort and sorted 129 | * accordingly using the I and V arrays, which are both of length old_size +1. */ 130 | int qsufsort(int64_t *I, int64_t *V, u_char *old, int64_t old_size) 131 | { 132 | int64_t buckets[QSUF_BUCKET_SIZE]; 133 | int64_t i, h, len; 134 | 135 | for (i = 0; i < QSUF_BUCKET_SIZE; i++) { 136 | buckets[i] = 0; 137 | } 138 | for (i = 0; i < old_size; i++) { 139 | buckets[old[i]]++; 140 | } 141 | for (i = 1; i < QSUF_BUCKET_SIZE; i++) { 142 | buckets[i] += buckets[i - 1]; 143 | } 144 | for (i = QSUF_BUCKET_SIZE - 1; i > 0; i--) { 145 | buckets[i] = buckets[i - 1]; 146 | } 147 | buckets[0] = 0; 148 | 149 | for (i = 0; i < old_size; i++) { 150 | if (buckets[old[i]] > old_size + 1) { 151 | return -1; 152 | } 153 | I[++buckets[old[i]]] = i; 154 | } 155 | 156 | for (i = 0; i < old_size; i++) { 157 | V[i] = buckets[old[i]]; 158 | } 159 | V[old_size] = 0; 160 | for (i = 1; i < QSUF_BUCKET_SIZE; i++) { 161 | if (buckets[i] == buckets[i - 1] + 1) { 162 | I[buckets[i]] = -1; 163 | } 164 | } 165 | I[0] = -1; 166 | 167 | for (h = 1; I[0] != -(old_size + 1); h += h) { 168 | len = 0; 169 | for (i = 0; i < old_size + 1;) { 170 | if (I[i] < 0) { 171 | len -= I[i]; 172 | i -= I[i]; 173 | } else { 174 | if (len) { 175 | I[i - len] = -len; 176 | } 177 | len = V[I[i]] + 1 - i; 178 | split(I, V, old_size, i, len, h); 179 | i += len; 180 | len = 0; 181 | } 182 | } 183 | if (len) { 184 | I[i - len] = -len; 185 | } 186 | } 187 | 188 | for (i = 0; i < old_size + 1; i++) { 189 | I[V[i]] = i; 190 | } 191 | 192 | return 0; 193 | } 194 | -------------------------------------------------------------------------------- /src/bsheader.h: -------------------------------------------------------------------------------- 1 | #ifndef __INCLUDE_GUARD_BSHEADER_H 2 | #define __INCLUDE_GUARD_BSHEADER_H 3 | 4 | #include 5 | #include // for u_char 6 | 7 | #include "bsdiff.h" 8 | 9 | /* process no more than 512MB sized files */ 10 | #define BSDIFF_MAX_FILESZ 512 * 1024 * 1024 11 | 12 | /* used for suffix sort in bsdiff */ 13 | #define QSUF_BUCKET_SIZE 256 14 | 15 | enum BSDIFF_BLOCKS { 16 | BSDIFF_BLOCK_CONTROL, 17 | BSDIFF_BLOCK_DIFF, 18 | BSDIFF_BLOCK_EXTRA, 19 | }; 20 | 21 | /* encodings bitfield */ 22 | typedef union { 23 | struct { 24 | uint16_t cblk_none : 1; /* control block enc's*/ 25 | uint16_t cblk_bzip2 : 1; 26 | uint16_t cblk_gzip : 1; 27 | uint16_t cblk_xz : 1; 28 | uint16_t : 1; /* unused */ 29 | uint16_t dblk_none : 1; /* diff block enc's */ 30 | uint16_t dblk_bzip2 : 1; 31 | uint16_t dblk_gzip : 1; 32 | uint16_t dblk_xz : 1; 33 | uint16_t dblk_zeros : 1; 34 | uint16_t eblk_none : 1; /* extra block enc's */ 35 | uint16_t eblk_bzip2 : 1; 36 | uint16_t eblk_gzip : 1; 37 | uint16_t eblk_xz : 1; 38 | uint16_t eblk_zeros : 1; 39 | uint16_t : 1; /* unused */ 40 | } ordered; 41 | uint16_t raw; 42 | } enc_flags_t; 43 | 44 | /*************************************************************** 45 | * v2.x headers 46 | ***************************************************************/ 47 | 48 | /* cleaned up typing and optimized fields */ 49 | #define BSDIFF_HDR_MAGIC_V20 "BSDIFF4U" 50 | /* directory header: uses only file_{mode|owner|group} */ 51 | #define BSDIFF_HDR_DIR_V20 "DIR_V20U" 52 | /* do a full download instead of reading a bsdiff, only magic field used */ 53 | #define BSDIFF_HDR_FULLDL "FULLV20U" 54 | struct header_v20 { 55 | unsigned char magic[8]; 56 | uint8_t offset_to_first_block; /* ~= header length */ 57 | uint32_t control_length; 58 | uint64_t diff_length; 59 | uint64_t extra_length; 60 | uint64_t old_file_length; 61 | uint64_t new_file_length; 62 | uint64_t mtime; /* unused */ 63 | uint32_t file_mode; 64 | uint32_t file_owner; 65 | uint32_t file_group; 66 | 67 | /* Supported encodings: uncompressed, bzip2, gzip, xz, zeros */ 68 | enc_flags_t encoding; 69 | } __attribute__((__packed__)); 70 | 71 | /* optimized for small files */ 72 | #define BSDIFF_HDR_MAGIC_V21 "BSDIFF4V" 73 | struct header_v21 { 74 | unsigned char magic[8]; 75 | uint8_t offset_to_first_block; /* ~= header length */ 76 | uint8_t control_length; 77 | uint16_t diff_length; 78 | uint16_t extra_length; 79 | uint16_t old_file_length; 80 | uint16_t new_file_length; 81 | uint32_t file_mode; 82 | uint32_t file_owner; 83 | uint32_t file_group; 84 | 85 | /* Supported encodings: uncompressed, bzip2, gzip, xz, zeros */ 86 | enc_flags_t encoding; 87 | } __attribute__((__packed__)); 88 | 89 | static inline void cblock_set_enc(enc_flags_t *enc, int method) 90 | { 91 | if (method == BSDIFF_ENC_NONE) { 92 | enc->ordered.cblk_none = 1; 93 | } else if (method == BSDIFF_ENC_BZIP2) { 94 | enc->ordered.cblk_bzip2 = 1; 95 | } else if (method == BSDIFF_ENC_GZIP) { 96 | enc->ordered.cblk_gzip = 1; 97 | } else if (method == BSDIFF_ENC_XZ) { 98 | enc->ordered.cblk_xz = 1; 99 | } 100 | } 101 | 102 | static inline void dblock_set_enc(enc_flags_t *enc, int method) 103 | { 104 | if (method == BSDIFF_ENC_NONE) { 105 | enc->ordered.dblk_none = 1; 106 | } else if (method == BSDIFF_ENC_BZIP2) { 107 | enc->ordered.dblk_bzip2 = 1; 108 | } else if (method == BSDIFF_ENC_GZIP) { 109 | enc->ordered.dblk_gzip = 1; 110 | } else if (method == BSDIFF_ENC_XZ) { 111 | enc->ordered.dblk_xz = 1; 112 | } else if (method == BSDIFF_ENC_ZEROS) { 113 | enc->ordered.dblk_zeros = 1; 114 | } 115 | } 116 | 117 | static inline void eblock_set_enc(enc_flags_t *enc, int method) 118 | { 119 | if (method == BSDIFF_ENC_NONE) { 120 | enc->ordered.eblk_none = 1; 121 | } else if (method == BSDIFF_ENC_BZIP2) { 122 | enc->ordered.eblk_bzip2 = 1; 123 | } else if (method == BSDIFF_ENC_GZIP) { 124 | enc->ordered.eblk_gzip = 1; 125 | } else if (method == BSDIFF_ENC_XZ) { 126 | enc->ordered.eblk_xz = 1; 127 | } else if (method == BSDIFF_ENC_ZEROS) { 128 | enc->ordered.eblk_zeros = 1; 129 | } 130 | } 131 | 132 | static inline int cblock_get_enc(enc_flags_t enc) 133 | { 134 | if (enc.ordered.cblk_none) { 135 | return BSDIFF_ENC_NONE; 136 | } else if (enc.ordered.cblk_bzip2) { 137 | return BSDIFF_ENC_BZIP2; 138 | } else if (enc.ordered.cblk_gzip) { 139 | return BSDIFF_ENC_GZIP; 140 | } else if (enc.ordered.cblk_xz) { 141 | return BSDIFF_ENC_XZ; 142 | } else { 143 | return BSDIFF_ENC_ANY; 144 | } 145 | } 146 | 147 | static inline int dblock_get_enc(enc_flags_t enc) 148 | { 149 | if (enc.ordered.dblk_none) { 150 | return BSDIFF_ENC_NONE; 151 | } else if (enc.ordered.dblk_bzip2) { 152 | return BSDIFF_ENC_BZIP2; 153 | } else if (enc.ordered.dblk_gzip) { 154 | return BSDIFF_ENC_GZIP; 155 | } else if (enc.ordered.dblk_xz) { 156 | return BSDIFF_ENC_XZ; 157 | } else if (enc.ordered.dblk_zeros) { 158 | return BSDIFF_ENC_ZEROS; 159 | } else { 160 | return BSDIFF_ENC_ANY; 161 | } 162 | } 163 | 164 | static inline int eblock_get_enc(enc_flags_t enc) 165 | { 166 | if (enc.ordered.eblk_none) { 167 | return BSDIFF_ENC_NONE; 168 | } else if (enc.ordered.eblk_bzip2) { 169 | return BSDIFF_ENC_BZIP2; 170 | } else if (enc.ordered.eblk_gzip) { 171 | return BSDIFF_ENC_GZIP; 172 | } else if (enc.ordered.eblk_xz) { 173 | return BSDIFF_ENC_XZ; 174 | } else if (enc.ordered.eblk_zeros) { 175 | return BSDIFF_ENC_ZEROS; 176 | } else { 177 | return BSDIFF_ENC_ANY; 178 | } 179 | } 180 | 181 | int qsufsort(int64_t *, int64_t *, u_char *, int64_t); 182 | 183 | #endif 184 | -------------------------------------------------------------------------------- /src/dump_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bsdiff. 3 | * 4 | * Copyright © 2012-2016 Intel Corporation. 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 | * 27 | * Authors: 28 | * Tim Pepper 29 | * 30 | */ 31 | 32 | #define _GNU_SOURCE 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "bsdiff.h" 41 | #include "bsheader.h" 42 | 43 | static char *algos[BSDIFF_ENC_LAST] = {"invalid", "none", "bzip2", "gzip", "xz", "zeros"}; 44 | 45 | static void banner(char **argv) 46 | { 47 | printf("Usage: %s FILE\n\n", argv[0]); 48 | printf("Dumps header information from the bsdiff FILE.\n"); 49 | exit(-EXIT_FAILURE); 50 | } 51 | 52 | static int read_v20_header(struct header_v20 *h, FILE *f, char *filename) 53 | { 54 | rewind(f); 55 | if (fread(h, sizeof(struct header_v20), 1, f) < 1) { 56 | printf("v2 magic, but short header (%s)\n", filename); 57 | return -1; 58 | } 59 | return 0; 60 | } 61 | 62 | static void print_v20_header(struct header_v20 *h, FILE *f) 63 | { 64 | int ret; 65 | uint64_t *zeros; 66 | 67 | printf("First block offset:\t%3u\n", h->offset_to_first_block); 68 | printf("Cblock length: %10u\n", h->control_length); 69 | printf(" encoding: %10s\n", algos[cblock_get_enc(h->encoding)]); 70 | printf("Dblock length: %10llu\n", (long long unsigned int)(h->diff_length)); 71 | printf(" encoding: %10s\n", algos[dblock_get_enc(h->encoding)]); 72 | if (dblock_get_enc(h->encoding) == BSDIFF_ENC_ZEROS) { 73 | zeros = malloc(sizeof(uint64_t)); 74 | assert(zeros); 75 | 76 | ret = fseek(f, h->offset_to_first_block + h->control_length, SEEK_SET); 77 | if (ret != 0) { 78 | printf(" numzeros: error seeking\n"); 79 | exit(-1); 80 | } 81 | 82 | if (fread(zeros, sizeof(uint64_t), 1, f) != 1) { 83 | printf(" numzeros: error reading\n"); 84 | } else { 85 | printf(" numzeros: %10llu\n", (long long unsigned int)(*zeros)); 86 | } 87 | free(zeros); 88 | } 89 | printf("Eblock length: %10llu\n", (long long unsigned int)(h->extra_length)); 90 | printf(" encoding: %10s\n", algos[eblock_get_enc(h->encoding)]); 91 | if (eblock_get_enc(h->encoding) == BSDIFF_ENC_ZEROS) { 92 | zeros = malloc(sizeof(uint64_t)); 93 | assert(zeros); 94 | 95 | ret = fseek(f, h->offset_to_first_block + h->control_length + h->diff_length, SEEK_SET); 96 | if (ret != 0) { 97 | printf(" numzeros: error seeking\n"); 98 | exit(-1); 99 | } 100 | 101 | if (fread(zeros, sizeof(uint64_t), 1, f) != 1) { 102 | printf(" numzeros: error reading\n"); 103 | } else { 104 | printf(" numzeros: %10llu\n", (long long unsigned int)(*zeros)); 105 | } 106 | free(zeros); 107 | } 108 | printf("Old file length: %10llu\n", (long long unsigned int)(h->old_file_length)); 109 | printf("New file length: %10llu\n", (long long unsigned int)(h->new_file_length)); 110 | if (h->mtime == 0) { 111 | printf("Mtime:\t(not set, as expected)\n"); 112 | } else { 113 | printf("Mtime:\t%s (probably means there is a bug)\n", ctime((const time_t *)&h->mtime)); 114 | } 115 | printf("Mode:\t%4o\n", h->file_mode); 116 | printf("Uid:\t%d\n", h->file_owner); 117 | printf("Gid:\t%d\n", h->file_group); 118 | } 119 | 120 | static int read_v21_header(struct header_v21 *h, FILE *f, char *filename) 121 | { 122 | rewind(f); 123 | if (fread(h, sizeof(struct header_v21), 1, f) < 1) { 124 | printf("v3 magic, but short header (%s)\n", filename); 125 | return -1; 126 | } 127 | return 0; 128 | } 129 | 130 | static void print_v21_header(struct header_v21 *h, FILE *f) 131 | { 132 | int ret; 133 | uint64_t *zeros; 134 | 135 | printf("First block offset:\t%3u\n", h->offset_to_first_block); 136 | printf("Cblock length: %10u\n", h->control_length); 137 | printf(" encoding: %10s\n", algos[cblock_get_enc(h->encoding)]); 138 | printf("Dblock length: %10u\n", h->diff_length); 139 | printf(" encoding: %10s\n", algos[dblock_get_enc(h->encoding)]); 140 | if (dblock_get_enc(h->encoding) == BSDIFF_ENC_ZEROS) { 141 | zeros = malloc(sizeof(uint64_t)); 142 | assert(zeros); 143 | 144 | ret = fseek(f, h->offset_to_first_block + h->control_length, SEEK_SET); 145 | if (ret != 0) { 146 | printf(" numzeros: error seeking\n"); 147 | exit(-1); 148 | } 149 | 150 | if (fread(zeros, sizeof(uint64_t), 1, f) != 1) { 151 | printf(" numzeros: error reading\n"); 152 | } else { 153 | printf(" numzeros: %10llu\n", (long long unsigned int)(*zeros)); 154 | } 155 | free(zeros); 156 | } 157 | printf("Eblock length: %10u\n", h->extra_length); 158 | printf(" encoding: %10s\n", algos[eblock_get_enc(h->encoding)]); 159 | if (eblock_get_enc(h->encoding) == BSDIFF_ENC_ZEROS) { 160 | zeros = malloc(sizeof(uint64_t)); 161 | assert(zeros); 162 | 163 | ret = fseek(f, h->offset_to_first_block + h->control_length + h->diff_length, SEEK_SET); 164 | if (ret != 0) { 165 | printf(" numzeros: error seeking\n"); 166 | exit(-1); 167 | } 168 | 169 | if (fread(zeros, sizeof(uint64_t), 1, f) != 1) { 170 | printf(" numzeros: error reading\n"); 171 | } else { 172 | printf(" numzeros: %10llu\n", (long long unsigned int)(*zeros)); 173 | } 174 | free(zeros); 175 | } 176 | printf("Old file length: %10u\n", h->old_file_length); 177 | printf("New file length: %10u\n", h->new_file_length); 178 | printf("Mode:\t%4o\n", h->file_mode); 179 | printf("Uid:\t%d\n", h->file_owner); 180 | printf("Gid:\t%d\n", h->file_group); 181 | } 182 | 183 | int main(int argc, char **argv) 184 | { 185 | FILE *infile; 186 | unsigned char magic[8]; 187 | int ret = 0; 188 | 189 | if (argc < 2) { 190 | banner(argv); 191 | } 192 | 193 | infile = fopen(argv[1], "r"); 194 | 195 | if (infile == NULL) { 196 | printf("Error opening file %s\n", argv[1]); 197 | banner(argv); 198 | } 199 | 200 | if (fread(&magic, 8, 1, infile) < 1) { 201 | printf("Magic: unknown (short)\n"); 202 | ret = -1; 203 | goto out; 204 | } 205 | 206 | if (memcmp(&magic, BSDIFF_HDR_MAGIC_V20, 8) == 0) { 207 | /* bsdiff v20: */ 208 | struct header_v20 h; 209 | memset(&h, 0, sizeof(struct header_v20)); 210 | 211 | ret = read_v20_header(&h, infile, argv[1]); 212 | if (ret != 0) { 213 | goto out; 214 | } 215 | 216 | printf("Magic: %s (v2.0)\n", BSDIFF_HDR_MAGIC_V20); 217 | print_v20_header(&h, infile); 218 | 219 | } else if (memcmp(&magic, BSDIFF_HDR_MAGIC_V21, 8) == 0) { 220 | /* bsdiff v21: */ 221 | struct header_v21 h; 222 | memset(&h, 0, sizeof(struct header_v21)); 223 | 224 | ret = read_v21_header(&h, infile, argv[1]); 225 | if (ret != 0) { 226 | goto out; 227 | } 228 | 229 | printf("Magic: %s (v2.1)\n", BSDIFF_HDR_MAGIC_V21); 230 | print_v21_header(&h, infile); 231 | 232 | } else if (memcmp(&magic, BSDIFF_HDR_DIR_V20, 8) == 0) { 233 | /* directory: anything interesting to print? */ 234 | struct header_v20 h; 235 | memset(&h, 0, sizeof(struct header_v20)); 236 | 237 | ret = read_v20_header(&h, infile, argv[1]); 238 | if (ret != 0) { 239 | goto out; 240 | } 241 | 242 | printf("Magic:\t%s\n", BSDIFF_HDR_DIR_V20); 243 | printf("Mode:\t%4o\n", h.file_mode); 244 | printf("Uid:\t%d\n", h.file_owner); 245 | printf("Gid:\t%d\n", h.file_group); 246 | 247 | } else if (memcmp(&magic, BSDIFF_HDR_FULLDL, 8) == 0) { 248 | /* full download req'd: possibly nothing more than the 8bytes 249 | magic, or maybe interesting info still */ 250 | uint8_t len; 251 | printf("Magic: %s\n", BSDIFF_HDR_FULLDL); 252 | if (fread(&len, 1, 1, infile) == 1) { 253 | if (len == sizeof(struct header_v20)) { 254 | struct header_v20 h; 255 | ret = read_v20_header(&h, infile, argv[1]); 256 | if (ret != 0) { 257 | goto out; 258 | } 259 | print_v20_header(&h, infile); 260 | } else if (len == sizeof(struct header_v21)) { 261 | struct header_v21 h; 262 | ret = read_v21_header(&h, infile, argv[1]); 263 | if (ret != 0) { 264 | goto out; 265 | } 266 | print_v21_header(&h, infile); 267 | } 268 | } 269 | } else { // unknown 270 | printf("Magic: unknown\n"); 271 | ret = -1; 272 | } 273 | out: 274 | fclose(infile); 275 | return ret; 276 | } 277 | -------------------------------------------------------------------------------- /test/data/9.bspatch.modified: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | The systemd Project 7 | http://www.freedesktop.org/wiki/Software/systemd 8 | 9 | 10 | Allow applications to inhibit system shutdown and suspend 11 | Authentication is required to allow an application to inhibit system shutdown or suspend. 12 | 13 | auth_admin_keep 14 | yes 15 | yes 16 | 17 | 18 | 19 | 20 | Allow applications to delay system shutdown and suspend 21 | Authentication is required to allow an application to delay system shutdown or suspend. 22 | 23 | yes 24 | yes 25 | yes 26 | 27 | 28 | 29 | 30 | Allow non-logged-in users to run programs 31 | Authentication is required to allow a non-logged-in user to run programs. 32 | 33 | auth_admin_keep 34 | auth_admin_keep 35 | auth_admin_keep 36 | 37 | 38 | 39 | 40 | Allow attaching devices to seats 41 | Authentication is required for attaching a device to a seat. 42 | 43 | auth_admin_keep 44 | auth_admin_keep 45 | auth_admin_keep 46 | 47 | 48 | 49 | 50 | Flush device to seat attachments 51 | Authentication is required for resetting how devices are attached to seats. 52 | 53 | auth_admin_keep 54 | auth_admin_keep 55 | auth_admin_keep 56 | 57 | 58 | 59 | 60 | Power off the system 61 | Authentication is required for powering off the system. 62 | 63 | auth_admin_keep 64 | auth_admin_keep 65 | yes 66 | 67 | 68 | 69 | 70 | Power off the system while other users are logged in 71 | Authentication is required for powering off the system while other users are logged in. 72 | 73 | auth_admin_keep 74 | auth_admin_keep 75 | auth_admin_keep 76 | 77 | 78 | 79 | 80 | Power off the system while an application asked to inhibit it 81 | Authentication is required for powering off the system while an application asked to inhibit it. 82 | 83 | auth_admin_keep 84 | auth_admin_keep 85 | auth_admin_keep 86 | 87 | 88 | 89 | 90 | Reboot the system 91 | Authentication is required for rebooting the system. 92 | 93 | auth_admin_keep 94 | auth_admin_keep 95 | yes 96 | 97 | 98 | 99 | 100 | Reboot the system while other users are logged in 101 | Authentication is required for rebooting the system while other users are logged in. 102 | 103 | auth_admin_keep 104 | auth_admin_keep 105 | auth_admin_keep 106 | 107 | 108 | 109 | 110 | Reboot the system while an application asked to inhibit it 111 | Authentication is required for rebooting the system while an application asked to inhibit it. 112 | 113 | auth_admin_keep 114 | auth_admin_keep 115 | auth_admin_keep 116 | 117 | 118 | 119 | 120 | Suspend the system 121 | Authentication is required for suspending the system. 122 | 123 | auth_admin_keep 124 | auth_admin_keep 125 | yes 126 | 127 | 128 | 129 | 130 | Suspend the system while other users are logged in 131 | Authentication is required for suspending the system while other users are logged in. 132 | 133 | auth_admin_keep 134 | auth_admin_keep 135 | yes 136 | 137 | 138 | 139 | 140 | Suspend the system while an application asked to inhibit it 141 | Authentication is required for suspending the system while an application asked to inhibit it. 142 | 143 | auth_admin_keep 144 | auth_admin_keep 145 | auth_admin_keep 146 | 147 | 148 | 149 | 150 | Hibernate the system 151 | Authentication is required for hibernating the system. 152 | 153 | auth_admin_keep 154 | auth_admin_keep 155 | yes 156 | 157 | 158 | 159 | 160 | Hibernate the system while other users are logged in 161 | Authentication is required for hibernating the system while other users are logged in. 162 | 163 | auth_admin_keep 164 | auth_admin_keep 165 | auth_admin_keep 166 | 167 | 168 | 169 | 170 | Hibernate the system while an application asked to inhibit it 171 | Authentication is required for hibernating the system while an application asked to inhibit it. 172 | 173 | auth_admin_keep 174 | auth_admin_keep 175 | auth_admin_keep 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /test/data/9.bspatch.original: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | The systemd Project 7 | http://www.freedesktop.org/wiki/Software/systemd 8 | 9 | 10 | Allow applications to inhibit system shutdown and suspend 11 | Authentication is required to allow an application to inhibit system shutdown or suspend. 12 | 13 | auth_admin_keep 14 | yes 15 | yes 16 | 17 | 18 | 19 | 20 | Allow applications to delay system shutdown and suspend 21 | Authentication is required to allow an application to delay system shutdown or suspend. 22 | 23 | yes 24 | yes 25 | yes 26 | 27 | 28 | 29 | 30 | Allow non-logged-in users to run programs 31 | Authentication is required to allow a non-logged-in user to run programs. 32 | 33 | auth_admin_keep 34 | auth_admin_keep 35 | auth_admin_keep 36 | 37 | 38 | 39 | 40 | Allow attaching devices to seats 41 | Authentication is required for attaching a device to a seat. 42 | 43 | auth_admin_keep 44 | auth_admin_keep 45 | auth_admin_keep 46 | 47 | 48 | 49 | 50 | Flush device to seat attachments 51 | Authentication is required for resetting how devices are attached to seats. 52 | 53 | auth_admin_keep 54 | auth_admin_keep 55 | auth_admin_keep 56 | 57 | 58 | 59 | 60 | Power off the system 61 | Authentication is required for powering off the system. 62 | 63 | auth_admin_keep 64 | auth_admin_keep 65 | yes 66 | 67 | 68 | 69 | 70 | Power off the system while other users are logged in 71 | Authentication is required for powering off the system while other users are logged in. 72 | 73 | auth_admin_keep 74 | auth_admin_keep 75 | auth_admin_keep 76 | 77 | 78 | 79 | 80 | Power off the system while an application asked to inhibit it 81 | Authentication is required for powering off the system while an application asked to inhibit it. 82 | 83 | auth_admin_keep 84 | auth_admin_keep 85 | auth_admin_keep 86 | 87 | 88 | 89 | 90 | Reboot the system 91 | Authentication is required for rebooting the system. 92 | 93 | auth_admin_keep 94 | auth_admin_keep 95 | yes 96 | 97 | 98 | 99 | 100 | Reboot the system while other users are logged in 101 | Authentication is required for rebooting the system while other users are logged in. 102 | 103 | auth_admin_keep 104 | auth_admin_keep 105 | auth_admin_keep 106 | 107 | 108 | 109 | 110 | Reboot the system while an application asked to inhibit it 111 | Authentication is required for rebooting the system while an application asked to inhibit it. 112 | 113 | auth_admin_keep 114 | auth_admin_keep 115 | auth_admin_keep 116 | 117 | 118 | 119 | 120 | Suspend the system 121 | Authentication is required for suspending the system. 122 | 123 | auth_admin_keep 124 | auth_admin_keep 125 | yes 126 | 127 | 128 | 129 | 130 | Suspend the system while other users are logged in 131 | Authentication is required for suspending the system while other users are logged in. 132 | 133 | auth_admin_keep 134 | auth_admin_keep 135 | auth_admin_keep 136 | 137 | 138 | 139 | 140 | Suspend the system while an application asked to inhibit it 141 | Authentication is required for suspending the system while an application asked to inhibit it. 142 | 143 | auth_admin_keep 144 | auth_admin_keep 145 | auth_admin_keep 146 | 147 | 148 | 149 | 150 | Hibernate the system 151 | Authentication is required for hibernating the system. 152 | 153 | auth_admin_keep 154 | auth_admin_keep 155 | yes 156 | 157 | 158 | 159 | 160 | Hibernate the system while other users are logged in 161 | Authentication is required for hibernating the system while other users are logged in. 162 | 163 | auth_admin_keep 164 | auth_admin_keep 165 | auth_admin_keep 166 | 167 | 168 | 169 | 170 | Hibernate the system while an application asked to inhibit it 171 | Authentication is required for hibernating the system while an application asked to inhibit it. 172 | 173 | auth_admin_keep 174 | auth_admin_keep 175 | auth_admin_keep 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /test/data/7.bspatch.original: -------------------------------------------------------------------------------- 1 | # Tcl autoload index file, version 2.0 2 | # This file is generated by the "auto_mkindex" command 3 | # and sourced to set up indexing information for one or 4 | # more commands. Typically each line is a command that 5 | # sets an element in the auto_index array, where the 6 | # element name is the name of a command and the value is 7 | # a script that loads the command. 8 | 9 | set auto_index(_delete_indexlock) [list source [file join $dir index.tcl]] 10 | set auto_index(_close_updateindex) [list source [file join $dir index.tcl]] 11 | set auto_index(update_indexinfo) [list source [file join $dir index.tcl]] 12 | set auto_index(write_update_indexinfo) [list source [file join $dir index.tcl]] 13 | set auto_index(update_index) [list source [file join $dir index.tcl]] 14 | set auto_index(write_update_index) [list source [file join $dir index.tcl]] 15 | set auto_index(checkout_index) [list source [file join $dir index.tcl]] 16 | set auto_index(write_checkout_index) [list source [file join $dir index.tcl]] 17 | set auto_index(unstage_helper) [list source [file join $dir index.tcl]] 18 | set auto_index(do_unstage_selection) [list source [file join $dir index.tcl]] 19 | set auto_index(add_helper) [list source [file join $dir index.tcl]] 20 | set auto_index(do_add_selection) [list source [file join $dir index.tcl]] 21 | set auto_index(do_add_all) [list source [file join $dir index.tcl]] 22 | set auto_index(revert_helper) [list source [file join $dir index.tcl]] 23 | set auto_index(do_revert_selection) [list source [file join $dir index.tcl]] 24 | set auto_index(do_select_commit_type) [list source [file join $dir index.tcl]] 25 | set auto_index(merge_resolve_one) [list source [file join $dir mergetool.tcl]] 26 | set auto_index(merge_stage_workdir) [list source [file join $dir mergetool.tcl]] 27 | set auto_index(do_merge_stage_workdir) [list source [file join $dir mergetool.tcl]] 28 | set auto_index(merge_add_resolution) [list source [file join $dir mergetool.tcl]] 29 | set auto_index(merge_force_stage) [list source [file join $dir mergetool.tcl]] 30 | set auto_index(merge_load_stages) [list source [file join $dir mergetool.tcl]] 31 | set auto_index(read_merge_stages) [list source [file join $dir mergetool.tcl]] 32 | set auto_index(merge_resolve_tool) [list source [file join $dir mergetool.tcl]] 33 | set auto_index(merge_resolve_tool2) [list source [file join $dir mergetool.tcl]] 34 | set auto_index(delete_temp_files) [list source [file join $dir mergetool.tcl]] 35 | set auto_index(merge_tool_get_stages) [list source [file join $dir mergetool.tcl]] 36 | set auto_index(merge_tool_start) [list source [file join $dir mergetool.tcl]] 37 | set auto_index(read_mtool_output) [list source [file join $dir mergetool.tcl]] 38 | set auto_index(merge_tool_finish) [list source [file join $dir mergetool.tcl]] 39 | set auto_index(::choose_font::pick) [list source [file join $dir choose_font.tcl]] 40 | set auto_index(::searchbar::new) [list source [file join $dir search.tcl]] 41 | set auto_index(::branch_create::dialog) [list source [file join $dir branch_create.tcl]] 42 | set auto_index(::branch_checkout::dialog) [list source [file join $dir branch_checkout.tcl]] 43 | set auto_index(_error_parent) [list source [file join $dir error.tcl]] 44 | set auto_index(error_popup) [list source [file join $dir error.tcl]] 45 | set auto_index(warn_popup) [list source [file join $dir error.tcl]] 46 | set auto_index(info_popup) [list source [file join $dir error.tcl]] 47 | set auto_index(ask_popup) [list source [file join $dir error.tcl]] 48 | set auto_index(hook_failed_popup) [list source [file join $dir error.tcl]] 49 | set auto_index(win32_read_lnk) [list source [file join $dir win32.tcl]] 50 | set auto_index(win32_create_lnk) [list source [file join $dir win32.tcl]] 51 | set auto_index(::linebar::new) [list source [file join $dir line.tcl]] 52 | set auto_index(::browser::new) [list source [file join $dir browser.tcl]] 53 | set auto_index(::browser_open::dialog) [list source [file join $dir browser.tcl]] 54 | set auto_index(do_about) [list source [file join $dir about.tcl]] 55 | set auto_index(InitTheme) [list source [file join $dir themed.tcl]] 56 | set auto_index(gold_frame) [list source [file join $dir themed.tcl]] 57 | set auto_index(tlabel) [list source [file join $dir themed.tcl]] 58 | set auto_index(paddedlabel) [list source [file join $dir themed.tcl]] 59 | set auto_index(Dialog) [list source [file join $dir themed.tcl]] 60 | set auto_index(pave_toplevel) [list source [file join $dir themed.tcl]] 61 | set auto_index(slistbox) [list source [file join $dir themed.tcl]] 62 | set auto_index(get_bg_color) [list source [file join $dir themed.tcl]] 63 | set auto_index(tspinbox) [list source [file join $dir themed.tcl]] 64 | set auto_index(tentry) [list source [file join $dir themed.tcl]] 65 | set auto_index(tentry_widgetproc) [list source [file join $dir themed.tcl]] 66 | set auto_index(tchoosefont) [list source [file join $dir themed.tcl]] 67 | set auto_index(on_choosefont) [list source [file join $dir themed.tcl]] 68 | set auto_index(::console::new) [list source [file join $dir console.tcl]] 69 | set auto_index(::console::embed) [list source [file join $dir console.tcl]] 70 | set auto_index(git_logo) [list source [file join $dir logo.tcl]] 71 | set auto_index(do_windows_shortcut) [list source [file join $dir shortcut.tcl]] 72 | set auto_index(do_cygwin_shortcut) [list source [file join $dir shortcut.tcl]] 73 | set auto_index(do_macosx_app) [list source [file join $dir shortcut.tcl]] 74 | set auto_index(::spellcheck::init) [list source [file join $dir spellcheck.tcl]] 75 | set auto_index(::spellcheck::_match_length) [list source [file join $dir spellcheck.tcl]] 76 | set auto_index(::spellcheck::available_langs) [list source [file join $dir spellcheck.tcl]] 77 | set auto_index(find_ssh_key) [list source [file join $dir sshkey.tcl]] 78 | set auto_index(do_ssh_key) [list source [file join $dir sshkey.tcl]] 79 | set auto_index(make_ssh_key) [list source [file join $dir sshkey.tcl]] 80 | set auto_index(kill_sshkey) [list source [file join $dir sshkey.tcl]] 81 | set auto_index(read_sshkey_output) [list source [file join $dir sshkey.tcl]] 82 | set auto_index(do_stats) [list source [file join $dir database.tcl]] 83 | set auto_index(do_gc) [list source [file join $dir database.tcl]] 84 | set auto_index(do_fsck_objects) [list source [file join $dir database.tcl]] 85 | set auto_index(hint_gc) [list source [file join $dir database.tcl]] 86 | set auto_index(::remote_branch_delete::dialog) [list source [file join $dir remote_branch_delete.tcl]] 87 | set auto_index(::remote_add::dialog) [list source [file join $dir remote_add.tcl]] 88 | set auto_index(load_last_commit) [list source [file join $dir commit.tcl]] 89 | set auto_index(committer_ident) [list source [file join $dir commit.tcl]] 90 | set auto_index(do_signoff) [list source [file join $dir commit.tcl]] 91 | set auto_index(create_new_commit) [list source [file join $dir commit.tcl]] 92 | set auto_index(setup_commit_encoding) [list source [file join $dir commit.tcl]] 93 | set auto_index(commit_tree) [list source [file join $dir commit.tcl]] 94 | set auto_index(commit_prehook_wait) [list source [file join $dir commit.tcl]] 95 | set auto_index(commit_commitmsg) [list source [file join $dir commit.tcl]] 96 | set auto_index(commit_commitmsg_wait) [list source [file join $dir commit.tcl]] 97 | set auto_index(commit_writetree) [list source [file join $dir commit.tcl]] 98 | set auto_index(commit_committree) [list source [file join $dir commit.tcl]] 99 | set auto_index(commit_postcommit_wait) [list source [file join $dir commit.tcl]] 100 | set auto_index(load_all_heads) [list source [file join $dir branch.tcl]] 101 | set auto_index(load_all_tags) [list source [file join $dir branch.tcl]] 102 | set auto_index(radio_selector) [list source [file join $dir branch.tcl]] 103 | set auto_index(::blame::new) [list source [file join $dir blame.tcl]] 104 | set auto_index(::tools_add::dialog) [list source [file join $dir tools_dlg.tcl]] 105 | set auto_index(::tools_remove::dialog) [list source [file join $dir tools_dlg.tcl]] 106 | set auto_index(::tools_askdlg::dialog) [list source [file join $dir tools_dlg.tcl]] 107 | set auto_index(class) [list source [file join $dir class.tcl]] 108 | set auto_index(field) [list source [file join $dir class.tcl]] 109 | set auto_index(constructor) [list source [file join $dir class.tcl]] 110 | set auto_index(method) [list source [file join $dir class.tcl]] 111 | set auto_index(create_this) [list source [file join $dir class.tcl]] 112 | set auto_index(delete_this) [list source [file join $dir class.tcl]] 113 | set auto_index(make_dialog) [list source [file join $dir class.tcl]] 114 | set auto_index(make_toplevel) [list source [file join $dir class.tcl]] 115 | set auto_index(tools_list) [list source [file join $dir tools.tcl]] 116 | set auto_index(tools_populate_all) [list source [file join $dir tools.tcl]] 117 | set auto_index(tools_create_item) [list source [file join $dir tools.tcl]] 118 | set auto_index(tools_populate_one) [list source [file join $dir tools.tcl]] 119 | set auto_index(tools_exec) [list source [file join $dir tools.tcl]] 120 | set auto_index(tools_run_silent) [list source [file join $dir tools.tcl]] 121 | set auto_index(tools_consume_input) [list source [file join $dir tools.tcl]] 122 | set auto_index(tools_complete) [list source [file join $dir tools.tcl]] 123 | set auto_index(clear_diff) [list source [file join $dir diff.tcl]] 124 | set auto_index(reshow_diff) [list source [file join $dir diff.tcl]] 125 | set auto_index(force_diff_encoding) [list source [file join $dir diff.tcl]] 126 | set auto_index(handle_empty_diff) [list source [file join $dir diff.tcl]] 127 | set auto_index(show_diff) [list source [file join $dir diff.tcl]] 128 | set auto_index(show_unmerged_diff) [list source [file join $dir diff.tcl]] 129 | set auto_index(advance_diff_queue) [list source [file join $dir diff.tcl]] 130 | set auto_index(show_other_diff) [list source [file join $dir diff.tcl]] 131 | set auto_index(get_conflict_marker_size) [list source [file join $dir diff.tcl]] 132 | set auto_index(start_show_diff) [list source [file join $dir diff.tcl]] 133 | set auto_index(parse_color_line) [list source [file join $dir diff.tcl]] 134 | set auto_index(read_diff) [list source [file join $dir diff.tcl]] 135 | set auto_index(apply_hunk) [list source [file join $dir diff.tcl]] 136 | set auto_index(apply_range_or_line) [list source [file join $dir diff.tcl]] 137 | set auto_index(::branch_rename::dialog) [list source [file join $dir branch_rename.tcl]] 138 | set auto_index(parse_git_date) [list source [file join $dir date.tcl]] 139 | set auto_index(format_date) [list source [file join $dir date.tcl]] 140 | set auto_index(reformat_date) [list source [file join $dir date.tcl]] 141 | set auto_index(build_encoding_table) [list source [file join $dir encoding.tcl]] 142 | set auto_index(tcl_encoding) [list source [file join $dir encoding.tcl]] 143 | set auto_index(force_path_encoding) [list source [file join $dir encoding.tcl]] 144 | set auto_index(get_path_encoding) [list source [file join $dir encoding.tcl]] 145 | set auto_index(build_encoding_submenu) [list source [file join $dir encoding.tcl]] 146 | set auto_index(popup_btn_menu) [list source [file join $dir encoding.tcl]] 147 | set auto_index(build_encoding_menu) [list source [file join $dir encoding.tcl]] 148 | set auto_index(do_build_encoding_menu) [list source [file join $dir encoding.tcl]] 149 | set auto_index(::choose_repository::pick) [list source [file join $dir choose_repository.tcl]] 150 | set auto_index(::choose_repository::_get_recentrepos) [list source [file join $dir choose_repository.tcl]] 151 | set auto_index(::choose_repository::_unset_recentrepo) [list source [file join $dir choose_repository.tcl]] 152 | set auto_index(::choose_repository::_append_recentrepos) [list source [file join $dir choose_repository.tcl]] 153 | set auto_index(::choose_repository::_is_git) [list source [file join $dir choose_repository.tcl]] 154 | set auto_index(::choose_repository::_objdir) [list source [file join $dir choose_repository.tcl]] 155 | set auto_index(::choose_repository::_new_ok) [list source [file join $dir choose_repository.tcl]] 156 | set auto_index(is_tracking_branch) [list source [file join $dir remote.tcl]] 157 | set auto_index(all_tracking_branches) [list source [file join $dir remote.tcl]] 158 | set auto_index(load_all_remotes) [list source [file join $dir remote.tcl]] 159 | set auto_index(add_fetch_entry) [list source [file join $dir remote.tcl]] 160 | set auto_index(add_push_entry) [list source [file join $dir remote.tcl]] 161 | set auto_index(make_sure_remote_submenues_exist) [list source [file join $dir remote.tcl]] 162 | set auto_index(update_all_remotes_menu_entry) [list source [file join $dir remote.tcl]] 163 | set auto_index(populate_remotes_menu) [list source [file join $dir remote.tcl]] 164 | set auto_index(add_single_remote) [list source [file join $dir remote.tcl]] 165 | set auto_index(delete_from_menu) [list source [file join $dir remote.tcl]] 166 | set auto_index(remove_remote) [list source [file join $dir remote.tcl]] 167 | set auto_index(::choose_rev::new) [list source [file join $dir choose_rev.tcl]] 168 | set auto_index(::choose_rev::new_unmerged) [list source [file join $dir choose_rev.tcl]] 169 | set auto_index(::choose_rev::_new) [list source [file join $dir choose_rev.tcl]] 170 | set auto_index(::status_bar::new) [list source [file join $dir status_bar.tcl]] 171 | set auto_index(::status_bar::two_line) [list source [file join $dir status_bar.tcl]] 172 | set auto_index(::branch_delete::dialog) [list source [file join $dir branch_delete.tcl]] 173 | set auto_index(fetch_from) [list source [file join $dir transport.tcl]] 174 | set auto_index(prune_from) [list source [file join $dir transport.tcl]] 175 | set auto_index(fetch_from_all) [list source [file join $dir transport.tcl]] 176 | set auto_index(prune_from_all) [list source [file join $dir transport.tcl]] 177 | set auto_index(push_to) [list source [file join $dir transport.tcl]] 178 | set auto_index(start_push_anywhere_action) [list source [file join $dir transport.tcl]] 179 | set auto_index(do_push_anywhere) [list source [file join $dir transport.tcl]] 180 | set auto_index(config_check_encodings) [list source [file join $dir option.tcl]] 181 | set auto_index(save_config) [list source [file join $dir option.tcl]] 182 | set auto_index(do_options) [list source [file join $dir option.tcl]] 183 | set auto_index(do_restore_defaults) [list source [file join $dir option.tcl]] 184 | set auto_index(do_save_config) [list source [file join $dir option.tcl]] 185 | set auto_index(::merge::dialog) [list source [file join $dir merge.tcl]] 186 | set auto_index(::merge::reset_hard) [list source [file join $dir merge.tcl]] 187 | set auto_index(::merge::_reset_wait) [list source [file join $dir merge.tcl]] 188 | set auto_index(::checkout_op::new) [list source [file join $dir checkout_op.tcl]] 189 | -------------------------------------------------------------------------------- /src/patch.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2003-2005 Colin Percival 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 | #if 0 28 | __FBSDID 29 | ("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $"); 30 | #endif 31 | 32 | #define _GNU_SOURCE 33 | #include "config.h" 34 | 35 | #include 36 | 37 | #ifdef BSDIFF_WITH_BZIP2 38 | #include 39 | #endif 40 | 41 | #include 42 | 43 | #ifdef BSDIFF_WITH_LZMA 44 | #include 45 | #endif 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | #include "bsheader.h" 66 | 67 | static inline int64_t offtin(u_char *buf) 68 | { 69 | return le64toh(*((int64_t *)buf)); 70 | } 71 | 72 | #ifdef BSDIFF_WITH_LZMA 73 | /* xzfile is a provisional stdio-like interface to xz/lzma2-compressed data. 74 | * liblzma does not currently include this functionality. The interface is 75 | * read-only and only supports sequential access. */ 76 | 77 | typedef struct { 78 | /* in and out are the underlying buffers to be used with lzma_stream. */ 79 | u_char in[BUFSIZ]; 80 | u_char out[BUFSIZ]; 81 | 82 | lzma_stream ls; 83 | FILE *f; 84 | 85 | /* read_out points to the first byte in out not yet consumed by an 86 | * xzread call. read_out_len tracks the amount of data available in 87 | * out beginning at read_out. */ 88 | u_char *read_out; 89 | size_t read_out_len; 90 | 91 | /* Error and end-of-file indicators. */ 92 | lzma_ret err; 93 | int eof; 94 | } xzfile; 95 | 96 | /* Initializes and returns a new xzfile pointer that will read from f. On 97 | * failure, returns NULL. If err is non-NULL, it will be set to indicate any 98 | * error that may have occurred. */ 99 | static xzfile *xzdopen(FILE *f, lzma_ret *err) 100 | { 101 | xzfile *xzf; 102 | lzma_stream ls = LZMA_STREAM_INIT; 103 | uint64_t memlimit; 104 | 105 | if (!(xzf = malloc(sizeof(xzfile)))) { 106 | if (err) { 107 | *err = LZMA_MEM_ERROR; 108 | } 109 | return NULL; 110 | } 111 | 112 | xzf->ls = ls; 113 | xzf->f = f; 114 | 115 | xzf->read_out = xzf->out; 116 | xzf->read_out_len = 0; 117 | 118 | xzf->err = LZMA_OK; 119 | xzf->eof = 0; 120 | 121 | /* Use the same memory limits used by xzdec and xz. Use 40% of 122 | * physical memory if 80MB or more, otherwise use 80% of physical 123 | * memory if 80MB or less, otherwise use 80MB. If physical memory 124 | * can't be determined, use 128MB. These limits should be sufficient 125 | * for any decompression on any general-purpose system. */ 126 | memlimit = 80 * 1024 * 1024; 127 | 128 | xzf->err = lzma_stream_decoder(&xzf->ls, memlimit, 129 | LZMA_TELL_NO_CHECK | 130 | LZMA_TELL_UNSUPPORTED_CHECK); 131 | if (xzf->err != LZMA_OK) { 132 | if (err) { 133 | *err = xzf->err; 134 | } 135 | free(xzf); 136 | return NULL; 137 | } 138 | 139 | if (err) { 140 | *err = xzf->err; 141 | } 142 | return xzf; 143 | } 144 | 145 | /* Closes an xzfile opened by xzopen, freeing all memory and closing all 146 | * files. Returns LZMA_OK normally, or LZMA_STREAM_END if fclose fails. */ 147 | static lzma_ret xzclose(xzfile *xzf) 148 | { 149 | lzma_ret lzma_err = LZMA_OK; 150 | 151 | lzma_end(&xzf->ls); 152 | if (fclose(xzf->f) != 0) { 153 | lzma_err = LZMA_STREAM_END; 154 | } 155 | free(xzf); 156 | 157 | return lzma_err; 158 | } 159 | 160 | /* Reads len uncompressed bytes from xzf into buf. Returns the number of bytes 161 | * read, which may be less than len at the end of the file. Upon error, if 162 | * err is non-NULL, it will be set to an appropriate value, which will either 163 | * be a return value from lzma_code (with the exception of LZMA_STREAM_END, 164 | * which is remapped to LZMA_OK), or LZMA_STREAM_END to indicate an I/O error. 165 | */ 166 | static size_t xzread(xzfile *xzf, u_char *buf, size_t len, lzma_ret *err) 167 | { 168 | lzma_action action = LZMA_RUN; 169 | size_t copylen; 170 | size_t nread = 0; 171 | 172 | *err = LZMA_OK; 173 | 174 | while (xzf->err == LZMA_OK && len > 0) { 175 | if (xzf->read_out_len == 0) { 176 | /* No unconsumed data is available, need to run 177 | * lzma_code to decompress. */ 178 | 179 | if (xzf->ls.avail_in == 0 && xzf->eof) { 180 | return 0; 181 | } 182 | if (xzf->ls.avail_in == 0 && !xzf->eof) { 183 | /* No input data available, need to read. */ 184 | xzf->ls.next_in = xzf->in; 185 | xzf->ls.avail_in = fread(xzf->in, 1, BUFSIZ, 186 | xzf->f); 187 | if (ferror(xzf->f)) { 188 | /* Map I/O errors to LZMA_STREAM_END. */ 189 | xzf->err = LZMA_STREAM_END; 190 | *err = xzf->err; 191 | return 0; 192 | } else if (feof(xzf->f)) { 193 | xzf->eof = 1; 194 | } 195 | } 196 | 197 | /* Use the full output buffer. */ 198 | xzf->ls.next_out = xzf->out; 199 | xzf->ls.avail_out = BUFSIZ; 200 | 201 | /* There must be something to decode. */ 202 | if (xzf->ls.avail_in == 0) { 203 | xzf->err = LZMA_BUF_ERROR; 204 | *err = xzf->err; 205 | return 0; 206 | } 207 | 208 | /* LZMA_FINISH is not critical because 209 | * LZMA_CONCATENATED is not in use. */ 210 | if (xzf->eof) { 211 | action = LZMA_FINISH; 212 | } 213 | 214 | /* Run the decoder. */ 215 | xzf->err = lzma_code(&xzf->ls, action); 216 | if (xzf->err == LZMA_STREAM_END) { 217 | xzf->eof = 1; 218 | xzf->err = LZMA_OK; 219 | /* if the stream ended, but no bytes were outputed.. we're at the end */ 220 | if (xzf->ls.avail_out == BUFSIZ) { 221 | len = 0; 222 | } 223 | } else if (xzf->err != LZMA_OK) { 224 | *err = xzf->err; 225 | return 0; 226 | } 227 | 228 | /* Everything that was decoded is now available for 229 | * reading into buf. */ 230 | xzf->read_out = xzf->out; 231 | xzf->read_out_len = BUFSIZ - xzf->ls.avail_out; 232 | } 233 | 234 | /* Copy everything available up to len, and push some 235 | * pointers. */ 236 | copylen = xzf->read_out_len; 237 | if (copylen > len) { 238 | copylen = len; 239 | } 240 | memcpy(buf, xzf->read_out, copylen); 241 | nread += copylen; 242 | buf += copylen; 243 | len -= copylen; 244 | xzf->read_out += copylen; 245 | xzf->read_out_len -= copylen; 246 | } 247 | 248 | *err = xzf->err; 249 | return nread; 250 | } 251 | #endif /* BSDIFF_WITH_LZMA */ 252 | 253 | /* cfile is a uniform interface to read from maybe-compressed files. */ 254 | 255 | typedef struct { 256 | FILE *f; /* method = NONE, BZIP2, ZEROS */ 257 | int fd; /* method = BZIP2 */ 258 | union { 259 | #ifdef BSDIFF_WITH_BZIP2 260 | BZFILE *bz2; /* method = BZIP2 */ 261 | #endif 262 | gzFile gz; /* method = GZIP */ 263 | #ifdef BSDIFF_WITH_LZMA 264 | xzfile *xz; /* method = XZ */ 265 | #endif 266 | } u; 267 | const char *tag; 268 | unsigned char method; 269 | } cfile; 270 | 271 | /* Opens a file at path, seeks to offset off, and prepares for reading using 272 | * the specified method in enum BSDIFF_ENCODINGS. The tag is an identifier 273 | * for error reporting. */ 274 | static int cfopen(cfile *cf, const char *path, int64_t off, 275 | const char *tag, unsigned char method) 276 | { 277 | #ifdef BSDIFF_WITH_BZIP2 278 | int bz2_err; 279 | #endif 280 | #ifdef BSDIFF_WITH_LZMA 281 | lzma_ret lzma_err; 282 | #endif 283 | 284 | if (method == BSDIFF_ENC_NONE || 285 | method == BSDIFF_ENC_BZIP2 || 286 | method == BSDIFF_ENC_XZ || 287 | method == BSDIFF_ENC_ZEROS) { 288 | /* Use stdio for uncompressed files. The bzip interface also 289 | * sits on top of a stdio FILE* but does not take "ownership" 290 | * of the FILE*. The xz/lzma2 interface sits on top of a FILE* 291 | * and does take ownership of the FILE*. */ 292 | if ((cf->f = fopen(path, "rb")) == NULL) { 293 | return -1; 294 | } 295 | if ((fseeko(cf->f, off, SEEK_SET)) != 0) { 296 | fclose(cf->f); 297 | return -1; 298 | } 299 | if (method == BSDIFF_ENC_BZIP2) { 300 | #ifdef BSDIFF_WITH_BZIP2 301 | if ((cf->u.bz2 = BZ2_bzReadOpen(&bz2_err, cf->f, 0, 0, 302 | NULL, 0)) == NULL) { 303 | fclose(cf->f); 304 | return -1; 305 | } 306 | #else /*BSDIFF_WITHOUT_BZIP2*/ 307 | fclose(cf->f); 308 | return -1; 309 | #endif 310 | } else if (method == BSDIFF_ENC_XZ) { 311 | #ifdef BSDIFF_WITH_LZMA 312 | if ((cf->u.xz = xzdopen(cf->f, &lzma_err)) == NULL) { 313 | fclose(cf->f); 314 | return -1; 315 | } 316 | /* cf->f belongs to the xzfile now, don't access it 317 | * from here. */ 318 | cf->f = NULL; 319 | #else /* BSDIFF_WITHOUT_LZMA */ 320 | fclose(cf->f); 321 | return -1; 322 | #endif 323 | } 324 | } else if (method == BSDIFF_ENC_GZIP) { 325 | if ((cf->fd = open(path, O_RDONLY)) < 0) { 326 | return -1; 327 | } 328 | if (lseek(cf->fd, off, SEEK_SET) != off) { 329 | close(cf->fd); 330 | return -1; 331 | } 332 | if ((cf->u.gz = gzdopen(cf->fd, "rb")) == NULL) { 333 | close(cf->fd); 334 | return -1; 335 | } 336 | } else { 337 | return -1; 338 | } 339 | 340 | cf->tag = tag; 341 | cf->method = method; 342 | 343 | return 0; 344 | } 345 | 346 | static void cfclose(cfile *cf) 347 | { 348 | #ifdef BSDIFF_WITH_BZIP2 349 | int bz2_err; 350 | #endif 351 | int gz_err; 352 | #ifdef BSDIFF_WITH_LZMA 353 | lzma_ret lzma_err; 354 | #endif 355 | 356 | if (cf->method == BSDIFF_ENC_NONE || 357 | cf->method == BSDIFF_ENC_BZIP2 || 358 | cf->method == BSDIFF_ENC_ZEROS) { 359 | if (cf->method == BSDIFF_ENC_BZIP2) { 360 | #ifdef BSDIFF_WITH_BZIP2 361 | bz2_err = BZ_OK; 362 | BZ2_bzReadClose(&bz2_err, cf->u.bz2); 363 | #else /*BSDIFF_WITHOUT_BZIP2*/ 364 | return; 365 | #endif 366 | } 367 | if (fclose(cf->f) != 0) { 368 | return; 369 | } 370 | } else if (cf->method == BSDIFF_ENC_GZIP) { 371 | if ((gz_err = gzclose(cf->u.gz)) != Z_OK) { 372 | return; 373 | } 374 | } else if (cf->method == BSDIFF_ENC_XZ) { 375 | #ifdef BSDIFF_WITH_LZMA 376 | if ((lzma_err = xzclose(cf->u.xz)) != LZMA_OK) { 377 | return; 378 | } 379 | #else /* BSDIFF_WITHOUT_LZMA */ 380 | return; 381 | #endif 382 | } 383 | } 384 | 385 | static int cfread(cfile *cf, u_char *buf, size_t len, int block, uint64_t *zeros) 386 | { 387 | size_t nread; 388 | #ifdef BSDIFF_WITH_BZIP2 389 | int bz2_err; 390 | #endif 391 | int gz_err; 392 | #ifdef BSDIFF_WITH_LZMA 393 | lzma_ret lzma_err; 394 | #endif 395 | 396 | if (len <= 0) { 397 | return 0; 398 | } 399 | 400 | if (cf->method == BSDIFF_ENC_NONE) { 401 | nread = fread(buf, 1, len, cf->f); 402 | if (nread != len) { 403 | return -1; 404 | } 405 | } else if (cf->method == BSDIFF_ENC_BZIP2) { 406 | #ifdef BSDIFF_WITH_BZIP2 407 | bz2_err = BZ_OK; 408 | if ((nread = BZ2_bzRead(&bz2_err, cf->u.bz2, buf, len)) != len) { 409 | return -1; 410 | } 411 | #else /*BSDIFF_WITHOUT_BZIP2*/ 412 | return -1; 413 | #endif 414 | } else if (cf->method == BSDIFF_ENC_GZIP) { 415 | if ((nread = gzread(cf->u.gz, buf, len)) != len) { 416 | gz_err = Z_OK; 417 | gzerror(cf->u.gz, &gz_err); 418 | return -1; 419 | } 420 | } else if (cf->method == BSDIFF_ENC_XZ) { 421 | #ifdef BSDIFF_WITH_LZMA 422 | if ((nread = xzread(cf->u.xz, buf, len, &lzma_err)) != len) { 423 | return -1; 424 | } 425 | #else /* BSDIFF_WITH_LZMA */ 426 | return -1; 427 | #endif 428 | } else if ((cf->method == BSDIFF_ENC_ZEROS) && 429 | ((block == BSDIFF_BLOCK_DIFF) || (block == BSDIFF_BLOCK_EXTRA))) { 430 | if (*zeros == ULONG_MAX) { 431 | uint64_t tmp; 432 | nread = fread(&tmp, sizeof(uint64_t), 1, cf->f); 433 | if (nread != 1) { 434 | return -1; 435 | } 436 | *zeros = tmp; 437 | } 438 | if (*zeros < len) { 439 | return -1; 440 | } 441 | if (*zeros >= len) { 442 | memset(buf, 0, len); 443 | *zeros -= len; 444 | } 445 | } else { 446 | return -1; 447 | } 448 | 449 | return 0; 450 | } 451 | 452 | static int check_header(FILE *f, enc_flags_t encoding, 453 | off_t control_length, off_t diff_length, off_t extra_length, 454 | off_t old_file_length, off_t new_file_length, off_t offset_to_first_block) 455 | { 456 | off_t patchsize; 457 | 458 | /* Read lengths from header */ 459 | if (control_length < 0 || diff_length < 0 || extra_length < 0) { 460 | return -1; 461 | } 462 | if (old_file_length < 0 || new_file_length < 0) { 463 | return -1; 464 | } 465 | if (fseeko(f, 0, SEEK_END) != 0 || (patchsize = ftello(f)) < 0) { 466 | return -1; 467 | } 468 | if (patchsize != offset_to_first_block + control_length + diff_length + extra_length) { 469 | return -1; 470 | } 471 | 472 | if (cblock_get_enc(encoding) == BSDIFF_ENC_ZEROS) { 473 | return -1; 474 | } 475 | return 0; 476 | } 477 | 478 | static int open_bsdiff_blocks(cfile *cf, cfile *df, cfile *ef, char *deltafile, 479 | off_t control_length, off_t diff_length, 480 | off_t offset_to_first_block, enc_flags_t encoding) 481 | { 482 | int ret; 483 | 484 | ret = cfopen(cf, deltafile, offset_to_first_block, 485 | "control", cblock_get_enc(encoding)); 486 | if (ret < 0) { 487 | return -1; 488 | } 489 | ret = cfopen(df, deltafile, offset_to_first_block + control_length, 490 | "diff", dblock_get_enc(encoding)); 491 | if (ret < 0) { 492 | cfclose(cf); 493 | return -1; 494 | } 495 | ret = cfopen(ef, deltafile, offset_to_first_block + control_length + diff_length, 496 | "extra", eblock_get_enc(encoding)); 497 | if (ret < 0) { 498 | cfclose(cf); 499 | cfclose(df); 500 | return -1; 501 | } 502 | return 0; 503 | } 504 | 505 | static int read_file(char *filename, unsigned char **data, off_t len) 506 | { 507 | int fd; 508 | struct stat sb; 509 | 510 | fd = open(filename, O_RDONLY, 0); 511 | if (fd < 0) { 512 | return -1; 513 | } 514 | 515 | if (fstat(fd, &sb) != 0) { 516 | close(fd); 517 | return -1; 518 | } 519 | 520 | if (len != sb.st_size) { 521 | close(fd); 522 | return -1; 523 | } 524 | 525 | *data = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 526 | close(fd); 527 | 528 | if (*data == MAP_FAILED) { 529 | *data = NULL; 530 | return -1; 531 | } 532 | 533 | return 0; 534 | } 535 | 536 | static int apply_delta_v2(int subver, FILE *f, 537 | char *old_filename, char *new_filename, char *delta_filename) 538 | { 539 | cfile cf, df, ef; 540 | unsigned char *old_data = NULL, *new_data; 541 | unsigned char buf[8]; 542 | off_t old_pos, new_pos; 543 | int64_t ctrl[3]; 544 | int i, ret, fd; 545 | off_t data_offset; 546 | off_t ctrllen, difflen, extralen; 547 | off_t old_size, new_size; 548 | mode_t mode; 549 | uid_t uid; 550 | gid_t gid; 551 | enc_flags_t encoding; 552 | uint64_t d_zeros = ULONG_MAX; 553 | uint64_t e_zeros = ULONG_MAX; 554 | 555 | if (subver == 0) { 556 | struct header_v20 header; 557 | if (fread(&header, sizeof(struct header_v20), 1, f) < 1) { 558 | return -1; 559 | } 560 | data_offset = header.offset_to_first_block; 561 | ctrllen = header.control_length; 562 | difflen = header.diff_length; 563 | extralen = header.extra_length; 564 | old_size = header.old_file_length; 565 | new_size = header.new_file_length; 566 | mode = header.file_mode; 567 | uid = header.file_owner; 568 | gid = header.file_group; 569 | encoding = header.encoding; 570 | } else if (subver == 1) { 571 | struct header_v21 header; 572 | if (fread(&header, sizeof(struct header_v21), 1, f) < 1) { 573 | return -1; 574 | } 575 | data_offset = header.offset_to_first_block; 576 | ctrllen = header.control_length; 577 | difflen = header.diff_length; 578 | extralen = header.extra_length; 579 | old_size = header.old_file_length; 580 | new_size = header.new_file_length; 581 | mode = header.file_mode; 582 | uid = header.file_owner; 583 | gid = header.file_group; 584 | encoding = header.encoding; 585 | } else { 586 | return -1; 587 | } 588 | 589 | if ((ret = check_header(f, encoding, 590 | ctrllen, difflen, extralen, 591 | old_size, new_size, data_offset)) < 0) { 592 | return ret; 593 | } 594 | 595 | if ((ret = open_bsdiff_blocks(&cf, &df, &ef, delta_filename, 596 | ctrllen, difflen, data_offset, encoding)) < 0) { 597 | return ret; 598 | } 599 | 600 | ret = read_file(old_filename, &old_data, old_size); 601 | if (ret < 0) { 602 | goto preperror; 603 | } 604 | 605 | if (new_size > BSDIFF_MAX_FILESZ) { 606 | munmap(old_data, old_size); 607 | ret = -1; 608 | goto preperror; 609 | } 610 | 611 | /* Allocate new_size+1 bytes instead of new_size bytes to ensure 612 | that we never try to malloc(0) and get a NULL pointer */ 613 | if ((new_data = malloc(new_size + 1)) == NULL) { 614 | munmap(old_data, old_size); 615 | ret = -1; 616 | goto preperror; 617 | } 618 | memset(new_data, 0, new_size + 1); 619 | 620 | old_pos = 0; 621 | new_pos = 0; 622 | while (new_pos < new_size) { 623 | /* Read control data: 624 | * ctrl[0] == offset into diff block 625 | * ctrl[1] == offset into extra block 626 | * ctrl[2] == adjustment factor for offset into old_data 627 | * 628 | * The three control block words manage reads of the diff, 629 | * extra and old_data so that those three sources can be 630 | * combined into new_data. ctrl[2] in particular may cause 631 | * old_pos to jump forward AND backward in order to allow 632 | * copies of the original file content rather than using 633 | * diff or extra content. 634 | */ 635 | for (i = 0; i <= 2; i++) { 636 | ret = cfread(&cf, buf, 8, BSDIFF_BLOCK_CONTROL, NULL); 637 | if (ret < 0) { 638 | goto readerror; 639 | } 640 | ctrl[i] = offtin(buf); 641 | } 642 | 643 | /* Sanity-check */ 644 | if (new_pos + ctrl[0] > new_size || ctrl[0] < 0 || new_pos + ctrl[0] < 0) { 645 | ret = -1; 646 | goto readerror; 647 | } 648 | 649 | /* Read diff string */ 650 | ret = cfread(&df, new_data + new_pos, ctrl[0], BSDIFF_BLOCK_DIFF, &d_zeros); 651 | if (ret < 0) { 652 | goto readerror; 653 | } 654 | 655 | /* Add old data to diff string */ 656 | for (i = 0; i < ctrl[0]; i++) { 657 | if ((old_pos + i >= 0) && (old_pos + i < old_size)) { 658 | new_data[new_pos + i] += old_data[old_pos + i]; 659 | } 660 | } 661 | 662 | /* Adjust pointers */ 663 | new_pos += ctrl[0]; 664 | old_pos += ctrl[0]; 665 | 666 | /* Sanity-check */ 667 | if (new_pos + ctrl[1] > new_size || ctrl[1] < 0 || new_pos + ctrl[1] < 0) { 668 | ret = -1; 669 | goto readerror; 670 | } 671 | if (old_pos + ctrl[2] > old_size || old_pos + ctrl[2] < 0) { 672 | ret = -1; 673 | goto readerror; 674 | } 675 | 676 | /* Read extra string */ 677 | ret = cfread(&ef, new_data + new_pos, ctrl[1], BSDIFF_BLOCK_EXTRA, &e_zeros); 678 | if (ret < 0) { 679 | goto readerror; 680 | } 681 | 682 | /* Adjust pointers */ 683 | new_pos += ctrl[1]; 684 | old_pos += ctrl[2]; 685 | } 686 | 687 | /* Clean up the readers */ 688 | cfclose(&cf); 689 | cfclose(&df); 690 | cfclose(&ef); 691 | 692 | /* Write the new file */ 693 | fd = open(new_filename, O_CREAT | O_EXCL | O_WRONLY, 00644); 694 | if (fd < 0) { 695 | ret = -1; 696 | goto writeerror; 697 | } 698 | 699 | if (write(fd, new_data, new_size) != new_size) { 700 | unlink(new_filename); 701 | close(fd); 702 | ret = -1; 703 | goto writeerror; 704 | } 705 | 706 | close(fd); 707 | 708 | ret = chown(new_filename, uid, gid); 709 | if (ret < 0) { 710 | free(new_data); 711 | return ret; 712 | } 713 | 714 | ret = chmod(new_filename, mode); 715 | if (ret < 0) { 716 | free(new_data); 717 | return ret; 718 | } 719 | 720 | writeerror: 721 | free(new_data); 722 | munmap(old_data, old_size); 723 | return ret; 724 | 725 | readerror: 726 | free(new_data); 727 | munmap(old_data, old_size); 728 | preperror: 729 | cfclose(&cf); 730 | cfclose(&df); 731 | cfclose(&ef); 732 | return ret; 733 | } 734 | 735 | int apply_bsdiff_delta(char *oldfile, char *newfile, char *deltafile) 736 | { 737 | FILE *f; 738 | unsigned char magic[8]; 739 | struct stat sb; 740 | int ret; 741 | 742 | /* Open patch file */ 743 | f = fopen(deltafile, "rb"); 744 | if (!f) { 745 | return -1; 746 | } 747 | 748 | if (stat(deltafile, &sb) == -1) { 749 | ret = -1; 750 | goto error; 751 | } 752 | /* Make sure delta file is at least big enough to have a header */ 753 | if (sb.st_size < 8) { 754 | ret = -2; 755 | goto error; 756 | } 757 | 758 | /* Read header magic */ 759 | if (fread(&magic, 8, 1, f) < 1) { 760 | ret = -1; 761 | goto error; 762 | } 763 | 764 | /* Deal with different header types */ 765 | if (memcmp(&magic, BSDIFF_HDR_MAGIC_V20, 8) == 0) { 766 | rewind(f); 767 | ret = apply_delta_v2(0, f, oldfile, newfile, deltafile); 768 | if (ret != 0) { 769 | goto error; 770 | } 771 | } else if (memcmp(&magic, BSDIFF_HDR_MAGIC_V21, 8) == 0) { 772 | rewind(f); 773 | ret = apply_delta_v2(1, f, oldfile, newfile, deltafile); 774 | if (ret != 0) { 775 | goto error; 776 | } 777 | } else if (memcmp(&magic, BSDIFF_HDR_DIR_V20, 8) == 0) { 778 | ret = -1; 779 | } else if (memcmp(&magic, BSDIFF_HDR_FULLDL, 8) == 0) { 780 | ret = -2; 781 | } else { 782 | ret = -1; 783 | } 784 | error: 785 | fclose(f); 786 | return ret; 787 | } 788 | -------------------------------------------------------------------------------- /src/diff.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2003-2005 Colin Percival 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 | #if 0 28 | __FBSDID 29 | ("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c,v 1.1 2005/08/06 01:59:05 cperciva Exp $"); 30 | #endif 31 | 32 | #define _GNU_SOURCE 33 | #include "config.h" 34 | 35 | #ifdef BSDIFF_WITH_BZIP2 36 | #include 37 | #endif 38 | 39 | #include 40 | #include 41 | 42 | #ifdef BSDIFF_WITH_LZMA 43 | #include 44 | #endif 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | 61 | #include "bsheader.h" 62 | 63 | static int bsdiff_files; 64 | static uint64_t bsdiff_newbytes; 65 | static uint64_t bsdiff_outputbytes; 66 | static int bsdiff_gzip; 67 | static int bsdiff_bzip2; 68 | static int bsdiff_xz; 69 | static int bsdiff_none; 70 | static int bsdiff_zeros; 71 | static int bsdiff_fulldl; 72 | 73 | /* TODO: oh dear, another MIN that multiple evaluates.... */ 74 | #undef MIN 75 | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) 76 | 77 | static int64_t matchlen(u_char *old, int64_t old_size, u_char *new, 78 | int64_t new_size) 79 | { 80 | int64_t i; 81 | 82 | for (i = 0; (i < old_size) && (i < new_size); i++) { 83 | if (old[i] != new[i]) { 84 | break; 85 | } 86 | } 87 | 88 | return i; 89 | } 90 | 91 | /** 92 | * Finds the longest matching array of bytes between the OLD and NEW file. The 93 | * old file is suffix-sorted; the suffix-sorted array is stored at I, and 94 | * indices to search between are indicated by ST (start) and EN (end). The 95 | * function does not return a value, but once a match is determined, OLD_POS is 96 | * updated to the position of the match within OLD, and MAX_LEN is set to the 97 | * match length. 98 | */ 99 | static void search(int64_t *I, u_char *old, int64_t old_size, 100 | u_char *new, int64_t new_size, int64_t st, int64_t en, 101 | int64_t *old_pos, int64_t *max_len) 102 | { 103 | int64_t x, y; 104 | 105 | /* Initialize max_len for the binary search */ 106 | if (st == 0 && en == old_size) { 107 | *max_len = matchlen(old, old_size, new, new_size); 108 | *old_pos = I[st]; 109 | } 110 | 111 | /* The binary search terminates here when "en" and "st" are adjacent 112 | * indices in the suffix-sorted array. */ 113 | if (en - st < 2) { 114 | x = matchlen(old + I[st], old_size - I[st], new, new_size); 115 | if (x > *max_len) { 116 | *max_len = x; 117 | *old_pos = I[st]; 118 | } 119 | y = matchlen(old + I[en], old_size - I[en], new, new_size); 120 | if (y > *max_len) { 121 | *max_len = y; 122 | *old_pos = I[en]; 123 | } 124 | 125 | return; 126 | } 127 | 128 | x = st + (en - st) / 2; 129 | 130 | int64_t length = MIN(old_size - I[x], new_size); 131 | u_char *oldoffset = old + I[x]; 132 | 133 | /* This match *could* be the longest one, so check for that here */ 134 | int64_t tmp = matchlen(oldoffset, length, new, length); 135 | if (tmp > *max_len) { 136 | *max_len = tmp; 137 | *old_pos = I[x]; 138 | } 139 | 140 | /* Determine how to continue the binary search */ 141 | if (memcmp(oldoffset, new, length) < 0) { 142 | return search(I, old, old_size, new, new_size, x, en, old_pos, max_len); 143 | } else { 144 | return search(I, old, old_size, new, new_size, st, x, old_pos, max_len); 145 | } 146 | } 147 | 148 | static inline void offtout(int64_t x, u_char *buf) 149 | { 150 | *((int64_t *)buf) = htole64(x); 151 | } 152 | 153 | /* zlib provides compress2, which deflates to deflate (zlib) format. This is 154 | * unfortunately distinct from gzip format in that the headers wrapping the 155 | * decompressed data are different. gbspatch reads gzip-compressed data using 156 | * the file-oriented gzread interface, which only supports gzip format. 157 | * compress2gzip is identical to zlib's compress2 except that it produces gzip 158 | * output compatible with gzread. This change is achieved by calling 159 | * deflateInit2 instead of deflateInit and specifying 31 for windowBits; 160 | * numbers greater than 15 cause the addition of a gzip wrapper. */ 161 | 162 | static int compress2gzip(Bytef *dest, size_t *destLen, 163 | const Bytef *source, uLong sourceLen, int level) 164 | { 165 | z_stream stream; 166 | int err; 167 | 168 | stream.next_in = (Bytef *)source; 169 | stream.avail_in = (uInt)sourceLen; 170 | 171 | stream.next_out = dest; 172 | stream.avail_out = (uInt)*destLen; 173 | if ((uLong)stream.avail_out != *destLen) { 174 | return Z_BUF_ERROR; 175 | } 176 | 177 | stream.zalloc = (alloc_func)0; 178 | stream.zfree = (free_func)0; 179 | stream.opaque = (voidpf)0; 180 | 181 | err = deflateInit2(&stream, 182 | level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); 183 | if (err != Z_OK) { 184 | return err; 185 | } 186 | 187 | err = deflate(&stream, Z_FINISH); 188 | if (err != Z_STREAM_END) { 189 | deflateEnd(&stream); 190 | return err == Z_OK ? Z_BUF_ERROR : err; 191 | } 192 | *destLen = stream.total_out; 193 | 194 | err = deflateEnd(&stream); 195 | return err; 196 | } 197 | 198 | #ifdef BSDIFF_WITH_LZMA 199 | static pthread_mutex_t lzma_mutex = PTHREAD_MUTEX_INITIALIZER; 200 | #endif 201 | 202 | static uint64_t count_nonzero(unsigned char *buf, uint64_t len) 203 | { 204 | uint64_t count = 0; 205 | uint64_t i = 0; 206 | while (i < len) { 207 | if (buf[i]) { 208 | count++; 209 | } 210 | i++; 211 | } 212 | return count; 213 | } 214 | 215 | /* Recompress buf of size buf_len using a supported algorithm. The smallest version is 216 | * used. The original uncompressed variant may be the smallest. Returns a 217 | * number identifying the encoding according to enum BSDIFF_ENCODINGS. 218 | * If the original uncompressed variant is not smallest, it is freed. The caller 219 | * must free any buf after this function returns. */ 220 | static char make_small(u_char **buf, 221 | uint64_t *buf_len, 222 | int enc, 223 | __attribute__((unused)) char *file, 224 | char *blockname) 225 | { 226 | u_char *source = *buf; 227 | uint64_t source_len = *buf_len; 228 | #ifdef BSDIFF_WITH_BZIP2 229 | u_char *bz2; 230 | unsigned int bz2_len; 231 | int bz2_err; 232 | int bzip_penalty = 512; 233 | #endif 234 | #ifdef BSDIFF_WITH_LZMA 235 | u_char *lzma = NULL; 236 | size_t lzma_len, lzma_pos; 237 | lzma_ret lzma_err; 238 | lzma_check lzma_ck; 239 | #endif 240 | u_char *gz; 241 | size_t gz_len; 242 | int gz_err; 243 | char smallest; 244 | 245 | __attribute__((unused)) uint64_t unc_size = 0, bzip_size = 0, gzip_size = 0, xz_size = 0, nonzero = 0; 246 | 247 | smallest = BSDIFF_ENC_NONE; 248 | 249 | if (enc == BSDIFF_ENC_NONE || source_len == 0) { 250 | return smallest; 251 | } 252 | unc_size = *buf_len; 253 | 254 | nonzero = count_nonzero(source, source_len); 255 | 256 | /* if it's an all-zeros block, we're done */ 257 | if (!nonzero) { 258 | if ((enc == BSDIFF_ENC_ANY) && 259 | ((strncmp(blockname, "diff", 4) == 0) || 260 | (strncmp(blockname, "extra", 5) == 0))) { 261 | uint64_t *zeros; 262 | zeros = malloc(sizeof(uint64_t)); 263 | assert(zeros); 264 | *zeros = source_len; 265 | free(source); 266 | *buf = (u_char *)zeros; 267 | *buf_len = sizeof(uint64_t); 268 | return BSDIFF_ENC_ZEROS; 269 | } 270 | #ifdef BSDIFF_WITH_BZIP2 271 | bzip_penalty = 0; 272 | #endif 273 | } 274 | 275 | /* we do gzip first. it's fast on decompression and does quite well on compression */ 276 | gz_len = source_len + 1; 277 | gz = malloc(gz_len); 278 | gz_err = compress2gzip(gz, &gz_len, source, source_len, 9); 279 | if (gz_err == Z_OK) { 280 | gzip_size = gz_len; 281 | 282 | if (gz_len < (unsigned int)*buf_len && 283 | (enc == BSDIFF_ENC_ANY || enc == BSDIFF_ENC_GZIP)) { 284 | smallest = BSDIFF_ENC_GZIP; 285 | *buf = gz; 286 | *buf_len = gz_len; 287 | } else { 288 | free(gz); 289 | gz = NULL; 290 | } 291 | } else if (gz_err == Z_BUF_ERROR) { 292 | free(gz); 293 | gz = NULL; 294 | } 295 | 296 | #ifdef BSDIFF_WITH_LZMA 297 | /* xz/lzma are slower on decompression, but esp for bigger files, compress better */ 298 | pthread_mutex_lock(&lzma_mutex); 299 | lzma_len = source_len + 1000; 300 | lzma = malloc(lzma_len); 301 | lzma_pos = 0; 302 | 303 | /* Equivalent to the options used by xz -9 -e. */ 304 | /* 305 | * We'd like to set LZMA_CHECK_NONE, since we do our own sha based checksum at the end. 306 | * However, that seems to generate undecodable compressed blocks, so we'll just do the 307 | * smallest and cheapest alternative to _NONE, which is CRC32 308 | */ 309 | lzma_ck = LZMA_CHECK_CRC32; 310 | if (!lzma_check_is_supported(lzma_ck)) { 311 | lzma_ck = LZMA_CHECK_CRC32; 312 | } 313 | lzma_err = lzma_easy_buffer_encode(9 | LZMA_PRESET_EXTREME, 314 | lzma_ck, NULL, 315 | source, source_len, 316 | lzma, &lzma_pos, lzma_len); 317 | if (lzma_err == LZMA_OK) { 318 | xz_size = lzma_pos; 319 | if (1.01 * lzma_pos + 64 < *buf_len && 320 | (enc == BSDIFF_ENC_ANY || enc == BSDIFF_ENC_XZ)) { 321 | smallest = BSDIFF_ENC_XZ; 322 | *buf = lzma; 323 | *buf_len = lzma_pos; 324 | } else { 325 | free(lzma); 326 | lzma = NULL; 327 | } 328 | } else if (lzma_err == LZMA_BUF_ERROR) { 329 | free(lzma); 330 | lzma = NULL; 331 | } 332 | 333 | pthread_mutex_unlock(&lzma_mutex); 334 | #endif /* BSDIFF_WITH_LZMA */ 335 | 336 | #ifdef BSDIFF_WITH_BZIP2 337 | /* bzip2 is the slowed of the set on decompress, but for some times of inputs, does really really well */ 338 | bz2_len = source_len + 1; 339 | bz2 = malloc(bz2_len); 340 | bz2_err = 341 | BZ2_bzBuffToBuffCompress((char *)bz2, &bz2_len, (char *)source, 342 | source_len, 9, 0, 0); 343 | if (bz2_err == BZ_OK) { 344 | bzip_size = bz2_len; 345 | 346 | /* we add a 5% + 1/2 Kb penalty to bzip2, due to the high cost on the client */ 347 | if (1.05 * bz2_len + bzip_penalty < (unsigned int)*buf_len && 348 | (enc == BSDIFF_ENC_ANY || enc == BSDIFF_ENC_BZIP2)) { 349 | smallest = BSDIFF_ENC_BZIP2; 350 | *buf = bz2; 351 | *buf_len = bz2_len; 352 | } else { 353 | free(bz2); 354 | bz2 = NULL; 355 | } 356 | } else if (bz2_err == BZ_OUTBUFF_FULL) { 357 | free(bz2); 358 | bz2 = NULL; 359 | } 360 | #endif 361 | 362 | if (smallest != BSDIFF_ENC_NONE) { 363 | free(source); 364 | } 365 | 366 | #ifdef BSDIFF_WITH_BZIP2 367 | if (smallest != BSDIFF_ENC_BZIP2) { 368 | free(bz2); 369 | } 370 | #endif 371 | if (smallest != BSDIFF_ENC_GZIP) { 372 | free(gz); 373 | } 374 | 375 | #ifdef BSDIFF_WITH_LZMA 376 | if (smallest != BSDIFF_ENC_XZ) { 377 | free(lzma); 378 | } 379 | #endif 380 | 381 | return smallest; 382 | } 383 | 384 | /* returns <0 on error, 0 on success, and 1 on "success" with a FULLDL header */ 385 | int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filename, int enc) 386 | { 387 | int fd, efd; 388 | u_char *old_data, *new_data; 389 | int64_t old_size, new_size; 390 | int64_t *I, *V; 391 | uint64_t cblen, dblen, eblen; 392 | u_char *cb, *db, *eb; 393 | struct stat new_stat; 394 | struct stat old_stat; 395 | int ret, smallfile; 396 | off_t first_block; 397 | int c_enc, d_enc, e_enc; 398 | enc_flags_t encodings; 399 | char delta_filename_unique[2 * PATH_MAX]; 400 | 401 | struct header_v20 large_header; 402 | struct header_v21 small_header; 403 | 404 | sprintf(delta_filename_unique, "%s.%i", delta_filename, getpid()); 405 | FILE *pf; 406 | 407 | ret = lstat(old_filename, &old_stat); 408 | if (ret < 0) { 409 | return -1; 410 | } 411 | 412 | ret = lstat(new_filename, &new_stat); 413 | if (ret < 0) { 414 | return -1; 415 | } 416 | 417 | ret = 0; 418 | 419 | if (S_ISDIR(new_stat.st_mode) || S_ISDIR(old_stat.st_mode)) { 420 | /* no delta on symlinks ! */ 421 | return -1; 422 | } 423 | 424 | if ((new_stat.st_size < 65536) && (old_stat.st_size < 65536)) { 425 | smallfile = 1; 426 | } else { 427 | smallfile = 0; 428 | } 429 | 430 | fd = open(old_filename, O_RDONLY, 0); 431 | if (fd < 0) { 432 | return -1; 433 | } 434 | if (fstat(fd, &old_stat) != 0) { 435 | close(fd); 436 | return -1; 437 | } 438 | 439 | old_size = old_stat.st_size; 440 | 441 | /* We may start with an empty file, if so, just mark it for full download 442 | * to throw into the pack. In the case that newfile is <200, it will quit 443 | * and ask for fulldownload, so we only need to check old_size */ 444 | if (old_size == 0) { 445 | memset(&small_header, 0, sizeof(struct header_v21)); 446 | memcpy(&small_header.magic, BSDIFF_HDR_FULLDL, 8); 447 | 448 | efd = open(delta_filename_unique, O_CREAT | O_EXCL | O_WRONLY, 00644); 449 | if (efd < 0) { 450 | close(fd); 451 | return -1; 452 | } 453 | if ((pf = fdopen(efd, "w")) == NULL) { 454 | close(efd); 455 | close(fd); 456 | return -1; 457 | } 458 | if (fwrite(&small_header, 8, 1, pf) != 1) { 459 | fclose(pf); 460 | close(fd); 461 | return -1; 462 | } 463 | fclose(pf); 464 | close(fd); 465 | rename(delta_filename_unique, delta_filename); 466 | return 1; 467 | } 468 | 469 | /* TODO: investigate why this needs to be +1 to not overrun; coverity complains 470 | * that we overrun old_data when we calculate differences otherwise. Tenatively, 471 | * since this is used in qsufsort, it may need to be +1 like I and V because of 472 | * a sentinel byte when sorting. However, new_size does not cause any overruns 473 | * when created with the regular file size */ 474 | old_data = mmap(NULL, old_size + 1, PROT_READ, MAP_SHARED, fd, 0); 475 | close(fd); 476 | 477 | if (old_data == MAP_FAILED) { 478 | old_data = NULL; 479 | return -1; 480 | } 481 | 482 | /* These arrays are size + 1 because suffix sort needs space for the 483 | * data + 1 sentinel element to actually do the sorting. Not because 484 | * old_size might be 0. */ 485 | if ((I = malloc((old_size + 1) * sizeof(int64_t))) == NULL) { 486 | munmap(old_data, old_size); 487 | return -1; 488 | } 489 | if ((V = malloc((old_size + 1) * sizeof(int64_t))) == NULL) { 490 | munmap(old_data, old_size); 491 | free(I); 492 | return -1; 493 | } 494 | 495 | if (qsufsort(I, V, old_data, old_size) != 0) { 496 | munmap(old_data, old_size); 497 | free(I); 498 | free(V); 499 | return -1; 500 | } 501 | 502 | free(V); 503 | 504 | if ((fd = open(new_filename, O_RDONLY, 0)) < 0) { 505 | munmap(old_data, old_size); 506 | free(I); 507 | return -1; 508 | } 509 | 510 | if (fstat(fd, &new_stat) != 0) { 511 | munmap(old_data, old_size); 512 | free(I); 513 | close(fd); 514 | return -1; 515 | } 516 | 517 | new_size = new_stat.st_size; 518 | 519 | /* Note: testing this to see how diffs between small files affect 520 | * updates. Small files seem to cause some problems between certain 521 | * files (buffer overrun/underrun perhaps). We try to avoid this 522 | * preemptively by marking the file as a FULLDL file and not create 523 | * a bsdiff at all, which leaves us with small files that may fail 524 | * the "is bsdiff < 90% of newfile size" check that would otherwise 525 | * be performed later on. 526 | */ 527 | if (new_size < 200) { 528 | memset(&small_header, 0, sizeof(struct header_v21)); 529 | memcpy(&small_header.magic, BSDIFF_HDR_FULLDL, 8); 530 | 531 | efd = open(delta_filename_unique, O_CREAT | O_EXCL | O_WRONLY, 00644); 532 | if (efd < 0) { 533 | close(fd); 534 | munmap(old_data, old_size); 535 | free(I); 536 | return -1; 537 | } 538 | if ((pf = fdopen(efd, "w")) == NULL) { 539 | close(efd); 540 | close(fd); 541 | munmap(old_data, old_size); 542 | free(I); 543 | return -1; 544 | } 545 | if (fwrite(&small_header, 8, 1, pf) != 1) { 546 | fclose(pf); 547 | close(fd); 548 | munmap(old_data, old_size); 549 | 550 | free(I); 551 | return -1; 552 | } 553 | fclose(pf); 554 | close(fd); 555 | munmap(old_data, old_size); 556 | free(I); 557 | rename(delta_filename_unique, delta_filename); 558 | return 1; 559 | } 560 | 561 | if ((new_data = malloc(new_size)) == NULL) { 562 | close(fd); 563 | munmap(old_data, old_size); 564 | free(I); 565 | return -1; 566 | } 567 | 568 | if (pread(fd, new_data, new_size, 0) != new_size) { 569 | close(fd); 570 | munmap(old_data, old_size); 571 | free(new_data); 572 | free(I); 573 | return -1; 574 | } 575 | if (close(fd) == -1) { 576 | munmap(old_data, old_size); 577 | free(new_data); 578 | free(I); 579 | return -1; 580 | } 581 | 582 | /* we can write 3 8 byte tupples extra, so allocate some headroom */ 583 | if ((cb = malloc(new_size + 25)) == NULL) { 584 | munmap(old_data, old_size); 585 | free(new_data); 586 | free(I); 587 | return -1; 588 | } 589 | if ((db = malloc(new_size + 25)) == NULL) { 590 | munmap(old_data, old_size); 591 | free(new_data); 592 | free(cb); 593 | free(I); 594 | return -1; 595 | } 596 | if ((eb = malloc(new_size + 25)) == NULL) { 597 | munmap(old_data, old_size); 598 | free(new_data); 599 | free(cb); 600 | free(db); 601 | free(I); 602 | return -1; 603 | } 604 | cblen = 0; 605 | dblen = 0; 606 | eblen = 0; 607 | 608 | /* Compute the differences */ 609 | int64_t new_pos = 0; 610 | int64_t old_pos = 0; 611 | int64_t match_len = 0; 612 | int64_t last_new_pos = 0; 613 | int64_t last_old_pos = 0; 614 | int64_t last_offset = 0; 615 | while (new_pos < new_size) { 616 | // Find an exact match between old and new files, and require 617 | // that more than 8 of the matching bytes "mismatch" from the 618 | // previous exact match. A score (old_score) is used to track 619 | // how many bytes match starting from new_pos in new, and from 620 | // old_pos in the previous iteration. 621 | // NOTE: the magic value 8 is a heuristic; further testing is 622 | // needed to prove whether this is the best number, or if the 623 | // number should vary according to other factors, etc. 624 | int64_t old_score = 0; 625 | int64_t new_peek; 626 | for (new_peek = new_pos += match_len; new_pos < new_size; new_pos++) { 627 | search(I, old_data, old_size, new_data + new_pos, new_size - new_pos, 628 | 0, old_size, &old_pos, &match_len); 629 | 630 | for (; new_peek < new_pos + match_len; new_peek++) { 631 | if ((new_peek + last_offset < old_size) && 632 | (old_data[new_peek + last_offset] == new_data[new_peek])) { 633 | old_score++; 634 | } 635 | } 636 | 637 | if (((match_len == old_score) && (match_len != 0)) || 638 | (match_len > old_score + 8)) { 639 | break; 640 | } 641 | 642 | // Before beginning the next loop iteration, decrement 643 | // old_score if needed, since new_pos will be 644 | // incremented. 645 | if ((new_pos + last_offset < old_size) && 646 | (old_data[new_pos + last_offset] == new_data[new_pos])) { 647 | old_score--; 648 | } 649 | } 650 | 651 | if ((match_len != old_score) || (new_pos == new_size)) { 652 | int64_t bytes = 0, max = 0; 653 | // Compute the length of a fuzzy match starting from 654 | // the beginning of the fuzzy match recorded at the end 655 | // of the previous iteration (i.e. len_fuzzybackward 656 | // less than the previous match positions). At least 657 | // half of the bytes match between old and new. This 658 | // fuzzy match will be used to construct a diff string 659 | // in the diff block. 660 | // NOTE: "at least half matching bytes" is a heuristic 661 | // for both fuzzy regions being constructed below; 662 | // further testing is needed to prove whether this is 663 | // the best percentage, or if the percentage should 664 | // vary according to other factors, etc. 665 | int64_t len_fuzzyforward = 0; 666 | for (int64_t i = 0; 667 | (last_new_pos + i < new_pos) && (last_old_pos + i < old_size);) { 668 | if (old_data[last_old_pos + i] == new_data[last_new_pos + i]) { 669 | bytes++; 670 | } 671 | i++; 672 | if (bytes * 2 - i > max * 2 - len_fuzzyforward) { 673 | max = bytes; 674 | len_fuzzyforward = i; 675 | } 676 | } 677 | 678 | // Compute the length of a fuzzy match ending at the 679 | // current positions in old and new files (old_pos and 680 | // new_pos). At least half of the bytes match between 681 | // old and new. This fuzzy match will be used for the 682 | // next iteration. 683 | int64_t len_fuzzybackward = 0; 684 | if (new_pos < new_size) { 685 | bytes = 0; 686 | max = 0; 687 | for (int64_t i = 1; 688 | (new_pos >= last_new_pos + i) && (old_pos >= i); 689 | i++) { 690 | if (old_data[old_pos - i] == new_data[new_pos - i]) { 691 | bytes++; 692 | } 693 | if (bytes * 2 - i > max * 2 - len_fuzzybackward) { 694 | max = bytes; 695 | len_fuzzybackward = i; 696 | } 697 | } 698 | } 699 | 700 | // If there is an overlap between len_fuzzyforward and 701 | // len_fuzzybackward in the new file, that overlap must 702 | // be eliminated. 703 | if (last_new_pos + len_fuzzyforward > new_pos - len_fuzzybackward) { 704 | bytes = 0; 705 | max = 0; 706 | int64_t overlap = (last_new_pos + len_fuzzyforward) - (new_pos - len_fuzzybackward); 707 | int64_t len_fuzzyshift = 0; 708 | // Scan the overlap area for differences 709 | // between old and new. If any mismatching 710 | // bytes are found, extend len_fuzzyforward to 711 | // cover those bytes, because we want them 712 | // included in the diff block. 713 | for (int64_t i = 0; i < overlap; i++) { 714 | if (new_data[last_new_pos + len_fuzzyforward - overlap + i] == 715 | old_data[last_old_pos + len_fuzzyforward - overlap + i]) { 716 | bytes++; 717 | } 718 | if (new_data[new_pos - len_fuzzybackward + i] == 719 | old_data[old_pos - len_fuzzybackward + i]) { 720 | bytes--; 721 | } 722 | if (bytes > max) { 723 | max = bytes; 724 | len_fuzzyshift = i + 1; 725 | } 726 | } 727 | 728 | len_fuzzyforward += len_fuzzyshift - overlap; 729 | len_fuzzybackward -= len_fuzzyshift; 730 | } 731 | 732 | // Set the diff string in the diff block. For each byte 733 | // in the fuzzy forward region, the byte from old is 734 | // subtracted from new. When applying the delta (with 735 | // bspatch) this operation is reversed, by performing 736 | // additions. 737 | for (int64_t i = 0; i < len_fuzzyforward; i++) { 738 | db[dblen + i] = 739 | new_data[last_new_pos + i] - old_data[last_old_pos + i]; 740 | } 741 | // Set the extra string in the extra block. The 742 | // contents are the bytes in new file between the fuzzy 743 | // forward and fuzzy backward regions. 744 | for (int64_t i = 0; i < (new_pos - len_fuzzybackward) - (last_new_pos + len_fuzzyforward); i++) { 745 | eb[eblen + i] = new_data[last_new_pos + len_fuzzyforward + i]; 746 | } 747 | 748 | dblen += len_fuzzyforward; 749 | eblen += (new_pos - len_fuzzybackward) - (last_new_pos + len_fuzzyforward); 750 | 751 | /* checking for control block overflow... 752 | * See regression test #15 for an example */ 753 | if ((int64_t)(cblen + 24) > (new_size + 25)) { 754 | munmap(old_data, old_size); 755 | free(new_data); 756 | free(cb); 757 | free(db); 758 | free(eb); 759 | free(I); 760 | return -1; 761 | } 762 | 763 | // Set three values in the control block: 764 | // 1. ADD instruction (value: length of the diff 765 | // string). It uses the offset of the third control 766 | // block value from the previous iteration. 767 | // 2. INSERT instruction (value: length of the extra 768 | // string) 769 | // 3. offset in old file for the next ADD instruction 770 | offtout(len_fuzzyforward, cb + cblen); 771 | cblen += 8; 772 | 773 | offtout((new_pos - len_fuzzybackward) - (last_new_pos + len_fuzzyforward), cb + cblen); 774 | cblen += 8; 775 | 776 | offtout((old_pos - len_fuzzybackward) - (last_old_pos + len_fuzzyforward), cb + cblen); 777 | cblen += 8; 778 | 779 | // Save old/new file positions to the beginning of the 780 | // fuzzy backward region, since the next fuzzy forward 781 | // region will be calculated from that point. 782 | last_new_pos = new_pos - len_fuzzybackward; 783 | last_old_pos = old_pos - len_fuzzybackward; 784 | last_offset = old_pos - new_pos; 785 | } 786 | } 787 | free(I); 788 | 789 | c_enc = make_small(&cb, &cblen, enc, new_filename, "control"); 790 | d_enc = make_small(&db, &dblen, enc, new_filename, "diff "); 791 | e_enc = make_small(&eb, &eblen, enc, new_filename, "extra "); 792 | 793 | if ((!cb) || (!db) || (!eb)) { 794 | ret = -1; 795 | goto fulldl_free; 796 | } 797 | 798 | /* Create the patch file */ 799 | 800 | efd = open(delta_filename_unique, O_CREAT | O_EXCL | O_WRONLY, 00644); 801 | if (efd < 0) { 802 | ret = -1; 803 | goto fulldl_free; 804 | } 805 | if ((pf = fdopen(efd, "w")) == NULL) { 806 | close(efd); 807 | ret = -1; 808 | goto fulldl_free; 809 | } 810 | 811 | if (smallfile && (cblen < 256) && (dblen < 65536) && (eblen < 65536)) { 812 | memset(&small_header, 0, sizeof(struct header_v21)); 813 | memcpy(&small_header.magic, BSDIFF_HDR_MAGIC_V21, 8); 814 | 815 | /* in the future we may need to push first_block out further to squeeze 816 | * something extra into the header */ 817 | first_block = sizeof(struct header_v21); 818 | small_header.offset_to_first_block = first_block; 819 | small_header.control_length = cblen; 820 | small_header.diff_length = dblen; 821 | small_header.extra_length = eblen; 822 | small_header.old_file_length = old_size; 823 | small_header.new_file_length = new_size; 824 | small_header.file_mode = new_stat.st_mode; 825 | small_header.file_owner = new_stat.st_uid; 826 | small_header.file_group = new_stat.st_gid; 827 | 828 | cblock_set_enc(&small_header.encoding, c_enc); 829 | dblock_set_enc(&small_header.encoding, d_enc); 830 | eblock_set_enc(&small_header.encoding, e_enc); 831 | encodings = small_header.encoding; 832 | 833 | if ((first_block + cblen + dblen + eblen > 0.90 * new_size) && (enc != BSDIFF_ENC_NONE)) { /* tune */ 834 | memcpy(&small_header.magic, BSDIFF_HDR_FULLDL, 8); 835 | ret = 1; 836 | if (fwrite(&small_header, 8, 1, pf) != 1) { 837 | ret = -1; 838 | goto fulldl_close_free; 839 | } 840 | bsdiff_fulldl++; 841 | goto fulldl_close_free; 842 | } 843 | 844 | if (fwrite(&small_header, sizeof(struct header_v21), 1, pf) != 1) { 845 | ret = -1; 846 | goto fulldl_close_free; 847 | } 848 | } else { 849 | smallfile = 0; 850 | 851 | memset(&large_header, 0, sizeof(struct header_v20)); 852 | memcpy(&large_header.magic, BSDIFF_HDR_MAGIC_V20, 8); 853 | 854 | /* in the future we may need to push this out further to squeeze 855 | * something extra into the header */ 856 | first_block = sizeof(struct header_v20); 857 | large_header.offset_to_first_block = first_block; 858 | large_header.control_length = cblen; 859 | large_header.diff_length = dblen; 860 | large_header.extra_length = eblen; 861 | large_header.old_file_length = old_size; 862 | large_header.new_file_length = new_size; 863 | large_header.file_mode = new_stat.st_mode; 864 | large_header.file_owner = new_stat.st_uid; 865 | large_header.file_group = new_stat.st_gid; 866 | 867 | cblock_set_enc(&large_header.encoding, c_enc); 868 | dblock_set_enc(&large_header.encoding, d_enc); 869 | eblock_set_enc(&large_header.encoding, e_enc); 870 | encodings = large_header.encoding; 871 | 872 | if ((first_block + cblen + dblen + eblen > 0.90 * new_size) && (enc != BSDIFF_ENC_NONE)) { /* tune */ 873 | memcpy(&large_header.magic, BSDIFF_HDR_FULLDL, 8); 874 | ret = 1; 875 | if (fwrite(&large_header, 8, 1, pf) != 1) { 876 | ret = -1; 877 | goto fulldl_close_free; 878 | } 879 | bsdiff_fulldl++; 880 | goto fulldl_close_free; 881 | } 882 | 883 | if (fwrite(&large_header, sizeof(struct header_v20), 1, pf) != 1) { 884 | ret = -1; 885 | goto fulldl_close_free; 886 | } 887 | } 888 | 889 | if (fwrite(cb, cblen, 1, pf) != 1) { 890 | ret = -1; 891 | goto fulldl_close_free; 892 | } 893 | if (dblen > 0 && fwrite(db, dblen, 1, pf) != 1) { 894 | ret = -1; 895 | goto fulldl_close_free; 896 | } 897 | if (eblen > 0 && fwrite(eb, eblen, 1, pf) != 1) { 898 | ret = -1; 899 | goto fulldl_close_free; 900 | } 901 | 902 | bsdiff_files++; 903 | bsdiff_newbytes += new_size; 904 | bsdiff_outputbytes += first_block + cblen + dblen + eblen; 905 | 906 | if (cblock_get_enc(encodings) == BSDIFF_ENC_NONE) { 907 | bsdiff_none++; 908 | } 909 | if (dblock_get_enc(encodings) == BSDIFF_ENC_NONE) { 910 | bsdiff_none++; 911 | } 912 | if (eblock_get_enc(encodings) == BSDIFF_ENC_NONE) { 913 | bsdiff_none++; 914 | } 915 | if (cblock_get_enc(encodings) == BSDIFF_ENC_GZIP) { 916 | bsdiff_gzip++; 917 | } 918 | if (dblock_get_enc(encodings) == BSDIFF_ENC_GZIP) { 919 | bsdiff_gzip++; 920 | } 921 | if (eblock_get_enc(encodings) == BSDIFF_ENC_GZIP) { 922 | bsdiff_gzip++; 923 | } 924 | if (cblock_get_enc(encodings) == BSDIFF_ENC_BZIP2) { 925 | bsdiff_bzip2++; 926 | } 927 | if (dblock_get_enc(encodings) == BSDIFF_ENC_BZIP2) { 928 | bsdiff_bzip2++; 929 | } 930 | if (eblock_get_enc(encodings) == BSDIFF_ENC_BZIP2) { 931 | bsdiff_bzip2++; 932 | } 933 | if (cblock_get_enc(encodings) == BSDIFF_ENC_XZ) { 934 | bsdiff_xz++; 935 | } 936 | if (dblock_get_enc(encodings) == BSDIFF_ENC_XZ) { 937 | bsdiff_xz++; 938 | } 939 | if (eblock_get_enc(encodings) == BSDIFF_ENC_XZ) { 940 | bsdiff_xz++; 941 | } 942 | if (dblock_get_enc(encodings) == BSDIFF_ENC_ZEROS) { 943 | bsdiff_zeros++; 944 | } 945 | if (eblock_get_enc(encodings) == BSDIFF_ENC_ZEROS) { 946 | bsdiff_zeros++; 947 | } 948 | 949 | ret = 0; 950 | 951 | fulldl_close_free: 952 | if (fclose(pf)) { 953 | ret = -1; 954 | } 955 | rename(delta_filename_unique, delta_filename); 956 | fulldl_free: 957 | /* Free the memory we used */ 958 | munmap(old_data, old_size); 959 | free(new_data); 960 | free(cb); 961 | free(db); 962 | free(eb); 963 | 964 | return ret; 965 | } 966 | --------------------------------------------------------------------------------