mirror of
https://github.com/expo/bsdiff.git
synced 2026-11-14 17:49:03 -10:00
resolve merge conflicts of f4e69fd to stage-aosp-master
am: f074bc8571
Change-Id: Id89d32f6fb8266dcbb490f100aec103956120984
This commit is contained in:
commit
884a50d9d7
10 files changed
+560
-274
No files matched your search
+113
@@ -0,0 +1,113 @@
|
||||
// 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.
|
||||
|
||||
cc_defaults {
|
||||
name: "bsdiff_defaults",
|
||||
host_supported: true,
|
||||
static_libs: ["libbz"],
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
// TODO(deymo): Remove include/bsdiff when all callers use the "bsdiff/"
|
||||
// prefix when including code.
|
||||
"include/bsdiff",
|
||||
],
|
||||
cflags: [
|
||||
"-D_FILE_OFFSET_BITS=64",
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
}
|
||||
|
||||
// Host and target static libraries.
|
||||
cc_library_static {
|
||||
name: "libbspatch",
|
||||
defaults: ["bsdiff_defaults"],
|
||||
|
||||
srcs: [
|
||||
"bspatch.cc",
|
||||
"buffer_file.cc",
|
||||
"extents.cc",
|
||||
"extents_file.cc",
|
||||
"file.cc",
|
||||
"memory_file.cc",
|
||||
"sink_file.cc",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libbsdiff",
|
||||
defaults: ["bsdiff_defaults"],
|
||||
|
||||
srcs: [
|
||||
"bsdiff.cc",
|
||||
"bz2_compressor.cc",
|
||||
"patch_writer.cc",
|
||||
],
|
||||
static_libs: [
|
||||
"libdivsufsort64",
|
||||
"libdivsufsort",
|
||||
],
|
||||
}
|
||||
|
||||
// Host and target Executables.
|
||||
cc_binary {
|
||||
name: "bspatch",
|
||||
defaults: ["bsdiff_defaults"],
|
||||
|
||||
srcs: ["bspatch_main.cc"],
|
||||
static_libs: [
|
||||
"libbspatch",
|
||||
"libbz",
|
||||
],
|
||||
}
|
||||
|
||||
// Host executables, bsdiff is only built for the host.
|
||||
cc_binary_host {
|
||||
name: "bsdiff",
|
||||
defaults: ["bsdiff_defaults"],
|
||||
|
||||
srcs: ["bsdiff_main.cc"],
|
||||
static_libs: [
|
||||
"libbsdiff",
|
||||
"libdivsufsort64",
|
||||
"libdivsufsort",
|
||||
"libbz",
|
||||
],
|
||||
}
|
||||
|
||||
// Unit tests.
|
||||
cc_test {
|
||||
name: "bsdiff_unittest",
|
||||
defaults: ["bsdiff_defaults"],
|
||||
test_suites: ["device-tests"],
|
||||
srcs: [
|
||||
"bsdiff_unittest.cc",
|
||||
"bspatch_unittest.cc",
|
||||
"extents_file_unittest.cc",
|
||||
"extents_unittest.cc",
|
||||
"test_utils.cc",
|
||||
"testrunner.cc",
|
||||
],
|
||||
cflags: ["-DBSDIFF_TARGET_UNITTEST"],
|
||||
static_libs: [
|
||||
"libbsdiff",
|
||||
"libbspatch",
|
||||
"libgmock",
|
||||
"libdivsufsort64",
|
||||
"libdivsufsort",
|
||||
"libbz",
|
||||
],
|
||||
}
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
# 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 \
|
||||
buffer_file.cc \
|
||||
extents.cc \
|
||||
extents_file.cc \
|
||||
file.cc \
|
||||
memory_file.cc \
|
||||
sink_file.cc
|
||||
|
||||
# Unit test files.
|
||||
bsdiff_common_unittests := \
|
||||
bsdiff_unittest.cc \
|
||||
bspatch_unittest.cc \
|
||||
extents_file_unittest.cc \
|
||||
extents_unittest.cc \
|
||||
test_utils.cc
|
||||
|
||||
# TODO(deymo): Remove include/bsdiff when all callers use the "bsdiff/" prefix
|
||||
# when including code.
|
||||
lib_export_includes := \
|
||||
$(LOCAL_PATH)/include \
|
||||
$(LOCAL_PATH)/include/bsdiff
|
||||
|
||||
# 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_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(lib_export_includes)
|
||||
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_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(lib_export_includes)
|
||||
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_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(lib_export_includes)
|
||||
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_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(lib_export_includes)
|
||||
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)
|
||||
|
||||
# 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_COMPATIBILITY_SUITE := device-tests
|
||||
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)
|
||||
@@ -33,7 +33,9 @@ clean:
|
||||
### List of source files for each project. Keep in sync with the Android.mk.
|
||||
# "bsdiff" program.
|
||||
bsdiff_src_files := \
|
||||
bsdiff.cc
|
||||
bsdiff.cc \
|
||||
bz2_compressor.cc \
|
||||
patch_writer.cc
|
||||
|
||||
# "bspatch" program.
|
||||
bspatch_src_files := \
|
||||
|
||||
@@ -32,7 +32,6 @@ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c,v 1.1 2005/08/06 01:59:05
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
@@ -42,6 +41,8 @@ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c,v 1.1 2005/08/06 01:59:05
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "patch_writer.h"
|
||||
|
||||
namespace bsdiff {
|
||||
|
||||
static off_t matchlen(const u_char* old, off_t oldsize, const u_char* new_buf,
|
||||
@@ -85,24 +86,6 @@ static off_t search(saidx_t* I, const u_char* old, off_t oldsize,
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -148,13 +131,7 @@ int bsdiff(const u_char* old_buf, off_t oldsize, const u_char* new_buf,
|
||||
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;
|
||||
BsdiffPatchWriter patch(old_buf, oldsize, new_buf, newsize);
|
||||
|
||||
if (I_cache && *I_cache) {
|
||||
I = *I_cache;
|
||||
@@ -167,35 +144,11 @@ int bsdiff(const u_char* old_buf, off_t oldsize, const u_char* new_buf,
|
||||
*I_cache = I;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!patch.Open(patch_filename))
|
||||
return 1;
|
||||
|
||||
/* 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) {
|
||||
@@ -271,79 +224,19 @@ int bsdiff(const u_char* old_buf, off_t oldsize, const u_char* new_buf,
|
||||
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);
|
||||
if (!patch.AddControlEntry(ControlEntry(lenf,
|
||||
(scan - lenb) - (lastscan + lenf),
|
||||
(pos - lenb) - (lastpos + lenf))))
|
||||
errx(1, "Writing a control entry");
|
||||
|
||||
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);
|
||||
if (!patch.Close())
|
||||
errx(1, "Closing the patch file");
|
||||
|
||||
/* 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);
|
||||
if (I_cache == nullptr)
|
||||
free(I);
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
},
|
||||
'sources': [
|
||||
'bsdiff.cc',
|
||||
"bz2_compressor.cc",
|
||||
"patch_writer.cc",
|
||||
],
|
||||
},
|
||||
# bsdiff executable
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright 2017 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 "bz2_compressor.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
using std::endl;
|
||||
|
||||
namespace {
|
||||
|
||||
// The BZ2 compression level used. Smaller compression levels are nowadays
|
||||
// pointless.
|
||||
const int kCompressionLevel = 9;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace bsdiff {
|
||||
|
||||
BZ2Compressor::BZ2Compressor() {
|
||||
memset(&bz_strm_, 0, sizeof(bz_strm_));
|
||||
int bz_err = BZ2_bzCompressInit(&bz_strm_, kCompressionLevel,
|
||||
0 /* verbosity */, 0 /* workFactor */);
|
||||
if (bz_err != BZ_OK) {
|
||||
LOG(ERROR) << "Initializing bz_strm, bz_error=" << bz_err << endl;
|
||||
} else {
|
||||
bz_strm_initialized_ = true;
|
||||
}
|
||||
comp_buffer_.resize(1024 * 1024);
|
||||
}
|
||||
|
||||
BZ2Compressor::~BZ2Compressor() {
|
||||
if (!bz_strm_initialized_)
|
||||
return;
|
||||
int bz_err = BZ2_bzCompressEnd(&bz_strm_);
|
||||
if (bz_err != BZ_OK) {
|
||||
LOG(ERROR) << "Deleting the compressor stream, bz_error=" << bz_err << endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool BZ2Compressor::Write(const uint8_t* buf, size_t size) {
|
||||
if (!bz_strm_initialized_)
|
||||
return false;
|
||||
|
||||
// The bz_stream struct defines the next_in as a non-const data pointer,
|
||||
// although the documentation says it won't modify it.
|
||||
bz_strm_.next_in = reinterpret_cast<char*>(const_cast<uint8_t*>(buf));
|
||||
bz_strm_.avail_in = size;
|
||||
|
||||
while (bz_strm_.avail_in) {
|
||||
bz_strm_.next_out = reinterpret_cast<char*>(comp_buffer_.data());
|
||||
bz_strm_.avail_out = comp_buffer_.size();
|
||||
int bz_err = BZ2_bzCompress(&bz_strm_, BZ_RUN);
|
||||
if (bz_err != BZ_RUN_OK) {
|
||||
LOG(ERROR) << "Compressing data, bz_error=" << bz_err << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t output_bytes = comp_buffer_.size() - bz_strm_.avail_out;
|
||||
if (output_bytes) {
|
||||
comp_chunks_.emplace_back(comp_buffer_.data(),
|
||||
comp_buffer_.data() + output_bytes);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BZ2Compressor::Finish() {
|
||||
if (!bz_strm_initialized_)
|
||||
return false;
|
||||
bz_strm_.next_in = nullptr;
|
||||
bz_strm_.avail_in = 0;
|
||||
|
||||
int bz_err = BZ_FINISH_OK;
|
||||
while (bz_err == BZ_FINISH_OK) {
|
||||
bz_strm_.next_out = reinterpret_cast<char*>(comp_buffer_.data());
|
||||
bz_strm_.avail_out = comp_buffer_.size();
|
||||
bz_err = BZ2_bzCompress(&bz_strm_, BZ_FINISH);
|
||||
|
||||
uint64_t output_bytes = comp_buffer_.size() - bz_strm_.avail_out;
|
||||
if (output_bytes) {
|
||||
comp_chunks_.emplace_back(comp_buffer_.data(),
|
||||
comp_buffer_.data() + output_bytes);
|
||||
}
|
||||
}
|
||||
if (bz_err != BZ_STREAM_END) {
|
||||
LOG(ERROR) << "Finishing compressing data, bz_error=" << bz_err << endl;
|
||||
return false;
|
||||
}
|
||||
bz_err = BZ2_bzCompressEnd(&bz_strm_);
|
||||
bz_strm_initialized_ = false;
|
||||
if (bz_err != BZ_OK) {
|
||||
LOG(ERROR) << "Deleting the compressor stream, bz_error=" << bz_err << endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& BZ2Compressor::GetCompressedData() {
|
||||
if (!comp_chunks_.empty()) {
|
||||
size_t chunks_size = 0;
|
||||
for (const auto& chunk : comp_chunks_)
|
||||
chunks_size += chunk.size();
|
||||
comp_data_.reserve(comp_data_.size() + chunks_size);
|
||||
for (const auto& chunk : comp_chunks_) {
|
||||
comp_data_.insert(comp_data_.end(), chunk.begin(), chunk.end());
|
||||
}
|
||||
comp_chunks_.clear();
|
||||
}
|
||||
return comp_data_;
|
||||
}
|
||||
|
||||
} // namespace bsdiff
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2017 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_BZ2_COMPRESSOR_H_
|
||||
#define _BSDIFF_BZ2_COMPRESSOR_H_
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace bsdiff {
|
||||
|
||||
// An in-memory class to wrap the low-level bzip2 compress functions. This class
|
||||
// allows to stream uncompressed data to it and then retrieve all the compressed
|
||||
// data at the end of the compression step. For that, all the compressed data
|
||||
// is stored in memory.
|
||||
|
||||
class BZ2Compressor {
|
||||
public:
|
||||
BZ2Compressor();
|
||||
~BZ2Compressor();
|
||||
|
||||
// Compress and write the data at |buf| of size |size|.
|
||||
bool Write(const uint8_t* buf, size_t size);
|
||||
|
||||
// Finish the compression step.
|
||||
bool Finish();
|
||||
|
||||
// Return the compressed data. This method must be only called after Finish().
|
||||
const std::vector<uint8_t>& GetCompressedData();
|
||||
|
||||
private:
|
||||
// The low-level bzip2 stream.
|
||||
bz_stream bz_strm_;
|
||||
|
||||
// Whether the bz_strm_ is initialized.
|
||||
bool bz_strm_initialized_{false};
|
||||
|
||||
// A list of chunks of compressed data. The final compressed representation is
|
||||
// the concatenation of all the compressed data.
|
||||
std::vector<std::vector<uint8_t>> comp_chunks_;
|
||||
|
||||
// A concatenated version of the |comp_chunks_|, used to store the compressed
|
||||
// memory after Finish() is called.
|
||||
std::vector<uint8_t> comp_data_;
|
||||
|
||||
// A temporary compression buffer for multiple calls to Write().
|
||||
std::vector<uint8_t> comp_buffer_;
|
||||
};
|
||||
|
||||
} // namespace bsdiff
|
||||
|
||||
#endif // _BSDIFF_BZ2_COMPRESSOR_H_
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017 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_LOGGING_H_
|
||||
#define _BSDIFF_LOGGING_H_
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// Simple error logging macro to avoid dependencies in other base libraries.
|
||||
// Unfortunately, you must pass std::endl as the last element on the stream to
|
||||
// produce the new line.
|
||||
#define LOG(X) (std::cerr << #X << " " << __FILE__ << ":" << __LINE__ << ": ")
|
||||
|
||||
#endif // _BSDIFF_LOGGING_H_
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
// Copyright 2017 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 "patch_writer.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
using std::endl;
|
||||
|
||||
namespace {
|
||||
|
||||
// The maximum positive number that we should encode. A number larger than this
|
||||
// for unsigned fields will be interpreted as a negative value and thus a
|
||||
// corrupt patch.
|
||||
const uint64_t kMaxEncodedUint64Value = (1ULL << 63) - 1;
|
||||
|
||||
constexpr uint8_t kMagicHeader[] = "BSDIFF40";
|
||||
|
||||
void EncodeInt64(int64_t x, uint8_t* buf) {
|
||||
uint64_t y = x < 0 ? (1ULL << 63ULL) - x : x;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
buf[i] = y & 0xff;
|
||||
y /= 256;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace bsdiff {
|
||||
|
||||
bool BsdiffPatchWriter::Open(const std::string& patch_filename) {
|
||||
fp_ = fopen(patch_filename.c_str(), "w");
|
||||
if (!fp_) {
|
||||
LOG(ERROR) << "Opening " << patch_filename << endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::AddControlEntry(const ControlEntry& entry) {
|
||||
if (entry.diff_size > kMaxEncodedUint64Value) {
|
||||
LOG(ERROR) << "Encoding value out of range " << entry.diff_size << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.extra_size > kMaxEncodedUint64Value) {
|
||||
LOG(ERROR) << "Encoding value out of range " << entry.extra_size << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (written_output_ + entry.diff_size + entry.extra_size > new_size_) {
|
||||
LOG(ERROR) << "Wrote more output than the declared new_size" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.diff_size > 0 &&
|
||||
(old_pos_ < 0 || static_cast<uint64_t>(old_pos_) >= old_size_)) {
|
||||
LOG(ERROR) << "The pointer in the old stream (" << old_pos_
|
||||
<< ") is out of bounds [0, " << old_size_ << ")" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate the 24 byte control entry.
|
||||
uint8_t buf[24];
|
||||
EncodeInt64(entry.diff_size, buf);
|
||||
EncodeInt64(entry.extra_size, buf + 8);
|
||||
EncodeInt64(entry.offset_increment, buf + 16);
|
||||
if (!ctrl_stream_.Write(buf, sizeof(buf)))
|
||||
return false;
|
||||
|
||||
// Generate the diff stream.
|
||||
std::vector<uint8_t> diff(entry.diff_size);
|
||||
for (uint64_t i = 0; i < entry.diff_size; ++i) {
|
||||
diff[i] = new_buf_[written_output_ + i] - old_buf_[old_pos_ + i];
|
||||
}
|
||||
if (!diff_stream_.Write(diff.data(), diff.size()))
|
||||
return false;
|
||||
|
||||
if (!extra_stream_.Write(new_buf_ + written_output_ + entry.diff_size,
|
||||
entry.extra_size))
|
||||
return false;
|
||||
|
||||
old_pos_ += entry.diff_size + entry.offset_increment;
|
||||
written_output_ += entry.diff_size + entry.extra_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::Close() {
|
||||
if (!fp_) {
|
||||
LOG(ERROR) << "File not open." << endl;
|
||||
return false;
|
||||
}
|
||||
if (written_output_ != new_size_) {
|
||||
LOG(ERROR) << "Close() called but not all the output was written" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ctrl_stream_.Finish() || !diff_stream_.Finish() ||
|
||||
!extra_stream_.Finish())
|
||||
return false;
|
||||
|
||||
auto ctrl_data = ctrl_stream_.GetCompressedData();
|
||||
auto diff_data = diff_stream_.GetCompressedData();
|
||||
auto extra_data = extra_stream_.GetCompressedData();
|
||||
|
||||
if (!WriteHeader(ctrl_data.size(), diff_data.size()))
|
||||
return false;
|
||||
|
||||
if (fwrite(ctrl_data.data(), 1, ctrl_data.size(), fp_) != ctrl_data.size()) {
|
||||
LOG(ERROR) << "Writing ctrl_data." << endl;
|
||||
return false;
|
||||
}
|
||||
if (fwrite(diff_data.data(), 1, diff_data.size(), fp_) != diff_data.size()) {
|
||||
LOG(ERROR) << "Writing diff_data." << endl;
|
||||
return false;
|
||||
}
|
||||
if (fwrite(extra_data.data(), 1, extra_data.size(), fp_) !=
|
||||
extra_data.size()) {
|
||||
LOG(ERROR) << "Writing extra_data." << endl;
|
||||
return false;
|
||||
}
|
||||
if (fclose(fp_) != 0) {
|
||||
LOG(ERROR) << "Closing the patch file." << endl;
|
||||
return false;
|
||||
}
|
||||
fp_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchWriter::WriteHeader(uint64_t ctrl_size, uint64_t diff_size) {
|
||||
/* Header format 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 format is
|
||||
* 0 32 Header
|
||||
* 32 ?? Bzip2ed ctrl block
|
||||
* ?? ?? Bzip2ed diff block
|
||||
* ?? ?? Bzip2ed extra block
|
||||
*/
|
||||
uint8_t header[32];
|
||||
memcpy(header, kMagicHeader, 8);
|
||||
EncodeInt64(ctrl_size, header + 8);
|
||||
EncodeInt64(diff_size, header + 16);
|
||||
EncodeInt64(new_size_, header + 24);
|
||||
if (fwrite(header, sizeof(header), 1, fp_) != 1) {
|
||||
LOG(ERROR) << "writing to the patch file" << endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace bsdiff
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2017 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_PATCH_WRITER_H_
|
||||
#define _BSDIFF_PATCH_WRITER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "bsdiff/common.h"
|
||||
#include "bz2_compressor.h"
|
||||
|
||||
namespace bsdiff {
|
||||
|
||||
struct ControlEntry {
|
||||
ControlEntry(uint64_t diff_size,
|
||||
uint64_t extra_size,
|
||||
int64_t offset_increment)
|
||||
: diff_size(diff_size),
|
||||
extra_size(extra_size),
|
||||
offset_increment(offset_increment) {}
|
||||
|
||||
// The number of bytes to copy from the source and diff stream.
|
||||
uint64_t diff_size;
|
||||
|
||||
// The number of bytes to copy from the extra stream.
|
||||
uint64_t extra_size;
|
||||
|
||||
// The value to add to the source pointer after patching from the diff stream.
|
||||
int64_t offset_increment;
|
||||
};
|
||||
|
||||
class BsdiffPatchWriter {
|
||||
public:
|
||||
BsdiffPatchWriter(const uint8_t* old_buf,
|
||||
uint64_t old_size,
|
||||
const uint8_t* new_buf,
|
||||
uint64_t new_size)
|
||||
: old_buf_(old_buf),
|
||||
old_size_(old_size),
|
||||
new_buf_(new_buf),
|
||||
new_size_(new_size) {}
|
||||
|
||||
// Create the file |patch_filename| where the patch will be written to.
|
||||
bool Open(const std::string& patch_filename);
|
||||
|
||||
// Add a new control triplet entry to the patch. The |entry.diff_size| bytes
|
||||
// for the diff stream and the |entry.extra_size| bytes for the extra stream
|
||||
// will be computed and added to the corresponding streams in the patch.
|
||||
// Returns whether the operation succeeded. The operation can fail if either
|
||||
// the old or new files are referenced out of bounds.
|
||||
bool AddControlEntry(const ControlEntry& entry);
|
||||
|
||||
bool Close();
|
||||
|
||||
private:
|
||||
// Write the BSDIFF patch header to the |fp_| given the size of the compressed
|
||||
// control block and the compressed diff block.
|
||||
bool WriteHeader(uint64_t ctrl_size, uint64_t diff_size);
|
||||
|
||||
|
||||
const uint8_t* old_buf_;
|
||||
uint64_t old_size_;
|
||||
const uint8_t* new_buf_;
|
||||
uint64_t new_size_;
|
||||
|
||||
// Bytes of the new_buf_ already written.
|
||||
uint64_t written_output_{0};
|
||||
|
||||
// The current position in the old buf.
|
||||
int64_t old_pos_{0};
|
||||
|
||||
// The current file we are writing to.
|
||||
FILE* fp_{nullptr};
|
||||
|
||||
// The three internal compressed streams.
|
||||
BZ2Compressor ctrl_stream_;
|
||||
BZ2Compressor diff_stream_;
|
||||
BZ2Compressor extra_stream_;
|
||||
};
|
||||
|
||||
} // namespace bsdiff
|
||||
|
||||
#endif // _BSDIFF_PATCH_WRITER_H_
|
||||
Reference in new issue
Block a user