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( 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 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 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 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 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 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. 337 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. 354 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). 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 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. 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 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 474 bool InstallerState::system_install() const { 475 DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL); 476 return level_ == SYSTEM_LEVEL; 477 } 478 479 bool InstallerState::is_multi_install() const { 480 DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE); 481 return package_type_ != SINGLE_PACKAGE; 482 } 483 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 494 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 504 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 556 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 570 bool InstallerState::IsChromeFrameRunning( 571 const InstallationState& machine_state) const { 572 return AnyExistsAndIsInUse(machine_state, CHROME_FRAME_DLL); 573 } 574 575 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 583 base::FilePath InstallerState::GetInstallerDirectory( 584 const Version& version) const { 585 return target_path().Append(ASCIIToWide(version.GetString())) 586 .Append(kInstallerDir); 587 } 588 589 // static 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 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 614 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 649 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 670 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 712 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 719 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 729 void InstallerState::UpdateStage(installer::InstallerStage stage) const { 730 InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage); 731 } 732 733 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 802 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 828 bool InstallerState::RequiresActiveSetup() const { 829 return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER); 830 } 831 832 } // namespace installer 833