├── 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 |
11 |
12 | Network Error (tcp_error)
13 |
14 |
15 |
16 | |
17 | |
18 |
19 | A communication error occurred: ""
20 |
21 | |
22 | |
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 | |
27 |
28 |
29 |
30 | For assistance, contact your network support team.
31 |
32 | |
33 |
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 |
--------------------------------------------------------------------------------