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