installer_state.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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 31namespace installer { 32 33bool 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 73InstallerState::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 85InstallerState::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 99void 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 product that is not the binaries and that is not being 218 // 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 if (machine_state.GetProductState(system_install(), type) == NULL) { 227 // The product is not installed. 228 continue; 229 } 230 231 // The product is installed. 232 233 if (!FindProduct(type)) { 234 // The product is not being uninstalled. 235 if (type != BrowserDistribution::CHROME_APP_HOST) { 236 keep_binaries = true; 237 break; 238 } else { 239 // If binaries/chrome are at system-level, we can discard them at 240 // user-level... 241 if (!machine_state.GetProductState( 242 true, // system-level 243 BrowserDistribution::CHROME_BROWSER) && 244 !machine_state.GetProductState( 245 true, // system-level 246 BrowserDistribution::CHROME_BINARIES)) { 247 // ... otherwise keep them. 248 keep_binaries = true; 249 break; 250 } 251 252 } 253 } 254 255 // The product is being uninstalled. 256 } 257 if (!keep_binaries && 258 machine_state.GetProductState(system_install(), 259 BrowserDistribution::CHROME_BINARIES)) { 260 Product* p = AddProductFromPreferences( 261 BrowserDistribution::CHROME_BINARIES, prefs, machine_state); 262 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") 263 << " distribution: " << p->distribution()->GetAppShortCutName(); 264 } 265 } 266 267 BrowserDistribution* operand = NULL; 268 269 if (is_uninstall) { 270 operation_ = UNINSTALL; 271 } else if (!prefs.is_multi_install()) { 272 // For a single-install, the current browser dist is the operand. 273 operand = BrowserDistribution::GetDistribution(); 274 operation_ = SINGLE_INSTALL_OR_UPDATE; 275 } else if (IsMultiInstallUpdate(prefs, machine_state)) { 276 // Updates driven by Google Update take place under the multi-installer's 277 // app guid. 278 operand = multi_package_distribution_; 279 operation_ = MULTI_UPDATE; 280 } else { 281 operation_ = MULTI_INSTALL; 282 } 283 284 // Initial, over, and un-installs will take place under one of the 285 // product app guids (Chrome, Chrome Frame, App Host, or Binaries, in order of 286 // preference). 287 if (operand == NULL) { 288 BrowserDistribution::Type operand_distribution_type = 289 BrowserDistribution::CHROME_BINARIES; 290 if (prefs.install_chrome()) 291 operand_distribution_type = BrowserDistribution::CHROME_BROWSER; 292 else if (prefs.install_chrome_frame()) 293 operand_distribution_type = BrowserDistribution::CHROME_FRAME; 294 else if (prefs.install_chrome_app_launcher()) 295 operand_distribution_type = BrowserDistribution::CHROME_APP_HOST; 296 297 operand = BrowserDistribution::GetSpecificDistribution( 298 operand_distribution_type); 299 } 300 301 state_key_ = operand->GetStateKey(); 302 state_type_ = operand->GetType(); 303 304 // Parse --critical-update-version=W.X.Y.Z 305 std::string critical_version_value( 306 command_line.GetSwitchValueASCII(switches::kCriticalUpdateVersion)); 307 critical_update_version_ = Version(critical_version_value); 308} 309 310void InstallerState::set_level(Level level) { 311 level_ = level; 312 switch (level) { 313 case USER_LEVEL: 314 root_key_ = HKEY_CURRENT_USER; 315 break; 316 case SYSTEM_LEVEL: 317 root_key_ = HKEY_LOCAL_MACHINE; 318 break; 319 default: 320 DCHECK(level == UNKNOWN_LEVEL); 321 level_ = UNKNOWN_LEVEL; 322 root_key_ = NULL; 323 break; 324 } 325} 326 327void InstallerState::set_package_type(PackageType type) { 328 package_type_ = type; 329 switch (type) { 330 case SINGLE_PACKAGE: 331 multi_package_distribution_ = NULL; 332 break; 333 case MULTI_PACKAGE: 334 multi_package_distribution_ = 335 BrowserDistribution::GetSpecificDistribution( 336 BrowserDistribution::CHROME_BINARIES); 337 break; 338 default: 339 DCHECK(type == UNKNOWN_PACKAGE_TYPE); 340 package_type_ = UNKNOWN_PACKAGE_TYPE; 341 multi_package_distribution_ = NULL; 342 break; 343 } 344} 345 346// Returns the Chrome binaries directory for multi-install or |dist|'s directory 347// otherwise. 348base::FilePath InstallerState::GetDefaultProductInstallPath( 349 BrowserDistribution* dist) const { 350 DCHECK(dist); 351 DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE); 352 353 if (package_type_ == SINGLE_PACKAGE) { 354 return GetChromeInstallPath(system_install(), dist); 355 } else { 356 return GetChromeInstallPath(system_install(), 357 BrowserDistribution::GetSpecificDistribution( 358 BrowserDistribution::CHROME_BINARIES)); 359 } 360} 361 362// Evaluates a product's eligibility for participation in this operation. 363// We never expect these checks to fail, hence they all terminate the process in 364// debug builds. See the log messages for details. 365bool InstallerState::CanAddProduct(const Product& product, 366 const base::FilePath* product_dir) const { 367 switch (package_type_) { 368 case SINGLE_PACKAGE: 369 if (!products_.empty()) { 370 LOG(DFATAL) << "Cannot process more than one single-install product."; 371 return false; 372 } 373 break; 374 case MULTI_PACKAGE: 375 if (!product.HasOption(kOptionMultiInstall)) { 376 LOG(DFATAL) << "Cannot process a single-install product with a " 377 "multi-install state."; 378 return false; 379 } 380 if (FindProduct(product.distribution()->GetType()) != NULL) { 381 LOG(DFATAL) << "Cannot process more than one product of the same type."; 382 return false; 383 } 384 if (!target_path_.empty()) { 385 base::FilePath default_dir; 386 if (product_dir == NULL) 387 default_dir = GetDefaultProductInstallPath(product.distribution()); 388 if (!base::FilePath::CompareEqualIgnoreCase( 389 (product_dir == NULL ? default_dir : *product_dir).value(), 390 target_path_.value())) { 391 LOG(DFATAL) << "Cannot process products in different directories."; 392 return false; 393 } 394 } 395 break; 396 default: 397 DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_); 398 break; 399 } 400 return true; 401} 402 403// Adds |product|, installed in |product_dir| to this object's collection. If 404// |product_dir| is NULL, the product's default install location is used. 405// Returns NULL if |product| is incompatible with this object. Otherwise, 406// returns a pointer to the product (ownership is held by this object). 407Product* InstallerState::AddProductInDirectory( 408 const base::FilePath* product_dir, 409 scoped_ptr<Product>* product) { 410 DCHECK(product != NULL); 411 DCHECK(product->get() != NULL); 412 const Product& the_product = *product->get(); 413 414 if (!CanAddProduct(the_product, product_dir)) 415 return NULL; 416 417 if (package_type_ == UNKNOWN_PACKAGE_TYPE) { 418 set_package_type(the_product.HasOption(kOptionMultiInstall) ? 419 MULTI_PACKAGE : SINGLE_PACKAGE); 420 } 421 422 if (target_path_.empty()) { 423 if (product_dir == NULL) 424 target_path_ = GetDefaultProductInstallPath(the_product.distribution()); 425 else 426 target_path_ = *product_dir; 427 } 428 429 if (state_key_.empty()) 430 state_key_ = the_product.distribution()->GetStateKey(); 431 432 products_.push_back(product->release()); 433 return products_[products_.size() - 1]; 434} 435 436Product* InstallerState::AddProduct(scoped_ptr<Product>* product) { 437 return AddProductInDirectory(NULL, product); 438} 439 440// Adds a product of type |distribution_type| constructed on the basis of 441// |prefs|, setting this object's msi flag if the product is represented in 442// |machine_state| and is msi-installed. Returns the product that was added, 443// or NULL if |state| is incompatible with this object. Ownership is not passed 444// to the caller. 445Product* InstallerState::AddProductFromPreferences( 446 BrowserDistribution::Type distribution_type, 447 const MasterPreferences& prefs, 448 const InstallationState& machine_state) { 449 scoped_ptr<Product> product_ptr( 450 new Product(BrowserDistribution::GetSpecificDistribution( 451 distribution_type))); 452 product_ptr->InitializeFromPreferences(prefs); 453 454 Product* product = AddProductInDirectory(NULL, &product_ptr); 455 456 if (product != NULL && !msi_) { 457 const ProductState* product_state = machine_state.GetProductState( 458 system_install(), distribution_type); 459 if (product_state != NULL) 460 msi_ = product_state->is_msi(); 461 } 462 463 return product; 464} 465 466Product* InstallerState::AddProductFromState( 467 BrowserDistribution::Type type, 468 const ProductState& state) { 469 scoped_ptr<Product> product_ptr( 470 new Product(BrowserDistribution::GetSpecificDistribution(type))); 471 product_ptr->InitializeFromUninstallCommand(state.uninstall_command()); 472 473 // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory(). 474 base::FilePath product_dir = 475 state.GetSetupPath().DirName().DirName().DirName(); 476 477 Product* product = AddProductInDirectory(&product_dir, &product_ptr); 478 479 if (product != NULL) 480 msi_ |= state.is_msi(); 481 482 return product; 483} 484 485bool InstallerState::system_install() const { 486 DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL); 487 return level_ == SYSTEM_LEVEL; 488} 489 490bool InstallerState::is_multi_install() const { 491 DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE); 492 return package_type_ != SINGLE_PACKAGE; 493} 494 495bool InstallerState::RemoveProduct(const Product* product) { 496 ScopedVector<Product>::iterator it = 497 std::find(products_.begin(), products_.end(), product); 498 if (it != products_.end()) { 499 products_.weak_erase(it); 500 return true; 501 } 502 return false; 503} 504 505const Product* InstallerState::FindProduct( 506 BrowserDistribution::Type distribution_type) const { 507 for (Products::const_iterator scan = products_.begin(), end = products_.end(); 508 scan != end; ++scan) { 509 if ((*scan)->is_type(distribution_type)) 510 return *scan; 511 } 512 return NULL; 513} 514 515Version* InstallerState::GetCurrentVersion( 516 const InstallationState& machine_state) const { 517 DCHECK(!products_.empty()); 518 scoped_ptr<Version> current_version; 519 // If we're doing a multi-install, the current version may be either an 520 // existing multi or an existing single product that is being migrated 521 // in place (i.e., Chrome). In the latter case, there is no existing 522 // CHROME_BINARIES installation so we need to search for the product. 523 BrowserDistribution::Type prod_type; 524 if (package_type_ == MULTI_PACKAGE) { 525 prod_type = BrowserDistribution::CHROME_BINARIES; 526 if (machine_state.GetProductState(level_ == SYSTEM_LEVEL, 527 prod_type) == NULL) { 528 // Search for a product on which we're operating that is installed in our 529 // target directory. 530 Products::const_iterator end = products().end(); 531 for (Products::const_iterator scan = products().begin(); scan != end; 532 ++scan) { 533 BrowserDistribution::Type product_type = 534 (*scan)->distribution()->GetType(); 535 const ProductState* state = 536 machine_state.GetProductState(level_ == SYSTEM_LEVEL, product_type); 537 if (state != NULL && target_path_.IsParent(state->GetSetupPath())) { 538 prod_type = product_type; 539 break; 540 } 541 } 542 } 543 } else { 544 prod_type = products_[0]->distribution()->GetType(); 545 } 546 const ProductState* product_state = 547 machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type); 548 549 if (product_state != NULL) { 550 const Version* version = NULL; 551 552 // Be aware that there might be a pending "new_chrome.exe" already in the 553 // installation path. If so, we use old_version, which holds the version of 554 // "chrome.exe" itself. 555 if (file_util::PathExists(target_path().Append(kChromeNewExe))) 556 version = product_state->old_version(); 557 558 if (version == NULL) 559 version = &product_state->version(); 560 561 current_version.reset(new Version(*version)); 562 } 563 564 return current_version.release(); 565} 566 567Version InstallerState::DetermineCriticalVersion( 568 const Version* current_version, 569 const Version& new_version) const { 570 DCHECK(current_version == NULL || current_version->IsValid()); 571 DCHECK(new_version.IsValid()); 572 if (critical_update_version_.IsValid() && 573 (current_version == NULL || 574 (current_version->CompareTo(critical_update_version_) < 0)) && 575 new_version.CompareTo(critical_update_version_) >= 0) { 576 return critical_update_version_; 577 } 578 return Version(); 579} 580 581bool InstallerState::IsChromeFrameRunning( 582 const InstallationState& machine_state) const { 583 // We check only for the current version (e.g. the version we are upgrading 584 // _from_). We don't need to check interstitial versions if any (as would 585 // occur in the case of multiple updates) since if they are in use, we are 586 // guaranteed that the current version is in use too. 587 bool in_use = false; 588 scoped_ptr<Version> current_version(GetCurrentVersion(machine_state)); 589 if (current_version != NULL) { 590 base::FilePath cf_install_path( 591 target_path().AppendASCII(current_version->GetString()) 592 .Append(kChromeFrameDll)); 593 in_use = file_util::PathExists(cf_install_path) && 594 IsFileInUse(cf_install_path); 595 } 596 return in_use; 597} 598 599base::FilePath InstallerState::GetInstallerDirectory( 600 const Version& version) const { 601 return target_path().Append(ASCIIToWide(version.GetString())) 602 .Append(kInstallerDir); 603} 604 605// static 606bool InstallerState::IsFileInUse(const base::FilePath& file) { 607 // Call CreateFile with a share mode of 0 which should cause this to fail 608 // with ERROR_SHARING_VIOLATION if the file exists and is in-use. 609 return !base::win::ScopedHandle(CreateFile(file.value().c_str(), 610 GENERIC_WRITE, 0, NULL, 611 OPEN_EXISTING, 0, 0)).IsValid(); 612} 613 614void InstallerState::GetExistingExeVersions( 615 std::set<std::string>* existing_versions) const { 616 617 static const wchar_t* const kChromeFilenames[] = { 618 installer::kChromeExe, 619 installer::kChromeNewExe, 620 installer::kChromeOldExe, 621 }; 622 623 for (int i = 0; i < arraysize(kChromeFilenames); ++i) { 624 base::FilePath chrome_exe(target_path().Append(kChromeFilenames[i])); 625 scoped_ptr<FileVersionInfo> file_version_info( 626 FileVersionInfo::CreateFileVersionInfo(chrome_exe)); 627 if (file_version_info) { 628 string16 version_string = file_version_info->file_version(); 629 if (!version_string.empty() && IsStringASCII(version_string)) 630 existing_versions->insert(WideToASCII(version_string)); 631 } 632 } 633} 634 635void InstallerState::RemoveOldVersionDirectories( 636 const Version& new_version, 637 Version* existing_version, 638 const base::FilePath& temp_path) const { 639 Version version; 640 std::vector<base::FilePath> key_files; 641 scoped_ptr<WorkItem> item; 642 643 std::set<std::string> existing_version_strings; 644 existing_version_strings.insert(new_version.GetString()); 645 if (existing_version) 646 existing_version_strings.insert(existing_version->GetString()); 647 648 // Make sure not to delete any version dir that is "referenced" by an existing 649 // Chrome executable. 650 GetExistingExeVersions(&existing_version_strings); 651 652 // Try to delete all directories that are not in the set we care to keep. 653 base::FileEnumerator version_enum(target_path(), false, 654 base::FileEnumerator::DIRECTORIES); 655 for (base::FilePath next_version = version_enum.Next(); !next_version.empty(); 656 next_version = version_enum.Next()) { 657 base::FilePath dir_name(next_version.BaseName()); 658 version = Version(WideToASCII(dir_name.value())); 659 // Delete the version folder if it is less than the new version and not 660 // equal to the old version (if we have an old version). 661 if (version.IsValid() && 662 existing_version_strings.count(version.GetString()) == 0) { 663 // Note: temporarily log old version deletion at ERROR level to make it 664 // more likely we see this in the installer log. 665 LOG(ERROR) << "Deleting old version directory: " << next_version.value(); 666 667 // Attempt to recursively delete the old version dir. 668 bool delete_succeeded = file_util::Delete(next_version, true); 669 670 // Note: temporarily log old version deletion at ERROR level to make it 671 // more likely we see this in the installer log. 672 LOG_IF(ERROR, !delete_succeeded) 673 << "Failed to delete old version directory: " << next_version.value(); 674 } 675 } 676} 677 678void InstallerState::AddComDllList( 679 std::vector<base::FilePath>* com_dll_list) const { 680 std::for_each(products_.begin(), products_.end(), 681 std::bind2nd(std::mem_fun(&Product::AddComDllList), 682 com_dll_list)); 683} 684 685bool InstallerState::SetChannelFlags(bool set, 686 ChannelInfo* channel_info) const { 687 bool modified = false; 688 for (Products::const_iterator scan = products_.begin(), end = products_.end(); 689 scan != end; ++scan) { 690 modified |= (*scan)->SetChannelFlags(set, channel_info); 691 } 692 return modified; 693} 694 695void InstallerState::UpdateStage(installer::InstallerStage stage) const { 696 InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage); 697} 698 699void InstallerState::UpdateChannels() const { 700 if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) { 701 VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_; 702 return; 703 } 704 705 // Update the "ap" value for the product being installed/updated. We get the 706 // current value from the registry since the InstallationState instance used 707 // by the bulk of the installer does not track changes made by UpdateStage. 708 // Create the app's ClientState key if it doesn't exist. 709 ChannelInfo channel_info; 710 base::win::RegKey state_key; 711 LONG result = state_key.Create(root_key_, state_key_.c_str(), 712 KEY_QUERY_VALUE | KEY_SET_VALUE); 713 if (result == ERROR_SUCCESS) { 714 channel_info.Initialize(state_key); 715 716 // This is a multi-install product. 717 bool modified = channel_info.SetMultiInstall(true); 718 719 // Add the appropriate modifiers for all products and their options. 720 modified |= SetChannelFlags(true, &channel_info); 721 722 VLOG(1) << "ap: " << channel_info.value(); 723 724 // Write the results if needed. 725 if (modified) 726 channel_info.Write(&state_key); 727 728 // Remove the -stage: modifier since we don't want to propagate that to the 729 // other app_guids. 730 channel_info.SetStage(NULL); 731 732 // Synchronize the other products and the package with this one. 733 ChannelInfo other_info; 734 for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { 735 BrowserDistribution::Type type = 736 static_cast<BrowserDistribution::Type>(i); 737 // Skip the app_guid we started with. 738 if (type == state_type_) 739 continue; 740 BrowserDistribution* dist = NULL; 741 // Always operate on the binaries. 742 if (i == BrowserDistribution::CHROME_BINARIES) { 743 dist = multi_package_distribution_; 744 } else { 745 const Product* product = FindProduct(type); 746 // Skip this one if it's for a product we're not operating on. 747 if (product == NULL) 748 continue; 749 dist = product->distribution(); 750 } 751 result = state_key.Create(root_key_, dist->GetStateKey().c_str(), 752 KEY_QUERY_VALUE | KEY_SET_VALUE); 753 if (result == ERROR_SUCCESS) { 754 other_info.Initialize(state_key); 755 if (!other_info.Equals(channel_info)) 756 channel_info.Write(&state_key); 757 } else { 758 LOG(ERROR) << "Failed opening key " << dist->GetStateKey() 759 << " to update app channels; result: " << result; 760 } 761 } 762 } else { 763 LOG(ERROR) << "Failed opening key " << state_key_ 764 << " to update app channels; result: " << result; 765 } 766} 767 768void InstallerState::WriteInstallerResult( 769 InstallStatus status, 770 int string_resource_id, 771 const std::wstring* const launch_cmd) const { 772 DWORD installer_result = 773 (InstallUtil::GetInstallReturnCode(status) == 0) ? 0 : 1; 774 // Use a no-rollback list since this is a best-effort deal. 775 scoped_ptr<WorkItemList> install_list( 776 WorkItem::CreateNoRollbackWorkItemList()); 777 const bool system_install = this->system_install(); 778 // Write the value for all products upon which we're operating. 779 Products::const_iterator end = products().end(); 780 for (Products::const_iterator scan = products().begin(); scan != end; 781 ++scan) { 782 InstallUtil::AddInstallerResultItems( 783 system_install, (*scan)->distribution()->GetStateKey(), status, 784 string_resource_id, launch_cmd, install_list.get()); 785 } 786 // And for the binaries if this is a multi-install. 787 if (is_multi_install()) { 788 InstallUtil::AddInstallerResultItems( 789 system_install, multi_package_binaries_distribution()->GetStateKey(), 790 status, string_resource_id, launch_cmd, install_list.get()); 791 } 792 if (!install_list->Do()) 793 LOG(ERROR) << "Failed to record installer error information in registry."; 794} 795 796bool InstallerState::RequiresActiveSetup() const { 797 return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER); 798} 799 800} // namespace installer 801