Home | History | Annotate | Download | only in util
      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