Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1206)

Side by Side Diff: chrome/browser/component_updater/recovery_component_installer.cc

Issue 359443002: Elevated install of recovery component when needed (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: directly invoke exe Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/component_updater/recovery_component_installer.h" 5 #include "chrome/browser/component_updater/recovery_component_installer.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 #include <string> 8 #include <string>
9 9
10 #include "base/base_paths.h" 10 #include "base/base_paths.h"
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/command_line.h" 12 #include "base/command_line.h"
13 #include "base/compiler_specific.h" 13 #include "base/compiler_specific.h"
14 #include "base/files/file_path.h" 14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h" 15 #include "base/files/file_util.h"
16 #include "base/json/json_file_value_serializer.h"
16 #include "base/logging.h" 17 #include "base/logging.h"
17 #include "base/path_service.h" 18 #include "base/path_service.h"
18 #include "base/prefs/pref_registry_simple.h" 19 #include "base/prefs/pref_registry_simple.h"
19 #include "base/prefs/pref_service.h" 20 #include "base/prefs/pref_service.h"
21 #include "base/process/kill.h"
20 #include "base/process/launch.h" 22 #include "base/process/launch.h"
21 #include "base/strings/string_util.h" 23 #include "chrome/common/chrome_switches.h"
22 #include "base/values.h" 24 #include "chrome/common/pref_names.h"
23 #include "components/component_updater/component_updater_paths.h" 25 #include "components/component_updater/component_updater_paths.h"
24 #include "components/component_updater/component_updater_service.h" 26 #include "components/component_updater/component_updater_service.h"
25 #include "components/component_updater/pref_names.h" 27 #include "components/component_updater/pref_names.h"
26 #include "content/public/browser/browser_thread.h" 28 #include "content/public/browser/browser_thread.h"
27 29
28 using content::BrowserThread; 30 using content::BrowserThread;
29 31
30 namespace component_updater { 32 namespace component_updater {
31 33
32 namespace { 34 namespace {
33 35
34 // CRX hash. The extension id is: npdjjkjlcidkjlamlmmdelcjbcpdjocm. 36 // CRX hash. The extension id is: npdjjkjlcidkjlamlmmdelcjbcpdjocm.
35 const uint8_t kSha2Hash[] = {0xdf, 0x39, 0x9a, 0x9b, 0x28, 0x3a, 0x9b, 0x0c, 37 const uint8_t kSha2Hash[] = {0xdf, 0x39, 0x9a, 0x9b, 0x28, 0x3a, 0x9b, 0x0c,
36 0xbc, 0xc3, 0x4b, 0x29, 0x12, 0xf3, 0x9e, 0x2c, 38 0xbc, 0xc3, 0x4b, 0x29, 0x12, 0xf3, 0x9e, 0x2c,
37 0x19, 0x7a, 0x71, 0x4b, 0x0a, 0x7c, 0x80, 0x1c, 39 0x19, 0x7a, 0x71, 0x4b, 0x0a, 0x7c, 0x80, 0x1c,
38 0xf6, 0x29, 0x7c, 0x0a, 0x5f, 0xea, 0x67, 0xb7}; 40 0xf6, 0x29, 0x7c, 0x0a, 0x5f, 0xea, 0x67, 0xb7};
39 41
40 // File name of the recovery binary on different platforms. 42 // File name of the recovery binary on different platforms.
41 const base::FilePath::CharType kRecoveryFileName[] = 43 const base::FilePath::CharType kRecoveryFileName[] =
42 #if defined(OS_WIN) 44 #if defined(OS_WIN)
43 FILE_PATH_LITERAL("ChromeRecovery.exe"); 45 FILE_PATH_LITERAL("ChromeRecovery.exe");
44 #else // OS_LINUX, OS_MACOSX, etc. 46 #else // OS_LINUX, OS_MACOSX, etc.
45 FILE_PATH_LITERAL("ChromeRecovery"); 47 FILE_PATH_LITERAL("ChromeRecovery");
46 #endif 48 #endif
47 49
48 const char kRecoveryManifestName[] = "ChromeRecovery"; 50 const char kRecoveryManifestName[] = "ChromeRecovery";
49 51
52 // This is the contract between ChromeRecovery and this installer for the
53 // meaning of ChromeRecovery process exit code.
54 enum ChromeRecvoeryExitCode {
waffles 2014/12/05 00:59:48 typo: "Recvoery"
Sorin Jianu 2014/12/05 02:10:36 Just simply write: // ChromeRecovery process exit
xiaoling 2014/12/05 19:45:56 Done.
xiaoling 2014/12/05 19:45:56 Done.
55 EXIT_CODE_RECOVERY_SUCCEEDED = 0,
56 EXIT_CODE_RECOVERY_SKIPPED = 1,
57 EXIT_CODE_ELEVATION_NEEDED = 2,
58 };
59
60 // Checks if elevated recovery simulation switch was present on the command
61 // line. This is for testing purpose.
62 bool SimulatingElevatedRecovery() {
63 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
Sorin Jianu 2014/12/05 02:10:36 we could use a pointer type here, no need to deref
xiaoling 2014/12/05 19:45:56 Done.
64 return cmd_line.HasSwitch(switches::kSimulateElevatedRecovery);
65 }
66
67 scoped_ptr<base::DictionaryValue> ReadManifest(const base::FilePath& manifest) {
68 JSONFileValueSerializer serializer(manifest);
69 std::string error;
70 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
Sorin Jianu 2014/12/05 02:10:36 Are we including "base/memory/scoped_ptr.h" anywhe
xiaoling 2014/12/05 19:45:56 Done.
71 if (!root.get())
Sorin Jianu 2014/12/05 02:10:36 this is mostly a matter of preference but you coul
xiaoling 2014/12/05 19:45:56 Done.
72 return scoped_ptr<base::DictionaryValue>();
73 if (!root->IsType(base::Value::TYPE_DICTIONARY))
74 return scoped_ptr<base::DictionaryValue>();
75 return scoped_ptr<base::DictionaryValue>(
76 static_cast<base::DictionaryValue*>(root.release())).Pass();
77 }
78
79 void DoElevatedInstallRecoveryComponent(const base::FilePath& path) {
80 base::FilePath main_file = path.Append(kRecoveryFileName);
Sorin Jianu 2014/12/05 02:10:36 Can any of these locals in this function be declar
xiaoling 2014/12/05 19:45:56 Done.
81 base::FilePath manifest_file =
82 path.Append(FILE_PATH_LITERAL("manifest.json"));
83 if (!base::PathExists(main_file) || !base::PathExists(manifest_file))
84 return;
85
86 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(manifest_file));
87 std::string name;
88 manifest->GetStringASCII("name", &name);
89 if (name != kRecoveryManifestName)
90 return;
91 std::string proposed_version;
92 manifest->GetStringASCII("version", &proposed_version);
93 Version version(proposed_version.c_str());
94 if (!version.IsValid())
95 return;
96
97 CommandLine cmdline(main_file);
98 std::string arguments;
99 if (manifest->GetStringASCII("x-recovery-args", &arguments))
100 cmdline.AppendArg(arguments);
101 std::string add_version;
102 if (manifest->GetStringASCII("x-recovery-add-version", &add_version) &&
103 add_version == "yes")
104 cmdline.AppendSwitchASCII("version", version.GetString());
Sorin Jianu 2014/12/05 02:10:36 I always use {} in cases where the conditional and
xiaoling 2014/12/05 19:45:56 Done.
105
106 base::LaunchOptions options;
107 #if defined(OS_WIN)
108 options.start_hidden = true;
109 base::LaunchElevatedProcess(cmdline, options);
110 #else
111 NOTREACHED();
112 #endif
113 }
114
115 void ElevatedInstallRecoveryComponent(const base::FilePath& installer_path) {
116 BrowserThread::PostTask(
Sorin Jianu 2014/12/05 02:10:36 In general, we wanted to get rid of executing code
xiaoling 2014/12/05 19:45:56 Done. Used WorkerPool thread to avoid hassle of ma
117 BrowserThread::FILE,
118 FROM_HERE,
119 base::Bind(&DoElevatedInstallRecoveryComponent, installer_path));
120 }
121
50 } // namespace 122 } // namespace
51 123
124 // Component installer that is responsible to repair the chrome installation
125 // or repair the Google update installation. This is a last resort safety
126 // mechanism.
127 // For user Chrome, recovery component just installs silently. For machine
128 // Chrome, elevation may be needed. If that happens, the installer will set
129 // preference flag prefs::kRecoveryComponentNeedsElevation to request that.
130 // There is a global error service monitors this flag and will pop up
131 // bubble if the flag is set to true.
132 // See chrome/browser/recovery/recovery_install_global_error.cc for details.
52 class RecoveryComponentInstaller : public ComponentInstaller { 133 class RecoveryComponentInstaller : public ComponentInstaller {
53 public: 134 public:
54 explicit RecoveryComponentInstaller(const Version& version, 135 RecoveryComponentInstaller(const Version& version, PrefService* prefs);
55 PrefService* prefs);
56
57 ~RecoveryComponentInstaller() override {} 136 ~RecoveryComponentInstaller() override {}
58 137
59 void OnUpdateError(int error) override; 138 void OnUpdateError(int error) override;
60 139
61 bool Install(const base::DictionaryValue& manifest, 140 bool Install(const base::DictionaryValue& manifest,
62 const base::FilePath& unpack_path) override; 141 const base::FilePath& unpack_path) override;
63 142
64 bool GetInstalledFile(const std::string& file, 143 bool GetInstalledFile(const std::string& file,
65 base::FilePath* installed_file) override; 144 base::FilePath* installed_file) override;
66 145
67 private: 146 private:
68 Version current_version_; 147 Version current_version_;
69 PrefService* prefs_; 148 PrefService* prefs_;
70 }; 149 };
71 150
151 void SimulateElevatedRecoveryHelper(PrefService* prefs) {
152 prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, true);
153 }
154
72 void RecoveryRegisterHelper(ComponentUpdateService* cus, PrefService* prefs) { 155 void RecoveryRegisterHelper(ComponentUpdateService* cus, PrefService* prefs) {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
74 Version version(prefs->GetString(prefs::kRecoveryComponentVersion)); 157 Version version(prefs->GetString(prefs::kRecoveryComponentVersion));
75 if (!version.IsValid()) { 158 if (!version.IsValid()) {
76 NOTREACHED(); 159 NOTREACHED();
77 return; 160 return;
78 } 161 }
79 162
80 CrxComponent recovery; 163 CrxComponent recovery;
81 recovery.name = "recovery"; 164 recovery.name = "recovery";
82 recovery.installer = new RecoveryComponentInstaller(version, prefs); 165 recovery.installer = new RecoveryComponentInstaller(version, prefs);
83 recovery.version = version; 166 recovery.version = version;
84 recovery.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]); 167 recovery.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
85 if (cus->RegisterComponent(recovery) != ComponentUpdateService::kOk) { 168 if (cus->RegisterComponent(recovery) != ComponentUpdateService::kOk) {
86 NOTREACHED() << "Recovery component registration failed."; 169 NOTREACHED() << "Recovery component registration failed.";
87 } 170 }
88 } 171 }
89 172
90 void RecoveryUpdateVersionHelper(const Version& version, PrefService* prefs) { 173 void RecoveryUpdateVersionHelper(const Version& version, PrefService* prefs) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
92 prefs->SetString(prefs::kRecoveryComponentVersion, version.GetString()); 175 prefs->SetString(prefs::kRecoveryComponentVersion, version.GetString());
93 } 176 }
94 177
178 void RecoveryUpdateElevationHelper(bool elevation_needed, PrefService* prefs) {
Sorin Jianu 2014/12/05 02:10:36 We can be more specific here and rename this funct
xiaoling 2014/12/05 19:45:56 Done.
179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
180 prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, elevation_needed);
181 }
182
183 void RecoveryUpdateUnpackPathHelper(const base::FilePath& unpack_path,
184 PrefService* prefs) {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
186 prefs->SetFilePath(prefs::kRecoveryComponentUnpackPath, unpack_path);
Sorin Jianu 2014/12/05 02:10:36 Same as above. SetRecoveryComponentNeedsUnpackPat
xiaoling 2014/12/05 19:45:56 Done.
187 }
188
95 RecoveryComponentInstaller::RecoveryComponentInstaller(const Version& version, 189 RecoveryComponentInstaller::RecoveryComponentInstaller(const Version& version,
96 PrefService* prefs) 190 PrefService* prefs)
97 : current_version_(version), prefs_(prefs) { 191 : current_version_(version), prefs_(prefs) {
98 DCHECK(version.IsValid()); 192 DCHECK(version.IsValid());
99 } 193 }
100 194
101 void RecoveryComponentInstaller::OnUpdateError(int error) { 195 void RecoveryComponentInstaller::OnUpdateError(int error) {
102 NOTREACHED() << "Recovery component update error: " << error; 196 NOTREACHED() << "Recovery component update error: " << error;
103 } 197 }
104 198
105 bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest, 199 bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest,
106 const base::FilePath& unpack_path) { 200 const base::FilePath& unpack_path) {
107 std::string name; 201 std::string name;
108 manifest.GetStringASCII("name", &name); 202 manifest.GetStringASCII("name", &name);
109 if (name != kRecoveryManifestName) 203 if (name != kRecoveryManifestName)
110 return false; 204 return false;
111 std::string proposed_version; 205 std::string proposed_version;
112 manifest.GetStringASCII("version", &proposed_version); 206 manifest.GetStringASCII("version", &proposed_version);
113 Version version(proposed_version.c_str()); 207 Version version(proposed_version.c_str());
114 if (!version.IsValid()) 208 if (!version.IsValid())
115 return false; 209 return false;
116 if (current_version_.CompareTo(version) >= 0) 210 if (current_version_.CompareTo(version) >= 0)
117 return false; 211 return false;
118 212
119 // Passed the basic tests. Copy the installation to a permanent directory. 213 // Passed the basic tests. Copy the installation to a permanent directory.
120 base::FilePath path; 214 base::FilePath path;
121 if (!PathService::Get(DIR_RECOVERY_BASE, &path)) 215 if (!PathService::Get(DIR_RECOVERY_BASE, &path))
122 return false; 216 return false;
123 if (!base::PathExists(path)) { 217 if (!base::PathExists(path) && !base::CreateDirectory(path))
124 if (!base::CreateDirectory(path)) {
125 return false; 218 return false;
126 }
127 }
128 path = path.AppendASCII(version.GetString()); 219 path = path.AppendASCII(version.GetString());
129 if (base::PathExists(path) && !base::DeleteFile(path, true)) 220 if (base::PathExists(path) && !base::DeleteFile(path, true))
130 return false; 221 return false;
131 if (!base::Move(unpack_path, path)) { 222 if (!base::Move(unpack_path, path)) {
132 DVLOG(1) << "Recovery component move failed."; 223 DVLOG(1) << "Recovery component move failed.";
133 return false; 224 return false;
134 } 225 }
135 226
136 base::FilePath main_file = path.Append(kRecoveryFileName); 227 base::FilePath main_file = path.Append(kRecoveryFileName);
137 if (!base::PathExists(main_file)) 228 if (!base::PathExists(main_file))
138 return false; 229 return false;
139 // Run the recovery component. 230 // Run the recovery component.
140 CommandLine cmdline(main_file); 231 CommandLine cmdline(main_file);
141 std::string arguments; 232 std::string arguments;
142 if (manifest.GetStringASCII("x-recovery-args", &arguments)) 233 if (manifest.GetStringASCII("x-recovery-args", &arguments))
143 cmdline.AppendArg(arguments); 234 cmdline.AppendArg(arguments);
144 std::string add_version; 235 std::string add_version;
145 if (manifest.GetStringASCII("x-recovery-add-version", &add_version)) { 236 if (manifest.GetStringASCII("x-recovery-add-version", &add_version) &&
146 if (add_version == "yes") 237 add_version == "yes")
147 cmdline.AppendSwitchASCII("version", current_version_.GetString()); 238 cmdline.AppendSwitchASCII("version", current_version_.GetString());
239
240 bool install_result = false;
241 #if defined(OS_WIN)
Sorin Jianu 2014/12/05 02:10:36 Refactor as discussed.
xiaoling 2014/12/05 19:45:56 Done.
242 base::ProcessHandle process_handle;
243 base::LaunchOptions options;
244 options.start_hidden = true;
245 if (base::LaunchProcess(cmdline, options, &process_handle)) {
246 int installer_exit_code = 0;
247 const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromSeconds(600);
248 if (base::WaitForExitCodeWithTimeout(process_handle,
249 &installer_exit_code,
250 kMaxWaitTime)) {
251 if (installer_exit_code == EXIT_CODE_ELEVATION_NEEDED) {
252 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
253 base::Bind(&RecoveryUpdateUnpackPathHelper, path, prefs_));
254 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
255 base::Bind(&RecoveryUpdateElevationHelper, true, prefs_));
256 }
257
258 // Mark install result as true regardless of exit code. We may need to do
259 // elevated install further, but from updater service perspective, the
260 // install is completed.
261 install_result = true;
262 } else {
263 // Ensure that the process terminates.
264 base::KillProcess(process_handle, -1, true);
265 }
148 } 266 }
149 current_version_ = version; 267 #else
150 if (prefs_) { 268 install_result = base::LaunchProcess(cmdline, base::LaunchOptions(), NULL);
151 BrowserThread::PostTask( 269 #endif
152 BrowserThread::UI, 270
153 FROM_HERE, 271 if (install_result) {
154 base::Bind(&RecoveryUpdateVersionHelper, version, prefs_)); 272 current_version_ = version;
273 if (prefs_)
274 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
275 base::Bind(&RecoveryUpdateVersionHelper, version, prefs_));
155 } 276 }
156 return base::LaunchProcess(cmdline, base::LaunchOptions(), NULL); 277 return install_result;
157 } 278 }
158 279
159 bool RecoveryComponentInstaller::GetInstalledFile( 280 bool RecoveryComponentInstaller::GetInstalledFile(
160 const std::string& file, 281 const std::string& file,
161 base::FilePath* installed_file) { 282 base::FilePath* installed_file) {
162 return false; 283 return false;
163 } 284 }
164 285
165 void RegisterRecoveryComponent(ComponentUpdateService* cus, 286 void RegisterRecoveryComponent(ComponentUpdateService* cus,
166 PrefService* prefs) { 287 PrefService* prefs) {
288 if (SimulatingElevatedRecovery()) {
289 BrowserThread::PostTask(
290 BrowserThread::UI,
291 FROM_HERE,
292 base::Bind(&SimulateElevatedRecoveryHelper, prefs));
293 }
294
167 #if !defined(OS_CHROMEOS) 295 #if !defined(OS_CHROMEOS)
168 // We delay execute the registration because we are not required in 296 // We delay execute the registration because we are not required in
169 // the critical path during browser startup. 297 // the critical path during browser startup.
170 BrowserThread::PostDelayedTask( 298 BrowserThread::PostDelayedTask(
171 BrowserThread::UI, 299 BrowserThread::UI,
172 FROM_HERE, 300 FROM_HERE,
173 base::Bind(&RecoveryRegisterHelper, cus, prefs), 301 base::Bind(&RecoveryRegisterHelper, cus, prefs),
174 base::TimeDelta::FromSeconds(6)); 302 base::TimeDelta::FromSeconds(6));
175 #endif 303 #endif
176 } 304 }
177 305
178 void RegisterPrefsForRecoveryComponent(PrefRegistrySimple* registry) { 306 void RegisterPrefsForRecoveryComponent(PrefRegistrySimple* registry) {
179 registry->RegisterStringPref(prefs::kRecoveryComponentVersion, "0.0.0.0"); 307 registry->RegisterStringPref(prefs::kRecoveryComponentVersion, "0.0.0.0");
308 registry->RegisterFilePathPref(prefs::kRecoveryComponentUnpackPath,
309 base::FilePath());
310 registry->RegisterBooleanPref(prefs::kRecoveryComponentNeedsElevation, false);
180 } 311 }
181 312
182 void AcceptedElevatedRecoveryInstall(PrefService* prefs) { 313 void AcceptedElevatedRecoveryInstall(PrefService* prefs) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
315
316 ElevatedInstallRecoveryComponent(
317 prefs->GetFilePath(prefs::kRecoveryComponentUnpackPath));
318 prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, false);
184 } 319 }
185 320
186 void DeclinedElevatedRecoveryInstall(PrefService* prefs) { 321 void DeclinedElevatedRecoveryInstall(PrefService* prefs) {
187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 prefs->SetBoolean(prefs::kRecoveryComponentNeedsElevation, false);
188 } 324 }
189 325
190 } // namespace component_updater 326 } // namespace component_updater
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698