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 |
+ |