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