diff options
-rw-r--r-- | README.md | 11 | ||||
-rw-r--r-- | element_detection_unittest.cc | 1 | ||||
-rw-r--r-- | fuzzers/apply_fuzzer.cc | 2 | ||||
-rw-r--r-- | fuzzers/raw_gen_fuzzer.cc | 2 | ||||
-rw-r--r-- | fuzzers/ztf_gen_fuzzer.cc | 2 | ||||
-rw-r--r-- | integration_test.cc | 10 | ||||
-rw-r--r-- | main_utils.cc | 2 | ||||
-rw-r--r-- | mapped_file.cc | 4 | ||||
-rw-r--r-- | mapped_file.h | 4 | ||||
-rw-r--r-- | zucchini.h | 33 | ||||
-rw-r--r-- | zucchini_apply.cc | 6 | ||||
-rw-r--r-- | zucchini_commands.cc | 66 | ||||
-rw-r--r-- | zucchini_gen.cc | 39 | ||||
-rw-r--r-- | zucchini_integration.cc | 154 | ||||
-rw-r--r-- | zucchini_integration.h | 53 |
15 files changed, 229 insertions, 160 deletions
@@ -145,6 +145,17 @@ values that describe content on a higher level of abstraction, masking away undesirable noise in raw content. Notably, the projection encodes references based on their associated label. +## Interfaces + +zucchini_lib: Core Zucchini library that operate on buffers to generate and +apply patches. + +zucchini_io: Wrapper on zucchini_lib that handles file I/O, using memory-mapped +I/O to interface with zucchini_lib. + +zucchini: Stand-alone executable that parses command-line arguments, and passes +the results to zucchini_io. Also implements various helper flows. + ## Zucchini Ensemble Patch Format ### Types diff --git a/element_detection_unittest.cc b/element_detection_unittest.cc index 6dbfa3f..769c839 100644 --- a/element_detection_unittest.cc +++ b/element_detection_unittest.cc @@ -4,6 +4,7 @@ #include "components/zucchini/element_detection.h" +#include <map> #include <vector> #include "base/bind.h" diff --git a/fuzzers/apply_fuzzer.cc b/fuzzers/apply_fuzzer.cc index 4e9b342..baad978 100644 --- a/fuzzers/apply_fuzzer.cc +++ b/fuzzers/apply_fuzzer.cc @@ -54,6 +54,6 @@ DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) { zucchini::MutableBufferView new_image(new_data.data(), new_size); // Fuzz target. - zucchini::Apply(old_image, *patch_reader, new_image); + zucchini::ApplyBuffer(old_image, *patch_reader, new_image); // No need to check whether output exist, or if so, whether it's valid. } diff --git a/fuzzers/raw_gen_fuzzer.cc b/fuzzers/raw_gen_fuzzer.cc index b27042e..de63d95 100644 --- a/fuzzers/raw_gen_fuzzer.cc +++ b/fuzzers/raw_gen_fuzzer.cc @@ -56,7 +56,7 @@ DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) { zucchini::EnsemblePatchWriter patch_writer(old_image, new_image); // Fuzz Target. - zucchini::GenerateRaw(old_image, new_image, &patch_writer); + zucchini::GenerateBufferRaw(old_image, new_image, &patch_writer); // Check that the patch size is sane. Crash the fuzzer if this isn't the case // as it is a failure in Zucchini's patch performance that is worth diff --git a/fuzzers/ztf_gen_fuzzer.cc b/fuzzers/ztf_gen_fuzzer.cc index 785aed4..ee2d47c 100644 --- a/fuzzers/ztf_gen_fuzzer.cc +++ b/fuzzers/ztf_gen_fuzzer.cc @@ -57,7 +57,7 @@ DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) { zucchini::EnsemblePatchWriter patch_writer(old_image, new_image); // Fuzz Target. - zucchini::GenerateEnsemble(old_image, new_image, &patch_writer); + zucchini::GenerateBuffer(old_image, new_image, &patch_writer); // Write to buffer to avoid IO. size_t patch_size = patch_writer.SerializedSize(); diff --git a/integration_test.cc b/integration_test.cc index c4c7004..e7df927 100644 --- a/integration_test.cc +++ b/integration_test.cc @@ -49,8 +49,8 @@ void TestGenApply(const std::string& old_filename, // Generate patch from "old" to "new". ASSERT_EQ(status::kStatusSuccess, - raw ? GenerateRaw(old_region, new_region, &patch_writer) - : GenerateEnsemble(old_region, new_region, &patch_writer)); + raw ? GenerateBufferRaw(old_region, new_region, &patch_writer) + : GenerateBuffer(old_region, new_region, &patch_writer)); size_t patch_size = patch_writer.SerializedSize(); EXPECT_GE(patch_size, 80U); // Minimum size is empty patch. @@ -73,9 +73,9 @@ void TestGenApply(const std::string& old_filename, // Apply patch to "old" to get "patched new", ensure it's identical to "new". std::vector<uint8_t> patched_new_buffer(new_region.size()); - ASSERT_EQ(status::kStatusSuccess, - Apply(old_region, *patch_reader, - {patched_new_buffer.data(), patched_new_buffer.size()})); + ASSERT_EQ(status::kStatusSuccess, ApplyBuffer(old_region, *patch_reader, + {patched_new_buffer.data(), + patched_new_buffer.size()})); // Note that |new_region| and |patched_new_buffer| are the same size. EXPECT_TRUE(std::equal(new_region.begin(), new_region.end(), diff --git a/main_utils.cc b/main_utils.cc index b6b5642..6a09b32 100644 --- a/main_utils.cc +++ b/main_utils.cc @@ -70,7 +70,7 @@ constexpr Command kCommands[] = { {"apply", "-apply <old_file> <patch_file> <new_file> [-keep]", 3, &MainApply}, {"read", "-read <exe> [-dump]", 1, &MainRead}, - {"detect", "-detect <archive_file> [-dd=format#]", 1, &MainDetect}, + {"detect", "-detect <archive_file>", 1, &MainDetect}, {"match", "-match <old_file> <new_file> [-impose=#+#=#+#,#+#=#+#,...]", 2, &MainMatch}, {"crc32", "-crc32 <file>", 1, &MainCrc32}, diff --git a/mapped_file.cc b/mapped_file.cc index 13c1afd..497b42a 100644 --- a/mapped_file.cc +++ b/mapped_file.cc @@ -12,7 +12,7 @@ namespace zucchini { -MappedFileReader::MappedFileReader(base::File&& file) { +MappedFileReader::MappedFileReader(base::File file) { if (!file.IsValid()) { error_ = "Invalid file."; return; // |buffer_| will be uninitialized, and therefore invalid. @@ -23,7 +23,7 @@ MappedFileReader::MappedFileReader(base::File&& file) { } MappedFileWriter::MappedFileWriter(const base::FilePath& file_path, - base::File&& file, + base::File file, size_t length) : file_path_(file_path), delete_behavior_(kManualDeleteOnClose) { if (!file.IsValid()) { diff --git a/mapped_file.h b/mapped_file.h index 540f947..e8cd590 100644 --- a/mapped_file.h +++ b/mapped_file.h @@ -23,7 +23,7 @@ class MappedFileReader { public: // Maps |file| to memory for reading. Also validates |file|. Errors are // available via HasError() and error(). - explicit MappedFileReader(base::File&& file); + explicit MappedFileReader(base::File file); const uint8_t* data() const { return buffer_.data(); } size_t length() const { return buffer_.length(); } @@ -47,7 +47,7 @@ class MappedFileWriter { // UNIX systems, but can be empty if auto delete is not needed. Errors are // available via HasError() and error(). MappedFileWriter(const base::FilePath& file_path, - base::File&& file, + base::File file, size_t length); ~MappedFileWriter(); @@ -11,7 +11,11 @@ #include "components/zucchini/patch_reader.h" #include "components/zucchini/patch_writer.h" -// Definitions, structures, and interfaces for the Zucchini library. +// Core Zucchini library, consisting of: +// - Global constants. +// - Patch gen and apply functions, where "old" and "new" data are represented +// as buffers, and patch data represented as EnsemblePatchWriter or +// EnsemblePatchReader. namespace zucchini { @@ -36,9 +40,9 @@ enum Code { // Generates ensemble patch from |old_image| to |new_image| using the default // element detection and matching heuristics, writes the results to // |patch_writer|, and returns a status::Code. -status::Code GenerateEnsemble(ConstBufferView old_image, - ConstBufferView new_image, - EnsemblePatchWriter* patch_writer); +status::Code GenerateBuffer(ConstBufferView old_image, + ConstBufferView new_image, + EnsemblePatchWriter* patch_writer); // Same as GenerateEnsemble(), but if |imposed_matches| is non-empty, then // overrides default element detection and matching heuristics with custom @@ -46,23 +50,22 @@ status::Code GenerateEnsemble(ConstBufferView old_image, // "#+#=#+#,#+#=#+#,..." (e.g., "1+2=3+4", "1+2=3+4,5+6=7+8"), // where "#+#=#+#" encodes a match as 4 unsigned integers: // [offset in "old", size in "old", offset in "new", size in "new"]. -status::Code GenerateEnsembleWithImposedMatches( - ConstBufferView old_image, - ConstBufferView new_image, - std::string imposed_matches, - EnsemblePatchWriter* patch_writer); +status::Code GenerateBufferImposed(ConstBufferView old_image, + ConstBufferView new_image, + std::string imposed_matches, + EnsemblePatchWriter* patch_writer); // Generates raw patch from |old_image| to |new_image|, and writes it to // |patch_writer|. -status::Code GenerateRaw(ConstBufferView old_image, - ConstBufferView new_image, - EnsemblePatchWriter* patch_writer); +status::Code GenerateBufferRaw(ConstBufferView old_image, + ConstBufferView new_image, + EnsemblePatchWriter* patch_writer); // Applies |patch_reader| to |old_image| to build |new_image|, which refers to // preallocated memory of sufficient size. -status::Code Apply(ConstBufferView old_image, - const EnsemblePatchReader& patch_reader, - MutableBufferView new_image); +status::Code ApplyBuffer(ConstBufferView old_image, + const EnsemblePatchReader& patch_reader, + MutableBufferView new_image); } // namespace zucchini diff --git a/zucchini_apply.cc b/zucchini_apply.cc index af4eab9..f6226bc 100644 --- a/zucchini_apply.cc +++ b/zucchini_apply.cc @@ -188,9 +188,9 @@ bool ApplyElement(ExecutableType exe_type, /******** Exported Functions ********/ -status::Code Apply(ConstBufferView old_image, - const EnsemblePatchReader& patch_reader, - MutableBufferView new_image) { +status::Code ApplyBuffer(ConstBufferView old_image, + const EnsemblePatchReader& patch_reader, + MutableBufferView new_image) { if (!patch_reader.CheckOldFile(old_image)) { LOG(ERROR) << "Invalid old_image."; return status::kStatusInvalidOldImage; diff --git a/zucchini_commands.cc b/zucchini_commands.cc index 62dd20d..1abe08f 100644 --- a/zucchini_commands.cc +++ b/zucchini_commands.cc @@ -38,67 +38,11 @@ constexpr char kSwitchRaw[] = "raw"; zucchini::status::Code MainGen(MainParams params) { CHECK_EQ(3U, params.file_paths.size()); - - // TODO(huangs): Move implementation to zucchini_integration.cc. - using base::File; - File old_file(params.file_paths[0], File::FLAG_OPEN | File::FLAG_READ); - zucchini::MappedFileReader old_image(std::move(old_file)); - if (old_image.HasError()) { - LOG(ERROR) << "Error with file " << params.file_paths[0].value() << ": " - << old_image.error(); - return zucchini::status::kStatusFileReadError; - } - File new_file(params.file_paths[1], File::FLAG_OPEN | File::FLAG_READ); - zucchini::MappedFileReader new_image(std::move(new_file)); - if (new_image.HasError()) { - LOG(ERROR) << "Error with file " << params.file_paths[1].value() << ": " - << new_image.error(); - return zucchini::status::kStatusFileReadError; - } - zucchini::EnsemblePatchWriter patch_writer(old_image.region(), - new_image.region()); - - zucchini::status::Code result = zucchini::status::kStatusSuccess; - if (params.command_line.HasSwitch(kSwitchRaw)) { - result = GenerateRaw(old_image.region(), new_image.region(), &patch_writer); - } else { - // May be empty. - std::string imposed_matches = - params.command_line.GetSwitchValueASCII(kSwitchImpose); - result = GenerateEnsembleWithImposedMatches( - old_image.region(), new_image.region(), std::move(imposed_matches), - &patch_writer); - } - - if (result != zucchini::status::kStatusSuccess) { - params.out << "Fatal error encountered when generating patch." << std::endl; - return result; - } - - // By default, delete patch on destruction, to avoid having lingering files in - // case of a failure. On Windows deletion can be done by the OS. - File patch_file(params.file_paths[2], File::FLAG_CREATE_ALWAYS | - File::FLAG_READ | File::FLAG_WRITE | - File::FLAG_SHARE_DELETE | - File::FLAG_CAN_DELETE_ON_CLOSE); - zucchini::MappedFileWriter patch(params.file_paths[2], std::move(patch_file), - patch_writer.SerializedSize()); - if (patch.HasError()) { - LOG(ERROR) << "Error with file " << params.file_paths[2].value() << ": " - << patch.error(); - return zucchini::status::kStatusFileWriteError; - } - - if (params.command_line.HasSwitch(kSwitchKeep)) - patch.Keep(); - - if (!patch_writer.SerializeInto(patch.region())) - return zucchini::status::kStatusPatchWriteError; - - // Successfully created patch. Explicitly request file to be kept. - if (!patch.Keep()) - return zucchini::status::kStatusFileWriteError; - return zucchini::status::kStatusSuccess; + return zucchini::Generate( + params.file_paths[0], params.file_paths[1], params.file_paths[2], + params.command_line.HasSwitch(kSwitchKeep), + params.command_line.HasSwitch(kSwitchRaw), + params.command_line.GetSwitchValueASCII(kSwitchImpose)); } zucchini::status::Code MainApply(MainParams params) { diff --git a/zucchini_gen.cc b/zucchini_gen.cc index 29be814..19da0af 100644 --- a/zucchini_gen.cc +++ b/zucchini_gen.cc @@ -319,13 +319,13 @@ bool GenerateExecutableElement(ExecutableType exe_type, reference_bytes_mixer.get(), patch_writer); } -status::Code GenerateEnsembleCommon(ConstBufferView old_image, - ConstBufferView new_image, - std::unique_ptr<EnsembleMatcher> matcher, - EnsemblePatchWriter* patch_writer) { +status::Code GenerateBufferCommon(ConstBufferView old_image, + ConstBufferView new_image, + std::unique_ptr<EnsembleMatcher> matcher, + EnsemblePatchWriter* patch_writer) { if (!matcher->RunMatch(old_image, new_image)) { LOG(INFO) << "RunMatch() failed, generating raw patch."; - return GenerateRaw(old_image, new_image, patch_writer); + return GenerateBufferRaw(old_image, new_image, patch_writer); } const std::vector<ElementMatch>& matches = matcher->matches(); @@ -335,7 +335,7 @@ status::Code GenerateEnsembleCommon(ConstBufferView old_image, size_t num_elements = matches.size(); if (num_elements == 0) { LOG(INFO) << "No nontrival matches, generating raw patch."; - return GenerateRaw(old_image, new_image, patch_writer); + return GenerateBufferRaw(old_image, new_image, patch_writer); } // "Gaps" are |new_image| bytes not covered by new_elements in |matches|. @@ -421,30 +421,29 @@ status::Code GenerateEnsembleCommon(ConstBufferView old_image, /******** Exported Functions ********/ -status::Code GenerateEnsemble(ConstBufferView old_image, - ConstBufferView new_image, - EnsemblePatchWriter* patch_writer) { - return GenerateEnsembleCommon( +status::Code GenerateBuffer(ConstBufferView old_image, + ConstBufferView new_image, + EnsemblePatchWriter* patch_writer) { + return GenerateBufferCommon( old_image, new_image, std::make_unique<HeuristicEnsembleMatcher>(nullptr), patch_writer); } -status::Code GenerateEnsembleWithImposedMatches( - ConstBufferView old_image, - ConstBufferView new_image, - std::string imposed_matches, - EnsemblePatchWriter* patch_writer) { +status::Code GenerateBufferImposed(ConstBufferView old_image, + ConstBufferView new_image, + std::string imposed_matches, + EnsemblePatchWriter* patch_writer) { if (imposed_matches.empty()) - return GenerateEnsemble(old_image, new_image, patch_writer); + return GenerateBuffer(old_image, new_image, patch_writer); - return GenerateEnsembleCommon( + return GenerateBufferCommon( old_image, new_image, std::make_unique<ImposedEnsembleMatcher>(imposed_matches), patch_writer); } -status::Code GenerateRaw(ConstBufferView old_image, - ConstBufferView new_image, - EnsemblePatchWriter* patch_writer) { +status::Code GenerateBufferRaw(ConstBufferView old_image, + ConstBufferView new_image, + EnsemblePatchWriter* patch_writer) { ImageIndex old_image_index(old_image); EncodedView old_view(old_image_index); std::vector<offset_t> old_sa = diff --git a/zucchini_integration.cc b/zucchini_integration.cc index 1149658..97790a3 100644 --- a/zucchini_integration.cc +++ b/zucchini_integration.cc @@ -19,92 +19,172 @@ struct FileNames { FileNames() : is_dummy(true) { // Use fake names. old_name = old_name.AppendASCII("old_name"); - patch_name = patch_name.AppendASCII("patch_name"); new_name = new_name.AppendASCII("new_name"); + patch_name = patch_name.AppendASCII("patch_name"); } FileNames(const base::FilePath& old_name, - const base::FilePath& patch_name, - const base::FilePath& new_name) + const base::FilePath& new_name, + const base::FilePath& patch_name) : old_name(old_name), - patch_name(patch_name), new_name(new_name), + patch_name(patch_name), is_dummy(false) {} base::FilePath old_name; - base::FilePath patch_name; base::FilePath new_name; + base::FilePath patch_name; // A flag to decide whether the filenames are only for error output. const bool is_dummy; }; -status::Code ApplyCommon(base::File&& old_file_handle, - base::File&& patch_file_handle, - base::File&& new_file_handle, +status::Code GenerateCommon(base::File old_file, + base::File new_file, + base::File patch_file, + const FileNames& names, + bool force_keep, + bool is_raw, + std::string imposed_matches) { + MappedFileReader mapped_old(std::move(old_file)); + if (mapped_old.HasError()) { + LOG(ERROR) << "Error with file " << names.old_name.value() << ": " + << mapped_old.error(); + return status::kStatusFileReadError; + } + + MappedFileReader mapped_new(std::move(new_file)); + if (mapped_new.HasError()) { + LOG(ERROR) << "Error with file " << names.new_name.value() << ": " + << mapped_new.error(); + return status::kStatusFileReadError; + } + + status::Code result = status::kStatusSuccess; + EnsemblePatchWriter patch_writer(mapped_old.region(), mapped_new.region()); + if (is_raw) { + result = GenerateBufferRaw(mapped_old.region(), mapped_new.region(), + &patch_writer); + } else { + result = GenerateBufferImposed(mapped_old.region(), mapped_new.region(), + std::move(imposed_matches), &patch_writer); + } + if (result != status::kStatusSuccess) { + LOG(ERROR) << "Fatal error encountered when generating patch."; + return result; + } + + // By default, delete patch on destruction, to avoid having lingering files in + // case of a failure. On Windows deletion can be done by the OS. + MappedFileWriter mapped_patch(names.patch_name, std::move(patch_file), + patch_writer.SerializedSize()); + if (mapped_patch.HasError()) { + LOG(ERROR) << "Error with file " << names.patch_name.value() << ": " + << mapped_patch.error(); + return status::kStatusFileWriteError; + } + if (force_keep) + mapped_patch.Keep(); + + if (!patch_writer.SerializeInto(mapped_patch.region())) + return status::kStatusPatchWriteError; + + // Successfully created patch. Explicitly request file to be kept. + if (!mapped_patch.Keep()) + return status::kStatusFileWriteError; + return status::kStatusSuccess; +} + +status::Code ApplyCommon(base::File old_file, + base::File patch_file, + base::File new_file, const FileNames& names, bool force_keep) { - MappedFileReader patch_file(std::move(patch_file_handle)); - if (patch_file.HasError()) { + MappedFileReader mapped_patch(std::move(patch_file)); + if (mapped_patch.HasError()) { LOG(ERROR) << "Error with file " << names.patch_name.value() << ": " - << patch_file.error(); + << mapped_patch.error(); return status::kStatusFileReadError; } - auto patch_reader = - zucchini::EnsemblePatchReader::Create(patch_file.region()); + auto patch_reader = EnsemblePatchReader::Create(mapped_patch.region()); if (!patch_reader.has_value()) { LOG(ERROR) << "Error reading patch header."; return status::kStatusPatchReadError; } - MappedFileReader old_file(std::move(old_file_handle)); - if (old_file.HasError()) { + MappedFileReader mapped_old(std::move(old_file)); + if (mapped_old.HasError()) { LOG(ERROR) << "Error with file " << names.old_name.value() << ": " - << old_file.error(); + << mapped_old.error(); return status::kStatusFileReadError; } - zucchini::PatchHeader header = patch_reader->header(); + PatchHeader header = patch_reader->header(); // By default, delete output on destruction, to avoid having lingering files // in case of a failure. On Windows deletion can be done by the OS. - base::FilePath file_path; - if (!names.is_dummy) - file_path = base::FilePath(names.new_name); - - MappedFileWriter new_file(file_path, std::move(new_file_handle), - header.new_size); - if (new_file.HasError()) { + MappedFileWriter mapped_new(names.new_name, std::move(new_file), + header.new_size); + if (mapped_new.HasError()) { LOG(ERROR) << "Error with file " << names.new_name.value() << ": " - << new_file.error(); + << mapped_new.error(); return status::kStatusFileWriteError; } - if (force_keep) - new_file.Keep(); + mapped_new.Keep(); - zucchini::status::Code result = - zucchini::Apply(old_file.region(), *patch_reader, new_file.region()); + status::Code result = + ApplyBuffer(mapped_old.region(), *patch_reader, mapped_new.region()); if (result != status::kStatusSuccess) { LOG(ERROR) << "Fatal error encountered while applying patch."; return result; } - // Successfully patch |new_file|. Explicitly request file to be kept. - if (!new_file.Keep()) + // Successfully patch |mapped_new|. Explicitly request file to be kept. + if (!mapped_new.Keep()) return status::kStatusFileWriteError; return status::kStatusSuccess; } } // namespace -status::Code Apply(base::File&& old_file_handle, - base::File&& patch_file_handle, - base::File&& new_file_handle, +status::Code Generate(base::File old_file, + base::File new_file, + base::File patch_file, + bool force_keep, + bool is_raw, + std::string imposed_matches) { + const FileNames file_names; + return GenerateCommon(std::move(old_file), std::move(new_file), + std::move(patch_file), file_names, force_keep, is_raw, + std::move(imposed_matches)); +} + +status::Code Generate(const base::FilePath& old_path, + const base::FilePath& new_path, + const base::FilePath& patch_path, + bool force_keep, + bool is_raw, + std::string imposed_matches) { + using base::File; + File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ); + File new_file(new_path, File::FLAG_OPEN | File::FLAG_READ); + File patch_file(patch_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ | + File::FLAG_WRITE | File::FLAG_SHARE_DELETE | + File::FLAG_CAN_DELETE_ON_CLOSE); + const FileNames file_names(old_path, new_path, patch_path); + return GenerateCommon(std::move(old_file), std::move(new_file), + std::move(patch_file), file_names, force_keep, is_raw, + std::move(imposed_matches)); +} + +status::Code Apply(base::File old_file, + base::File patch_file, + base::File new_file, bool force_keep) { const FileNames file_names; - return ApplyCommon(std::move(old_file_handle), std::move(patch_file_handle), - std::move(new_file_handle), file_names, force_keep); + return ApplyCommon(std::move(old_file), std::move(patch_file), + std::move(new_file), file_names, force_keep); } status::Code Apply(const base::FilePath& old_path, @@ -117,7 +197,7 @@ status::Code Apply(const base::FilePath& old_path, File new_file(new_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE | File::FLAG_SHARE_DELETE | File::FLAG_CAN_DELETE_ON_CLOSE); - const FileNames file_names(old_path, patch_path, new_path); + const FileNames file_names(old_path, new_path, patch_path); return ApplyCommon(std::move(old_file), std::move(patch_file), std::move(new_file), file_names, force_keep); } diff --git a/zucchini_integration.h b/zucchini_integration.h index ce98b28..2ae6091 100644 --- a/zucchini_integration.h +++ b/zucchini_integration.h @@ -5,28 +5,59 @@ #ifndef COMPONENTS_ZUCCHINI_ZUCCHINI_INTEGRATION_H_ #define COMPONENTS_ZUCCHINI_ZUCCHINI_INTEGRATION_H_ +#include <string> + #include "base/files/file.h" #include "base/files/file_path.h" #include "components/zucchini/zucchini.h" +// Zucchini integration interface to wrap core Zucchini library with file I/O. + namespace zucchini { -// Applies the patch in |patch_file| to the bytes in |old_file| and writes the -// result to |new_file|. Since this uses memory mapped files, crashes are -// expected in case of I/O errors. On Windows, |new_file| is kept iff returned +// Generates a patch to transform |old_file| to |new_file|, and writes the +// result to |patch_file|. Since this uses memory mapped files, crashes are +// expected in case of I/O errors. On Windows, |patch_file| is kept iff returned // code is kStatusSuccess or if |force_keep == true|, and is deleted otherwise. // For UNIX systems the caller needs to do cleanup since it has ownership of the -// base::File params and Zucchini has no knowledge of which base::FilePath to +// base::File params, and Zucchini has no knowledge of which base::FilePath to +// delete. If |is_raw == true| then uses Raw Zucchini. If |imposed_matches| is +// non-empty, then overrides default element detection and matching heuristics +// with custom element matching encoded in |imposed_matches|, which should be +// formatted as: +// "#+#=#+#,#+#=#+#,..." (e.g., "1+2=3+4", "1+2=3+4,5+6=7+8"), +// where "#+#=#+#" encodes a match as 4 unsigned integers: +// [offset in "old", size in "old", offset in "new", size in "new"]. +status::Code Generate(base::File old_file, + base::File new_file, + base::File patch_file, + bool force_keep = false, + bool is_raw = false, + std::string imposed_matches = ""); + +// Alternative Generate() interface that takes base::FilePath as arguments. +// Performs proper cleanup in Windows and UNIX if failure occurs. +status::Code Generate(const base::FilePath& old_path, + const base::FilePath& new_path, + const base::FilePath& patch_path, + bool force_keep = false, + bool is_raw = false, + std::string imposed_matches = ""); + +// Applies the patch in |patch_file| to |old_file|, and writes the result to +// |new_file|. Since this uses memory mapped files, crashes are expected in case +// of I/O errors. On Windows, |new_file| is kept iff returned code is +// kStatusSuccess or if |force_keep == true|, and is deleted otherwise. For UNIX +// systems the caller needs to do cleanup since it has ownership of the +// base::File params, and Zucchini has no knowledge of which base::FilePath to // delete. -status::Code Apply(base::File&& old_file, - base::File&& patch_file, - base::File&& new_file, +status::Code Apply(base::File old_file, + base::File patch_file, + base::File new_file, bool force_keep = false); -// Applies the patch in |patch_path| to the bytes in |old_path| and writes the -// result to |new_path|. Since this uses memory mapped files, crashes are -// expected in case of I/O errors. |new_path| is kept iff returned code is -// kStatusSuccess or if |force_keep == true|, and is deleted otherwise. +// Alternative Apply() interface that takes base::FilePath as arguments. +// Performs proper cleanup in Windows and UNIX if failure occurs. status::Code Apply(const base::FilePath& old_path, const base::FilePath& patch_path, const base::FilePath& new_path, |