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
3 // found in the LICENSE file.
4
5 #include "chrome/installer/util/installer_state.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <utility>
10
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/file_version_info.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/win/registry.h"
20 #include "base/win/scoped_handle.h"
21 #include "chrome/installer/util/delete_tree_work_item.h"
22 #include "chrome/installer/util/helper.h"
23 #include "chrome/installer/util/install_util.h"
24 #include "chrome/installer/util/installation_state.h"
25 #include "chrome/installer/util/master_preferences.h"
26 #include "chrome/installer/util/master_preferences_constants.h"
27 #include "chrome/installer/util/product.h"
28 #include "chrome/installer/util/work_item.h"
29 #include "chrome/installer/util/work_item_list.h"
30
31 namespace installer {
32
IsMultiInstallUpdate(const MasterPreferences & prefs,const InstallationState & machine_state)33 bool InstallerState::IsMultiInstallUpdate(
34 const MasterPreferences& prefs,
35 const InstallationState& machine_state) {
36 // First, are the binaries present?
37 const ProductState* binaries =
38 machine_state.GetProductState(level_ == SYSTEM_LEVEL,
39 BrowserDistribution::CHROME_BINARIES);
40 if (binaries == NULL) {
41 // The multi-install binaries have not been installed, so they certainly
42 // aren't being updated.
43 return false;
44 }
45
46 if (prefs.install_chrome()) {
47 const ProductState* product =
48 machine_state.GetProductState(level_ == SYSTEM_LEVEL,
49 BrowserDistribution::CHROME_BROWSER);
50 if (product == NULL) {
51 VLOG(2) << "It seems that chrome is being installed for the first time.";
52 return false;
53 }
54 if (!product->channel().Equals(binaries->channel())) {
55 VLOG(2) << "It seems that chrome is being over installed.";
56 return false;
57 }
58 }
59
60 VLOG(2) << "It seems that the binaries are being updated.";
61
62 return true;
63 }
64
InstallerState()65 InstallerState::InstallerState()
66 : operation_(UNINITIALIZED),
67 state_type_(BrowserDistribution::CHROME_BROWSER),
68 multi_package_distribution_(NULL),
69 level_(UNKNOWN_LEVEL),
70 package_type_(UNKNOWN_PACKAGE_TYPE),
71 root_key_(NULL),
72 msi_(false),
73 verbose_logging_(false),
74 ensure_google_update_present_(false) {
75 }
76
InstallerState(Level level)77 InstallerState::InstallerState(Level level)
78 : operation_(UNINITIALIZED),
79 state_type_(BrowserDistribution::CHROME_BROWSER),
80 multi_package_distribution_(NULL),
81 level_(UNKNOWN_LEVEL),
82 package_type_(UNKNOWN_PACKAGE_TYPE),
83 root_key_(NULL),
84 msi_(false),
85 verbose_logging_(false),
86 ensure_google_update_present_(false) {
87 // Use set_level() so that root_key_ is updated properly.
88 set_level(level);
89 }
90
Initialize(const CommandLine & command_line,const MasterPreferences & prefs,const InstallationState & machine_state)91 void InstallerState::Initialize(const CommandLine& command_line,
92 const MasterPreferences& prefs,
93 const InstallationState& machine_state) {
94 Clear();
95
96 bool pref_bool;
97 if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool))
98 pref_bool = false;
99 set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL);
100
101 if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_))
102 verbose_logging_ = false;
103
104 if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool))
105 pref_bool = false;
106 set_package_type(pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE);
107
108 if (!prefs.GetBool(master_preferences::kMsi, &msi_))
109 msi_ = false;
110
111 ensure_google_update_present_ =
112 command_line.HasSwitch(installer::switches::kEnsureGoogleUpdatePresent);
113
114 const bool is_uninstall = command_line.HasSwitch(switches::kUninstall);
115
116 if (prefs.install_chrome()) {
117 Product* p = AddProductFromPreferences(
118 BrowserDistribution::CHROME_BROWSER, prefs, machine_state);
119 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
120 << " distribution: " << p->distribution()->GetDisplayName();
121 }
122
123 if (prefs.install_chrome_app_launcher()) {
124 Product* p = AddProductFromPreferences(
125 BrowserDistribution::CHROME_APP_HOST, prefs, machine_state);
126 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
127 << " distribution: " << p->distribution()->GetDisplayName();
128 }
129
130 if (!is_uninstall && is_multi_install()) {
131 bool need_binaries = false;
132 if (FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
133 // App Host will happily use Chrome at system level, or binaries at system
134 // level, even if app host is user level.
135 const ProductState* chrome_state = machine_state.GetProductState(
136 true, // system level
137 BrowserDistribution::CHROME_BROWSER);
138 // If Chrome is at system-level, multi- or otherwise. We'll use it.
139 if (!chrome_state) {
140 const ProductState* binaries_state = machine_state.GetProductState(
141 true, // system level
142 BrowserDistribution::CHROME_BINARIES);
143 if (!binaries_state)
144 need_binaries = true;
145 }
146 }
147
148 // Chrome multi needs Binaries at its own level.
149 if (FindProduct(BrowserDistribution::CHROME_BROWSER))
150 need_binaries = true;
151
152 if (need_binaries && !FindProduct(BrowserDistribution::CHROME_BINARIES)) {
153 // Force binaries to be installed/updated.
154 Product* p = AddProductFromPreferences(
155 BrowserDistribution::CHROME_BINARIES, prefs, machine_state);
156 VLOG(1) << "Install distribution: "
157 << p->distribution()->GetDisplayName();
158 }
159 }
160
161 if (is_uninstall && prefs.is_multi_install()) {
162 if (FindProduct(BrowserDistribution::CHROME_BROWSER)) {
163 // Uninstall each product of type |type| listed below based on the
164 // presence or absence of |switch_name| in that product's uninstall
165 // command.
166 const struct {
167 BrowserDistribution::Type type;
168 const char* switch_name;
169 bool switch_expected;
170 } conditional_additions[] = {
171 // If the App Host is installed, but not the App Launcher, remove it
172 // with Chrome. Note however that for system-level Chrome uninstalls,
173 // any installed user-level App Host will remain even if there is no
174 // App Launcher present (the orphaned app_host.exe will prompt the user
175 // for further action when executed).
176 { BrowserDistribution::CHROME_APP_HOST,
177 switches::kChromeAppLauncher,
178 false },
179 };
180
181 for (size_t i = 0; i < arraysize(conditional_additions); ++i) {
182 const ProductState* product_state = machine_state.GetProductState(
183 system_install(), conditional_additions[i].type);
184 if (product_state != NULL &&
185 product_state->uninstall_command().HasSwitch(
186 conditional_additions[i].switch_name) ==
187 conditional_additions[i].switch_expected &&
188 !FindProduct(conditional_additions[i].type)) {
189 Product* p = AddProductFromPreferences(
190 conditional_additions[i].type, prefs, machine_state);
191 VLOG(1) << "Uninstall distribution: "
192 << p->distribution()->GetDisplayName();
193 }
194 }
195 }
196
197 bool keep_binaries = false;
198 // Look for a multi-install product that is not the binaries and that is not
199 // being uninstalled. If not found, binaries are uninstalled too.
200 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
201 BrowserDistribution::Type type =
202 static_cast<BrowserDistribution::Type>(i);
203
204 if (type == BrowserDistribution::CHROME_BINARIES)
205 continue;
206
207 const ProductState* product_state =
208 machine_state.GetProductState(system_install(), type);
209 if (product_state == NULL) {
210 // The product is not installed.
211 continue;
212 }
213
214 if (!product_state->is_multi_install() &&
215 type != BrowserDistribution::CHROME_BROWSER) {
216 // The product is not sharing the binaries. It is ordinarily impossible
217 // for single-install Chrome to be installed along with any
218 // multi-install product. Treat single-install Chrome the same as any
219 // multi-install product just in case the impossible happens.
220 continue;
221 }
222
223 // The product is installed.
224
225 if (!FindProduct(type)) {
226 // The product is not being uninstalled.
227 if (type != BrowserDistribution::CHROME_APP_HOST) {
228 keep_binaries = true;
229 break;
230 } else {
231 // If binaries/chrome are at system-level, we can discard them at
232 // user-level...
233 if (!machine_state.GetProductState(
234 true, // system-level
235 BrowserDistribution::CHROME_BROWSER) &&
236 !machine_state.GetProductState(
237 true, // system-level
238 BrowserDistribution::CHROME_BINARIES)) {
239 // ... otherwise keep them.
240 keep_binaries = true;
241 break;
242 }
243
244 }
245 }
246
247 // The product is being uninstalled.
248 }
249 if (!keep_binaries &&
250 machine_state.GetProductState(system_install(),
251 BrowserDistribution::CHROME_BINARIES)) {
252 Product* p = AddProductFromPreferences(
253 BrowserDistribution::CHROME_BINARIES, prefs, machine_state);
254 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
255 << " distribution: " << p->distribution()->GetDisplayName();
256 }
257 }
258
259 BrowserDistribution* operand = NULL;
260
261 if (is_uninstall) {
262 operation_ = UNINSTALL;
263 } else if (!prefs.is_multi_install()) {
264 // For a single-install, the current browser dist is the operand.
265 operand = BrowserDistribution::GetDistribution();
266 operation_ = SINGLE_INSTALL_OR_UPDATE;
267 } else if (IsMultiInstallUpdate(prefs, machine_state)) {
268 // Updates driven by Google Update take place under the multi-installer's
269 // app guid.
270 operand = multi_package_distribution_;
271 operation_ = MULTI_UPDATE;
272 } else {
273 operation_ = MULTI_INSTALL;
274 }
275
276 // Initial, over, and un-installs will take place under one of the product app
277 // guids (Chrome, App Host, or Binaries, in order of preference).
278 if (operand == NULL) {
279 BrowserDistribution::Type operand_distribution_type =
280 BrowserDistribution::CHROME_BINARIES;
281 if (prefs.install_chrome())
282 operand_distribution_type = BrowserDistribution::CHROME_BROWSER;
283 else if (prefs.install_chrome_app_launcher())
284 operand_distribution_type = BrowserDistribution::CHROME_APP_HOST;
285
286 operand = BrowserDistribution::GetSpecificDistribution(
287 operand_distribution_type);
288 }
289
290 state_key_ = operand->GetStateKey();
291 state_type_ = operand->GetType();
292
293 // Parse --critical-update-version=W.X.Y.Z
294 std::string critical_version_value(
295 command_line.GetSwitchValueASCII(switches::kCriticalUpdateVersion));
296 critical_update_version_ = Version(critical_version_value);
297 }
298
set_level(Level level)299 void InstallerState::set_level(Level level) {
300 level_ = level;
301 switch (level) {
302 case USER_LEVEL:
303 root_key_ = HKEY_CURRENT_USER;
304 break;
305 case SYSTEM_LEVEL:
306 root_key_ = HKEY_LOCAL_MACHINE;
307 break;
308 default:
309 DCHECK(level == UNKNOWN_LEVEL);
310 level_ = UNKNOWN_LEVEL;
311 root_key_ = NULL;
312 break;
313 }
314 }
315
set_package_type(PackageType type)316 void InstallerState::set_package_type(PackageType type) {
317 package_type_ = type;
318 switch (type) {
319 case SINGLE_PACKAGE:
320 multi_package_distribution_ = NULL;
321 break;
322 case MULTI_PACKAGE:
323 multi_package_distribution_ =
324 BrowserDistribution::GetSpecificDistribution(
325 BrowserDistribution::CHROME_BINARIES);
326 break;
327 default:
328 DCHECK(type == UNKNOWN_PACKAGE_TYPE);
329 package_type_ = UNKNOWN_PACKAGE_TYPE;
330 multi_package_distribution_ = NULL;
331 break;
332 }
333 }
334
335 // Returns the Chrome binaries directory for multi-install or |dist|'s directory
336 // otherwise.
GetDefaultProductInstallPath(BrowserDistribution * dist) const337 base::FilePath InstallerState::GetDefaultProductInstallPath(
338 BrowserDistribution* dist) const {
339 DCHECK(dist);
340 DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE);
341
342 if (package_type_ == SINGLE_PACKAGE) {
343 return GetChromeInstallPath(system_install(), dist);
344 } else {
345 return GetChromeInstallPath(system_install(),
346 BrowserDistribution::GetSpecificDistribution(
347 BrowserDistribution::CHROME_BINARIES));
348 }
349 }
350
351 // Evaluates a product's eligibility for participation in this operation.
352 // We never expect these checks to fail, hence they all terminate the process in
353 // debug builds. See the log messages for details.
CanAddProduct(const Product & product,const base::FilePath * product_dir) const354 bool InstallerState::CanAddProduct(const Product& product,
355 const base::FilePath* product_dir) const {
356 switch (package_type_) {
357 case SINGLE_PACKAGE:
358 if (!products_.empty()) {
359 LOG(DFATAL) << "Cannot process more than one single-install product.";
360 return false;
361 }
362 break;
363 case MULTI_PACKAGE:
364 if (!product.HasOption(kOptionMultiInstall)) {
365 LOG(DFATAL) << "Cannot process a single-install product with a "
366 "multi-install state.";
367 return false;
368 }
369 if (FindProduct(product.distribution()->GetType()) != NULL) {
370 LOG(DFATAL) << "Cannot process more than one product of the same type.";
371 return false;
372 }
373 if (!target_path_.empty()) {
374 base::FilePath default_dir;
375 if (product_dir == NULL)
376 default_dir = GetDefaultProductInstallPath(product.distribution());
377 if (!base::FilePath::CompareEqualIgnoreCase(
378 (product_dir == NULL ? default_dir : *product_dir).value(),
379 target_path_.value())) {
380 LOG(DFATAL) << "Cannot process products in different directories.";
381 return false;
382 }
383 }
384 break;
385 default:
386 DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_);
387 break;
388 }
389 return true;
390 }
391
392 // Adds |product|, installed in |product_dir| to this object's collection. If
393 // |product_dir| is NULL, the product's default install location is used.
394 // Returns NULL if |product| is incompatible with this object. Otherwise,
395 // returns a pointer to the product (ownership is held by this object).
AddProductInDirectory(const base::FilePath * product_dir,scoped_ptr<Product> * product)396 Product* InstallerState::AddProductInDirectory(
397 const base::FilePath* product_dir,
398 scoped_ptr<Product>* product) {
399 DCHECK(product != NULL);
400 DCHECK(product->get() != NULL);
401 const Product& the_product = *product->get();
402
403 if (!CanAddProduct(the_product, product_dir))
404 return NULL;
405
406 if (package_type_ == UNKNOWN_PACKAGE_TYPE) {
407 set_package_type(the_product.HasOption(kOptionMultiInstall) ?
408 MULTI_PACKAGE : SINGLE_PACKAGE);
409 }
410
411 if (target_path_.empty()) {
412 if (product_dir == NULL)
413 target_path_ = GetDefaultProductInstallPath(the_product.distribution());
414 else
415 target_path_ = *product_dir;
416 }
417
418 if (state_key_.empty())
419 state_key_ = the_product.distribution()->GetStateKey();
420
421 products_.push_back(product->release());
422 return products_[products_.size() - 1];
423 }
424
AddProduct(scoped_ptr<Product> * product)425 Product* InstallerState::AddProduct(scoped_ptr<Product>* product) {
426 return AddProductInDirectory(NULL, product);
427 }
428
429 // Adds a product of type |distribution_type| constructed on the basis of
430 // |prefs|, setting this object's msi flag if the product is represented in
431 // |machine_state| and is msi-installed. Returns the product that was added,
432 // or NULL if |state| is incompatible with this object. Ownership is not passed
433 // to the caller.
AddProductFromPreferences(BrowserDistribution::Type distribution_type,const MasterPreferences & prefs,const InstallationState & machine_state)434 Product* InstallerState::AddProductFromPreferences(
435 BrowserDistribution::Type distribution_type,
436 const MasterPreferences& prefs,
437 const InstallationState& machine_state) {
438 scoped_ptr<Product> product_ptr(
439 new Product(BrowserDistribution::GetSpecificDistribution(
440 distribution_type)));
441 product_ptr->InitializeFromPreferences(prefs);
442
443 Product* product = AddProductInDirectory(NULL, &product_ptr);
444
445 if (product != NULL && !msi_) {
446 const ProductState* product_state = machine_state.GetProductState(
447 system_install(), distribution_type);
448 if (product_state != NULL)
449 msi_ = product_state->is_msi();
450 }
451
452 return product;
453 }
454
AddProductFromState(BrowserDistribution::Type type,const ProductState & state)455 Product* InstallerState::AddProductFromState(
456 BrowserDistribution::Type type,
457 const ProductState& state) {
458 scoped_ptr<Product> product_ptr(
459 new Product(BrowserDistribution::GetSpecificDistribution(type)));
460 product_ptr->InitializeFromUninstallCommand(state.uninstall_command());
461
462 // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory().
463 base::FilePath product_dir =
464 state.GetSetupPath().DirName().DirName().DirName();
465
466 Product* product = AddProductInDirectory(&product_dir, &product_ptr);
467
468 if (product != NULL)
469 msi_ |= state.is_msi();
470
471 return product;
472 }
473
system_install() const474 bool InstallerState::system_install() const {
475 DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL);
476 return level_ == SYSTEM_LEVEL;
477 }
478
is_multi_install() const479 bool InstallerState::is_multi_install() const {
480 DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE);
481 return package_type_ != SINGLE_PACKAGE;
482 }
483
RemoveProduct(const Product * product)484 bool InstallerState::RemoveProduct(const Product* product) {
485 ScopedVector<Product>::iterator it =
486 std::find(products_.begin(), products_.end(), product);
487 if (it != products_.end()) {
488 products_.weak_erase(it);
489 return true;
490 }
491 return false;
492 }
493
FindProduct(BrowserDistribution::Type distribution_type) const494 const Product* InstallerState::FindProduct(
495 BrowserDistribution::Type distribution_type) const {
496 for (Products::const_iterator scan = products_.begin(), end = products_.end();
497 scan != end; ++scan) {
498 if ((*scan)->is_type(distribution_type))
499 return *scan;
500 }
501 return NULL;
502 }
503
GetCurrentVersion(const InstallationState & machine_state) const504 Version* InstallerState::GetCurrentVersion(
505 const InstallationState& machine_state) const {
506 DCHECK(!products_.empty());
507 scoped_ptr<Version> current_version;
508 // If we're doing a multi-install, the current version may be either an
509 // existing multi or an existing single product that is being migrated
510 // in place (i.e., Chrome). In the latter case, there is no existing
511 // CHROME_BINARIES installation so we need to search for the product.
512 BrowserDistribution::Type prod_type;
513 if (package_type_ == MULTI_PACKAGE) {
514 prod_type = BrowserDistribution::CHROME_BINARIES;
515 if (machine_state.GetProductState(level_ == SYSTEM_LEVEL,
516 prod_type) == NULL) {
517 // Search for a product on which we're operating that is installed in our
518 // target directory.
519 Products::const_iterator end = products().end();
520 for (Products::const_iterator scan = products().begin(); scan != end;
521 ++scan) {
522 BrowserDistribution::Type product_type =
523 (*scan)->distribution()->GetType();
524 const ProductState* state =
525 machine_state.GetProductState(level_ == SYSTEM_LEVEL, product_type);
526 if (state != NULL && target_path_.IsParent(state->GetSetupPath())) {
527 prod_type = product_type;
528 break;
529 }
530 }
531 }
532 } else {
533 prod_type = products_[0]->distribution()->GetType();
534 }
535 const ProductState* product_state =
536 machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type);
537
538 if (product_state != NULL) {
539 const Version* version = NULL;
540
541 // Be aware that there might be a pending "new_chrome.exe" already in the
542 // installation path. If so, we use old_version, which holds the version of
543 // "chrome.exe" itself.
544 if (base::PathExists(target_path().Append(kChromeNewExe)))
545 version = product_state->old_version();
546
547 if (version == NULL)
548 version = &product_state->version();
549
550 current_version.reset(new Version(*version));
551 }
552
553 return current_version.release();
554 }
555
DetermineCriticalVersion(const Version * current_version,const Version & new_version) const556 Version InstallerState::DetermineCriticalVersion(
557 const Version* current_version,
558 const Version& new_version) const {
559 DCHECK(current_version == NULL || current_version->IsValid());
560 DCHECK(new_version.IsValid());
561 if (critical_update_version_.IsValid() &&
562 (current_version == NULL ||
563 (current_version->CompareTo(critical_update_version_) < 0)) &&
564 new_version.CompareTo(critical_update_version_) >= 0) {
565 return critical_update_version_;
566 }
567 return Version();
568 }
569
IsChromeFrameRunning(const InstallationState & machine_state) const570 bool InstallerState::IsChromeFrameRunning(
571 const InstallationState& machine_state) const {
572 return AnyExistsAndIsInUse(machine_state, CHROME_FRAME_DLL);
573 }
574
AreBinariesInUse(const InstallationState & machine_state) const575 bool InstallerState::AreBinariesInUse(
576 const InstallationState& machine_state) const {
577 return AnyExistsAndIsInUse(
578 machine_state,
579 (CHROME_FRAME_HELPER_EXE | CHROME_FRAME_HELPER_DLL |
580 CHROME_FRAME_DLL | CHROME_DLL));
581 }
582
GetInstallerDirectory(const Version & version) const583 base::FilePath InstallerState::GetInstallerDirectory(
584 const Version& version) const {
585 return target_path().Append(ASCIIToWide(version.GetString()))
586 .Append(kInstallerDir);
587 }
588
589 // static
IsFileInUse(const base::FilePath & file)590 bool InstallerState::IsFileInUse(const base::FilePath& file) {
591 // Call CreateFile with a share mode of 0 which should cause this to fail
592 // with ERROR_SHARING_VIOLATION if the file exists and is in-use.
593 return !base::win::ScopedHandle(CreateFile(file.value().c_str(),
594 GENERIC_WRITE, 0, NULL,
595 OPEN_EXISTING, 0, 0)).IsValid();
596 }
597
Clear()598 void InstallerState::Clear() {
599 operation_ = UNINITIALIZED;
600 target_path_.clear();
601 state_key_.clear();
602 state_type_ = BrowserDistribution::CHROME_BROWSER;
603 products_.clear();
604 multi_package_distribution_ = NULL;
605 critical_update_version_ = base::Version();
606 level_ = UNKNOWN_LEVEL;
607 package_type_ = UNKNOWN_PACKAGE_TYPE;
608 root_key_ = NULL;
609 msi_ = false;
610 verbose_logging_ = false;
611 ensure_google_update_present_ = false;
612 }
613
AnyExistsAndIsInUse(const InstallationState & machine_state,uint32 file_bits) const614 bool InstallerState::AnyExistsAndIsInUse(
615 const InstallationState& machine_state,
616 uint32 file_bits) const {
617 static const wchar_t* const kBinaryFileNames[] = {
618 kChromeDll,
619 kChromeFrameDll,
620 kChromeFrameHelperDll,
621 kChromeFrameHelperExe,
622 };
623 DCHECK_NE(file_bits, 0U);
624 DCHECK_LT(file_bits, 1U << NUM_BINARIES);
625 COMPILE_ASSERT(CHROME_DLL == 1, no_youre_out_of_order);
626 COMPILE_ASSERT(CHROME_FRAME_DLL == 2, no_youre_out_of_order);
627 COMPILE_ASSERT(CHROME_FRAME_HELPER_DLL == 4, no_youre_out_of_order);
628 COMPILE_ASSERT(CHROME_FRAME_HELPER_EXE == 8, no_youre_out_of_order);
629
630 // Check only for the current version (i.e., the version we are upgrading
631 // _from_). Later versions from pending in-use updates need not be checked
632 // since the current version is guaranteed to be in use if any such are.
633 bool in_use = false;
634 scoped_ptr<Version> current_version(GetCurrentVersion(machine_state));
635 if (!current_version)
636 return false;
637 base::FilePath directory(
638 target_path().AppendASCII(current_version->GetString()));
639 for (int i = 0; i < NUM_BINARIES; ++i) {
640 if (!(file_bits & (1U << i)))
641 continue;
642 base::FilePath file(directory.Append(kBinaryFileNames[i]));
643 if (base::PathExists(file) && IsFileInUse(file))
644 return true;
645 }
646 return false;
647 }
648
GetExistingExeVersions(std::set<std::string> * existing_versions) const649 void InstallerState::GetExistingExeVersions(
650 std::set<std::string>* existing_versions) const {
651
652 static const wchar_t* const kChromeFilenames[] = {
653 installer::kChromeExe,
654 installer::kChromeNewExe,
655 installer::kChromeOldExe,
656 };
657
658 for (int i = 0; i < arraysize(kChromeFilenames); ++i) {
659 base::FilePath chrome_exe(target_path().Append(kChromeFilenames[i]));
660 scoped_ptr<FileVersionInfo> file_version_info(
661 FileVersionInfo::CreateFileVersionInfo(chrome_exe));
662 if (file_version_info) {
663 string16 version_string = file_version_info->file_version();
664 if (!version_string.empty() && IsStringASCII(version_string))
665 existing_versions->insert(WideToASCII(version_string));
666 }
667 }
668 }
669
RemoveOldVersionDirectories(const Version & new_version,Version * existing_version,const base::FilePath & temp_path) const670 void InstallerState::RemoveOldVersionDirectories(
671 const Version& new_version,
672 Version* existing_version,
673 const base::FilePath& temp_path) const {
674 Version version;
675 scoped_ptr<WorkItem> item;
676
677 std::set<std::string> existing_version_strings;
678 existing_version_strings.insert(new_version.GetString());
679 if (existing_version)
680 existing_version_strings.insert(existing_version->GetString());
681
682 // Make sure not to delete any version dir that is "referenced" by an existing
683 // Chrome executable.
684 GetExistingExeVersions(&existing_version_strings);
685
686 // Try to delete all directories that are not in the set we care to keep.
687 base::FileEnumerator version_enum(target_path(), false,
688 base::FileEnumerator::DIRECTORIES);
689 for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
690 next_version = version_enum.Next()) {
691 base::FilePath dir_name(next_version.BaseName());
692 version = Version(WideToASCII(dir_name.value()));
693 // Delete the version folder if it is less than the new version and not
694 // equal to the old version (if we have an old version).
695 if (version.IsValid() &&
696 existing_version_strings.count(version.GetString()) == 0) {
697 // Note: temporarily log old version deletion at ERROR level to make it
698 // more likely we see this in the installer log.
699 LOG(ERROR) << "Deleting old version directory: " << next_version.value();
700
701 // Attempt to recursively delete the old version dir.
702 bool delete_succeeded = base::DeleteFile(next_version, true);
703
704 // Note: temporarily log old version deletion at ERROR level to make it
705 // more likely we see this in the installer log.
706 LOG_IF(ERROR, !delete_succeeded)
707 << "Failed to delete old version directory: " << next_version.value();
708 }
709 }
710 }
711
AddComDllList(std::vector<base::FilePath> * com_dll_list) const712 void InstallerState::AddComDllList(
713 std::vector<base::FilePath>* com_dll_list) const {
714 std::for_each(products_.begin(), products_.end(),
715 std::bind2nd(std::mem_fun(&Product::AddComDllList),
716 com_dll_list));
717 }
718
SetChannelFlags(bool set,ChannelInfo * channel_info) const719 bool InstallerState::SetChannelFlags(bool set,
720 ChannelInfo* channel_info) const {
721 bool modified = false;
722 for (Products::const_iterator scan = products_.begin(), end = products_.end();
723 scan != end; ++scan) {
724 modified |= (*scan)->SetChannelFlags(set, channel_info);
725 }
726 return modified;
727 }
728
UpdateStage(installer::InstallerStage stage) const729 void InstallerState::UpdateStage(installer::InstallerStage stage) const {
730 InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage);
731 }
732
UpdateChannels() const733 void InstallerState::UpdateChannels() const {
734 if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) {
735 VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_;
736 return;
737 }
738
739 // Update the "ap" value for the product being installed/updated. We get the
740 // current value from the registry since the InstallationState instance used
741 // by the bulk of the installer does not track changes made by UpdateStage.
742 // Create the app's ClientState key if it doesn't exist.
743 ChannelInfo channel_info;
744 base::win::RegKey state_key;
745 LONG result = state_key.Create(root_key_, state_key_.c_str(),
746 KEY_QUERY_VALUE | KEY_SET_VALUE);
747 if (result == ERROR_SUCCESS) {
748 channel_info.Initialize(state_key);
749
750 // This is a multi-install product.
751 bool modified = channel_info.SetMultiInstall(true);
752
753 // Add the appropriate modifiers for all products and their options.
754 modified |= SetChannelFlags(true, &channel_info);
755
756 VLOG(1) << "ap: " << channel_info.value();
757
758 // Write the results if needed.
759 if (modified)
760 channel_info.Write(&state_key);
761
762 // Remove the -stage: modifier since we don't want to propagate that to the
763 // other app_guids.
764 channel_info.SetStage(NULL);
765
766 // Synchronize the other products and the package with this one.
767 ChannelInfo other_info;
768 for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
769 BrowserDistribution::Type type =
770 static_cast<BrowserDistribution::Type>(i);
771 // Skip the app_guid we started with.
772 if (type == state_type_)
773 continue;
774 BrowserDistribution* dist = NULL;
775 // Always operate on the binaries.
776 if (i == BrowserDistribution::CHROME_BINARIES) {
777 dist = multi_package_distribution_;
778 } else {
779 const Product* product = FindProduct(type);
780 // Skip this one if it's for a product we're not operating on.
781 if (product == NULL)
782 continue;
783 dist = product->distribution();
784 }
785 result = state_key.Create(root_key_, dist->GetStateKey().c_str(),
786 KEY_QUERY_VALUE | KEY_SET_VALUE);
787 if (result == ERROR_SUCCESS) {
788 other_info.Initialize(state_key);
789 if (!other_info.Equals(channel_info))
790 channel_info.Write(&state_key);
791 } else {
792 LOG(ERROR) << "Failed opening key " << dist->GetStateKey()
793 << " to update app channels; result: " << result;
794 }
795 }
796 } else {
797 LOG(ERROR) << "Failed opening key " << state_key_
798 << " to update app channels; result: " << result;
799 }
800 }
801
WriteInstallerResult(InstallStatus status,int string_resource_id,const std::wstring * const launch_cmd) const802 void InstallerState::WriteInstallerResult(
803 InstallStatus status,
804 int string_resource_id,
805 const std::wstring* const launch_cmd) const {
806 // Use a no-rollback list since this is a best-effort deal.
807 scoped_ptr<WorkItemList> install_list(
808 WorkItem::CreateNoRollbackWorkItemList());
809 const bool system_install = this->system_install();
810 // Write the value for all products upon which we're operating.
811 Products::const_iterator end = products().end();
812 for (Products::const_iterator scan = products().begin(); scan != end;
813 ++scan) {
814 InstallUtil::AddInstallerResultItems(
815 system_install, (*scan)->distribution()->GetStateKey(), status,
816 string_resource_id, launch_cmd, install_list.get());
817 }
818 // And for the binaries if this is a multi-install.
819 if (is_multi_install()) {
820 InstallUtil::AddInstallerResultItems(
821 system_install, multi_package_binaries_distribution()->GetStateKey(),
822 status, string_resource_id, launch_cmd, install_list.get());
823 }
824 if (!install_list->Do())
825 LOG(ERROR) << "Failed to record installer error information in registry.";
826 }
827
RequiresActiveSetup() const828 bool InstallerState::RequiresActiveSetup() const {
829 return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER);
830 }
831
832 } // namespace installer
833