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