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