blob: 4cb22d39d4abbaacf4eedb3e847df89659671752 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/out_of_process_patcher.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/command_line.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/sequence_checker.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/util/util.h"
#include "components/services/patch/public/mojom/file_patcher.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "third_party/zlib/google/zip.h"
namespace updater {
namespace {
// A PatchOperation is an invocation of puffin or zucchini patching.
class PatchOperation : public base::RefCountedThreadSafe<PatchOperation> {
public:
explicit PatchOperation(base::OnceCallback<void(int)> callback)
: callback_(std::move(callback)) {}
// Start the helper process and return true if successful.
bool StartHelper(UpdaterScope scope) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::optional<base::FilePath> updater_path =
GetUpdaterExecutablePath(scope);
if (!updater_path) {
Done(kErrorGettingUpdaterPath);
return false;
}
base::CommandLine command_line(*updater_path);
command_line.AppendSwitch(kPatchWorkerSwitch);
if (IsSystemInstall(scope)) {
command_line.AppendSwitch(kSystemSwitch);
}
base::LaunchOptions options;
mojo::PlatformChannel channel;
channel.PrepareToPassRemoteEndpoint(&options, &command_line);
base::Process process = base::LaunchProcess(command_line, options);
VLOG(2) << "Launching " << command_line.GetArgumentsString();
if (!process.IsValid()) {
VLOG(2) << "Failure.";
Done(kErrorLaunchingProcess);
return false;
}
channel.RemoteProcessLaunchAttempted();
mojo::ScopedMessagePipeHandle pipe = mojo::OutgoingInvitation::SendIsolated(
channel.TakeLocalEndpoint(), {}, process.Handle());
if (!pipe) {
VLOG(0) << "Failed to send Mojo invitation to the patcher process.";
Done(kErrorMojoConnectionFailure);
return false;
}
mojo::PendingRemote<patch::mojom::FilePatcher> pending_remote(
std::move(pipe), patch::mojom::FilePatcher::Version_);
if (!pending_remote) {
VLOG(0) << "Failed to establish IPC with the patcher process.";
Done(kErrorMojoConnectionFailure);
return false;
}
patcher_.Bind(std::move(pending_remote));
patcher_.set_disconnect_handler(
base::BindOnce(&PatchOperation::Done, this, kErrorIpcDisconnect));
return true;
}
void PatchZucchini(UpdaterScope scope,
base::File old,
base::File patch,
base::File out) {
if (!StartHelper(scope)) {
return;
}
patcher_->PatchFileZucchini(
std::move(old), std::move(patch), std::move(out),
base::BindOnce([](zucchini::status::Code status) {
return static_cast<int>(status);
}).Then(base::BindOnce(&PatchOperation::Done, this)));
}
void PatchPuffPatch(UpdaterScope scope,
base::File old,
base::File patch,
base::File out) {
if (!StartHelper(scope)) {
return;
}
patcher_->PatchFilePuffPatch(std::move(old), std::move(patch),
std::move(out),
base::BindOnce(&PatchOperation::Done, this));
}
private:
void Done(int result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback_).Run(result);
callback_ = base::DoNothing();
patcher_.reset();
}
private:
friend class base::RefCountedThreadSafe<PatchOperation>;
~PatchOperation() = default;
SEQUENCE_CHECKER(sequence_checker_);
mojo::Remote<patch::mojom::FilePatcher> patcher_;
base::OnceCallback<void(int)> callback_;
};
class OutOfProcessPatcher : public update_client::Patcher {
public:
explicit OutOfProcessPatcher(UpdaterScope scope) : scope_(scope) {}
OutOfProcessPatcher(const OutOfProcessPatcher&) = delete;
OutOfProcessPatcher& operator=(const OutOfProcessPatcher&) = delete;
// Overrides for update_client::Patcher.
void PatchPuffPatch(base::File old_file,
base::File patch_file,
base::File destination_file,
PatchCompleteCallback callback) const override {
base::MakeRefCounted<PatchOperation>(std::move(callback))
->PatchPuffPatch(scope_, std::move(old_file), std::move(patch_file),
std::move(destination_file));
}
void PatchZucchini(base::File old_file,
base::File patch_file,
base::File destination_file,
PatchCompleteCallback callback) const override {
base::MakeRefCounted<PatchOperation>(std::move(callback))
->PatchZucchini(scope_, std::move(old_file), std::move(patch_file),
std::move(destination_file));
}
private:
~OutOfProcessPatcher() override = default;
UpdaterScope scope_;
};
} // namespace
OutOfProcessPatcherFactory::OutOfProcessPatcherFactory(UpdaterScope scope)
: scope_(scope) {}
scoped_refptr<update_client::Patcher> OutOfProcessPatcherFactory::Create()
const {
return base::MakeRefCounted<OutOfProcessPatcher>(scope_);
}
} // namespace updater