Project import
diff --git a/bsdiff/.clang-format b/bsdiff/.clang-format
new file mode 100644
index 0000000..998f530
--- /dev/null
+++ b/bsdiff/.clang-format
@@ -0,0 +1,4 @@
+BasedOnStyle: Chromium
+
+MaxEmptyLinesToKeep: 2
+AllowShortBlocksOnASingleLine: true
diff --git a/bsdiff/Android.mk b/bsdiff/Android.mk
new file mode 100644
index 0000000..12ee3e8
--- /dev/null
+++ b/bsdiff/Android.mk
@@ -0,0 +1,154 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Common project flags.
+bsdiff_common_cflags := \
+ -D_FILE_OFFSET_BITS=64 \
+ -Wall \
+ -Werror \
+ -Wextra \
+ -Wno-unused-parameter
+
+bsdiff_common_static_libs := \
+ libbz
+
+# "bsdiff" program.
+bsdiff_static_libs := \
+ libdivsufsort64 \
+ libdivsufsort \
+ $(bsdiff_common_static_libs)
+
+bsdiff_src_files := \
+ bsdiff.cc
+
+# "bspatch" program.
+bspatch_src_files := \
+ bspatch.cc \
+ extents.cc \
+ extents_file.cc \
+ file.cc \
+ memory_file.cc
+
+# Unit test files.
+bsdiff_common_unittests := \
+ bsdiff_unittest.cc \
+ bspatch_unittest.cc \
+ extents_file_unittest.cc \
+ extents_unittest.cc \
+ test_utils.cc
+
+# Target static libraries.
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbspatch
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := $(bspatch_src_files)
+LOCAL_CFLAGS := $(bsdiff_common_cflags)
+LOCAL_STATIC_LIBRARIES := $(bsdiff_common_static_libs)
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbsdiff
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := $(bsdiff_src_files)
+LOCAL_CFLAGS := $(bsdiff_common_cflags)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_STATIC_LIBRARIES := $(bsdiff_static_libs)
+include $(BUILD_STATIC_LIBRARY)
+
+# Host static libraries.
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbspatch
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := $(bspatch_src_files)
+LOCAL_CFLAGS := $(bsdiff_common_cflags)
+LOCAL_STATIC_LIBRARIES := $(bsdiff_common_static_libs)
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbsdiff
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := $(bsdiff_src_files)
+LOCAL_CFLAGS := $(bsdiff_common_cflags)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_STATIC_LIBRARIES := $(bsdiff_static_libs)
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# Target executables.
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bspatch
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := bspatch_main.cc
+LOCAL_CFLAGS := $(bsdiff_common_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ libbspatch \
+ $(bsdiff_common_static_libs)
+include $(BUILD_EXECUTABLE)
+
+# bspatch used in recovery by update_engine_sideload.
+include $(CLEAR_VARS)
+LOCAL_MODULE := bspatch_recovery
+LOCAL_MODULE_STEM := bspatch
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := bspatch_main.cc
+LOCAL_CFLAGS := $(bsdiff_common_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ libbspatch \
+ $(bsdiff_common_static_libs)
+include $(BUILD_EXECUTABLE)
+
+# Host executables.
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bsdiff
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := bsdiff_main.cc
+LOCAL_CFLAGS := $(bsdiff_common_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ libbsdiff \
+ $(bsdiff_static_libs)
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bspatch
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := bspatch_main.cc
+LOCAL_CFLAGS := $(bsdiff_common_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ libbspatch \
+ $(bsdiff_common_static_libs)
+include $(BUILD_HOST_EXECUTABLE)
+
+# Unit tests.
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bsdiff_unittest
+LOCAL_MODULE_TAGS := debug tests
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := $(bsdiff_common_unittests) \
+ testrunner.cc
+LOCAL_CFLAGS := $(bsdiff_common_cflags) \
+ -DBSDIFF_TARGET_UNITTEST
+LOCAL_STATIC_LIBRARIES := \
+ libbsdiff \
+ libbspatch \
+ libgmock \
+ $(bsdiff_static_libs)
+include $(BUILD_NATIVE_TEST)
diff --git a/bsdiff/CleanSpec.mk b/bsdiff/CleanSpec.mk
new file mode 100644
index 0000000..b84e1b6
--- /dev/null
+++ b/bsdiff/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/bsdiff/LICENSE b/bsdiff/LICENSE
new file mode 100644
index 0000000..c8607a9
--- /dev/null
+++ b/bsdiff/LICENSE
@@ -0,0 +1,58 @@
+This project governed by the following BSD-style licenses. Check each file
+header to known the licenses applying to it:
+
+--------------------------------------------------------------------------------
+
+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.
+
+--------------------------------------------------------------------------------
+
+Copyright 2015 The Chromium OS Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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 COPYRIGHT
+OWNER OR CONTRIBUTORS 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.
diff --git a/bsdiff/MODULE_LICENSE_BSD_LIKE b/bsdiff/MODULE_LICENSE_BSD_LIKE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/bsdiff/MODULE_LICENSE_BSD_LIKE
diff --git a/bsdiff/Makefile b/bsdiff/Makefile
new file mode 100644
index 0000000..fbfe56a
--- /dev/null
+++ b/bsdiff/Makefile
@@ -0,0 +1,85 @@
+# Default options
+USE_BSDIFF ?= y
+
+BINARIES-y = bspatch
+BINARIES-$(USE_BSDIFF) += bsdiff
+
+BINARIES += $(BINARIES-y)
+
+INSTALL = install
+CFLAGS += -O3 -Wall -Werror
+CXXFLAGS += -std=c++11
+
+DESTDIR ?=
+PREFIX = /usr
+BINDIR = $(PREFIX)/bin
+DATADIR = $(PREFIX)/share
+MANDIR = $(DATADIR)/man
+MAN1DIR = $(MANDIR)/man1
+INSTALL_PROGRAM ?= $(INSTALL) -c -m 755
+INSTALL_MAN ?= $(INSTALL) -c -m 444
+
+.PHONY: all test clean
+all: $(BINARIES)
+test: unittests
+clean:
+ rm -f *.o $(BINARIES) unittests
+
+BSDIFF_LIBS = -lbz2 -ldivsufsort -ldivsufsort64
+BSDIFF_OBJS = \
+ bsdiff.o
+
+BSPATCH_LIBS = -lbz2
+BSPATCH_OBJS = \
+ bspatch.o \
+ extents.o \
+ extents_file.o \
+ file.o \
+ memory_file.o
+
+UNITTEST_LIBS = -lgmock -lgtest -lpthread
+UNITTEST_OBJS = \
+ bsdiff_unittest.o \
+ bspatch_unittest.o \
+ extents_file_unittest.o \
+ extents_unittest.o \
+ test_utils.o \
+ testrunner.o
+
+bsdiff: $(BSDIFF_OBJS) bsdiff_main.o
+bsdiff: LDLIBS += $(BSDIFF_LIBS)
+
+bspatch: $(BSPATCH_OBJS) bspatch_main.o
+bspatch: LDLIBS += $(BSPATCH_LIBS)
+
+unittests: LDLIBS += $(BSDIFF_LIBS) $(BSPATCH_LIBS) $(UNITTEST_LIBS)
+unittests: $(BSPATCH_OBJS) $(BSDIFF_OBJS) $(UNITTEST_OBJS)
+
+unittests bsdiff bspatch:
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LDLIBS)
+
+# Source file dependencies.
+bsdiff.o: bsdiff.cc
+bsdiff_main.o: bsdiff_main.cc bsdiff.h
+bsdiff_unittest.o: bsdiff_unittest.cc bsdiff.h test_utils.h
+bspatch.o: bspatch.cc bspatch.h extents.h extents_file.h file_interface.h \
+ file.h
+bspatch_main.o: bspatch_main.cc bspatch.h
+bspatch_unittest.o: bspatch_unittest.cc bspatch.h test_utils.h
+extents.o: extents.cc extents.h extents_file.h file_interface.h
+extents_file.o: extents_file.cc extents_file.h file_interface.h
+extents_file_unittest.o: extents_file_unittest.cc extents_file.h \
+ file_interface.h
+extents_unittest.o: extents_unittest.cc extents.h extents_file.h \
+ file_interface.h
+file.o: file.cc file.h file_interface.h
+memory_file.o: memory_file.cc memory_file.h file_interface.h
+testrunner.o: testrunner.cc
+test_utils.o: test_utils.cc test_utils.h
+
+install:
+ mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MAN1DIR)
+ $(INSTALL_PROGRAM) $(BINARIES) $(DESTDIR)$(BINDIR)
+ifndef WITHOUT_MAN
+ $(INSTALL_MAN) $(BINARIES:=.1) $(DESTDIR)$(MAN1DIR)
+endif
diff --git a/bsdiff/NOTICE b/bsdiff/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/bsdiff/NOTICE
@@ -0,0 +1 @@
+LICENSE
\ No newline at end of file
diff --git a/bsdiff/README.android b/bsdiff/README.android
new file mode 100644
index 0000000..9458d41
--- /dev/null
+++ b/bsdiff/README.android
@@ -0,0 +1,6 @@
+This is bsdiff-4.3 from http://www.daemonology.net/bsdiff/.
+
+This file, the Android.mk makefile, and the empty
+MODULE_LICENSE_BSD_LIKE file were added.
+
+Changes in the source are marked with "// android" comments.
diff --git a/bsdiff/README.chromium b/bsdiff/README.chromium
new file mode 100644
index 0000000..5ae005b
--- /dev/null
+++ b/bsdiff/README.chromium
@@ -0,0 +1,12 @@
+Homepage: http://www.daemonology.net/bsdiff/
+Version: 4.3.1
+License: BSD-2
+
+Description:
+
+Binary diff/patch utility.
+
+bsdiff and bspatch are tools for building and applying patches to binary
+files. By using suffix sorting and taking advantage of how executable files
+change, bsdiff routinely produces binary patches 50-80% smaller than those
+produced by Xdelta, and 15% smaller than those produced by .RTPatch.
diff --git a/bsdiff/bsdiff.1 b/bsdiff/bsdiff.1
new file mode 100644
index 0000000..659f5ff
--- /dev/null
+++ b/bsdiff/bsdiff.1
@@ -0,0 +1,64 @@
+.\"-
+.\" 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.
+.\"
+.\" $FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.1,v 1.1 2005/08/06 01:59:05 cperciva Exp $
+.\"
+.Dd May 18, 2003
+.Dt BSDIFF 1
+.Os FreeBSD
+.Sh NAME
+.Nm bsdiff
+.Nd generate a patch between two binary files
+.Sh SYNOPSIS
+.Nm
+.Ar oldfile newfile patchfile
+.Sh DESCRIPTION
+.Nm
+compares
+.Ar oldfile
+to
+.Ar newfile
+and writes to
+.Ar patchfile
+a binary patch suitable for use by
+.Xr bspatch 1 .
+When
+.Ar oldfile
+and
+.Ar newfile
+are two versions of an executable program, the
+patches produced are on average a factor of five smaller
+than those produced by any other binary patch tool known
+to the author.
+.Pp
+.Nm
+uses memory equal to 17 times the size of
+.Ar oldfile ,
+and requires
+an absolute minimum working set size of 8 times the size of oldfile.
+.Sh SEE ALSO
+.Xr bspatch 1
+.Sh AUTHORS
+.An Colin Percival Aq cperciva@freebsd.org
diff --git a/bsdiff/bsdiff.cc b/bsdiff/bsdiff.cc
new file mode 100644
index 0000000..669a1e5
--- /dev/null
+++ b/bsdiff/bsdiff.cc
@@ -0,0 +1,350 @@
+/*-
+ * 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.
+ */
+
+#if 0
+__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c,v 1.1 2005/08/06 01:59:05 cperciva Exp $");
+#endif
+
+#include "bsdiff.h"
+
+#include <sys/types.h>
+
+#include <bzlib.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#if _FILE_OFFSET_BITS == 64
+#include "divsufsort64.h"
+#define saidx_t saidx64_t
+#define divsufsort divsufsort64
+#else
+#include "divsufsort.h"
+#endif
+
+namespace bsdiff {
+
+static off_t matchlen(const u_char* old, off_t oldsize, const u_char* new_buf,
+ off_t newsize) {
+ off_t i;
+
+ for(i=0;(i<oldsize)&&(i<newsize);i++)
+ if(old[i]!=new_buf[i]) break;
+
+ return i;
+}
+
+// This is a binary search of the string |new_buf| of size |newsize| (or a
+// prefix of it) in the |old| string with size |oldsize| using the suffix array
+// |I|. |st| and |en| is the start and end of the search range (inclusive).
+// Returns the length of the longest prefix found and stores the position of the
+// string found in |*pos|.
+static off_t search(saidx_t* I, const u_char* old, off_t oldsize,
+ const u_char* new_buf, off_t newsize, off_t st, off_t en,
+ off_t* pos) {
+ off_t x,y;
+
+ if(en-st<2) {
+ x=matchlen(old+I[st],oldsize-I[st],new_buf,newsize);
+ y=matchlen(old+I[en],oldsize-I[en],new_buf,newsize);
+
+ if(x>y) {
+ *pos=I[st];
+ return x;
+ } else {
+ *pos=I[en];
+ return y;
+ }
+ };
+
+ x=st+(en-st)/2;
+ if(memcmp(old+I[x],new_buf,std::min(oldsize-I[x],newsize))<=0) {
+ return search(I,old,oldsize,new_buf,newsize,x,en,pos);
+ } else {
+ return search(I,old,oldsize,new_buf,newsize,st,x,pos);
+ };
+}
+
+static void offtout(off_t x,u_char *buf)
+{
+ off_t y;
+
+ if(x<0) y=-x; else y=x;
+
+ buf[0]=y%256;y-=buf[0];
+ y=y/256;buf[1]=y%256;y-=buf[1];
+ y=y/256;buf[2]=y%256;y-=buf[2];
+ y=y/256;buf[3]=y%256;y-=buf[3];
+ y=y/256;buf[4]=y%256;y-=buf[4];
+ y=y/256;buf[5]=y%256;y-=buf[5];
+ y=y/256;buf[6]=y%256;y-=buf[6];
+ y=y/256;buf[7]=y%256;
+
+ if(x<0) buf[7]|=0x80;
+}
+
+int bsdiff(const char* old_filename, const char* new_filename,
+ const char* patch_filename) {
+ int fd;
+ u_char *old_buf,*new_buf;
+ off_t oldsize,newsize;
+
+ /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure
+ that we never try to malloc(0) and get a NULL pointer */
+ if(((fd=open(old_filename,O_RDONLY,0))<0) ||
+ ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
+ ((old_buf=static_cast<u_char*>(malloc(oldsize+1)))==NULL) ||
+ (lseek(fd,0,SEEK_SET)!=0) ||
+ (read(fd,old_buf,oldsize)!=oldsize) ||
+ (close(fd)==-1)) err(1,"%s",old_filename);
+
+ /* Allocate newsize+1 bytes instead of newsize bytes to ensure
+ that we never try to malloc(0) and get a NULL pointer */
+ if(((fd=open(new_filename,O_RDONLY,0))<0) ||
+ ((newsize=lseek(fd,0,SEEK_END))==-1) ||
+ ((new_buf = static_cast<u_char*>(malloc(newsize+1)))==NULL) ||
+ (lseek(fd,0,SEEK_SET)!=0) ||
+ (read(fd,new_buf,newsize)!=newsize) ||
+ (close(fd)==-1)) err(1,"%s",new_filename);
+
+ int ret = bsdiff(old_buf, oldsize, new_buf, newsize, patch_filename);
+
+ free(old_buf);
+ free(new_buf);
+
+ return ret;
+}
+
+int bsdiff(const u_char* old_buf, off_t oldsize, const u_char* new_buf,
+ off_t newsize, const char* patch_filename) {
+ saidx_t *I;
+ off_t scan,pos=0,len;
+ off_t lastscan,lastpos,lastoffset;
+ off_t oldscore,scsc;
+ off_t s,Sf,lenf,Sb,lenb;
+ off_t overlap,Ss,lens;
+ off_t i;
+ off_t dblen,eblen;
+ u_char *db,*eb;
+ u_char buf[8];
+ u_char header[32];
+ FILE * pf;
+ BZFILE * pfbz2;
+ int bz2err;
+
+ if((I=static_cast<saidx_t*>(malloc((oldsize+1)*sizeof(saidx_t))))==NULL)
+ err(1,NULL);
+
+ if(divsufsort(old_buf, I, oldsize)) err(1, "divsufsort");
+
+ if(((db=static_cast<u_char*>(malloc(newsize+1)))==NULL) ||
+ ((eb=static_cast<u_char*>(malloc(newsize+1)))==NULL)) err(1,NULL);
+ dblen=0;
+ eblen=0;
+
+ /* Create the patch file */
+ if ((pf = fopen(patch_filename, "w")) == NULL)
+ err(1, "%s", patch_filename);
+
+ /* Header is
+ 0 8 "BSDIFF40"
+ 8 8 length of bzip2ed ctrl block
+ 16 8 length of bzip2ed diff block
+ 24 8 length of new file */
+ /* File is
+ 0 32 Header
+ 32 ?? Bzip2ed ctrl block
+ ?? ?? Bzip2ed diff block
+ ?? ?? Bzip2ed extra block */
+ memcpy(header,"BSDIFF40",8);
+ offtout(0, header + 8);
+ offtout(0, header + 16);
+ offtout(newsize, header + 24);
+ if (fwrite(header, 32, 1, pf) != 1)
+ err(1, "fwrite(%s)", patch_filename);
+
+ /* Compute the differences, writing ctrl as we go */
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+ errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+ scan=0;len=0;
+ lastscan=0;lastpos=0;lastoffset=0;
+ while(scan<newsize) {
+ oldscore=0;
+
+ /* If we come across a large block of data that only differs
+ * by less than 8 bytes, this loop will take a long time to
+ * go past that block of data. We need to track the number of
+ * times we're stuck in the block and break out of it. */
+ int num_less_than_eight = 0;
+ off_t prev_len, prev_oldscore, prev_pos;
+ for(scsc=scan+=len;scan<newsize;scan++) {
+ prev_len=len;
+ prev_oldscore=oldscore;
+ prev_pos=pos;
+
+ len=search(I,old_buf,oldsize,new_buf+scan,newsize-scan,
+ 0,oldsize-1,&pos);
+
+ for(;scsc<scan+len;scsc++)
+ if((scsc+lastoffset<oldsize) &&
+ (old_buf[scsc+lastoffset] == new_buf[scsc]))
+ oldscore++;
+
+ if(((len==oldscore) && (len!=0)) ||
+ (len>oldscore+8)) break;
+
+ if((scan+lastoffset<oldsize) &&
+ (old_buf[scan+lastoffset] == new_buf[scan]))
+ oldscore--;
+
+ const off_t fuzz = 8;
+ if (prev_len-fuzz<=len && len<=prev_len &&
+ prev_oldscore-fuzz<=oldscore &&
+ oldscore<=prev_oldscore &&
+ prev_pos<=pos && pos <=prev_pos+fuzz &&
+ oldscore<=len && len<=oldscore+fuzz)
+ ++num_less_than_eight;
+ else
+ num_less_than_eight=0;
+ if (num_less_than_eight > 100) break;
+ };
+
+ if((len!=oldscore) || (scan==newsize)) {
+ s=0;Sf=0;lenf=0;
+ for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
+ if(old_buf[lastpos+i]==new_buf[lastscan+i]) s++;
+ i++;
+ if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
+ };
+
+ lenb=0;
+ if(scan<newsize) {
+ s=0;Sb=0;
+ for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
+ if(old_buf[pos-i]==new_buf[scan-i]) s++;
+ if(s*2-i>Sb*2-lenb) { Sb=s; lenb=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_buf[lastscan+lenf-overlap+i]==
+ old_buf[lastpos+lenf-overlap+i]) s++;
+ if(new_buf[scan-lenb+i]==
+ old_buf[pos-lenb+i]) s--;
+ if(s>Ss) { Ss=s; lens=i+1; };
+ };
+
+ lenf+=lens-overlap;
+ lenb-=lens;
+ };
+
+ for(i=0;i<lenf;i++)
+ db[dblen+i]=new_buf[lastscan+i]-old_buf[lastpos+i];
+ for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
+ eb[eblen+i]=new_buf[lastscan+lenf+i];
+
+ dblen+=lenf;
+ eblen+=(scan-lenb)-(lastscan+lenf);
+
+ offtout(lenf,buf);
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+ offtout((scan-lenb)-(lastscan+lenf),buf);
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+ offtout((pos-lenb)-(lastpos+lenf),buf);
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+ lastscan=scan-lenb;
+ lastpos=pos-lenb;
+ lastoffset=pos-scan;
+ };
+ };
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+ /* Compute size of compressed ctrl data */
+ if ((len = ftello(pf)) == -1)
+ err(1, "ftello");
+ offtout(len-32, header + 8);
+
+ /* Write compressed diff data */
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+ errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+ BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+ /* Compute size of compressed diff data */
+ if ((newsize = ftello(pf)) == -1)
+ err(1, "ftello");
+ offtout(newsize - len, header + 16);
+
+ /* Write compressed extra data */
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+ errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+ BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+ /* Seek to the beginning, write the header, and close the file */
+ if (fseeko(pf, 0, SEEK_SET))
+ err(1, "fseeko");
+ if (fwrite(header, 32, 1, pf) != 1)
+ err(1, "fwrite(%s)", patch_filename);
+ if (fclose(pf))
+ err(1, "fclose");
+
+ /* Free the memory we used */
+ free(db);
+ free(eb);
+ free(I);
+
+ return 0;
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/bsdiff.h b/bsdiff/bsdiff.h
new file mode 100644
index 0000000..370ee03
--- /dev/null
+++ b/bsdiff/bsdiff.h
@@ -0,0 +1,24 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_BSDIFF_H_
+#define _BSDIFF_BSDIFF_H_
+
+#include <sys/types.h>
+
+namespace bsdiff {
+
+int bsdiff(const char* old_filename,
+ const char* new_filename,
+ const char* patch_filename);
+
+int bsdiff(const u_char* old_buf,
+ off_t oldsize,
+ const u_char* new_buf,
+ off_t newsize,
+ const char* patch_filename);
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_BSDIFF_H_
diff --git a/bsdiff/bsdiff_main.cc b/bsdiff/bsdiff_main.cc
new file mode 100644
index 0000000..db3026c
--- /dev/null
+++ b/bsdiff/bsdiff_main.cc
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <err.h>
+
+#include "bsdiff.h"
+
+int main(int argc, char* argv[]) {
+ if (argc != 4)
+ errx(1, "usage: %s oldfile newfile patchfile\n", argv[0]);
+
+ return bsdiff::bsdiff(argv[1], argv[2], argv[3]);
+}
diff --git a/bsdiff/bsdiff_unittest.cc b/bsdiff/bsdiff_unittest.cc
new file mode 100644
index 0000000..b3aae3e
--- /dev/null
+++ b/bsdiff/bsdiff_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bsdiff.h"
+
+#include <gtest/gtest.h>
+#include <string>
+#include <vector>
+
+#include "test_utils.h"
+
+using std::string;
+using std::vector;
+using test_utils::BsdiffPatchFile;
+
+namespace bsdiff {
+
+class BsdiffTest : public testing::Test {
+ protected:
+ BsdiffTest()
+ : old_file_("bsdiff_oldfile.XXXXXX"),
+ new_file_("bsdiff_newfile.XXXXXX"),
+ patch_file_("bsdiff_patchfile.XXXXXX") {
+ }
+ ~BsdiffTest() override {}
+
+ test_utils::ScopedTempFile old_file_;
+ test_utils::ScopedTempFile new_file_;
+ test_utils::ScopedTempFile patch_file_;
+};
+
+// Check that a file with no changes has a very small patch (no extra data).
+TEST_F(BsdiffTest, EqualEmptyFiles) {
+ // Empty old and new files.
+ EXPECT_EQ(0, bsdiff(old_file_.filename().c_str(),
+ new_file_.filename().c_str(),
+ patch_file_.filename().c_str()));
+ BsdiffPatchFile patch;
+ EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
+ EXPECT_TRUE(patch.IsValid());
+
+ // An empty bz2 file will have 14 bytes.
+ EXPECT_EQ(14, patch.diff_len);
+ EXPECT_EQ(14U, patch.extra_len);
+}
+
+TEST_F(BsdiffTest, EqualSmallFiles) {
+ string some_text = "Hello world!";
+ vector<uint8_t> vec_some_text(some_text.begin(), some_text.end());
+ test_utils::WriteFile(old_file_.filename(), vec_some_text);
+ EXPECT_EQ(0, bsdiff(old_file_.filename().c_str(),
+ new_file_.filename().c_str(),
+ patch_file_.filename().c_str()));
+ BsdiffPatchFile patch;
+ EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
+ EXPECT_TRUE(patch.IsValid());
+
+ // An empty bz2 file will have 14 bytes.
+ EXPECT_EQ(14, patch.diff_len);
+ EXPECT_EQ(14U, patch.extra_len);
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/bspatch.1 b/bsdiff/bspatch.1
new file mode 100644
index 0000000..ecac8db
--- /dev/null
+++ b/bsdiff/bspatch.1
@@ -0,0 +1,77 @@
+.\"-
+.\" 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.
+.\"
+.\" $FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.1,v 1.1 2005/08/06 01:59:06 cperciva Exp $
+.\"
+.Dd May 18, 2003
+.Dt BSPATCH 1
+.Os FreeBSD
+.Sh NAME
+.Nm bspatch
+.Nd apply a patch built with bsdiff(1)
+.Sh SYNOPSIS
+.Nm
+.Ar oldfile newfile patchfile
+.Op Ar old-extents new-extents
+.Sh DESCRIPTION
+.Nm
+generates
+.Ar newfile
+from
+.Ar oldfile
+and
+.Ar patchfile ,
+where
+.Ar patchfile
+is a binary patch built by
+.Xr bsdiff 1 .
+.Pp
+When provided,
+.Ar old-extents
+and
+.Ar new-extents
+instruct
+.Nm
+to read specific chunks of data from the old file and to write to specific
+locations in the new file, respectively. Each is a comma-separated list of
+extents of the form
+.Ar offset : Ns Ar length ,
+where
+.Ar offset
+is either -1 or a non-negative integer and
+.Ar length
+is a positive integer. An offset value of -1 denotes a sparse extent, namely a
+sequence of zeros that entails neither reading nor writing of actual file
+content.
+.Pp
+.Nm
+uses memory equal to the size of
+.Ar newfile ,
+but can tolerate a very small working set without a dramatic loss
+of performance.
+.Sh SEE ALSO
+.Xr bsdiff 1
+.Sh AUTHORS
+.An Colin Percival Aq cperciva@freebsd.org
diff --git a/bsdiff/bspatch.cc b/bsdiff/bspatch.cc
new file mode 100644
index 0000000..550c6c7
--- /dev/null
+++ b/bsdiff/bspatch.cc
@@ -0,0 +1,358 @@
+/*-
+ * 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.
+ */
+
+#if 0
+__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
+#endif
+
+#include "bspatch.h"
+
+#include <bzlib.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <memory>
+#include <limits>
+#include <vector>
+
+#include "extents.h"
+#include "extents_file.h"
+#include "file.h"
+#include "file_interface.h"
+#include "memory_file.h"
+
+namespace {
+
+int64_t ParseInt64(u_char* buf) {
+ int64_t y;
+
+ y = buf[7] & 0x7F;
+ y = y * 256;
+ y += buf[6];
+ y = y * 256;
+ y += buf[5];
+ y = y * 256;
+ y += buf[4];
+ y = y * 256;
+ y += buf[3];
+ y = y * 256;
+ y += buf[2];
+ y = y * 256;
+ y += buf[1];
+ y = y * 256;
+ y += buf[0];
+
+ if (buf[7] & 0x80)
+ y = -y;
+
+ return y;
+}
+
+bool ReadBZ2(BZFILE* pfbz2, uint8_t* data, size_t size) {
+ int bz2err;
+ size_t lenread = BZ2_bzRead(&bz2err, pfbz2, data, size);
+ if (lenread < size || (bz2err != BZ_OK && bz2err != BZ_STREAM_END))
+ return false;
+ return true;
+}
+
+bool ReadBZ2AndWriteAll(const std::unique_ptr<bsdiff::FileInterface>& file,
+ BZFILE* pfbz2,
+ size_t size,
+ uint8_t* buf,
+ size_t buf_size) {
+ while (size > 0) {
+ size_t bytes_to_read = std::min(size, buf_size);
+ if (!ReadBZ2(pfbz2, buf, bytes_to_read))
+ return false;
+ if (!WriteAll(file, buf, bytes_to_read))
+ return false;
+ size -= bytes_to_read;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace bsdiff {
+
+bool WriteAll(const std::unique_ptr<FileInterface>& file,
+ const uint8_t* data,
+ size_t size) {
+ size_t offset = 0, written;
+ while (offset < size) {
+ if (!file->Write(data + offset, size - offset, &written) || written == 0)
+ return false;
+ offset += written;
+ }
+ return true;
+}
+
+bool IsOverlapping(const char* old_filename,
+ const char* new_filename,
+ const std::vector<ex_t>& old_extents,
+ const std::vector<ex_t>& new_extents) {
+ struct stat old_stat, new_stat;
+ if (stat(new_filename, &new_stat) == -1) {
+ if (errno == ENOENT)
+ return false;
+ err(1, "Error stat the new filename %s", new_filename);
+ }
+ if (stat(old_filename, &old_stat) == -1)
+ err(1, "Error stat the old filename %s", old_filename);
+
+ if (old_stat.st_dev != new_stat.st_dev || old_stat.st_ino != new_stat.st_ino)
+ return false;
+
+ if (old_extents.empty() && new_extents.empty())
+ return true;
+
+ for (ex_t old_ex : old_extents)
+ for (ex_t new_ex : new_extents)
+ if (static_cast<uint64_t>(old_ex.off) < new_ex.off + new_ex.len &&
+ static_cast<uint64_t>(new_ex.off) < old_ex.off + old_ex.len)
+ return true;
+
+ return false;
+}
+
+int bspatch(
+ const char* old_filename, const char* new_filename,
+ const char* patch_filename,
+ const char* old_extents, const char* new_extents) {
+ FILE* f, *cpf, *dpf, *epf;
+ BZFILE* cpfbz2, *dpfbz2, *epfbz2;
+ int bz2err;
+ ssize_t bzctrllen, bzdatalen;
+ u_char header[32], buf[8];
+ off_t ctrl[3];
+
+ int using_extents = (old_extents != NULL || new_extents != NULL);
+
+ // Open patch file.
+ if ((f = fopen(patch_filename, "r")) == NULL)
+ err(1, "fopen(%s)", patch_filename);
+
+ // File format:
+ // 0 8 "BSDIFF40"
+ // 8 8 X
+ // 16 8 Y
+ // 24 8 sizeof(new_filename)
+ // 32 X bzip2(control block)
+ // 32+X Y bzip2(diff block)
+ // 32+X+Y ??? bzip2(extra block)
+ // with control block a set of triples (x,y,z) meaning "add x bytes
+ // from oldfile to x bytes from the diff block; copy y bytes from the
+ // extra block; seek forwards in oldfile by z bytes".
+
+ // Read header.
+ if (fread(header, 1, 32, f) < 32) {
+ if (feof(f))
+ errx(1, "Corrupt patch\n");
+ err(1, "fread(%s)", patch_filename);
+ }
+
+ // Check for appropriate magic.
+ if (memcmp(header, "BSDIFF40", 8) != 0)
+ errx(1, "Corrupt patch\n");
+
+ // Read lengths from header.
+ uint64_t oldsize, newsize;
+ bzctrllen = ParseInt64(header + 8);
+ bzdatalen = ParseInt64(header + 16);
+ int64_t signed_newsize = ParseInt64(header + 24);
+ newsize = signed_newsize;
+ if ((bzctrllen < 0) || (bzdatalen < 0) || (signed_newsize < 0))
+ errx(1, "Corrupt patch\n");
+
+ // Close patch file and re-open it via libbzip2 at the right places.
+ if (fclose(f))
+ err(1, "fclose(%s)", patch_filename);
+ if ((cpf = fopen(patch_filename, "r")) == NULL)
+ err(1, "fopen(%s)", patch_filename);
+ if (fseek(cpf, 32, SEEK_SET))
+ err(1, "fseeko(%s, %lld)", patch_filename, (long long)32);
+ if ((cpfbz2 = BZ2_bzReadOpen(&bz2err, cpf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", bz2err);
+ if ((dpf = fopen(patch_filename, "r")) == NULL)
+ err(1, "fopen(%s)", patch_filename);
+ if (fseek(dpf, 32 + bzctrllen, SEEK_SET))
+ err(1, "fseeko(%s, %lld)", patch_filename, (long long)(32 + bzctrllen));
+ if ((dpfbz2 = BZ2_bzReadOpen(&bz2err, dpf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", bz2err);
+ if ((epf = fopen(patch_filename, "r")) == NULL)
+ err(1, "fopen(%s)", patch_filename);
+ if (fseek(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
+ err(1, "fseeko(%s, %lld)", patch_filename,
+ (long long)(32 + bzctrllen + bzdatalen));
+ if ((epfbz2 = BZ2_bzReadOpen(&bz2err, epf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", bz2err);
+
+ // Open input file for reading.
+ std::unique_ptr<FileInterface> old_file = File::FOpen(old_filename, O_RDONLY);
+ if (!old_file)
+ err(1, "Error opening the old filename");
+
+ std::vector<ex_t> parsed_old_extents;
+ if (using_extents) {
+ if (!ParseExtentStr(old_extents, &parsed_old_extents))
+ errx(1, "Error parsing the old extents");
+ old_file.reset(new ExtentsFile(std::move(old_file), parsed_old_extents));
+ }
+
+ if (!old_file->GetSize(&oldsize))
+ err(1, "cannot obtain the size of %s", old_filename);
+ uint64_t old_file_pos = 0;
+
+ // Open output file for writing.
+ std::unique_ptr<FileInterface> new_file =
+ File::FOpen(new_filename, O_CREAT | O_WRONLY);
+ if (!new_file)
+ err(1, "Error opening the new filename %s", new_filename);
+
+ std::vector<ex_t> parsed_new_extents;
+ if (using_extents) {
+ if (!ParseExtentStr(new_extents, &parsed_new_extents))
+ errx(1, "Error parsing the new extents");
+ new_file.reset(new ExtentsFile(std::move(new_file), parsed_new_extents));
+ }
+
+ if (IsOverlapping(old_filename, new_filename, parsed_old_extents,
+ parsed_new_extents)) {
+ // New and old file is overlapping, we can not stream output to new file,
+ // cache it in the memory and write to the file at the end.
+ new_file.reset(new MemoryFile(std::move(new_file), newsize));
+ }
+
+ // The oldpos can be negative, but the new pos is only incremented linearly.
+ int64_t oldpos = 0;
+ uint64_t newpos = 0;
+ std::vector<uint8_t> old_buf(1024 * 1024), new_buf(1024 * 1024);
+ while (newpos < newsize) {
+ int64_t i;
+ // Read control data.
+ for (i = 0; i <= 2; i++) {
+ if (!ReadBZ2(cpfbz2, buf, 8))
+ errx(1, "Corrupt patch\n");
+ ctrl[i] = ParseInt64(buf);
+ }
+
+ // Sanity-check.
+ if (ctrl[0] < 0 || ctrl[1] < 0)
+ errx(1, "Corrupt patch\n");
+
+ // Sanity-check.
+ if (newpos + ctrl[0] > newsize)
+ errx(1, "Corrupt patch\n");
+
+ // Add old data to diff string. It is enough to fseek once, at
+ // the beginning of the sequence, to avoid unnecessary overhead.
+ if ((i = oldpos) < 0) {
+ // Write diff block directly to new file without adding old data,
+ // because we will skip part where |oldpos| < 0.
+ if (!ReadBZ2AndWriteAll(new_file, dpfbz2, -i, new_buf.data(),
+ new_buf.size()))
+ errx(1, "Error during ReadBZ2AndWriteAll()");
+
+ i = 0;
+ }
+
+ // We just checked that |i| is not negative.
+ if (static_cast<uint64_t>(i) != old_file_pos && !old_file->Seek(i))
+ err(1, "error seeking input file to offset %" PRId64, i);
+ if ((old_file_pos = oldpos + ctrl[0]) > oldsize)
+ old_file_pos = oldsize;
+
+ size_t chunk_size = old_file_pos - i;
+ while (chunk_size > 0) {
+ size_t read_bytes;
+ size_t bytes_to_read = std::min(chunk_size, old_buf.size());
+ if (!old_file->Read(old_buf.data(), bytes_to_read, &read_bytes))
+ err(1, "error reading from input file");
+ if (!read_bytes)
+ errx(1, "EOF reached while reading from input file");
+ // Read same amount of bytes from diff block
+ if (!ReadBZ2(dpfbz2, new_buf.data(), read_bytes))
+ errx(1, "Corrupt patch\n");
+ // new_buf already has data from diff block, adds old data to it.
+ for (size_t k = 0; k < read_bytes; k++)
+ new_buf[k] += old_buf[k];
+ if (!WriteAll(new_file, new_buf.data(), read_bytes))
+ err(1, "Error writing new file.");
+ chunk_size -= read_bytes;
+ }
+
+ // Adjust pointers.
+ newpos += ctrl[0];
+ oldpos += ctrl[0];
+
+ if (oldpos > static_cast<int64_t>(oldsize)) {
+ // Write diff block directly to new file without adding old data,
+ // because we skipped part where |oldpos| > oldsize.
+ if (!ReadBZ2AndWriteAll(new_file, dpfbz2, oldpos - oldsize,
+ new_buf.data(), new_buf.size()))
+ errx(1, "Error during ReadBZ2AndWriteAll()");
+ }
+
+ // Sanity-check.
+ if (newpos + ctrl[1] > newsize)
+ errx(1, "Corrupt patch\n");
+
+ // Read extra block.
+ if (!ReadBZ2AndWriteAll(new_file, epfbz2, ctrl[1], new_buf.data(),
+ new_buf.size()))
+ errx(1, "Error during ReadBZ2AndWriteAll()");
+
+ // Adjust pointers.
+ newpos += ctrl[1];
+ oldpos += ctrl[2];
+ }
+
+ // Close input file.
+ old_file->Close();
+
+ // Clean up the bzip2 reads.
+ BZ2_bzReadClose(&bz2err, cpfbz2);
+ BZ2_bzReadClose(&bz2err, dpfbz2);
+ BZ2_bzReadClose(&bz2err, epfbz2);
+ if (fclose(cpf) || fclose(dpf) || fclose(epf))
+ err(1, "fclose(%s)", patch_filename);
+
+ if (!new_file->Close())
+ err(1, "Error closing new file %s", new_filename);
+
+ return 0;
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/bspatch.h b/bsdiff/bspatch.h
new file mode 100644
index 0000000..3de2874
--- /dev/null
+++ b/bsdiff/bspatch.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_BSPATCH_H_
+#define _BSDIFF_BSPATCH_H_
+
+#include <memory>
+#include <vector>
+
+#include "extents_file.h"
+
+namespace bsdiff {
+
+int bspatch(const char* old_filename,
+ const char* new_filename,
+ const char* patch_filename,
+ const char* old_extents,
+ const char* new_extents);
+
+bool WriteAll(const std::unique_ptr<FileInterface>& file,
+ const uint8_t* data,
+ size_t size);
+
+bool IsOverlapping(const char* old_filename,
+ const char* new_filename,
+ const std::vector<ex_t>& old_extents,
+ const std::vector<ex_t>& new_extents);
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_BSPATCH_H_
diff --git a/bsdiff/bspatch_main.cc b/bsdiff/bspatch_main.cc
new file mode 100644
index 0000000..51ceebd
--- /dev/null
+++ b/bsdiff/bspatch_main.cc
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <err.h>
+#include <stdlib.h>
+
+#include "bspatch.h"
+
+#define USAGE_TEMPLATE_STR \
+ "usage: %s oldfile newfile patchfile [old-extents new-extents]\n" \
+ "with extents taking the form \"off_1:len_1,...,off_n:len_n\"\n"
+
+int main(int argc, char* argv[]) {
+ const char* old_extents = NULL;
+ const char* new_extents = NULL;
+
+ if ((argc != 6) && (argc != 4))
+ errx(1, USAGE_TEMPLATE_STR, argv[0]);
+
+ if (argc == 6) {
+ old_extents = argv[4];
+ new_extents = argv[5];
+ }
+
+ return bsdiff::bspatch(argv[1], argv[2], argv[3], old_extents, new_extents);
+}
diff --git a/bsdiff/bspatch_unittest.cc b/bsdiff/bspatch_unittest.cc
new file mode 100644
index 0000000..04ec666
--- /dev/null
+++ b/bsdiff/bspatch_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bspatch.h"
+
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include "test_utils.h"
+
+namespace bsdiff {
+
+class BspatchTest : public testing::Test {
+ protected:
+ BspatchTest()
+ : old_file_("bsdiff_oldfile.XXXXXX"),
+ new_file_("bsdiff_newfile.XXXXXX") {}
+
+ test_utils::ScopedTempFile old_file_;
+ test_utils::ScopedTempFile new_file_;
+};
+
+TEST_F(BspatchTest, IsOverlapping) {
+ const char* old_filename = old_file_.c_str();
+ const char* new_filename = new_file_.c_str();
+ EXPECT_FALSE(IsOverlapping(old_filename, "does-not-exist", {}, {}));
+ EXPECT_FALSE(IsOverlapping(old_filename, new_filename, {}, {}));
+ EXPECT_EQ(0, unlink(new_filename));
+ EXPECT_EQ(0, symlink(old_filename, new_filename));
+ EXPECT_TRUE(IsOverlapping(old_filename, new_filename, {}, {}));
+ EXPECT_TRUE(IsOverlapping(old_filename, old_filename, {}, {}));
+ EXPECT_FALSE(IsOverlapping(old_filename, old_filename, {{0, 1}}, {{1, 1}}));
+ EXPECT_FALSE(IsOverlapping(old_filename, old_filename, {{2, 1}}, {{1, 1}}));
+ EXPECT_TRUE(IsOverlapping(old_filename, old_filename, {{0, 1}}, {{0, 1}}));
+ EXPECT_TRUE(IsOverlapping(old_filename, old_filename, {{0, 4}}, {{2, 1}}));
+ EXPECT_TRUE(IsOverlapping(old_filename, old_filename, {{1, 1}}, {{0, 2}}));
+ EXPECT_TRUE(IsOverlapping(old_filename, old_filename, {{3, 2}}, {{2, 2}}));
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/extents.cc b/bsdiff/extents.cc
new file mode 100644
index 0000000..52cf404
--- /dev/null
+++ b/bsdiff/extents.cc
@@ -0,0 +1,113 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extents.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <limits>
+
+namespace bsdiff {
+
+/* The maximum accepted value for a given integer type when parsed as a signed
+ * long long integer. This is defined to be the smaller of the maximum value
+ * that can be represented by this type and LLONG_MAX. This bound allows us to
+ * properly check that parsed values do not exceed the capacity of their
+ * intended store, regardless of how its size relates to that of a signed long
+ * long integer. Note: this may mean that we are losing the most significant
+ * bit of an unsigned 64-bit field (e.g. size_t on some platforms), however
+ * still permitting values up to 2^62, which is more than enough for all
+ * practical purposes. */
+#define MAX_ALLOWED(t) \
+ (std::min(static_cast<uint64_t>(std::numeric_limits<t>::max()), \
+ static_cast<uint64_t>(std::numeric_limits<long long>::max())))
+
+/* Get the type of a struct field. */
+#define FIELD_TYPE(t, f) decltype(((t*)0)->f)
+
+
+/* Reads a long long integer from |s| into |*val_p|. Returns a pointer to the
+ * character immediately following the specified |delim|, unless (a) parsing
+ * failed (overflow or no valid digits); (b) the read value is less than
+ * |min_val| or greater than |max_val|; (c) the delimiter character is not
+ * |delim|, or the string ends although |may_end| is false. In any of these
+ * cases, returns NULL. */
+const char* read_llong(const char* s,
+ long long* val_p,
+ long long min_val,
+ long long max_val,
+ char delim,
+ int may_end) {
+ assert(val_p);
+ const char* next_s;
+ errno = 0;
+ long long val = strtoll(s, (char**)&next_s, 10);
+ if (((val == LLONG_MAX || val == LLONG_MIN) && errno == ERANGE) ||
+ next_s == s || val < min_val || val > max_val ||
+ (*next_s ? *next_s != delim : !may_end))
+ return NULL; /* bad value or delimiter */
+ *val_p = val;
+ if (*next_s)
+ next_s++; /* skip delimeter */
+ return next_s;
+}
+
+
+/* Reads a comma-separated list of "offset:length" extents from |ex_str|. If
+ * |ex_arr| is NULL, then |ex_count| is ignored and it attempts to parse valid
+ * extents until the end of the string is reached. Otherwise, stores up to
+ * |ex_count| extents into |ex_arr|, which must be of at least this size.
+ * Returns the number of correctly parsed extents, or -1 if a malformed extent
+ * was found. */
+static ssize_t extents_read(const char* ex_str, ex_t* ex_arr, size_t ex_count) {
+ size_t i;
+ size_t last_i = ex_count - 1;
+ if (!ex_arr) {
+ ex_count = SIZE_MAX;
+ last_i = 0;
+ }
+ for (i = 0; *ex_str && i < ex_count; i++) {
+ long long raw_off = 0, raw_len = 0;
+ if (!((ex_str =
+ read_llong(ex_str, &raw_off, -1,
+ MAX_ALLOWED(FIELD_TYPE(ex_t, off)), ':', false)) &&
+ (ex_str = read_llong(ex_str, &raw_len, 1,
+ MAX_ALLOWED(FIELD_TYPE(ex_t, len)), ',',
+ i >= last_i))))
+ return -1; /* parsing error */
+ if (ex_arr) {
+ ex_arr[i].off = raw_off;
+ ex_arr[i].len = raw_len;
+ }
+ }
+ return i;
+}
+
+
+bool ParseExtentStr(const char* ex_str, std::vector<ex_t>* extents) {
+ // Sanity check: a string must be provided.
+ if (!ex_str)
+ return false;
+
+ /* Parse string and count extents. */
+ ssize_t ret = extents_read(ex_str, NULL, 0);
+ if (ret < 0)
+ return false; // parsing error.
+
+ // Input is good, commit to extent count.
+ extents->resize(ret);
+ if (ret == 0)
+ return true; // No extents, nothing to do.
+
+ // Populate the extent array.
+ extents_read(ex_str, extents->data(), extents->size());
+ return true;
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/extents.h b/bsdiff/extents.h
new file mode 100644
index 0000000..de3eb27
--- /dev/null
+++ b/bsdiff/extents.h
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_EXTENTS_H_
+#define _BSDIFF_EXTENTS_H_
+
+#include <vector>
+
+#include "extents_file.h"
+
+namespace bsdiff {
+
+// Parses a string representation |ex_str| and populates the vector |extents|
+// of ex_t. The string is expected to be a comma-separated list of pairs of the
+// form "offset:length". An offset may be -1 or a non-negative integer; the
+// former indicates a sparse extent (consisting of zeros). A length is a
+// positive integer. Returns whether the parsing was successful.
+bool ParseExtentStr(const char* ex_str, std::vector<ex_t>* extents);
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_EXTENTS_H_
diff --git a/bsdiff/extents_file.cc b/bsdiff/extents_file.cc
new file mode 100644
index 0000000..01d31a6
--- /dev/null
+++ b/bsdiff/extents_file.cc
@@ -0,0 +1,117 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extents_file.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+// Extent files implementation extending FileInterface.
+//
+// This class allows to map linear positions in a file to a list of regions in
+// another file. All the reads and writes are unbuffered, passed directly to the
+// underlying file. Seeking is done in O(log(N)), where N is the number of
+// extents in the file, but sequential reads jump to the next extent in O(1).
+
+namespace bsdiff {
+
+ExtentsFile::ExtentsFile(std::unique_ptr<FileInterface> file,
+ const std::vector<ex_t>& extents)
+ : file_(std::move(file)), extents_(extents) {
+ acc_len_.reserve(extents.size());
+ for (const ex_t& extent : extents) {
+ acc_len_.push_back(total_ex_len_);
+ total_ex_len_ += extent.len;
+ }
+}
+
+ExtentsFile::~ExtentsFile() {
+ Close();
+}
+
+bool ExtentsFile::Read(void* buf, size_t count, size_t* bytes_read) {
+ return IOOperation(&FileInterface::Read, buf, count, bytes_read);
+}
+
+
+bool ExtentsFile::Write(const void* buf, size_t count, size_t* bytes_written) {
+ return IOOperation(&FileInterface::Write, buf, count, bytes_written);
+}
+
+bool ExtentsFile::Seek(off_t pos) {
+ if (pos < 0 || static_cast<uint64_t>(pos) > total_ex_len_)
+ return false;
+ if (acc_len_.empty())
+ return true;
+ // Note that the first element of acc_len_ is always 0, and pos is at least 0,
+ // so the upper_bound will never return acc_len_.begin().
+ curr_pos_ = pos;
+ curr_ex_idx_ = std::upper_bound(acc_len_.begin(), acc_len_.end(), pos) -
+ acc_len_.begin();
+ // We handle the corner case where |pos| is the size of all the extents by
+ // leaving the value of curr_ex_idx_ the same way AdvancePos(0) would leave it
+ // after the seek.
+ if (curr_pos_ < total_ex_len_)
+ curr_ex_idx_--;
+ return true;
+}
+
+bool ExtentsFile::Close() {
+ return file_->Close();
+}
+
+bool ExtentsFile::GetSize(uint64_t* size) {
+ *size = total_ex_len_;
+ return true;
+}
+
+void ExtentsFile::AdvancePos(uint64_t size) {
+ curr_pos_ += size;
+ for (; curr_ex_idx_ < extents_.size(); curr_ex_idx_++) {
+ if (curr_pos_ < acc_len_[curr_ex_idx_] + extents_[curr_ex_idx_].len)
+ return;
+ }
+ return;
+}
+
+template <typename T>
+bool ExtentsFile::IOOperation(bool (FileInterface::*io_op)(T*, size_t, size_t*),
+ T* buf,
+ size_t count,
+ size_t* bytes_processed) {
+ bool result = true;
+ size_t processed = 0;
+ AdvancePos(0);
+ while (count > 0 && curr_ex_idx_ < extents_.size()) {
+ const ex_t& ex = extents_[curr_ex_idx_];
+ off_t curr_ex_off = curr_pos_ - acc_len_[curr_ex_idx_];
+ size_t chunk_size =
+ std::min(static_cast<uint64_t>(count), ex.len - curr_ex_off);
+ size_t chunk_processed = 0;
+ if (ex.off < 0) {
+ chunk_processed = chunk_size;
+ } else {
+ if (!file_->Seek(ex.off + curr_ex_off) ||
+ !(file_.get()->*io_op)(buf, chunk_size, &chunk_processed)) {
+ processed += chunk_processed;
+ result = processed > 0;
+ break;
+ }
+ }
+ processed += chunk_processed;
+ count -= chunk_processed;
+ // T can be either const void* or void*. We const_cast it back to void* and
+ // then char* to do the arithmetic operation, but |buf| will continue to be
+ // const void* if it was defined that way.
+ buf = static_cast<char*>(const_cast<void*>(buf)) + chunk_processed;
+ AdvancePos(chunk_processed);
+ if (!chunk_processed)
+ break;
+ }
+ *bytes_processed = processed;
+ return result;
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/extents_file.h b/bsdiff/extents_file.h
new file mode 100644
index 0000000..865e2a2
--- /dev/null
+++ b/bsdiff/extents_file.h
@@ -0,0 +1,92 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_EXTENTS_FILE_H_
+#define _BSDIFF_EXTENTS_FILE_H_
+
+#include <stdio.h>
+
+#include <memory>
+#include <vector>
+
+#include "file_interface.h"
+
+/*
+ * Extent files.
+ *
+ * This modules provides a familiar interface for handling files through an
+ * indirection layer of extents, which are contiguous chunks of variable length
+ * at arbitrary offsets within a file. Once an extent file handle is obtained,
+ * users may read, write and seek as they do with ordinary files, having the I/O
+ * with the underlying file done for them by the extent file implementation. The
+ * implementation supports "sparse extents", which are assumed to contain zeros
+ * but otherwise have no actual representation in the underlying file; these are
+ * denoted by negative offset values.
+ *
+ * Unlike ordinary files, the size of an extent file is fixed; it is not
+ * truncated on open, nor is writing past the extent span allowed. Also, writing
+ * to a sparse extent has no effect and will not raise an error.
+ */
+
+namespace bsdiff {
+
+/* An extent, defined by an offset and a length. */
+struct ex_t {
+ off_t off; // the extent offset; negative indicates a sparse extent.
+ uint64_t len; // the extent length.
+};
+
+class ExtentsFile : public FileInterface {
+ public:
+ // Creates an ExtentsFile based on the underlying |file| passed. The positions
+ // in the ExtentsFile will be linearly mapped to the extents provided in
+ // |extents|. The created ExtentsFile takes ownership of the |file| will close
+ // it on destruction.
+ ExtentsFile(std::unique_ptr<FileInterface> file,
+ const std::vector<ex_t>& extents);
+
+ ~ExtentsFile() override;
+
+ // FileInterface overrides.
+ bool Read(void* buf, size_t count, size_t* bytes_read) override;
+ bool Write(const void* buf, size_t count, size_t* bytes_written) override;
+ bool Seek(off_t pos) override;
+ bool Close() override;
+ bool GetSize(uint64_t* size) override;
+
+ private:
+ void AdvancePos(uint64_t size);
+
+ // Performs an I/O operation (either read or write). This template shares the
+ // code for both Read() and Write() implementations.
+ template <typename T>
+ bool IOOperation(bool (FileInterface::*io_op)(T*, size_t, size_t*),
+ T* buf,
+ size_t count,
+ size_t* bytes_processed);
+
+ // The underlying FileInterace instance.
+ std::unique_ptr<FileInterface> file_;
+
+ // The list of extents mapping this instance to |file_|.
+ const std::vector<ex_t> extents_;
+
+ // The accumulated length of the extents. The i-th element contains the sum of
+ // the length of all the extents from 0 up to but not including the i-th
+ // extent. This reduces the complexity for random-access Seek() calls.
+ std::vector<uint64_t> acc_len_;
+
+ // Current extent index.
+ size_t curr_ex_idx_{0};
+
+ // Current logical file position.
+ uint64_t curr_pos_{0};
+
+ // Total length of all extents (constant).
+ uint64_t total_ex_len_{0};
+};
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_EXTENTS_FILE_H_
diff --git a/bsdiff/extents_file_unittest.cc b/bsdiff/extents_file_unittest.cc
new file mode 100644
index 0000000..73cedb2
--- /dev/null
+++ b/bsdiff/extents_file_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extents_file.h"
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <string>
+#include <vector>
+
+#include "file_interface.h"
+
+using std::string;
+using std::vector;
+using testing::AnyNumber;
+using testing::StrictMock;
+using testing::Return;
+using testing::InSequence;
+using testing::_;
+
+namespace bsdiff {
+
+// Mock class for the underlying file interface.
+class MockFile : public FileInterface {
+ public:
+ MOCK_METHOD3(Read, bool(void*, size_t, size_t*));
+ MOCK_METHOD3(Write, bool(const void*, size_t, size_t*));
+ MOCK_METHOD1(Seek, bool(off_t));
+ MOCK_METHOD0(Close, bool());
+ MOCK_METHOD1(GetSize, bool(uint64_t*));
+};
+
+ACTION(SucceedIO) {
+ // Check that arg1 (count) can be converted
+ *arg2 = arg1;
+ return true;
+}
+
+ACTION_P(SucceedPartialIO, bytes) {
+ // Check that arg1 (count) can be converted
+ *arg2 = bytes;
+ return true;
+}
+
+class ExtentsFileTest : public testing::Test {
+ protected:
+ void SetUp() {
+ mock_file_ = new StrictMock<MockFile>();
+ mock_file_ptr_.reset(mock_file_);
+ // The destructor of the ExtentsFile will call Close once.
+ EXPECT_CALL(*mock_file_, Close()).WillOnce(Return(true));
+ }
+
+ // Pointer to the underlying File owned by the ExtentsFile under test. This
+ // pointer is invalidated whenever the ExtentsFile is destroyed.
+ StrictMock<MockFile>* mock_file_;
+ std::unique_ptr<FileInterface> mock_file_ptr_;
+};
+
+TEST_F(ExtentsFileTest, DestructorCloses) {
+ ExtentsFile file(std::move(mock_file_ptr_), {});
+}
+
+TEST_F(ExtentsFileTest, CloseIsForwarded) {
+ ExtentsFile file(std::move(mock_file_ptr_), {});
+ EXPECT_TRUE(file.Close());
+ EXPECT_CALL(*mock_file_, Close()).WillOnce(Return(false));
+}
+
+TEST_F(ExtentsFileTest, GetSizeSumExtents) {
+ ExtentsFile file(std::move(mock_file_ptr_),
+ {ex_t{10, 5}, ex_t{20, 5}, {25, 2}});
+ uint64_t size;
+ EXPECT_TRUE(file.GetSize(&size));
+ EXPECT_EQ(12U, size);
+}
+
+TEST_F(ExtentsFileTest, SeekToRightOffsets) {
+ ExtentsFile file(std::move(mock_file_ptr_),
+ {ex_t{10, 5}, ex_t{20, 5}, {25, 2}});
+ vector<std::pair<off_t, off_t>> tests = {
+ // Seek to the beginning of the file.
+ {0, 10},
+ // Seek to the middle of a extent.
+ {3, 13},
+ {11, 26},
+ // Seek to the extent boundary.
+ {5, 20}, // Seeks to the first byte in the second extent.
+ {10, 25},
+ };
+ for (const auto& offset_pair : tests) {
+ // We use a failing Read() call to trigger the actual seek call to the
+ // underlying file.
+ EXPECT_CALL(*mock_file_, Seek(offset_pair.second)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(_, _, _)).WillOnce(Return(false));
+
+ EXPECT_TRUE(file.Seek(offset_pair.first));
+ size_t bytes_read;
+ EXPECT_FALSE(file.Read(nullptr, 1, &bytes_read));
+ }
+
+ // Seeking to the end of the file is ok, but not past it.
+ EXPECT_TRUE(file.Seek(12));
+ EXPECT_FALSE(file.Seek(13));
+
+ EXPECT_FALSE(file.Seek(-1));
+}
+
+TEST_F(ExtentsFileTest, ReadAcrossAllExtents) {
+ ExtentsFile file(std::move(mock_file_ptr_),
+ {ex_t{10, 5}, ex_t{20, 7}, {27, 3}});
+ InSequence s;
+ char* buf = reinterpret_cast<char*>(0x1234);
+
+ EXPECT_CALL(*mock_file_, Seek(10)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf, 5, _)).WillOnce(SucceedIO());
+ EXPECT_CALL(*mock_file_, Seek(20)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf + 5, 7, _)).WillOnce(SucceedIO());
+ EXPECT_CALL(*mock_file_, Seek(27)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf + 12, 3, _)).WillOnce(SucceedIO());
+
+ // FileExtents::Read() should read everything in one shot, by reading all
+ // the little chunks. Note that it doesn't attempt to read past the end of the
+ // FileExtents.
+ size_t bytes_read = 0;
+ EXPECT_TRUE(file.Read(buf, 100, &bytes_read));
+ EXPECT_EQ(15U, bytes_read);
+}
+
+TEST_F(ExtentsFileTest, MultiReadAcrossAllExtents) {
+ ExtentsFile file(std::move(mock_file_ptr_),
+ {ex_t{10, 5}, ex_t{20, 7}, {27, 3}});
+ InSequence s;
+ char* buf = reinterpret_cast<char*>(0x1234);
+
+ EXPECT_CALL(*mock_file_, Seek(10)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf, 2, _)).WillOnce(SucceedIO());
+ EXPECT_CALL(*mock_file_, Seek(12)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf, 3, _)).WillOnce(SucceedIO());
+ EXPECT_CALL(*mock_file_, Seek(20)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf + 3, 5, _)).WillOnce(SucceedIO());
+ EXPECT_CALL(*mock_file_, Seek(25)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf, 2, _)).WillOnce(SucceedIO());
+ EXPECT_CALL(*mock_file_, Seek(27)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf + 2, 3, _)).WillOnce(SucceedIO());
+
+ size_t bytes_read = 0;
+ EXPECT_TRUE(file.Read(buf, 2, &bytes_read));
+ EXPECT_EQ(2U, bytes_read);
+ EXPECT_TRUE(file.Read(buf, 8, &bytes_read));
+ EXPECT_EQ(8U, bytes_read);
+ EXPECT_TRUE(file.Read(buf, 100, &bytes_read));
+ EXPECT_EQ(5U, bytes_read);
+}
+
+TEST_F(ExtentsFileTest, ReadSmallChunks) {
+ ExtentsFile file(std::move(mock_file_ptr_), {ex_t{10, 1}, ex_t{20, 10}});
+ InSequence s;
+ char* buf = reinterpret_cast<char*>(0x1234);
+
+ EXPECT_CALL(*mock_file_, Seek(10)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(buf, 1, _)).WillOnce(SucceedIO());
+ EXPECT_CALL(*mock_file_, Seek(20)).WillOnce(Return(true));
+ // We expect to read only part of the second extent.
+ EXPECT_CALL(*mock_file_, Read(buf + 1, 1, _)).WillOnce(SucceedIO());
+
+ size_t bytes_read = 0;
+ EXPECT_TRUE(file.Read(buf, 2, &bytes_read));
+ EXPECT_EQ(2U, bytes_read);
+}
+
+TEST_F(ExtentsFileTest, ReadFailureFails) {
+ ExtentsFile file(std::move(mock_file_ptr_), {ex_t{10, 1}, ex_t{20, 10}});
+ EXPECT_CALL(*mock_file_, Seek(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_file_, Read(_, 1, _)).WillOnce(SucceedIO());
+ // A second read that fails will succeed if there was partial data read.
+ EXPECT_CALL(*mock_file_, Read(_, 10, _)).WillOnce(Return(false));
+
+ size_t bytes_read = 0;
+ EXPECT_TRUE(file.Read(nullptr, 100, &bytes_read));
+ EXPECT_EQ(1U, bytes_read);
+}
+
+TEST_F(ExtentsFileTest, ReadFails) {
+ ExtentsFile file(std::move(mock_file_ptr_), {ex_t{10, 1}, ex_t{20, 10}});
+ EXPECT_CALL(*mock_file_, Seek(10)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_file_, Read(_, 1, _)).WillOnce(Return(false));
+ size_t bytes_read;
+ EXPECT_FALSE(file.Read(nullptr, 1, &bytes_read));
+}
+
+TEST_F(ExtentsFileTest, ReadPartialReadsAndEOF) {
+ ExtentsFile file(std::move(mock_file_ptr_), {ex_t{10, 1}, ex_t{20, 10}});
+ EXPECT_CALL(*mock_file_, Seek(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(true));
+ char* buf = reinterpret_cast<char*>(0x1234);
+ InSequence s;
+ EXPECT_CALL(*mock_file_, Read(buf, 1, _)).WillOnce(SucceedIO());
+ EXPECT_CALL(*mock_file_, Read(buf + 1, _, _)).WillOnce(SucceedPartialIO(3));
+ EXPECT_CALL(*mock_file_, Read(buf + 4, _, _)).WillOnce(SucceedPartialIO(0));
+
+ size_t bytes_read = 0;
+ EXPECT_TRUE(file.Read(buf, 100, &bytes_read));
+ EXPECT_EQ(4U, bytes_read);
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/extents_unittest.cc b/bsdiff/extents_unittest.cc
new file mode 100644
index 0000000..84be2e1
--- /dev/null
+++ b/bsdiff/extents_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extents.h"
+
+#include <gtest/gtest.h>
+#include <vector>
+
+namespace bsdiff {
+
+// ex_t comparator used for testing.
+bool operator==(const struct ex_t& lhs, const struct ex_t& rhs) {
+ return lhs.off == rhs.off && lhs.len == rhs.len;
+}
+
+// PrintTo is used by gtest framework whenever it needs to print our type.
+void PrintTo(const struct ex_t& extent, ::std::ostream* os) {
+ *os << extent.off << ":" << extent.len;
+}
+
+class ExtentsTest : public testing::Test {
+ protected:
+ std::vector<ex_t> extents_;
+};
+
+TEST_F(ExtentsTest, CornerCasesHandledTest) {
+ EXPECT_TRUE(ParseExtentStr("", &extents_));
+ EXPECT_TRUE(extents_.empty());
+}
+
+TEST_F(ExtentsTest, SimpleCasesTest) {
+ EXPECT_TRUE(ParseExtentStr("10:20,30:40", &extents_));
+ std::vector<ex_t> expected_values = {{10, 20}, {30, 40}};
+ EXPECT_EQ(expected_values, extents_);
+}
+
+TEST_F(ExtentsTest, MalformedExtentsTest) {
+ std::vector<const char*> test_cases = {
+ ":", ",", "1,2", "1:", "1,", ":2", ",2", "1,2:3", "10:-1", "-2:10"};
+ for (const char* test_case : test_cases) {
+ std::vector<ex_t> extents;
+ EXPECT_FALSE(ParseExtentStr(test_case, &extents)) << "while testing case \""
+ << test_case << "\"";
+ EXPECT_EQ(std::vector<ex_t>(), extents);
+ }
+}
+
+TEST_F(ExtentsTest, NegativeValuesTest) {
+ // |-1| is used as a special case to read zeros for that extent.
+ EXPECT_TRUE(ParseExtentStr("10:20,-1:40,50:60", &extents_));
+ std::vector<ex_t> expected_values = {{10, 20}, {-1, 40}, {50, 60}};
+ EXPECT_EQ(expected_values, extents_);
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/file.cc b/bsdiff/file.cc
new file mode 100644
index 0000000..11b6104
--- /dev/null
+++ b/bsdiff/file.cc
@@ -0,0 +1,131 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <linux/fs.h>
+#endif // __linux__
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// TEMP_FAILURE_RETRY is defined by some versions of <unistd.h>.
+#ifndef TEMP_FAILURE_RETRY
+#include <utils/Compat.h>
+#endif
+
+#include <algorithm>
+
+namespace bsdiff {
+
+std::unique_ptr<File> File::FOpen(const char* pathname, int flags) {
+ int fd = TEMP_FAILURE_RETRY(open(pathname, flags, 0644));
+ if (fd < 0)
+ return std::unique_ptr<File>();
+ return std::unique_ptr<File>(new File(fd));
+}
+
+File::~File() {
+ Close();
+}
+
+bool File::Read(void* buf, size_t count, size_t* bytes_read) {
+ if (fd_ < 0) {
+ errno = EBADF;
+ return false;
+ }
+ ssize_t rc = TEMP_FAILURE_RETRY(read(fd_, buf, count));
+ if (rc == -1)
+ return false;
+ *bytes_read = static_cast<size_t>(rc);
+ return true;
+}
+
+bool File::Write(const void* buf, size_t count, size_t* bytes_written) {
+ if (fd_ < 0) {
+ errno = EBADF;
+ return false;
+ }
+ ssize_t rc = TEMP_FAILURE_RETRY(write(fd_, buf, count));
+ if (rc == -1)
+ return false;
+ *bytes_written = static_cast<size_t>(rc);
+ return true;
+}
+
+bool File::Seek(off_t pos) {
+ if (fd_ < 0) {
+ errno = EBADF;
+ return false;
+ }
+ // fseek() uses a long value for the offset which could be smaller than off_t.
+ if (pos > std::numeric_limits<long>::max()) {
+ errno = EOVERFLOW;
+ return false;
+ }
+ off_t newpos = lseek(fd_, pos, SEEK_SET);
+ if (newpos < 0)
+ return false;
+ if (newpos != pos) {
+ errno = EINVAL;
+ return false;
+ }
+ return true;
+}
+
+bool File::Close() {
+ if (fd_ < 0) {
+ errno = EBADF;
+ return false;
+ }
+ bool success = close(fd_) == 0;
+ if (!success && errno == EINTR)
+ success = true;
+ fd_ = -1;
+ return success;
+}
+
+bool File::GetSize(uint64_t* size) {
+ struct stat stbuf;
+ if (fstat(fd_, &stbuf) == -1)
+ return false;
+ if (S_ISREG(stbuf.st_mode)) {
+ *size = stbuf.st_size;
+ return true;
+ }
+ if (S_ISBLK(stbuf.st_mode)) {
+#if defined(BLKGETSIZE64)
+ return ioctl(fd_, BLKGETSIZE64, size);
+#elif defined(DKIOCGETBLOCKCOUNT)
+ uint64_t sectors = 0;
+ if (ioctl(fd_, DKIOCGETBLOCKCOUNT, §ors) == 0) {
+ *size = sectors << 9;
+ return true;
+ }
+ return false;
+#else
+ // Fall back to doing seeks to know the EOF.
+ off_t pos = lseek(fd_, 0, SEEK_CUR);
+ if (pos == -1)
+ return false;
+ off_t end_pos = lseek(fd_, 0, SEEK_END);
+ if (end_pos == -1)
+ return false;
+ *size = end_pos;
+ lseek(fd_, 0, SEEK_END);
+ return true;
+#endif
+ }
+ return false;
+}
+
+File::File(int fd)
+ : fd_(fd) {}
+
+} // namespace bsdiff
diff --git a/bsdiff/file.h b/bsdiff/file.h
new file mode 100644
index 0000000..ad1fd2f
--- /dev/null
+++ b/bsdiff/file.h
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_FILE_H_
+#define _BSDIFF_FILE_H_
+
+#include <memory>
+
+#include "file_interface.h"
+
+namespace bsdiff {
+
+class File : public FileInterface {
+ public:
+ // Opens a file |pathname| with flags |flags| as defined by open(2). In case
+ // of error, an empty unique_ptr is returned and errno is set accordingly.
+ static std::unique_ptr<File> FOpen(const char* pathname, int flags);
+
+ ~File() override;
+
+ // FileInterface overrides.
+ bool Read(void* buf, size_t count, size_t* bytes_read) override;
+ bool Write(const void* buf, size_t count, size_t* bytes_written) override;
+ bool Seek(off_t pos) override;
+ bool Close() override;
+ bool GetSize(uint64_t* size) override;
+
+ private:
+ // Creates the File instance for the |fd|. Takes ownership of the file
+ // descriptor.
+ explicit File(int fd);
+
+ int fd_;
+};
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_FILE_H_
diff --git a/bsdiff/file_interface.h b/bsdiff/file_interface.h
new file mode 100644
index 0000000..a643f45
--- /dev/null
+++ b/bsdiff/file_interface.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_FILE_INTERFACE_H_
+#define _BSDIFF_FILE_INTERFACE_H_
+
+#include <sys/types.h>
+
+namespace bsdiff {
+
+class FileInterface {
+ public:
+ virtual ~FileInterface() = default;
+
+ // Reads synchronously from the current file position up to |count| bytes into
+ // the passed |buf| buffer. On success, stores in |bytes_read| how many bytes
+ // were actually read from the file. In case of error returns false. This
+ // method may read less than |count| bytes even if there's no error. If the
+ // end of file is reached, 0 bytes will be read and this methods returns true.
+ virtual bool Read(void* buf, size_t count, size_t* bytes_read) = 0;
+
+ // Writes synchronously up to |count| bytes from to passed |buf| buffer to
+ // the file. On success, stores in |bytes_written| how many bytes
+ // were actually written to the file. This method may write less than |count|
+ // bytes and return successfully, or even write 0 bytes if there's no more
+ // space left on the device. Returns whether the write succeeded.
+ virtual bool Write(const void* buf, size_t count, size_t* bytes_written) = 0;
+
+ // Change the current file position to |pos| bytes from the beginning of the
+ // file. Return whether the seek succeeded.
+ virtual bool Seek(off_t pos) = 0;
+
+ // Closes the file and flushes any cached data. Returns whether the close
+ // succeeded.
+ virtual bool Close() = 0;
+
+ // Compute the size of the file and store it in |size|. Returns whether it
+ // computed the size successfully.
+ virtual bool GetSize(uint64_t* size) = 0;
+
+ protected:
+ FileInterface() = default;
+};
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_FILE_INTERFACE_H_
diff --git a/bsdiff/memory_file.cc b/bsdiff/memory_file.cc
new file mode 100644
index 0000000..59e2c7d
--- /dev/null
+++ b/bsdiff/memory_file.cc
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "memory_file.h"
+
+#include "bspatch.h"
+
+namespace bsdiff {
+
+MemoryFile::MemoryFile(std::unique_ptr<FileInterface> file, size_t size)
+ : file_(std::move(file)) {
+ buffer_.reserve(size);
+}
+
+MemoryFile::~MemoryFile() {
+ Close();
+}
+
+bool MemoryFile::Read(void* buf, size_t count, size_t* bytes_read) {
+ return false;
+}
+
+bool MemoryFile::Write(const void* buf, size_t count, size_t* bytes_written) {
+ const uint8_t* data = static_cast<const uint8_t*>(buf);
+ buffer_.insert(buffer_.end(), data, data + count);
+ *bytes_written = count;
+ return true;
+}
+
+bool MemoryFile::Seek(off_t pos) {
+ return false;
+}
+
+bool MemoryFile::Close() {
+ if (!WriteAll(file_, buffer_.data(), buffer_.size()))
+ return false;
+ // Prevent writing |buffer_| to |file_| again if Close() is called more than
+ // once.
+ buffer_.clear();
+ return file_->Close();
+}
+
+bool MemoryFile::GetSize(uint64_t* size) {
+ *size = buffer_.size();
+ return true;
+}
+
+} // namespace bsdiff
diff --git a/bsdiff/memory_file.h b/bsdiff/memory_file.h
new file mode 100644
index 0000000..3e80b8a
--- /dev/null
+++ b/bsdiff/memory_file.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_MEMORY_FILE_H_
+#define _BSDIFF_MEMORY_FILE_H_
+
+#include <memory>
+#include <vector>
+
+#include "file_interface.h"
+
+namespace bsdiff {
+
+class MemoryFile : public FileInterface {
+ public:
+ // Creates a MemoryFile based on the underlying |file| passed. The MemoryFile
+ // will cache all the write in memory and write it to to |file| when it's
+ // closed. MemoryFile does not support read and seek.
+ // |size| should be the estimated total file size, it is used to reserve
+ // buffer space.
+ MemoryFile(std::unique_ptr<FileInterface> file, size_t size);
+
+ ~MemoryFile() override;
+
+ // FileInterface overrides.
+ bool Read(void* buf, size_t count, size_t* bytes_read) override;
+ bool Write(const void* buf, size_t count, size_t* bytes_written) override;
+ bool Seek(off_t pos) override;
+ bool Close() override;
+ bool GetSize(uint64_t* size) override;
+
+ private:
+ // The underlying FileInterace instance.
+ std::unique_ptr<FileInterface> file_;
+
+ std::vector<uint8_t> buffer_;
+};
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_MEMORY_FILE_H_
diff --git a/bsdiff/test_utils.cc b/bsdiff/test_utils.cc
new file mode 100644
index 0000000..f369f80
--- /dev/null
+++ b/bsdiff/test_utils.cc
@@ -0,0 +1,146 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "test_utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+#include <vector>
+
+using std::string;
+using std::vector;
+
+namespace {
+
+// If |path| is absolute, or explicit relative to the current working directory,
+// leaves it as is. Otherwise, if TMPDIR is defined in the environment and is
+// non-empty, prepends it to |path|. Otherwise, prepends /tmp. Returns the
+// resulting path.
+const string PrependTmpdir(const string& path) {
+ if (path[0] == '/')
+ return path;
+
+ const char* tmpdir = getenv("TMPDIR");
+ const string prefix = (tmpdir && *tmpdir ? tmpdir : "/tmp");
+ return prefix + "/" + path;
+}
+
+bool MakeTempFile(const string& base_filename_template, string* filename) {
+ const string filename_template = PrependTmpdir(base_filename_template);
+ vector<char> result(filename_template.size() + 1, '\0');
+ memcpy(result.data(), filename_template.data(), filename_template.size());
+
+ int mkstemp_fd = mkstemp(result.data());
+ if (mkstemp_fd < 0) {
+ perror("mkstemp()");
+ return false;
+ }
+ close(mkstemp_fd);
+
+ if (filename)
+ *filename = result.data();
+ return true;
+}
+
+} // namespace
+
+namespace test_utils {
+
+void BsdiffTestEnvironment::SetUp() {
+#ifdef BSDIFF_TARGET_UNITTEST
+#define BSDIFF_TARGET_TMP_BASE "/data/tmp"
+ if (access(BSDIFF_TARGET_TMP_BASE, F_OK) == -1) {
+ mkdir(BSDIFF_TARGET_TMP_BASE, S_IRWXU | S_IRWXG | S_IROTH | S_IWOTH);
+ }
+ setenv("TMPDIR", BSDIFF_TARGET_TMP_BASE, 1);
+#endif // defined (BSDIFF_TARGET_UNITTEST)
+}
+
+bool ReadFile(const string& path, vector<uint8_t>* out) {
+ FILE* fp = fopen(path.c_str(), "r");
+ if (!fp)
+ return false;
+ out->clear();
+
+ uint8_t buf[16 * 1024];
+ while (true) {
+ size_t bytes_read = fread(buf, 1, sizeof(buf), fp);
+ if (!bytes_read)
+ break;
+ out->insert(out->end(), buf, buf + bytes_read);
+ }
+ bool result = !ferror(fp);
+ fclose(fp);
+ return result;
+}
+
+bool WriteFile(const string& path, vector<uint8_t> contents) {
+ FILE* fp = fopen(path.c_str(), "r");
+ if (!fp)
+ return false;
+ size_t written = fwrite(contents.data(), 1, contents.size(), fp);
+ bool result = written == contents.size() && !ferror(fp);
+ fclose(fp);
+ return result;
+}
+
+ScopedTempFile::ScopedTempFile(const string& pattern) {
+ EXPECT_TRUE(MakeTempFile(pattern, &filename_));
+}
+
+ScopedTempFile::~ScopedTempFile() {
+ if (!filename_.empty() && unlink(filename_.c_str()) < 0) {
+ perror("Unable to remove temporary file");
+ }
+}
+
+bool BsdiffPatchFile::LoadFromFile(const string& filename) {
+ vector<uint8_t> contents;
+ if (!ReadFile(filename, &contents))
+ return false;
+ file_size = contents.size();
+ // Check that the file includes at least the header.
+ TEST_AND_RETURN_FALSE(contents.size() >= kHeaderSize);
+ magic = string(contents.data(), contents.data() + 8);
+ memcpy(&ctrl_len, contents.data() + 8, sizeof(ctrl_len));
+ memcpy(&diff_len, contents.data() + 16, sizeof(diff_len));
+ memcpy(&new_file_len, contents.data() + 24, sizeof(new_file_len));
+
+ // Sanity check before we attempt to parse the bz2 streams.
+ TEST_AND_RETURN_FALSE(ctrl_len >= 0);
+ TEST_AND_RETURN_FALSE(diff_len >= 0);
+
+ // The cast is safe since ctrl_len and diff_len are both positive.
+ TEST_AND_RETURN_FALSE(file_size >=
+ static_cast<uint64_t>(kHeaderSize + ctrl_len + diff_len));
+ extra_len = file_size - kHeaderSize - ctrl_len - diff_len;
+
+ uint8_t* ptr = contents.data() + kHeaderSize;
+ bz2_ctrl = vector<uint8_t>(ptr, ptr + ctrl_len);
+ ptr += ctrl_len;
+ bz2_diff = vector<uint8_t>(ptr, ptr + diff_len);
+ ptr += diff_len;
+ bz2_extra = vector<uint8_t>(ptr, ptr + extra_len);
+
+ return true;
+}
+
+bool BsdiffPatchFile::IsValid() const {
+ TEST_AND_RETURN_FALSE(ctrl_len >= 0);
+ TEST_AND_RETURN_FALSE(diff_len >= 0);
+ TEST_AND_RETURN_FALSE(new_file_len >= 0);
+
+ // TODO(deymo): Test that the length of the decompressed bz2 streams |diff|
+ // plus |extra| are equal to the |new_file_len|.
+ // TODO(deymo): Test that all the |bz2_ctrl| triplets (x, y, z) have a "x"
+ // and "y" value >= 0 ("z" can be negative).
+ return true;
+}
+
+} // namespace test_utils
diff --git a/bsdiff/test_utils.h b/bsdiff/test_utils.h
new file mode 100644
index 0000000..7132fe5
--- /dev/null
+++ b/bsdiff/test_utils.h
@@ -0,0 +1,94 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_TEST_UTILS_H_
+#define _BSDIFF_TEST_UTILS_H_
+
+#include <gtest/gtest.h>
+#include <string>
+#include <vector>
+
+#define TEST_AND_RETURN_FALSE(_x) \
+ do { \
+ if (!static_cast<bool>(_x)) { \
+ fprintf(stderr, "%s failed.", #_x); \
+ return false; \
+ } \
+ } while (0)
+
+namespace test_utils {
+
+class BsdiffTestEnvironment : public ::testing::Environment {
+ public:
+ virtual void SetUp();
+};
+
+// Reads all the contents of the file |path| into |out|. Returns whether it
+// read up to the end of file.
+bool ReadFile(const std::string& path, std::vector<uint8_t>* out);
+
+// Overrides the file |path| with the contents passed in |out|. Returns whether
+// the operation succeeded.
+bool WriteFile(const std::string& path, std::vector<uint8_t> contents);
+
+// Utility class to create and delete a temp file.
+class ScopedTempFile {
+ public:
+ // Creates a temp file with the passed |pattern|. The pattern should end with
+ // "XXXXXX", that will be replaced with a random string. The file will be
+ // removed when this instance is destroyed.
+ explicit ScopedTempFile(const std::string& pattern);
+ ~ScopedTempFile();
+
+ std::string filename() const { return filename_; }
+ const char* c_str() const { return filename_.c_str(); }
+
+ // Releases the temporary file. It will not be deleted when this instance is
+ // destroyed.
+ void release() { filename_.clear(); }
+
+ private:
+ std::string filename_;
+};
+
+// This struct representes a parsed BSDIFF40 file.
+struct BsdiffPatchFile {
+ static const size_t kHeaderSize = 32;
+
+ // Parses a BSDIFF40 file and stores the contents in the local methods.
+ bool LoadFromFile(const std::string& filename);
+
+ // Returns wheter the patch file is valid.
+ bool IsValid() const;
+
+ // The magic string in the header file. Normally "BSDIFF40".
+ std::string magic;
+
+ // The length of the first (ctrl) bzip2 stream. Negative values are invalid.
+ int64_t ctrl_len = -1;
+
+ // The length of the first (diff) bzip2 stream. Negative values are invalid.
+ int64_t diff_len = -1;
+
+ // The length of the first (diff) bzip2 stream. This value is not stored in
+ // the file, but generated based on the |file_size|.
+ uint64_t extra_len = 0;
+
+ // The length of the new file after applying the patch. Negative values are
+ // invalid.
+ int64_t new_file_len = -1;
+
+ // The three compressed streams.
+ std::vector<uint8_t> bz2_ctrl;
+ std::vector<uint8_t> bz2_diff;
+ std::vector<uint8_t> bz2_extra;
+
+ uint64_t file_size = 0;
+};
+
+
+} // namespace test_utils
+
+
+#endif // _BSDIFF_TEST_UTILS_H_
diff --git a/bsdiff/testrunner.cc b/bsdiff/testrunner.cc
new file mode 100644
index 0000000..8b598e2
--- /dev/null
+++ b/bsdiff/testrunner.cc
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "test_utils.h"
+
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(new test_utils::BsdiffTestEnvironment);
+ return RUN_ALL_TESTS();
+}