// 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"); patch_name = patch_name.AppendASCII("patch_name"); new_name = new_name.AppendASCII("new_name"); } FileNames(const base::FilePath& old_name, const base::FilePath& patch_name, const base::FilePath& new_name) : old_name(old_name), patch_name(patch_name), new_name(new_name), is_dummy(false) {} base::FilePath old_name; base::FilePath patch_name; base::FilePath new_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, const FileNames& names, bool force_keep) { MappedFileReader patch_file(std::move(patch_file_handle)); if (patch_file.HasError()) { LOG(ERROR) << "Error with file " << names.patch_name.value() << ": " << patch_file.error(); return status::kStatusFileReadError; } auto patch_reader = zucchini::EnsemblePatchReader::Create(patch_file.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()) { LOG(ERROR) << "Error with file " << names.old_name.value() << ": " << old_file.error(); return status::kStatusFileReadError; } zucchini::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()) { LOG(ERROR) << "Error with file " << names.new_name.value() << ": " << new_file.error(); return status::kStatusFileWriteError; } if (force_keep) new_file.Keep(); zucchini::status::Code result = zucchini::Apply(old_file.region(), *patch_reader, new_file.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()) 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, 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); } 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); File patch_file(patch_path, File::FLAG_OPEN | File::FLAG_READ); 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); return ApplyCommon(std::move(old_file), std::move(patch_file), std::move(new_file), file_names, force_keep); } } // namespace zucchini