36 Commits
Author SHA1 Message Date
Robert Dower eb522b1dc9 archive repository 2025-08-07 13:27:50 -07:00
Patrick McCarty 53ce8b6d66 Remove trailing whitespace
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2022-08-30 17:56:16 +00:00
Arjan van de Ven 02b6820a3d deal with two error cases 2022-08-30 14:30:34 +00:00
Patrick McCarty f563c9a475 Fix style issues detected with clang-format v14
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2022-08-30 02:53:29 +00:00
Arjan van de Ven 72a6259228 write to a unique-ish file and then rename at the end to avoid clashes 2022-08-29 18:20:53 -07:00
Patrick McCarty 5c2c4c76ac gitignore: ignore incomplete or extracted dist tarballs
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2021-01-03 21:56:51 +00:00
Patrick McCarty b45ff19ee4 gitignore: ignore more editor swap files 2021-01-03 21:54:05 +00:00
Patrick McCarty fcd3298583 mostlyclean: also remove .i and .s files
These files will be generated by `make` if CFLAGS contains `-save-temps`
or related flags.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2021-01-03 21:53:36 +00:00
Patrick McCarty 0d3d976fe4 distclean: more closely follow automake's guidelines
According to the documentation, `distclean` should delete files that
`configure` built. Since many of the files we have been declaring for
`distclean-local` are generated by `autoreconf`, avoid deleting several
of those files.

Also, note that the `autom4te.cache` directory is deleted by
`maintainer-clean` already. Most of the others are required for a clean
`configure` run.

To restore the tree to a pristine state, use `git clean -xffd`.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2021-01-03 21:43:36 +00:00
Patrick McCarty f192356812 distcheck: fix findstatic.pl runs for vpath build
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2021-01-03 21:42:50 +00:00
Patrick McCarty f81fb5a612 build: add placeholder m4 directory to avoid aclocal warning
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2020-12-30 04:02:54 +00:00
Patrick McCarty 0322f0839c configure: incorporate one more change from autoupdate
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2020-12-30 03:56:29 +00:00
Patrick McCarty 4807cb41f5 configure: address new warning from latest autoreconf
autoreconf from autoconf 2.70 warns that AC_PROG_CC_STDC is obsolete,
recommending to use AC_PROG_CC instead. We already call the latter
macro, and the behavior change appears to have been introduced in
autoconf 2.54 (released 2002-09-13), which is older than our minimum
supported version of 2.66.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2020-12-30 03:46:48 +00:00
Patrick McCarty dd93df7347 Ignore editor swap files
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2020-12-30 03:35:13 +00:00
Patrick McCarty 0d1a46ed6c Fix a code style issue raised by clang-format
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2020-04-10 10:53:44 -07:00
Patrick McCarty a1c17f3e9d Add additional code comments about some magic numbers
These values originate from Colin's paper directly, but I think more
data-driven testing and analysis is needed to determine whether the
values are generally optimal.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-10-09 21:43:32 -07:00
Patrick McCarty 9807f60b46 Rename several more variables and add code comments
The goal with these variables renamings is twofold: to clarify meaning
of the values being used, and to be consistently named for ease of
readability.

For example, the "scan" variable stored the position in the new file
where a match is searched for, and "pos" stored the position in the old
file where a match is found. These variables are now named "new_pos" and
"old_pos" to reflect that they both store position information,
referencing the new and old files, respectively.

Also, declare the renamed variables closer to where they are used, since
the function is quite lengthy already, and we build the code with
-std=gnu99, so this syntax is supported.

Finally, add some (hopefully) helpful code comments to clarify the diff
creation algorithm. The bspatch code in src/patch.c is better documented
than the diff code, so I am starting with the diff code documentation.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-10-09 21:30:45 -07:00
Patrick McCarty e27563e653 Rename oldsize->old_size and newsize->new_size
To align better with variable renamings in the forthcoming commit,
rename the two variables indicating size of the old and new files first.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-10-09 21:30:45 -07:00
Patrick McCarty e0e8bcda58 Release v1.0.4
This release fixes an issue with libbsdiff thread safety. This was a
regression introduced in v1.0.3.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-06-14 14:50:54 -07:00
Patrick McCarty 2e5b80929c Make revised search algorithm thread safe
In commit 85b1c5345b, I introduced a new "max_len" variable, used by the
search() function to store the maximum match length found in course of
the binary search.

However, I overlooked the fact that the search() function then lost
thread safety, as "max_len" would be shared by all threads of a process
using libbsdiff.

Fix the issue by not using a global variable, instead updating "max_len"
via another pointer, just like the "pos" variable is handled already.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-06-14 14:46:52 -07:00
Patrick McCarty 6127bd6095 Release v1.0.3
This release fixes a bug that results in significantly improved diff
creation times in certain cases.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-02-22 18:04:24 -08:00
Patrick McCarty 85b1c5345b Find the longest match in the suffix-sorted array
Fixes #2

The current implementation performs a binary search through the
suffix-sorted array to find byte sequence matches between old and new
files. However, the algorithm is not optimal when it repeatedly matches
very short strings, leading to performance issues as reported in issue
 #2.

This commit changes the algorithm to consider *all* matches encountered
during the binary search and choose the longest of these matches.

