// 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 #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; } status::Code VerifyPatchCommon(base::File patch_file, base::FilePath patch_name) { MappedFileReader mapped_patch(std::move(patch_file)); if (mapped_patch.HasError()) { LOG(ERROR) << "Error with file " << 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; } 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_WIN_SHARE_DELETE); File new_file(new_path, File::FLAG_OPEN | File::FLAG_READ | base::File::FLAG_WIN_SHARE_DELETE); File patch_file(patch_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE | File::FLAG_WIN_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_WIN_SHARE_DELETE); File patch_file(patch_path, File::FLAG_OPEN | File::FLAG_READ | base::File::FLAG_WIN_SHARE_DELETE); File new_file(new_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE | File::FLAG_WIN_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); } status::Code VerifyPatch(base::File patch_file) { return VerifyPatchCommon(std::move(patch_file), base::FilePath()); } status::Code VerifyPatch(const base::FilePath& patch_path) { using base::File; File patch_file(patch_path, File::FLAG_OPEN | File::FLAG_READ | base::File::FLAG_SHARE_DELETE); return VerifyPatchCommon(std::move(patch_file), patch_path); } } // namespace zucchini