| Index: chrome/browser/component_updater/recovery_component_installer.cc
|
| diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc
|
| index 4baa3666db5b1d6e3a8e2e4ab269c5ae4fb8d7f7..bad65945e347f2846726b790ec95614a7a9ff157 100644
|
| --- a/chrome/browser/component_updater/recovery_component_installer.cc
|
| +++ b/chrome/browser/component_updater/recovery_component_installer.cc
|
| @@ -13,12 +13,15 @@
|
| #include "base/file_util.h"
|
| #include "base/files/file_path.h"
|
| #include "base/logging.h"
|
| +#include "base/memory/singleton.h"
|
| #include "base/path_service.h"
|
| #include "base/prefs/pref_registry_simple.h"
|
| #include "base/prefs/pref_service.h"
|
| +#include "base/process/kill.h"
|
| #include "base/process/launch.h"
|
| #include "base/strings/string_util.h"
|
| #include "base/values.h"
|
| +#include "chrome/browser/component_updater/component_unpacker.h"
|
| #include "chrome/browser/component_updater/component_updater_service.h"
|
| #include "chrome/common/chrome_paths.h"
|
| #include "chrome/common/chrome_version_info.h"
|
| @@ -47,12 +50,21 @@ const base::FilePath::CharType kRecoveryFileName[] =
|
|
|
| const char kRecoveryManifestName[] = "ChromeRecovery";
|
|
|
| +// This is a contract between ChromeRecovery and this installer for the
|
| +// meaning of ChromeRecovery process exit code.
|
| +enum ChromeRecvoeryExitCode {
|
| + EXIT_CODE_RECOVERY_SUCCEEDED = 0,
|
| + EXIT_CODE_RECOVERY_SKIPPED = 1,
|
| + EXIT_CODE_ELEVATION_NEEDED = 2,
|
| +};
|
| +
|
| } // namespace
|
|
|
| class RecoveryComponentInstaller : public ComponentInstaller {
|
| public:
|
| - explicit RecoveryComponentInstaller(const Version& version,
|
| - PrefService* prefs);
|
| + RecoveryComponentInstaller(const Version& version,
|
| + PrefService* prefs,
|
| + bool elevated_install);
|
|
|
| virtual ~RecoveryComponentInstaller() {}
|
|
|
| @@ -61,15 +73,53 @@ class RecoveryComponentInstaller : public ComponentInstaller {
|
| virtual bool Install(const base::DictionaryValue& manifest,
|
| const base::FilePath& unpack_path) OVERRIDE;
|
|
|
| + virtual void OnInstallError(
|
| + const InstallerSourceSerializer& serializer) OVERRIDE;
|
| +
|
| virtual bool GetInstalledFile(const std::string& file,
|
| base::FilePath* installed_file) OVERRIDE;
|
|
|
| private:
|
| + static void DoneSaveInstallSourceInfo(PrefService* prefs, bool result);
|
| +
|
| Version current_version_;
|
| PrefService* prefs_;
|
| + bool elevated_install_;
|
| + int installer_exit_code_;
|
| +};
|
| +
|
| +class ElevatedInstallWrapper {
|
| + public:
|
| + static ElevatedInstallWrapper* GetInstance();
|
| + void SaveInstallSourceInfo(const InstallerSourceSerializer& serializer,
|
| + const base::Callback<void(bool)>& callback);
|
| +
|
| + void Install(PrefService* prefs, bool elevation_allowed,
|
| + const base::Closure& callback);
|
| +
|
| + private:
|
| + friend struct DefaultSingletonTraits<ElevatedInstallWrapper>;
|
| +
|
| + ElevatedInstallWrapper();
|
| +
|
| + bool DoSaveInstallSourceInfo(const InstallerSourceSerializer& serializer);
|
| +
|
| + void DoInstall(PrefService* prefs, const Version& version);
|
| + void DoneInstall(ComponentUnpacker::Error error, int extended_error);
|
| +
|
| + static base::FilePath GetInstallerBackupPath();
|
| +
|
| + PrefService* prefs_;
|
| + scoped_refptr<ComponentUnpacker> unpacker_;
|
| + scoped_ptr<RecoveryComponentInstaller> rc_installer_;
|
| +
|
| + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ElevatedInstallWrapper);
|
| };
|
|
|
| -void RecoveryRegisterHelper(ComponentUpdateService* cus, PrefService* prefs) {
|
| +void RecoveryRegisterHelper(ComponentUpdateService* cus,
|
| + PrefService* prefs) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| Version version(prefs->GetString(prefs::kRecoveryComponentVersion));
|
| if (!version.IsValid()) {
|
| @@ -79,7 +129,7 @@ void RecoveryRegisterHelper(ComponentUpdateService* cus, PrefService* prefs) {
|
|
|
| CrxComponent recovery;
|
| recovery.name = "recovery";
|
| - recovery.installer = new RecoveryComponentInstaller(version, prefs);
|
| + recovery.installer = new RecoveryComponentInstaller(version, prefs, false);
|
| recovery.version = version;
|
| recovery.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
|
| if (cus->RegisterComponent(recovery) != ComponentUpdateService::kOk) {
|
| @@ -92,9 +142,17 @@ void RecoveryUpdateVersionHelper(const Version& version, PrefService* prefs) {
|
| prefs->SetString(prefs::kRecoveryComponentVersion, version.GetString());
|
| }
|
|
|
| -RecoveryComponentInstaller::RecoveryComponentInstaller(const Version& version,
|
| - PrefService* prefs)
|
| - : current_version_(version), prefs_(prefs) {
|
| +void RecoveryUpdateElevationHelper(bool needs_elevation, PrefService* prefs) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, needs_elevation);
|
| +}
|
| +
|
| +RecoveryComponentInstaller::RecoveryComponentInstaller(
|
| + const Version& version, PrefService* prefs, bool elevated_install)
|
| + : current_version_(version),
|
| + prefs_(prefs),
|
| + elevated_install_(elevated_install),
|
| + installer_exit_code_(0) {
|
| DCHECK(version.IsValid());
|
| }
|
|
|
| @@ -115,23 +173,11 @@ bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest,
|
| return false;
|
| if (current_version_.CompareTo(version) >= 0)
|
| return false;
|
| -
|
| - // Passed the basic tests. Copy the installation to a permanent directory.
|
| - base::FilePath path;
|
| - if (!PathService::Get(chrome::DIR_RECOVERY_BASE, &path))
|
| - return false;
|
| - path = path.AppendASCII(version.GetString());
|
| - if (base::PathExists(path) && !base::DeleteFile(path, true))
|
| - return false;
|
| - if (!base::Move(unpack_path, path)) {
|
| - DVLOG(1) << "Recovery component move failed.";
|
| - return false;
|
| - }
|
| -
|
| - base::FilePath main_file = path.Append(kRecoveryFileName);
|
| + base::FilePath main_file = unpack_path.Append(kRecoveryFileName);
|
| if (!base::PathExists(main_file))
|
| return false;
|
| - // Run the recovery component.
|
| + // Passed the basic tests. The installation continues with the
|
| + // recovery component itself running from the temp directory.
|
| CommandLine cmdline(main_file);
|
| std::string arguments;
|
| if (manifest.GetStringASCII("x-recovery-args", &arguments))
|
| @@ -143,20 +189,141 @@ bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest,
|
| }
|
| current_version_ = version;
|
| if (prefs_) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::UI,
|
| - FROM_HERE,
|
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
| base::Bind(&RecoveryUpdateVersionHelper, version, prefs_));
|
| }
|
| - return base::LaunchProcess(cmdline, base::LaunchOptions(), NULL);
|
| +
|
| + if (elevated_install_) {
|
| + return base::LaunchElevatedProcess(cmdline, base::LaunchOptions(), NULL);
|
| + }
|
| +
|
| + base::ProcessHandle process_handle;
|
| + if (!base::LaunchProcess(cmdline, base::LaunchOptions(), &process_handle)) {
|
| + return false;
|
| + }
|
| +
|
| + installer_exit_code_ = 0;
|
| + const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromSeconds(60);
|
| + if (!base::WaitForExitCodeWithTimeout(process_handle,
|
| + &installer_exit_code_,
|
| + kMaxWaitTime)) {
|
| + // Ensure that the process terminates.
|
| + base::KillProcess(process_handle, -1, true);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void RecoveryComponentInstaller::OnInstallError(
|
| + const InstallerSourceSerializer& serializer) {
|
| + if (installer_exit_code_ == EXIT_CODE_ELEVATION_NEEDED && prefs_) {
|
| + ElevatedInstallWrapper::GetInstance()->SaveInstallSourceInfo(
|
| + serializer,
|
| + base::Bind(&RecoveryComponentInstaller::DoneSaveInstallSourceInfo,
|
| + prefs_));
|
| + }
|
| }
|
|
|
| bool RecoveryComponentInstaller::GetInstalledFile(
|
| - const std::string& file,
|
| - base::FilePath* installed_file) {
|
| + const std::string& file, base::FilePath* installed_file) {
|
| return false;
|
| }
|
|
|
| +void RecoveryComponentInstaller::DoneSaveInstallSourceInfo(PrefService* prefs,
|
| + bool result) {
|
| + if (!result) {
|
| + DLOG(WARNING) << "Failed to save CRX file for elevated install.";
|
| + return;
|
| + }
|
| +
|
| + // Set a flag in the preference so UpgradeDetector can detect and notify user.
|
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
| + base::Bind(&RecoveryUpdateElevationHelper, true, prefs));
|
| +}
|
| +
|
| +ElevatedInstallWrapper::ElevatedInstallWrapper() : prefs_(NULL) {
|
| + // |blocking_task_runner_| is responsbile for elevated install of recovery
|
| + // component and other related file operations (namely installer source backup
|
| + // and cleanup).
|
| + blocking_task_runner_ = BrowserThread::GetBlockingPool()->
|
| + GetSequencedTaskRunnerWithShutdownBehavior(
|
| + BrowserThread::GetBlockingPool()->GetSequenceToken(),
|
| + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
|
| +}
|
| +
|
| +ElevatedInstallWrapper* ElevatedInstallWrapper::GetInstance() {
|
| + return Singleton<ElevatedInstallWrapper>::get();
|
| +}
|
| +
|
| +base::FilePath ElevatedInstallWrapper::GetInstallerBackupPath() {
|
| + base::FilePath path;
|
| + PathService::Get(chrome::DIR_RECOVERY_COMPONENT_INSTALLER, &path);
|
| + return path.NormalizePathSeparators();
|
| +}
|
| +
|
| +void ElevatedInstallWrapper::SaveInstallSourceInfo(
|
| + const InstallerSourceSerializer& serializer,
|
| + const base::Callback<void(bool)>& callback) {
|
| + base::PostTaskAndReplyWithResult(
|
| + ElevatedInstallWrapper::GetInstance()->blocking_task_runner_,
|
| + FROM_HERE,
|
| + base::Bind(&ElevatedInstallWrapper::DoSaveInstallSourceInfo,
|
| + base::Unretained(this), serializer),
|
| + callback);
|
| +}
|
| +
|
| +bool ElevatedInstallWrapper::DoSaveInstallSourceInfo(
|
| + const InstallerSourceSerializer& serializer) {
|
| + return serializer.Run(GetInstallerBackupPath());
|
| +}
|
| +
|
| +void ElevatedInstallWrapper::Install(PrefService* prefs,
|
| + bool elevation_allowed,
|
| + const base::Closure& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + BrowserThread::PostTaskAndReply(BrowserThread::UI, FROM_HERE,
|
| + base::Bind(&RecoveryUpdateElevationHelper, false, prefs),
|
| + callback);
|
| + if (!elevation_allowed) {
|
| + return;
|
| + }
|
| +
|
| + Version version(prefs->GetString(prefs::kRecoveryComponentVersion));
|
| + blocking_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ElevatedInstallWrapper::DoInstall,
|
| + base::Unretained(this),
|
| + prefs,
|
| + version));
|
| +}
|
| +
|
| +void ElevatedInstallWrapper::DoInstall(PrefService* prefs,
|
| + const Version& version) {
|
| + prefs_ = prefs;
|
| +
|
| + rc_installer_.reset(new RecoveryComponentInstaller(version, prefs, true));
|
| +
|
| + unpacker_ = ComponentUnpacker::CreateFromBackup(GetInstallerBackupPath(),
|
| + rc_installer_.get(),
|
| + blocking_task_runner_);
|
| +
|
| + if (unpacker_.get()) {
|
| + unpacker_->Unpack(base::Bind(&ElevatedInstallWrapper::DoneInstall,
|
| + base::Unretained(this)));
|
| + }
|
| +}
|
| +
|
| +void ElevatedInstallWrapper::DoneInstall(ComponentUnpacker::Error error,
|
| + int extended_error) {
|
| + base::DeleteFile(GetInstallerBackupPath(), true);
|
| +
|
| + prefs_ = NULL;
|
| + unpacker_ = NULL;
|
| + rc_installer_.reset();
|
| +}
|
| +
|
| void RegisterRecoveryComponent(ComponentUpdateService* cus,
|
| PrefService* prefs) {
|
| #if !defined(OS_CHROMEOS)
|
| @@ -172,6 +339,15 @@ void RegisterRecoveryComponent(ComponentUpdateService* cus,
|
|
|
| void RegisterPrefsForRecoveryComponent(PrefRegistrySimple* registry) {
|
| registry->RegisterStringPref(prefs::kRecoveryComponentVersion, "0.0.0.0");
|
| + registry->RegisterBooleanPref(prefs::kRecoveryComponentNeedsElevation, false);
|
| +}
|
| +
|
| +void PerformRecoveryElevated(
|
| + PrefService* prefs, bool elevation_allowed, const base::Closure& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + ElevatedInstallWrapper::GetInstance()->Install(prefs, elevation_allowed,
|
| + callback);
|
| }
|
|
|
| } // namespace component_updater
|
| +
|
|
|