The overall performance impact is yet to be determined, but it appears
to yield a small percentage increase in diff creation time (expected)
and a large percentage *decrease* for the case of diffing the files from
issue #2. In my testing, the creation time there decreases from approx
64 minutes to 4.5 seconds.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-02-22 18:01:17 -08:00
Patrick McCarty d039492824 Fix code style
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-12 11:33:09 -07:00
Patrick McCarty 43a817a2e5 build: add 'compliant' makefile target for fixing code style
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-12 11:32:33 -07:00
Patrick McCarty 4e868b4772 Split suffix sort code into a separate source file
To prepare for the possibility of testing other suffix sort algorithms
in the future, split this code into a separate source file for clarity.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-12 11:24:09 -07:00
Patrick McCarty 71b9d7e78a Allow for skipping valgrind use in tests
In case the system valgrind is not functioning properly, allow the test
suite to run without using the tool by setting SKIP_VALGRIND=1 for `make
check`.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-13 10:07:22 -08:00
Patrick McCarty b46a4e2c0f Run clang-format to fix code style
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-13 09:49:33 -08:00
Patrick McCarty 5342235446 Release version 1.0.2
This release enables the functional test suite by default. To disable
the ability to run the test suite and its dependency checks, pass
--disable-tests to configure for the build.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-21 13:25:41 -08:00
Patrick McCarty 8ed6fb38aa Fix distcheck
The remaining test files need to be distributed in order to run the test
suite, and export abs_builddir to the test script to point to the
binaries.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-21 13:25:41 -08:00
Patrick McCarty f23f25a43c Enable travis-ci integration
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-21 10:50:39 -08:00
Patrick McCarty 8654e611cf Use 'readlink -f' instead of 'realpath'
Some distros do not ship the coreutils 'realpath', so use 'readlink -f'
instead, which is equivalent for the usage here.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-21 10:48:23 -08:00
Patrick McCarty fb5ced7c2c configure: fix libcheck minimum version
TAP support was added in libcheck version 0.9.12.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-10-28 17:50:36 -07:00
Patrick McCarty 150cc28bbe configure: disable libcheck test if test build is disabled
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-10-28 17:48:22 -07:00
Patrick McCarty 8c0a87b7c9 Convert functional test suite to TAP
Automake provides a nice test harness that can parse TAP results, so
switch to using that.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-26 15:46:30 -08:00
Patrick McCarty 253dcbbbc0 Provide configure option to disable tests
Building the tests is enabled by default, but because the test suite
requires root privileges to run at the moment, people should have to
option to disable the tests entirely.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-26 15:44:21 -08:00
Patrick McCarty a11afb9d3b Cleanups for building with test coverage
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-26 15:42:59 -08:00
14 changed files with 698 additions and 433 deletions
+15 -2
View File
@@ -1,4 +1,8 @@
*~
*.swp
*.o
*.i
*.s
.libs/
*.lo
*.pc
@@ -13,15 +17,24 @@ bsdump
bspatch
compile
configure
coverage/
depcomp
install-sh
libbsdiff.la
libtool
ltmain.sh
m4/
m4/*
!m4/.gitignore
missing
src/.dirstamp
src/*.gcda
src/*.gcno
stamp-h1
bsdiff-*.tar.xz
/bsdiff-*.tar.xz
/bsdiff-*/
test/*.diff
test/*.out
test/*.log
test/*.trs
tap-driver.sh
test-suite.log
+20
View File
@@ -0,0 +1,20 @@
sudo: required
dist: trusty
language: c
# Pre-install missing build dependencies:
# - valgrind (from the repo)
# - libcheck 0.9.10 is slightly too old, since 0.9.12 adds TAP support
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y valgrind
install:
- wget http://downloads.sourceforge.net/project/check/check/0.10.0/check-0.10.0.tar.gz
- tar -xvf check-0.10.0.tar.gz
- pushd check-0.10.0 && ./configure --prefix=/usr && make -j48 && sudo make install && popd
# Ubuntu's default umask is 0002, but tests are written with the expectation of a 0022 default.
script:
- autoreconf --verbose --warnings=none --install --force && ./configure && make -j48 && sudo sh -c 'umask 0022 && make -j48 check'
after_failure: cat test-suite.log
+81 -6
View File
@@ -28,11 +28,15 @@ EXTRA_DIST = \
AUTOMAKE_OPTIONS = color-tests parallel-tests
if COVERAGE
coverage:
AM_CFLAGS += --coverage
coverage: coverage-clean
mkdir -p coverage
lcov --compat-libtool --directory . --capture --output-file coverage/report
genhtml -o coverage/ coverage/report
AM_CFLAGS += --coverage
coverage-clean:
rm -rf coverage
endif
bin_PROGRAMS = \
@@ -63,7 +67,8 @@ lib_LTLIBRARIES = \
libbsdiff_la_SOURCES = \
src/diff.c \
src/patch.c
src/patch.c \
src/sufsort.c
libbsdiff_la_LIBADD = \
$(zlib_LIBS)
@@ -92,13 +97,83 @@ libbsdiff_la_LDFLAGS = \
-version-info $(LIBBSDIFF_CURRENT):$(LIBBSDIFF_REVISION):$(LIBBSDIFF_AGE) \
-Wl,--version-script=$(top_srcdir)/src/bsdiff.sym
mostlyclean-local:
-rm -f *.i
-rm -f *.s
distclean-local:
rm -rf aclocal.m4 ar-lib autom4te.cache config.guess config.h.in config.h.in~ config.sub configure depcomp install-sh ltmain.sh m4 Makefile.in missing compile
-rm -f config.guess~
-rm -f config.h.in~
-rm -f config.sub~
-rm -f configure~
install-exec-hook:
perl findstatic.pl */*.o | grep -v Checking ||:
perl $(top_srcdir)/findstatic.pl $(top_builddir)/src/*.o | grep -v Checking || :
check_PROGRAMS =
TEST_EXTENSIONS = .sh
EXTRA_DIST += \
test/data/5.bspatch.diff \
test/data/5.bspatch.original \
test/data/6.bspatch.diff \
test/data/6.bspatch.original \
test/data/7.bspatch.diff \
test/data/7.bspatch.original \
test/data/8.bspatch.diff \
test/data/8.bspatch.original \
test/data/9.bspatch.diff \
test/data/9.bspatch.modified \
test/data/9.bspatch.original \
test/data/10.bspatch.diff \
test/data/10.bspatch.modified \
test/data/10.bspatch.original \
test/data/11.bspatch.diff \
test/data/12.bspatch.diff \
test/data/12.bspatch.modified \
test/data/12.bspatch.original \
test/data/13.bspatch.modified \
test/data/13.bspatch.original \
test/data/14.bspatch.modified \
test/data/14.bspatch.original \
test/data/15.bspatch.modified \
test/data/15.bspatch.original \
test/data/16.bspatch.diff \
test/data/16.bspatch.original
if ENABLE_TESTS
AM_TESTS_ENVIRONMENT = \
abs_builddir=$(abs_builddir); export abs_builddir;
tap_driver = env AM_TAP_AWK='$(AWK)' $(SHELL) \
$(top_srcdir)/tap-driver.sh
LOG_DRIVER = $(tap_driver)
SH_LOG_DRIVER = $(tap_driver)
TESTS = $(dist_check_SCRIPTS)
dist_check_SCRIPTS = \
test/run.sh
endif
compliant:
@git diff --quiet --exit-code include src; ret=$$?; \
if [ $$ret -eq 1 ]; then \
echo "Error: can only check code style when include/ and src/ are clean."; \
echo "Stash or commit your changes and try again."; \
exit $$ret; \
elif [ $$ret -gt 1 ]; then \
exit $$ret; \
fi; \
clang-format -i -style=file include/*.h src/*.c; ret=$$?; \
if [ $$ret -ne 0 ]; then \
exit $$ret; \
fi; \
git diff --quiet --exit-code include src; ret=$$?; \
if [ $$ret -eq 1 ]; then \
echo "Code style issues found. Run 'git diff' to view issues."; \
elif [ $$ret -eq 0 ]; then \
echo "No code style issues found."; \
fi; \
exit $$ret
release:
@git rev-parse v$(PACKAGE_VERSION) &> /dev/null; \
+7
View File
@@ -0,0 +1,7 @@
## DISCONTINUATION OF PROJECT.
This project will no longer be maintained by Intel.
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.
Contact: webadmin@linux.intel.com
+15 -3
View File
@@ -1,8 +1,7 @@
AC_PREREQ([2.66])
AC_INIT([bsdiff], [1.0.1], [patrick.mccarty@intel.com])
AC_INIT([bsdiff],[1.0.4],[patrick.mccarty@intel.com])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
AC_PROG_CC_STDC
AC_LANG(C)
AC_CONFIG_HEADERS([config.h])
AC_PREFIX_DEFAULT(/usr/local)
@@ -14,7 +13,6 @@ AM_SILENT_RULES([yes])
LT_INIT
PKG_CHECK_MODULES([zlib], [zlib])
PKG_CHECK_MODULES([CHECK], [check >= 0.9])
AC_ARG_ENABLE([bzip2],
[AS_HELP_STRING([--disable-bzip2],[Do not use bzip2 compression (uses bzip2 by default)])])
@@ -33,6 +31,11 @@ AS_IF([test "$enable_lzma" != "no"], [
])
AM_CONDITIONAL([ENABLE_LZMA], [test "$enable_lzma" != "no"])
AC_ARG_ENABLE(
[tests],
[AS_HELP_STRING([--disable-tests], [Do not enable functional tests (enabled by default)])]
)
have_coverage=no
AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage], [enable test coverage]))
if test "x$enable_coverage" = "xyes" ; then
@@ -52,8 +55,17 @@ if test "x$enable_coverage" = "xyes" ; then
fi
AM_CONDITIONAL([COVERAGE], [test "$have_coverage" = "yes"])
AS_IF([test "$enable_tests" != "no"], [
PKG_CHECK_MODULES([CHECK], [check >= 0.9.12])
AC_PATH_PROG([have_valgrind], [valgrind])
AS_IF([test -z "${have_valgrind}"], [
AC_MSG_ERROR([Must have valgrind installed to run functional tests])
])
])
AM_CONDITIONAL([ENABLE_TESTS], [test "$enable_tests" != "no"])
AC_CONFIG_FILES([Makefile data/bsdiff.pc])
AC_REQUIRE_AUX_FILE([tap-driver.sh])
AC_OUTPUT
AC_MSG_RESULT([
View File
+4 -1
View File
@@ -2,6 +2,7 @@
#define __INCLUDE_GUARD_BSHEADER_H
#include <stdint.h>
#include <sys/types.h> // for u_char
#include "bsdiff.h"
@@ -58,7 +59,7 @@ struct header_v20 {
uint64_t extra_length;
uint64_t old_file_length;
uint64_t new_file_length;
uint64_t mtime; /* unused */
uint64_t mtime; /* unused */
uint32_t file_mode;
uint32_t file_owner;
uint32_t file_group;
@@ -177,4 +178,6 @@ static inline int eblock_get_enc(enc_flags_t enc)
}
}
int qsufsort(int64_t *, int64_t *, u_char *, int64_t);
#endif
+236 -321
View File
@@ -3,7 +3,7 @@
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
@@ -32,8 +32,6 @@ __FBSDID
#define _GNU_SOURCE
#include "config.h"
#include <sys/types.h>
#ifdef BSDIFF_WITH_BZIP2
#include <bzlib.h>
#endif
@@ -45,20 +43,20 @@ __FBSDID
#include <lzma.h>
#endif
#include <assert.h>
#include <endian.h>
#include <grp.h>
#include <pthread.h>
#include <pwd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <zlib.h>
#include <endian.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <pthread.h>
#include <assert.h>
#include <sys/mman.h>
#include "bsheader.h"
@@ -76,178 +74,12 @@ static int bsdiff_fulldl;
#undef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
/* NOTES:
* I and V are chunks of memory (arrays) with length = (oldfile size +1) * sizeof(int64_t).
* Additionally, we pass in arraylen now. The parent function qsufsort receives it, so it
* should be available here as well for error checking.
* start: is actually the point in the array sent in during the suffix sort, which sorts by
* small blocks/chunks.
* len: refers to the length of the current chunk being processed - NOT the array length(s).
* h: will never be more than 8, and increases by *2 during suffix sort (h += h) */
static void split(int64_t *I, int64_t *V, int64_t arraylen, int64_t start, int64_t len,
int64_t h)
{
int64_t i, j, k, x, tmp, jj, kk;
if (len < 16) {
for (k = start; k < start + len; k += j) {
j = 1;
x = V[I[k] + h];
for (i = 1; k + i < start + len; i++) {
if (V[I[k + i] + h] < x) {
x = V[I[k + i] + h];
j = 0;
}
if (V[I[k + i] + h] == x) {
tmp = I[k + j];
I[k + j] = I[k + i];
I[k + i] = tmp;
j++;
}
}
for (i = 0; i < j; i++) {
V[I[k + i]] = k + j - 1;
}
if (j == 1) {
I[k] = -1;
}
}
return;
}
x = V[I[start + len / 2] + h];
jj = 0;
kk = 0;
for (i = start; i < start + len; i++) {
if (V[I[i] + h] < x) {
jj++;
}
if (V[I[i] + h] == x) {
kk++;
}
}
jj += start;
kk += jj;
i = start;
j = 0;
k = 0;
while (i < jj) {
if (V[I[i] + h] < x) {
i++;
} else if (V[I[i] + h] == x) {
tmp = I[i];
I[i] = I[jj + j];
I[jj + j] = tmp;
j++;
} else {
tmp = I[i];
I[i] = I[kk + k];
I[kk + k] = tmp;
k++;
}
}
while (jj + j < kk) {
if (V[I[jj + j] + h] == x) {
j++;
} else {
tmp = I[jj + j];
I[jj + j] = I[kk + k];
I[kk + k] = tmp;
k++;
}
}
if (jj > start) {
split(I, V, arraylen, start, jj - start, h);
}
for (i = 0; i < kk - jj; i++) {
V[I[jj + i]] = kk - 1;
}
if (jj == kk - 1) {
I[jj] = -1;
}
if (start + len > kk) {
split(I, V, arraylen, kk, start + len - kk, h);
}
}
/* The old_data (previous file data) is passed into this suffix sort and sorted
* accordingly using the I and V arrays, which are both of length oldsize +1. */
static int qsufsort(int64_t *I, int64_t *V, u_char *old, int64_t oldsize)
{
int64_t buckets[QSUF_BUCKET_SIZE];
int64_t i, h, len;
for (i = 0; i < QSUF_BUCKET_SIZE; i++) {
buckets[i] = 0;
}
for (i = 0; i < oldsize; i++) {
buckets[old[i]]++;
}
for (i = 1; i < QSUF_BUCKET_SIZE; i++) {
buckets[i] += buckets[i - 1];
}
for (i = QSUF_BUCKET_SIZE - 1; i > 0; i--) {
buckets[i] = buckets[i - 1];
}
buckets[0] = 0;
for (i = 0; i < oldsize; i++) {
if (buckets[old[i]] > oldsize + 1) {
return -1;
}
I[++buckets[old[i]]] = i;
}
for (i = 0; i < oldsize; i++) {
V[i] = buckets[old[i]];
}
V[oldsize] = 0;
for (i = 1; i < QSUF_BUCKET_SIZE; i++) {
if (buckets[i] == buckets[i - 1] + 1) {
I[buckets[i]] = -1;
}
}
I[0] = -1;
for (h = 1; I[0] != -(oldsize + 1); h += h) {
len = 0;
for (i = 0; i < oldsize + 1;) {
if (I[i] < 0) {
len -= I[i];
i -= I[i];
} else {
if (len) {
I[i - len] = -len;
}
len = V[I[i]] + 1 - i;
split(I, V, oldsize, i, len, h);
i += len;
len = 0;
}
}
if (len) {
I[i - len] = -len;
}
}
for (i = 0; i < oldsize + 1; i++) {
I[V[i]] = i;
}
return 0;
}
static int64_t matchlen(u_char *old, int64_t oldsize, u_char *new,
int64_t newsize)
static int64_t matchlen(u_char *old, int64_t old_size, u_char *new,
int64_t new_size)
{
int64_t i;
for (i = 0; (i < oldsize) && (i < newsize); i++) {
for (i = 0; (i < old_size) && (i < new_size); i++) {
if (old[i] != new[i]) {
break;
}
@@ -256,30 +88,60 @@ static int64_t matchlen(u_char *old, int64_t oldsize, u_char *new,
return i;
}
static int64_t search(int64_t *I, u_char *old, int64_t oldsize,
u_char *new, int64_t newsize, int64_t st, int64_t en,
int64_t *pos)
/**
* Finds the longest matching array of bytes between the OLD and NEW file. The
* old file is suffix-sorted; the suffix-sorted array is stored at I, and
* indices to search between are indicated by ST (start) and EN (end). The
* function does not return a value, but once a match is determined, OLD_POS is
* updated to the position of the match within OLD, and MAX_LEN is set to the
* match length.
*/
static void search(int64_t *I, u_char *old, int64_t old_size,
u_char *new, int64_t new_size, int64_t st, int64_t en,
int64_t *old_pos, int64_t *max_len)
{
int64_t x, y;
if (en - st < 2) {
x = matchlen(old + I[st], oldsize - I[st], new, newsize);
y = matchlen(old + I[en], oldsize - I[en], new, newsize);
/* Initialize max_len for the binary search */
if (st == 0 && en == old_size) {
*max_len = matchlen(old, old_size, new, new_size);
*old_pos = I[st];
}
if (x > y) {
*pos = I[st];
return x;
} else {
*pos = I[en];
return y;
/* The binary search terminates here when "en" and "st" are adjacent
* indices in the suffix-sorted array. */
if (en - st < 2) {
x = matchlen(old + I[st], old_size - I[st], new, new_size);
if (x > *max_len) {
*max_len = x;
*old_pos = I[st];
}
y = matchlen(old + I[en], old_size - I[en], new, new_size);
if (y > *max_len) {
*max_len = y;
*old_pos = I[en];
}
return;
}
x = st + (en - st) / 2;
if (memcmp(old + I[x], new, MIN(oldsize - I[x], newsize)) < 0) {
return search(I, old, oldsize, new, newsize, x, en, pos);
int64_t length = MIN(old_size - I[x], new_size);
u_char *oldoffset = old + I[x];
/* This match *could* be the longest one, so check for that here */
int64_t tmp = matchlen(oldoffset, length, new, length);
if (tmp > *max_len) {
*max_len = tmp;
*old_pos = I[x];
}
/* Determine how to continue the binary search */
if (memcmp(oldoffset, new, length) < 0) {
return search(I, old, old_size, new, new_size, x, en, old_pos, max_len);
} else {
return search(I, old, oldsize, new, newsize, st, x, pos);
return search(I, old, old_size, new, new_size, st, x, old_pos, max_len);
}
}
@@ -524,16 +386,8 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
{
int fd, efd;
u_char *old_data, *new_data;
int64_t oldsize, newsize;
int64_t old_size, new_size;
int64_t *I, *V;
int64_t scan;
int64_t pos = 0;
int64_t len;
int64_t lastscan, lastpos, lastoffset;
int64_t oldscore, scsc;
int64_t s, Sf, lenf, Sb, lenb;
int64_t overlap, Ss, lens;
int64_t i;
uint64_t cblen, dblen, eblen;
u_char *cb, *db, *eb;
struct stat new_stat;
@@ -542,9 +396,12 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
off_t first_block;
int c_enc, d_enc, e_enc;
enc_flags_t encodings;
char delta_filename_unique[2 * PATH_MAX];
struct header_v20 large_header;
struct header_v21 small_header;
sprintf(delta_filename_unique, "%s.%i", delta_filename, getpid());
FILE *pf;
ret = lstat(old_filename, &old_stat);
@@ -557,6 +414,8 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
return -1;
}
ret = 0;
if (S_ISDIR(new_stat.st_mode) || S_ISDIR(old_stat.st_mode)) {
/* no delta on symlinks ! */
return -1;
@@ -577,16 +436,16 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
return -1;
}
oldsize = old_stat.st_size;
old_size = old_stat.st_size;
/* We may start with an empty file, if so, just mark it for full download
* to throw into the pack. In the case that newfile is <200, it will quit
* and ask for fulldownload, so we only need to check oldsize */
if (oldsize == 0) {
* and ask for fulldownload, so we only need to check old_size */
if (old_size == 0) {
memset(&small_header, 0, sizeof(struct header_v21));
memcpy(&small_header.magic, BSDIFF_HDR_FULLDL, 8);
efd = open(delta_filename, O_CREAT | O_EXCL | O_WRONLY, 00644);
efd = open(delta_filename_unique, O_CREAT | O_EXCL | O_WRONLY, 00644);
if (efd < 0) {
close(fd);
return -1;
@@ -603,15 +462,16 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
}
fclose(pf);
close(fd);
rename(delta_filename_unique, delta_filename);
return 1;
}
/* TODO: investigate why this needs to be +1 to not overrun; coverity complains
* that we overrun old_data when we calculate differences otherwise. Tenatively,
* since this is used in qsufsort, it may need to be +1 like I and V because of
* a sentinel byte when sorting. However, newsize does not cause any overruns
* a sentinel byte when sorting. However, new_size does not cause any overruns
* when created with the regular file size */
old_data = mmap(NULL, oldsize + 1, PROT_READ, MAP_SHARED, fd, 0);
old_data = mmap(NULL, old_size + 1, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
if (old_data == MAP_FAILED) {
@@ -621,19 +481,19 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
/* These arrays are size + 1 because suffix sort needs space for the
* data + 1 sentinel element to actually do the sorting. Not because
* oldsize might be 0. */
if ((I = malloc((oldsize + 1) * sizeof(int64_t))) == NULL) {
munmap(old_data, oldsize);
* old_size might be 0. */
if ((I = malloc((old_size + 1) * sizeof(int64_t))) == NULL) {
munmap(old_data, old_size);
return -1;
}
if ((V = malloc((oldsize + 1) * sizeof(int64_t))) == NULL) {
munmap(old_data, oldsize);
if ((V = malloc((old_size + 1) * sizeof(int64_t))) == NULL) {
munmap(old_data, old_size);
free(I);
return -1;
}
if (qsufsort(I, V, old_data, oldsize) != 0) {
munmap(old_data, oldsize);
if (qsufsort(I, V, old_data, old_size) != 0) {
munmap(old_data, old_size);
free(I);
free(V);
return -1;
@@ -642,19 +502,19 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
free(V);
if ((fd = open(new_filename, O_RDONLY, 0)) < 0) {
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(I);
return -1;
}
if (fstat(fd, &new_stat) != 0) {
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(I);
close(fd);
return -1;
}
newsize = new_stat.st_size;
new_size = new_stat.st_size;
/* Note: testing this to see how diffs between small files affect
* updates. Small files seem to cause some problems between certain
@@ -664,76 +524,77 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
* the "is bsdiff < 90% of newfile size" check that would otherwise
* be performed later on.
*/
if (newsize < 200) {
if (new_size < 200) {
memset(&small_header, 0, sizeof(struct header_v21));
memcpy(&small_header.magic, BSDIFF_HDR_FULLDL, 8);
efd = open(delta_filename, O_CREAT | O_EXCL | O_WRONLY, 00644);
efd = open(delta_filename_unique, O_CREAT | O_EXCL | O_WRONLY, 00644);
if (efd < 0) {
close(fd);
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(I);
return -1;
}
if ((pf = fdopen(efd, "w")) == NULL) {
close(efd);
close(fd);
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(I);
return -1;
}
if (fwrite(&small_header, 8, 1, pf) != 1) {
fclose(pf);
close(fd);
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(I);
return -1;
}
fclose(pf);
close(fd);
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(I);
rename(delta_filename_unique, delta_filename);
return 1;
}
if ((new_data = malloc(newsize)) == NULL) {
if ((new_data = malloc(new_size)) == NULL) {
close(fd);
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(I);
return -1;
}
if (pread(fd, new_data, newsize, 0) != newsize) {
if (pread(fd, new_data, new_size, 0) != new_size) {
close(fd);
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(new_data);
free(I);
return -1;
}
if (close(fd) == -1) {
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(new_data);
free(I);
return -1;
}
/* we can write 3 8 byte tupples extra, so allocate some headroom */
if ((cb = malloc(newsize + 25)) == NULL) {
munmap(old_data, oldsize);
if ((cb = malloc(new_size + 25)) == NULL) {
munmap(old_data, old_size);
free(new_data);
free(I);
return -1;
}
if ((db = malloc(newsize + 25)) == NULL) {
munmap(old_data, oldsize);
if ((db = malloc(new_size + 25)) == NULL) {
munmap(old_data, old_size);
free(new_data);
free(cb);
free(I);
return -1;
}
if ((eb = malloc(newsize + 25)) == NULL) {
munmap(old_data, oldsize);
if ((eb = malloc(new_size + 25)) == NULL) {
munmap(old_data, old_size);
free(new_data);
free(cb);
free(db);
@@ -745,109 +606,152 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
eblen = 0;
/* Compute the differences */
scan = 0;
len = 0;
lastscan = 0;
lastpos = 0;
lastoffset = 0;
while (scan < newsize) {
oldscore = 0;
int64_t new_pos = 0;
int64_t old_pos = 0;
int64_t match_len = 0;
int64_t last_new_pos = 0;
int64_t last_old_pos = 0;
int64_t last_offset = 0;
while (new_pos < new_size) {
// Find an exact match between old and new files, and require
// that more than 8 of the matching bytes "mismatch" from the
// previous exact match. A score (old_score) is used to track
// how many bytes match starting from new_pos in new, and from
// old_pos in the previous iteration.
// NOTE: the magic value 8 is a heuristic; further testing is
// needed to prove whether this is the best number, or if the
// number should vary according to other factors, etc.
int64_t old_score = 0;
int64_t new_peek;
for (new_peek = new_pos += match_len; new_pos < new_size; new_pos++) {
search(I, old_data, old_size, new_data + new_pos, new_size - new_pos,
0, old_size, &old_pos, &match_len);
for (scsc = scan += len; scan < newsize; scan++) {
len =
search(I, old_data, oldsize, new_data + scan, newsize - scan,
0, oldsize, &pos);
for (; scsc < scan + len; scsc++) {
if ((scsc + lastoffset < oldsize) &&
(old_data[scsc + lastoffset] == new_data[scsc])) {
oldscore++;
for (; new_peek < new_pos + match_len; new_peek++) {
if ((new_peek + last_offset < old_size) &&
(old_data[new_peek + last_offset] == new_data[new_peek])) {
old_score++;
}
}
if (((len == oldscore) && (len != 0)) ||
(len > oldscore + 8)) {
if (((match_len == old_score) && (match_len != 0)) ||
(match_len > old_score + 8)) {
break;
}
if ((scan + lastoffset < oldsize) &&
(old_data[scan + lastoffset] == new_data[scan])) {
oldscore--;
// Before beginning the next loop iteration, decrement
// old_score if needed, since new_pos will be
// incremented.
if ((new_pos + last_offset < old_size) &&
(old_data[new_pos + last_offset] == new_data[new_pos])) {
old_score--;
}
}
if ((len != oldscore) || (scan == newsize)) {
s = 0;
Sf = 0;
lenf = 0;
for (i = 0;
(lastscan + i < scan) && (lastpos + i < oldsize);) {
if (old_data[lastpos + i] == new_data[lastscan + i]) {
s++;
if ((match_len != old_score) || (new_pos == new_size)) {
int64_t bytes = 0, max = 0;
// Compute the length of a fuzzy match starting from
// the beginning of the fuzzy match recorded at the end
// of the previous iteration (i.e. len_fuzzybackward
// less than the previous match positions). At least
// half of the bytes match between old and new. This
// fuzzy match will be used to construct a diff string
// in the diff block.
// NOTE: "at least half matching bytes" is a heuristic
// for both fuzzy regions being constructed below;
// further testing is needed to prove whether this is
// the best percentage, or if the percentage should
// vary according to other factors, etc.
int64_t len_fuzzyforward = 0;
for (int64_t i = 0;
(last_new_pos + i < new_pos) && (last_old_pos + i < old_size);) {
if (old_data[last_old_pos + i] == new_data[last_new_pos + i]) {
bytes++;
}
i++;
if (s * 2 - i > Sf * 2 - lenf) {
Sf = s;
lenf = i;
if (bytes * 2 - i > max * 2 - len_fuzzyforward) {
max = bytes;
len_fuzzyforward = i;
}
}
lenb = 0;
if (scan < newsize) {
s = 0;
Sb = 0;
for (i = 1;
(scan >= lastscan + i) && (pos >= i);
// Compute the length of a fuzzy match ending at the
// current positions in old and new files (old_pos and
// new_pos). At least half of the bytes match between
// old and new. This fuzzy match will be used for the
// next iteration.
int64_t len_fuzzybackward = 0;
if (new_pos < new_size) {
bytes = 0;
max = 0;
for (int64_t i = 1;
(new_pos >= last_new_pos + i) && (old_pos >= i);
i++) {
if (old_data[pos - i] == new_data[scan - i]) {
s++;
if (old_data[old_pos - i] == new_data[new_pos - i]) {
bytes++;
}
if (s * 2 - i > Sb * 2 - lenb) {
Sb = s;
lenb = i;
if (bytes * 2 - i > max * 2 - len_fuzzybackward) {
max = bytes;
len_fuzzybackward = i;
}
}
}
if (lastscan + lenf > scan - lenb) {
overlap = (lastscan + lenf) - (scan - lenb);
s = 0;
Ss = 0;
lens = 0;
for (i = 0; i < overlap; i++) {
if (new_data[lastscan + lenf - overlap + i] ==
old_data[lastpos + lenf - overlap + i]) {
s++;
// If there is an overlap between len_fuzzyforward and
// len_fuzzybackward in the new file, that overlap must
// be eliminated.
if (last_new_pos + len_fuzzyforward > new_pos - len_fuzzybackward) {
bytes = 0;
max = 0;
int64_t overlap = (last_new_pos + len_fuzzyforward) - (new_pos - len_fuzzybackward);
int64_t len_fuzzyshift = 0;
// Scan the overlap area for differences
// between old and new. If any mismatching
// bytes are found, extend len_fuzzyforward to
// cover those bytes, because we want them
// included in the diff block.
for (int64_t i = 0; i < overlap; i++) {
if (new_data[last_new_pos + len_fuzzyforward - overlap + i] ==
old_data[last_old_pos + len_fuzzyforward - overlap + i]) {
bytes++;
}
if (new_data[scan - lenb + i] ==
old_data[pos - lenb + i]) {
s--;
if (new_data[new_pos - len_fuzzybackward + i] ==
old_data[old_pos - len_fuzzybackward + i]) {
bytes--;
}
if (s > Ss) {
Ss = s;
lens = i + 1;
if (bytes > max) {
max = bytes;
len_fuzzyshift = i + 1;
}
}
lenf += lens - overlap;
lenb -= lens;
len_fuzzyforward += len_fuzzyshift - overlap;
len_fuzzybackward -= len_fuzzyshift;
}
for (i = 0; i < lenf; i++) {
// Set the diff string in the diff block. For each byte
// in the fuzzy forward region, the byte from old is
// subtracted from new. When applying the delta (with
// bspatch) this operation is reversed, by performing
// additions.
for (int64_t i = 0; i < len_fuzzyforward; i++) {
db[dblen + i] =
new_data[lastscan + i] - old_data[lastpos + i];
new_data[last_new_pos + i] - old_data[last_old_pos + i];
}
for (i = 0; i < (scan - lenb) - (lastscan + lenf); i++) {
eb[eblen + i] = new_data[lastscan + lenf + i];
// Set the extra string in the extra block. The
// contents are the bytes in new file between the fuzzy
// forward and fuzzy backward regions.
for (int64_t i = 0; i < (new_pos - len_fuzzybackward) - (last_new_pos + len_fuzzyforward); i++) {
eb[eblen + i] = new_data[last_new_pos + len_fuzzyforward + i];
}
dblen += lenf;
eblen += (scan - lenb) - (lastscan + lenf);
dblen += len_fuzzyforward;
eblen += (new_pos - len_fuzzybackward) - (last_new_pos + len_fuzzyforward);
/* checking for control block overflow...
* See regression test #15 for an example */
if ((int64_t)(cblen + 24) > (newsize + 25)) {
munmap(old_data, oldsize);
if ((int64_t)(cblen + 24) > (new_size + 25)) {
munmap(old_data, old_size);
free(new_data);
free(cb);
free(db);
@@ -856,18 +760,28 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
return -1;
}
offtout(lenf, cb + cblen);
// Set three values in the control block:
// 1. ADD instruction (value: length of the diff
// string). It uses the offset of the third control
// block value from the previous iteration.
// 2. INSERT instruction (value: length of the extra
// string)
// 3. offset in old file for the next ADD instruction
offtout(len_fuzzyforward, cb + cblen);
cblen += 8;
offtout((scan - lenb) - (lastscan + lenf), cb + cblen);
offtout((new_pos - len_fuzzybackward) - (last_new_pos + len_fuzzyforward), cb + cblen);
cblen += 8;
offtout((pos - lenb) - (lastpos + lenf), cb + cblen);
offtout((old_pos - len_fuzzybackward) - (last_old_pos + len_fuzzyforward), cb + cblen);
cblen += 8;
lastscan = scan - lenb;
lastpos = pos - lenb;
lastoffset = pos - scan;
// Save old/new file positions to the beginning of the
// fuzzy backward region, since the next fuzzy forward
// region will be calculated from that point.
last_new_pos = new_pos - len_fuzzybackward;
last_old_pos = old_pos - len_fuzzybackward;
last_offset = old_pos - new_pos;
}
}
free(I);
@@ -883,7 +797,7 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
/* Create the patch file */
efd = open(delta_filename, O_CREAT | O_EXCL | O_WRONLY, 00644);
efd = open(delta_filename_unique, O_CREAT | O_EXCL | O_WRONLY, 00644);
if (efd < 0) {
ret = -1;
goto fulldl_free;
@@ -905,8 +819,8 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
small_header.control_length = cblen;
small_header.diff_length = dblen;
small_header.extra_length = eblen;
small_header.old_file_length = oldsize;
small_header.new_file_length = newsize;
small_header.old_file_length = old_size;
small_header.new_file_length = new_size;
small_header.file_mode = new_stat.st_mode;
small_header.file_owner = new_stat.st_uid;
small_header.file_group = new_stat.st_gid;
@@ -916,7 +830,7 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
eblock_set_enc(&small_header.encoding, e_enc);
encodings = small_header.encoding;
if ((first_block + cblen + dblen + eblen > 0.90 * newsize) && (enc != BSDIFF_ENC_NONE)) { /* tune */
if ((first_block + cblen + dblen + eblen > 0.90 * new_size) && (enc != BSDIFF_ENC_NONE)) { /* tune */
memcpy(&small_header.magic, BSDIFF_HDR_FULLDL, 8);
ret = 1;
if (fwrite(&small_header, 8, 1, pf) != 1) {
@@ -944,8 +858,8 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
large_header.control_length = cblen;
large_header.diff_length = dblen;
large_header.extra_length = eblen;
large_header.old_file_length = oldsize;
large_header.new_file_length = newsize;
large_header.old_file_length = old_size;
large_header.new_file_length = new_size;
large_header.file_mode = new_stat.st_mode;
large_header.file_owner = new_stat.st_uid;
large_header.file_group = new_stat.st_gid;
@@ -955,7 +869,7 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
eblock_set_enc(&large_header.encoding, e_enc);
encodings = large_header.encoding;
if ((first_block + cblen + dblen + eblen > 0.90 * newsize) && (enc != BSDIFF_ENC_NONE)) { /* tune */
if ((first_block + cblen + dblen + eblen > 0.90 * new_size) && (enc != BSDIFF_ENC_NONE)) { /* tune */
memcpy(&large_header.magic, BSDIFF_HDR_FULLDL, 8);
ret = 1;
if (fwrite(&large_header, 8, 1, pf) != 1) {
@@ -986,7 +900,7 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
}
bsdiff_files++;
bsdiff_newbytes += newsize;
bsdiff_newbytes += new_size;
bsdiff_outputbytes += first_block + cblen + dblen + eblen;
if (cblock_get_enc(encodings) == BSDIFF_ENC_NONE) {
@@ -1038,9 +952,10 @@ fulldl_close_free:
if (fclose(pf)) {
ret = -1;
}
rename(delta_filename_unique, delta_filename);
fulldl_free:
/* Free the memory we used */
munmap(old_data, oldsize);
munmap(old_data, old_size);
free(new_data);
free(cb);
free(db);
+1 -1
View File
@@ -4,7 +4,7 @@
* Copyright © 2012-2016 Intel Corporation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
+4 -4
View File
@@ -4,7 +4,7 @@
* Copyright © 2012-2016 Intel Corporation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
@@ -30,12 +30,12 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#include "bsdiff.h"
#include "bsheader.h"
@@ -110,7 +110,7 @@ static void print_v20_header(struct header_v20 *h, FILE *f)
if (h->mtime == 0) {
printf("Mtime:\t(not set, as expected)\n");
} else {
printf("Mtime:\t%s (probably means there is a bug)\n", ctime((const time_t*)&h->mtime));
printf("Mtime:\t%s (probably means there is a bug)\n", ctime((const time_t *)&h->mtime));
}
printf("Mode:\t%4o\n", h->file_mode);
printf("Uid:\t%d\n", h->file_owner);
+48 -48
View File
@@ -3,7 +3,7 @@
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
@@ -44,23 +44,23 @@ __FBSDID
#include <lzma.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <zlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/fs.h>
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <linux/fs.h>
#include <pwd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <zlib.h>
#include "bsheader.h"
@@ -254,12 +254,12 @@ static size_t xzread(xzfile *xzf, u_char *buf, size_t len, lzma_ret *err)
typedef struct {
FILE *f; /* method = NONE, BZIP2, ZEROS */
int fd; /* method = BZIP2 */
int fd; /* method = BZIP2 */
union {
#ifdef BSDIFF_WITH_BZIP2
BZFILE *bz2; /* method = BZIP2 */
#endif
gzFile gz; /* method = GZIP */
gzFile gz; /* method = GZIP */
#ifdef BSDIFF_WITH_LZMA
xzfile *xz; /* method = XZ */
#endif
@@ -539,12 +539,12 @@ static int apply_delta_v2(int subver, FILE *f,
cfile cf, df, ef;
unsigned char *old_data = NULL, *new_data;
unsigned char buf[8];
off_t oldpos, newpos;
off_t old_pos, new_pos;
int64_t ctrl[3];
int i, ret, fd;
off_t data_offset;
off_t ctrllen, difflen, extralen;
off_t oldsize, newsize;
off_t old_size, new_size;
mode_t mode;
uid_t uid;
gid_t gid;
@@ -561,8 +561,8 @@ static int apply_delta_v2(int subver, FILE *f,
ctrllen = header.control_length;
difflen = header.diff_length;
extralen = header.extra_length;
oldsize = header.old_file_length;
newsize = header.new_file_length;
old_size = header.old_file_length;
new_size = header.new_file_length;
mode = header.file_mode;
uid = header.file_owner;
gid = header.file_group;
@@ -576,8 +576,8 @@ static int apply_delta_v2(int subver, FILE *f,
ctrllen = header.control_length;
difflen = header.diff_length;
extralen = header.extra_length;
oldsize = header.old_file_length;
newsize = header.new_file_length;
old_size = header.old_file_length;
new_size = header.new_file_length;
mode = header.file_mode;
uid = header.file_owner;
gid = header.file_group;
@@ -588,7 +588,7 @@ static int apply_delta_v2(int subver, FILE *f,
if ((ret = check_header(f, encoding,
ctrllen, difflen, extralen,
oldsize, newsize, data_offset)) < 0) {
old_size, new_size, data_offset)) < 0) {
return ret;
}
@@ -597,29 +597,29 @@ static int apply_delta_v2(int subver, FILE *f,
return ret;
}
ret = read_file(old_filename, &old_data, oldsize);
ret = read_file(old_filename, &old_data, old_size);
if (ret < 0) {
goto preperror;
}
if (newsize > BSDIFF_MAX_FILESZ) {
munmap(old_data, oldsize);
if (new_size > BSDIFF_MAX_FILESZ) {
munmap(old_data, old_size);
ret = -1;
goto preperror;
}
/* Allocate newsize+1 bytes instead of newsize bytes to ensure
/* Allocate new_size+1 bytes instead of new_size bytes to ensure
that we never try to malloc(0) and get a NULL pointer */
if ((new_data = malloc(newsize + 1)) == NULL) {
munmap(old_data, oldsize);
if ((new_data = malloc(new_size + 1)) == NULL) {
munmap(old_data, old_size);
ret = -1;
goto preperror;
}
memset(new_data, 0, newsize + 1);
memset(new_data, 0, new_size + 1);
oldpos = 0;
newpos = 0;
while (newpos < newsize) {
old_pos = 0;
new_pos = 0;
while (new_pos < new_size) {
/* Read control data:
* ctrl[0] == offset into diff block
* ctrl[1] == offset into extra block
@@ -628,7 +628,7 @@ static int apply_delta_v2(int subver, FILE *f,
* The three control block words manage reads of the diff,
* extra and old_data so that those three sources can be
* combined into new_data. ctrl[2] in particular may cause
* oldpos to jump forward AND backward in order to allow
* old_pos to jump forward AND backward in order to allow
* copies of the original file content rather than using
* diff or extra content.
*/
@@ -641,47 +641,47 @@ static int apply_delta_v2(int subver, FILE *f,
}
/* Sanity-check */
if (newpos + ctrl[0] > newsize || ctrl[0] < 0 || newpos + ctrl[0] < 0) {
if (new_pos + ctrl[0] > new_size || ctrl[0] < 0 || new_pos + ctrl[0] < 0) {
ret = -1;
goto readerror;
}
/* Read diff string */
ret = cfread(&df, new_data + newpos, ctrl[0], BSDIFF_BLOCK_DIFF, &d_zeros);
ret = cfread(&df, new_data + new_pos, ctrl[0], BSDIFF_BLOCK_DIFF, &d_zeros);
if (ret < 0) {
goto readerror;
}
/* Add old data to diff string */
for (i = 0; i < ctrl[0]; i++) {
if ((oldpos + i >= 0) && (oldpos + i < oldsize)) {
new_data[newpos + i] += old_data[oldpos + i];
if ((old_pos + i >= 0) && (old_pos + i < old_size)) {
new_data[new_pos + i] += old_data[old_pos + i];
}
}
/* Adjust pointers */
newpos += ctrl[0];
oldpos += ctrl[0];
new_pos += ctrl[0];
old_pos += ctrl[0];
/* Sanity-check */
if (newpos + ctrl[1] > newsize || ctrl[1] < 0 || newpos + ctrl[1] < 0) {
if (new_pos + ctrl[1] > new_size || ctrl[1] < 0 || new_pos + ctrl[1] < 0) {
ret = -1;
goto readerror;
}
if (oldpos + ctrl[2] > oldsize || oldpos + ctrl[2] < 0) {
if (old_pos + ctrl[2] > old_size || old_pos + ctrl[2] < 0) {
ret = -1;
goto readerror;
}
/* Read extra string */
ret = cfread(&ef, new_data + newpos, ctrl[1], BSDIFF_BLOCK_EXTRA, &e_zeros);
ret = cfread(&ef, new_data + new_pos, ctrl[1], BSDIFF_BLOCK_EXTRA, &e_zeros);
if (ret < 0) {
goto readerror;
}
/* Adjust pointers */
newpos += ctrl[1];
oldpos += ctrl[2];
new_pos += ctrl[1];
old_pos += ctrl[2];
}
/* Clean up the readers */
@@ -696,7 +696,7 @@ static int apply_delta_v2(int subver, FILE *f,
goto writeerror;
}
if (write(fd, new_data, newsize) != newsize) {
if (write(fd, new_data, new_size) != new_size) {
unlink(new_filename);
close(fd);
ret = -1;
@@ -719,12 +719,12 @@ static int apply_delta_v2(int subver, FILE *f,
writeerror:
free(new_data);
munmap(old_data, oldsize);
munmap(old_data, old_size);
return ret;
readerror:
free(new_data);
munmap(old_data, oldsize);
munmap(old_data, old_size);
preperror:
cfclose(&cf);
cfclose(&df);
+1 -1
View File
@@ -4,7 +4,7 @@
* Copyright © 2012-2016 Intel Corporation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
+193
View File
@@ -0,0 +1,193 @@
/*-
* Copyright 2003-2005 Colin Percival
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "bsheader.h"
/* NOTES:
* I and V are chunks of memory (arrays) with length = (oldfile size +1) * sizeof(int64_t).
* Additionally, we pass in arraylen now. The parent function qsufsort receives it, so it
* should be available here as well for error checking.
* start: is actually the point in the array sent in during the suffix sort, which sorts by
* small blocks/chunks.
* len: refers to the length of the current chunk being processed - NOT the array length(s).
* h: will never be more than 8, and increases by *2 during suffix sort (h += h) */
static void split(int64_t *I, int64_t *V, int64_t arraylen, int64_t start, int64_t len,
int64_t h)
{
int64_t i, j, k, x, tmp, jj, kk;
if (len < 16) {
for (k = start; k < start + len; k += j) {
j = 1;
x = V[I[k] + h];
for (i = 1; k + i < start + len; i++) {
if (V[I[k + i] + h] < x) {
x = V[I[k + i] + h];
j = 0;
}
if (V[I[k + i] + h] == x) {
tmp = I[k + j];
I[k + j] = I[k + i];
I[k + i] = tmp;
j++;
}
}
for (i = 0; i < j; i++) {
V[I[k + i]] = k + j - 1;
}
if (j == 1) {
I[k] = -1;
}
}
return;
}
x = V[I[start + len / 2] + h];
jj = 0;
kk = 0;
for (i = start; i < start + len; i++) {
if (V[I[i] + h] < x) {
jj++;
}
if (V[I[i] + h] == x) {
kk++;
}
}
jj += start;
kk += jj;
i = start;
j = 0;
k = 0;
while (i < jj) {
if (V[I[i] + h] < x) {
i++;
} else if (V[I[i] + h] == x) {
tmp = I[i];
I[i] = I[jj + j];
I[jj + j] = tmp;
j++;
} else {
tmp = I[i];
I[i] = I[kk + k];
I[kk + k] = tmp;
k++;
}
}
while (jj + j < kk) {
if (V[I[jj + j] + h] == x) {
j++;
} else {
tmp = I[jj + j];
I[jj + j] = I[kk + k];
I[kk + k] = tmp;
k++;
}
}
if (jj > start) {
split(I, V, arraylen, start, jj - start, h);
}
for (i = 0; i < kk - jj; i++) {
V[I[jj + i]] = kk - 1;
}
if (jj == kk - 1) {
I[jj] = -1;
}
if (start + len > kk) {
split(I, V, arraylen, kk, start + len - kk, h);
}
}
/* The old_data (previous file data) is passed into this suffix sort and sorted
* accordingly using the I and V arrays, which are both of length old_size +1. */
int qsufsort(int64_t *I, int64_t *V, u_char *old, int64_t old_size)
{
int64_t buckets[QSUF_BUCKET_SIZE];
int64_t i, h, len;
for (i = 0; i < QSUF_BUCKET_SIZE; i++) {
buckets[i] = 0;
}
for (i = 0; i < old_size; i++) {
buckets[old[i]]++;
}
for (i = 1; i < QSUF_BUCKET_SIZE; i++) {
buckets[i] += buckets[i - 1];
}
for (i = QSUF_BUCKET_SIZE - 1; i > 0; i--) {
buckets[i] = buckets[i - 1];
}
buckets[0] = 0;
for (i = 0; i < old_size; i++) {
if (buckets[old[i]] > old_size + 1) {
return -1;
}
I[++buckets[old[i]]] = i;
}
for (i = 0; i < old_size; i++) {
V[i] = buckets[old[i]];
}
V[old_size] = 0;
for (i = 1; i < QSUF_BUCKET_SIZE; i++) {
if (buckets[i] == buckets[i - 1] + 1) {
I[buckets[i]] = -1;
}
}
I[0] = -1;
for (h = 1; I[0] != -(old_size + 1); h += h) {
len = 0;
for (i = 0; i < old_size + 1;) {
if (I[i] < 0) {
len -= I[i];
i -= I[i];
} else {
if (len) {
I[i - len] = -len;
}
len = V[I[i]] + 1 - i;
split(I, V, old_size, i, len, h);
i += len;
len = 0;
}
}
if (len) {
I[i - len] = -len;
}
}
for (i = 0; i < old_size + 1; i++) {
I[V[i]] = i;
}
return 0;
}
+73 -46
View File
@@ -1,57 +1,89 @@
#!/bin/bash
# $srcdir variable is set by automake environment
cd $srcdir/test
# number is incremented after running every test
testnum=0
sudo rm -f *.diff *.out
libdir="$(realpath "../.libs")"
ldpath="LD_LIBRARY_PATH=$libdir"
BSDIFF="sudo $ldpath valgrind -q $libdir/bsdiff"
BSPATCH="sudo $ldpath valgrind -q $libdir/bspatch"
VALGRIND="valgrind -q"
if [ -n "$SKIP_VALGRIND" ]; then
VALGRIND=""
fi
echo -n "5.."
libdir="$abs_builddir/.libs"
ldpath="LD_LIBRARY_PATH=$libdir"
BSDIFF="sudo $ldpath $VALGRIND $libdir/bsdiff"
BSPATCH="sudo $ldpath $VALGRIND $libdir/bspatch"
# If exit status is 0, the test succeeded. Else it failed.
check_success() {
res=$?
[ -n "$1" ] && msg="$1" || msg=""
testnum=$(expr $testnum + 1)
if [ $res -ne 0 ]; then
echo "not ok $testnum - $msg"
else
echo "ok $testnum"
fi
}
# If exit status is 255, the test succeeded. Else it failed.
check_failure() {
res=$?
[ -n "$1" ] && msg="$1" || msg=""
testnum=$(expr $testnum + 1)
if [ $res -ne 255 ]; then
echo "not ok $testnum - $msg"
else
echo "ok $testnum"
fi
}
echo "Running test #5 ..."
$BSPATCH data/5.bspatch.original 5.out data/5.bspatch.diff
echo -n "6.."
check_success
echo "Running test #6 ..."
$BSPATCH data/6.bspatch.original 6.out data/6.bspatch.diff
echo -n "7.."
check_success
echo "Running test #7 ..."
$BSPATCH data/7.bspatch.original 7.out data/7.bspatch.diff
echo -n "8.."
check_success
echo "Running test #8 ..."
$BSPATCH data/8.bspatch.original 8.out data/8.bspatch.diff
echo -n "9.."
check_success
echo "Running test #9 ..."
$BSPATCH data/9.bspatch.original 9.out data/9.bspatch.diff
diff data/9.bspatch.modified 9.out
if [ $? -ne 0 ]
then
echo "bspatch 9 output does not match expected!!"
fi
echo -n "10.."
check_success "output does not match expected!!"
echo "Running test #10 ..."
$BSPATCH data/10.bspatch.original 10.out data/10.bspatch.diff
diff data/10.bspatch.modified 10.out
if [ $? -ne 0 ]
then
echo "bspatch 10 output does not match expected!!"
fi
check_success "output does not match expected!!"
#same as 9 but with zeros encoding
echo -n "11.."
echo "Running test #11 ..."
$BSPATCH data/9.bspatch.original 11.out data/11.bspatch.diff
diff data/9.bspatch.modified 11.out
if [ $? -ne 0 ]
then
echo "bspatch 11 output does not match expected!!"
fi
echo -n "12.."
check_success "output does not match expected!!"
echo "Running test #12 ..."
$BSPATCH data/12.bspatch.original 12.out data/12.bspatch.diff
diff data/12.bspatch.modified 12.out
if [ $? -ne 0 ]
then
echo "bspatch 12 output does not match expected!!"
fi
echo -n "13.."
check_success "output does not match expected!!"
echo "Running test #13 ..."
$BSDIFF data/13.bspatch.original data/13.bspatch.modified 13.diff any
$BSPATCH data/13.bspatch.original 13.out 13.diff
diff data/13.bspatch.modified 13.out
if [ $? -ne 0 ]
then
echo "bspatch 13 output does not match expected!!"
fi
check_success "output does not match expected!!"
# Next a very loooong running test, but one which successfully condenses the 2MB
# original file pair into a 26kB bsdiff. The bsdiff computation alone (ie:
@@ -62,26 +94,21 @@ fi
# used in a regression test run at every check-in of code changes to the bsdiff
# implementation.
#
#echo -n "14.."
#echo "Running test #14 ..."
#$BSDIFF data/14.bspatch.original data/14.bspatch.modified 14.diff any
#$BSPATCH data/14.bspatch.original 14.out 14.diff
#diff data/14.bspatch.modified 14.out
#if [ $? -ne 0 ]
#then
# echo "bspatch 14 output does not match expected!!"
#fi
#check_success "output does not match expected!!"
echo -n "15.."
echo "Running test #15 ..."
$BSDIFF data/15.bspatch.original data/15.bspatch.modified 15.diff any
# expected output: "Failed to create delta (-1)"
if [ $? -ne 255 ]
then
echo "bspatch 15 creation has memory management issue!"
fi
check_failure "patch creation has memory management issue!"
echo -n "16.."
echo "Running test #16 ..."
# any valgrind errors may indicate a buffer overflow
$BSPATCH data/16.bspatch.original 16.out data/16.bspatch.diff
check_success
# add final newline
echo ""
# For TAP support, output the plan
echo "1..${testnum}"