910e62b5创建于 1月15日历史提交
// 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