Merge changes Ibdc1482e,Ibbc55426,I966b9d02

* changes:
  Refactor bsdiff patch writing code to another file.
  Convert bsdiff to soong
  Add test config to bsdiff_unittest
This commit is contained in:
Alex Deymo authored and Gerrit Code Review committed 2017-09-19 11:57:05 +00:00
commit f4e69fdf89
11 files changed
+586 -273

No files matched your search

+113
View File
@@ -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",
],
}
-154
View File
@@ -1,154 +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_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)
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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.
-->
<configuration description="Config for bsdiff_unittest">
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="bsdiff_unittest->/data/local/tmp/bsdiff_unittest" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="bsdiff_unittest" />
</test>
</configuration>
+3 -1
View File
@@ -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 := \
+11 -118
View File
@@ -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);
+2
View File
@@ -48,6 +48,8 @@
},
'sources': [
'bsdiff.cc',
"bz2_compressor.cc",
"patch_writer.cc",
],
},
# bsdiff executable
+116
View File
@@ -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
+55
View File
@@ -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_
+15
View File
@@ -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
View File
@@ -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
+87
View File
@@ -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_