| // Copyright 2017 The Chromium 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 "components/zucchini/zucchini_integration.h" |
| |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "components/zucchini/buffer_view.h" |
| #include "components/zucchini/mapped_file.h" |
| #include "components/zucchini/patch_reader.h" |
| |
| namespace zucchini { |
| |
| namespace { |
| |
| struct FileNames { |
| FileNames() : is_dummy(true) { |
| // Use fake names. |
| old_name = old_name.AppendASCII("old_name"); |
| new_name = new_name.AppendASCII("new_name"); |
| patch_name = patch_name.AppendASCII("patch_name"); |
| } |
| |
| FileNames(const base::FilePath& old_name, |
| const base::FilePath& new_name, |
| const base::FilePath& patch_name) |
| : old_name(old_name), |
| new_name(new_name), |
| patch_name(patch_name), |
| is_dummy(false) {} |
| |
| base::FilePath old_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 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 mapped_patch(std::move(patch_file)); |
| if (mapped_patch.HasError()) { |
| LOG(ERROR) << "Error with file " << names.patch_name.value() << ": " |
| << mapped_patch.error(); |
| return status::kStatusFileReadError; |
| } |
| |
| auto patch_reader = EnsemblePatchReader::Create(mapped_patch.region()); |
| if (!patch_reader.has_value()) { |
| LOG(ERROR) << "Error reading patch header."; |
| return status::kStatusPatchReadError; |
| } |
| |
| 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; |
| } |
| |
| 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. |
| 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() << ": " |
| << mapped_new.error(); |
| return status::kStatusFileWriteError; |
| } |
| if (force_keep) |
| mapped_new.Keep(); |
| |
| 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 |mapped_new|. Explicitly request file to be kept. |
| if (!mapped_new.Keep()) |
| return status::kStatusFileWriteError; |
| return status::kStatusSuccess; |
| } |
| |
| } // namespace |
| |
| 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 | |
| base::File::FLAG_SHARE_DELETE); |
| File new_file(new_path, File::FLAG_OPEN | File::FLAG_READ | |
| base::File::FLAG_SHARE_DELETE); |
| 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), std::move(patch_file), |
| std::move(new_file), file_names, force_keep); |
| } |
| |
| status::Code Apply(const base::FilePath& old_path, |
| const base::FilePath& patch_path, |
| const base::FilePath& new_path, |
| bool force_keep) { |
| using base::File; |
| File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ | |
| base::File::FLAG_SHARE_DELETE); |
| File patch_file(patch_path, File::FLAG_OPEN | File::FLAG_READ | |
| base::File::FLAG_SHARE_DELETE); |
| 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, new_path, patch_path); |
| return ApplyCommon(std::move(old_file), std::move(patch_file), |
| std::move(new_file), file_names, force_keep); |
| } |
| |
| } // namespace zucchini |