blob: 11496587e8c642db1fd2d00d9425990a76806a30 [file] [log] [blame]
// 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");
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