Home | History | Annotate | Download | only in test
      1 // Copyright (c) 2011 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 // The file contains the implementation of the mini_installer re-versioner.
      6 // The main function (GenerateNextVersion) does the following in a temp dir:
      7 // - Extracts and unpacks setup.exe and the Chrome-bin folder from
      8 //   mini_installer.exe.
      9 // - Inspects setup.exe to determine the current version.
     10 // - Runs through all .dll and .exe files:
     11 //   - Replacing all occurrences of the Unicode version string in the files'
     12 //     resources with the updated string.
     13 //   - For all resources in which the string substitution is made, the binary
     14 //     form of the version is also replaced.
     15 // - Re-packs setup.exe and Chrome-bin.
     16 // - Inserts them into the target mini_installer.exe.
     17 //
     18 // This code assumes that the host program 1) initializes the process-wide
     19 // CommandLine instance, and 2) resides in the output directory of a build
     20 // tree.  When #2 is not the case, the --7za_path command-line switch may be
     21 // used to provide the (relative or absolute) path to the directory containing
     22 // 7za.exe.
     23 
     24 #include "chrome/installer/test/alternate_version_generator.h"
     25 
     26 #include <windows.h>
     27 
     28 #include <algorithm>
     29 #include <limits>
     30 #include <sstream>
     31 #include <utility>
     32 #include <vector>
     33 
     34 #include "base/basictypes.h"
     35 #include "base/command_line.h"
     36 #include "base/file_util.h"
     37 #include "base/files/file_enumerator.h"
     38 #include "base/files/file_path.h"
     39 #include "base/logging.h"
     40 #include "base/path_service.h"
     41 #include "base/platform_file.h"
     42 #include "base/process/launch.h"
     43 #include "base/process/process_handle.h"
     44 #include "base/strings/string_util.h"
     45 #include "base/version.h"
     46 #include "base/win/pe_image.h"
     47 #include "base/win/scoped_handle.h"
     48 #include "chrome/installer/test/pe_image_resources.h"
     49 #include "chrome/installer/test/resource_loader.h"
     50 #include "chrome/installer/test/resource_updater.h"
     51 #include "chrome/installer/util/lzma_util.h"
     52 
     53 namespace {
     54 
     55 const wchar_t k7zaExe[] = L"7za.exe";
     56 const wchar_t k7zaPathRelative[] = L"..\\..\\third_party\\lzma_sdk\\Executable";
     57 const wchar_t kB7[] = L"B7";
     58 const wchar_t kBl[] = L"BL";
     59 const wchar_t kChrome7z[] = L"chrome.7z";
     60 const wchar_t kChromeBin[] = L"Chrome-bin";
     61 const wchar_t kChromePacked7z[] = L"chrome.packed.7z";
     62 const wchar_t kExe[] = L"exe";
     63 const wchar_t kExpandExe[] = L"expand.exe";
     64 const wchar_t kExtDll[] = L".dll";
     65 const wchar_t kExtExe[] = L".exe";
     66 const wchar_t kMakeCab[] = L"makecab.exe";
     67 const wchar_t kSetupEx_[] = L"setup.ex_";
     68 const wchar_t kSetupExe[] = L"setup.exe";
     69 const char kSwitch7zaPath[] = "7za_path";
     70 const wchar_t kTempDirPrefix[] = L"mini_installer_test_temp";
     71 
     72 // A helper class for creating and cleaning a temporary directory.  A temporary
     73 // directory is created in Initialize and destroyed (along with all of its
     74 // contents) when the guard instance is destroyed.
     75 class ScopedTempDirectory {
     76  public:
     77   ScopedTempDirectory() { }
     78   ~ScopedTempDirectory() {
     79     if (!directory_.empty() && !base::DeleteFile(directory_, true)) {
     80       LOG(DFATAL) << "Failed deleting temporary directory \""
     81                   << directory_.value() << "\"";
     82     }
     83   }
     84   // Creates a temporary directory.
     85   bool Initialize() {
     86     DCHECK(directory_.empty());
     87     if (!file_util::CreateNewTempDirectory(&kTempDirPrefix[0], &directory_)) {
     88       LOG(DFATAL) << "Failed creating temporary directory.";
     89       return false;
     90     }
     91     return true;
     92   }
     93   const base::FilePath& directory() const {
     94     DCHECK(!directory_.empty());
     95     return directory_;
     96   }
     97 
     98  private:
     99   base::FilePath directory_;
    100   DISALLOW_COPY_AND_ASSIGN(ScopedTempDirectory);
    101 };  // class ScopedTempDirectory
    102 
    103 // A helper class for manipulating a Chrome product version.
    104 class ChromeVersion {
    105  public:
    106   static ChromeVersion FromHighLow(DWORD high, DWORD low) {
    107     return ChromeVersion(static_cast<ULONGLONG>(high) << 32 |
    108                          static_cast<ULONGLONG>(low));
    109   }
    110   static ChromeVersion FromString(const std::string& version_string) {
    111     Version version(version_string);
    112     DCHECK(version.IsValid());
    113     const std::vector<uint16>& c(version.components());
    114     return ChromeVersion(static_cast<ULONGLONG>(c[0]) << 48 |
    115                          static_cast<ULONGLONG>(c[1]) << 32 |
    116                          static_cast<ULONGLONG>(c[2]) << 16 |
    117                          static_cast<ULONGLONG>(c[3]));
    118   }
    119 
    120   ChromeVersion() { }
    121   explicit ChromeVersion(ULONGLONG value) : version_(value) { }
    122   WORD major() const { return static_cast<WORD>(version_ >> 48); }
    123   WORD minor() const { return static_cast<WORD>(version_ >> 32); }
    124   WORD build() const { return static_cast<WORD>(version_ >> 16); }
    125   WORD patch() const { return static_cast<WORD>(version_); }
    126   DWORD high() const { return static_cast<DWORD>(version_ >> 32); }
    127   DWORD low() const { return static_cast<DWORD>(version_); }
    128   ULONGLONG value() const { return version_; }
    129   void set_value(ULONGLONG value) { version_ = value; }
    130   std::wstring ToString() const;
    131  private:
    132   ULONGLONG version_;
    133 };  // class ChromeVersion
    134 
    135 std::wstring ChromeVersion::ToString() const {
    136   wchar_t buffer[24];
    137   int string_len =
    138       swprintf_s(&buffer[0], arraysize(buffer), L"%hu.%hu.%hu.%hu",
    139                  major(), minor(), build(), patch());
    140   DCHECK_NE(-1, string_len);
    141   DCHECK_GT(static_cast<int>(arraysize(buffer)), string_len);
    142   return std::wstring(&buffer[0], string_len);
    143 }
    144 
    145 
    146 // A read/write mapping of a file.
    147 // Note: base::MemoryMappedFile is not used because it doesn't support
    148 // read/write mappings.  Adding such support across all platforms for this
    149 // Windows-only test code seems like overkill.
    150 class MappedFile {
    151  public:
    152   MappedFile() : size_(), mapping_(), view_() { }
    153   ~MappedFile();
    154   bool Initialize(base::PlatformFile file);
    155   void* data() const { return view_; }
    156   size_t size() const { return size_; }
    157  private:
    158   size_t size_;
    159   HANDLE mapping_;
    160   void* view_;
    161   DISALLOW_COPY_AND_ASSIGN(MappedFile);
    162 };  // class MappedFile
    163 
    164 MappedFile::~MappedFile() {
    165   if (view_ != NULL) {
    166     if (UnmapViewOfFile(view_) == 0) {
    167       PLOG(DFATAL) << "MappedFile failed to unmap view.";
    168     }
    169   }
    170   if (mapping_ != NULL) {
    171     if (CloseHandle(mapping_) == 0) {
    172       PLOG(DFATAL) << "Could not close file mapping handle.";
    173     }
    174   }
    175 }
    176 
    177 bool MappedFile::Initialize(base::PlatformFile file) {
    178   DCHECK(mapping_ == NULL);
    179   bool result = false;
    180   base::PlatformFileInfo file_info;
    181 
    182   if (base::GetPlatformFileInfo(file, &file_info)) {
    183     if (file_info.size <=
    184         static_cast<int64>(std::numeric_limits<DWORD>::max())) {
    185       mapping_ = CreateFileMapping(file, NULL, PAGE_READWRITE, 0,
    186                                    static_cast<DWORD>(file_info.size), NULL);
    187       if (mapping_ != NULL) {
    188         view_ = MapViewOfFile(mapping_, FILE_MAP_WRITE, 0, 0,
    189                               static_cast<size_t>(file_info.size));
    190         if (view_ != NULL) {
    191           result = true;
    192         } else {
    193           PLOG(DFATAL) << "MapViewOfFile failed";
    194         }
    195       } else {
    196         PLOG(DFATAL) << "CreateFileMapping failed";
    197       }
    198     } else {
    199       LOG(DFATAL) << "Files larger than " << std::numeric_limits<DWORD>::max()
    200                   << " are not supported.";
    201     }
    202   } else {
    203     PLOG(DFATAL) << "GetPlatformFileInfo failed";
    204   }
    205   return result;
    206 }
    207 
    208 // Calls CreateProcess with good default parameters and waits for the process
    209 // to terminate returning the process exit code.
    210 bool RunProcessAndWait(const wchar_t* exe_path, const std::wstring& cmdline,
    211                        int* exit_code) {
    212   bool result = true;
    213   base::ProcessHandle process;
    214   base::LaunchOptions options;
    215   options.wait = true;
    216   options.start_hidden = true;
    217   if (base::LaunchProcess(cmdline, options, &process)) {
    218     if (exit_code) {
    219       if (!GetExitCodeProcess(process,
    220                               reinterpret_cast<DWORD*>(exit_code))) {
    221         PLOG(DFATAL) << "Failed getting the exit code for \""
    222                      << cmdline << "\".";
    223         result = false;
    224       } else {
    225         DCHECK_NE(*exit_code, STILL_ACTIVE);
    226       }
    227     }
    228   } else {
    229     result = false;
    230   }
    231 
    232   CloseHandle(process);
    233   return result;
    234 }
    235 
    236 // Retrieves the version number of |pe_file| from its version
    237 // resource, placing the value in |version|.  Returns true on success.
    238 bool GetFileVersion(const base::FilePath& pe_file, ChromeVersion* version) {
    239   DCHECK(version);
    240   bool result = false;
    241   upgrade_test::ResourceLoader pe_file_loader;
    242   std::pair<const uint8*, DWORD> version_info_data;
    243 
    244   if (pe_file_loader.Initialize(pe_file) &&
    245       pe_file_loader.Load(VS_VERSION_INFO, reinterpret_cast<WORD>(RT_VERSION),
    246                           &version_info_data)) {
    247     const VS_FIXEDFILEINFO* fixed_file_info;
    248     UINT ver_info_len;
    249     if (VerQueryValue(version_info_data.first, L"\\",
    250                       reinterpret_cast<void**>(
    251                           const_cast<VS_FIXEDFILEINFO**>(&fixed_file_info)),
    252                       &ver_info_len) != 0) {
    253       DCHECK_EQ(sizeof(VS_FIXEDFILEINFO), static_cast<size_t>(ver_info_len));
    254       *version = ChromeVersion::FromHighLow(fixed_file_info->dwFileVersionMS,
    255                                             fixed_file_info->dwFileVersionLS);
    256       result = true;
    257     } else {
    258       LOG(DFATAL) << "VerQueryValue failed to retrieve VS_FIXEDFILEINFO";
    259     }
    260   }
    261 
    262   return result;
    263 }
    264 
    265 // Retrieves the version number of setup.exe in |work_dir| from its version
    266 // resource, placing the value in |version|.  Returns true on success.
    267 bool GetSetupExeVersion(const base::FilePath& work_dir,
    268                         ChromeVersion* version) {
    269   return GetFileVersion(work_dir.Append(&kSetupExe[0]), version);
    270 }
    271 
    272 
    273 // Replace all occurrences in the sequence [|dest_first|, |dest_last) that
    274 // equals [|src_first|, |src_last) with the sequence at |replacement_first| of
    275 // the same length.  Returns true on success.  If non-NULL, |replacements_made|
    276 // is set to true/false accordingly.
    277 bool ReplaceAll(uint8* dest_first, uint8* dest_last,
    278                 const uint8* src_first, const uint8* src_last,
    279                 const uint8* replacement_first, bool* replacements_made) {
    280   bool result = true;
    281   bool changed = false;
    282   do {
    283     dest_first = std::search(dest_first, dest_last, src_first, src_last);
    284     if (dest_first == dest_last) {
    285       break;
    286     }
    287     changed = true;
    288     if (memcpy_s(dest_first, dest_last - dest_first,
    289                  replacement_first, src_last - src_first) != 0) {
    290       result = false;
    291       break;
    292     }
    293     dest_first += (src_last - src_first);
    294   } while (true);
    295 
    296   if (replacements_made != NULL) {
    297     *replacements_made = changed;
    298   }
    299 
    300   return result;
    301 }
    302 
    303 // A context structure in support of our EnumResource_Fn callback.
    304 struct VisitResourceContext {
    305   ChromeVersion current_version;
    306   std::wstring current_version_str;
    307   ChromeVersion new_version;
    308   std::wstring new_version_str;
    309 };  // struct VisitResourceContext
    310 
    311 // Replaces the old version with the new in a resource.  A first pass is made to
    312 // replace the string form (e.g., "9.0.584.0").  If any replacements are made, a
    313 // second pass is made to replace the binary form (e.g., 0x0000024800000009).
    314 void VisitResource(const upgrade_test::EntryPath& path,
    315                    uint8* data, DWORD size, DWORD code_page,
    316                    uintptr_t context) {
    317   VisitResourceContext& ctx = *reinterpret_cast<VisitResourceContext*>(context);
    318 
    319   // Replace all occurrences of current_version_str with new_version_str
    320   bool changing_version = false;
    321   if (ReplaceAll(
    322           data,
    323           data + size,
    324           reinterpret_cast<const uint8*>(ctx.current_version_str.c_str()),
    325           reinterpret_cast<const uint8*>(ctx.current_version_str.c_str() +
    326               ctx.current_version_str.size() + 1),
    327           reinterpret_cast<const uint8*>(ctx.new_version_str.c_str()),
    328           &changing_version) &&
    329       changing_version) {
    330     // Replace all occurrences of current_version with new_version
    331     struct VersionPair {
    332       DWORD high;
    333       DWORD low;
    334     };
    335     VersionPair cur_ver = {
    336       ctx.current_version.high(), ctx.current_version.low()
    337     };
    338     VersionPair new_ver = {
    339       ctx.new_version.high(), ctx.new_version.low()
    340     };
    341     ReplaceAll(data, data + size, reinterpret_cast<const uint8*>(&cur_ver),
    342                reinterpret_cast<const uint8*>(&cur_ver) + sizeof(cur_ver),
    343                reinterpret_cast<const uint8*>(&new_ver), NULL);
    344   }
    345 }
    346 
    347 // Updates the version strings and numbers in all of |image_file|'s resources.
    348 bool UpdateVersionIfMatch(const base::FilePath& image_file,
    349                           VisitResourceContext* context) {
    350   if (!context ||
    351       context->current_version_str.size() < context->new_version_str.size()) {
    352     return false;
    353   }
    354 
    355   bool result = false;
    356   base::win::ScopedHandle image_handle(base::CreatePlatformFile(
    357       image_file,
    358       (base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
    359        base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_READ |
    360        base::PLATFORM_FILE_EXCLUSIVE_WRITE), NULL, NULL));
    361   // It turns out that the underlying CreateFile can fail due to unhelpful
    362   // security software locking the newly created DLL. So add a few brief
    363   // retries to help tests that use this pass on machines thusly encumbered.
    364   int retries = 3;
    365   while (!image_handle.IsValid() && retries-- > 0) {
    366     LOG(WARNING) << "Failed to open \"" << image_file.value() << "\"."
    367                  << " Retrying " << retries << " more times.";
    368     Sleep(1000);
    369     image_handle.Set(base::CreatePlatformFile(
    370       image_file,
    371       (base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
    372        base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_READ |
    373        base::PLATFORM_FILE_EXCLUSIVE_WRITE), NULL, NULL));
    374   }
    375 
    376   if (image_handle.IsValid()) {
    377     MappedFile image_mapping;
    378     if (image_mapping.Initialize(image_handle)) {
    379       base::win::PEImageAsData image(
    380           reinterpret_cast<HMODULE>(image_mapping.data()));
    381       // PEImage class does not support other-architecture images.
    382       if (image.GetNTHeaders()->OptionalHeader.Magic ==
    383           IMAGE_NT_OPTIONAL_HDR_MAGIC) {
    384         result = upgrade_test::EnumResources(
    385             image, &VisitResource, reinterpret_cast<uintptr_t>(context));
    386       } else {
    387         result = true;
    388       }
    389     }
    390   } else {
    391     PLOG(DFATAL) << "Failed to open \"" << image_file.value() << "\"";
    392   }
    393   return result;
    394 }
    395 
    396 bool IncrementNewVersion(upgrade_test::Direction direction,
    397                          VisitResourceContext* ctx) {
    398   DCHECK(ctx);
    399 
    400   // Figure out a past or future version with the same string length as this one
    401   // by decrementing or incrementing each component.
    402   LONGLONG incrementer = (direction == upgrade_test::PREVIOUS_VERSION ? -1 : 1);
    403 
    404   do {
    405     if (incrementer == 0) {
    406       LOG(DFATAL) << "Improbable version at the cusp of complete rollover";
    407       return false;
    408     }
    409     ctx->new_version.set_value(ctx->current_version.value() + incrementer);
    410     ctx->new_version_str = ctx->new_version.ToString();
    411     incrementer <<= 16;
    412   } while (ctx->new_version_str.size() != ctx->current_version_str.size());
    413 
    414   return true;
    415 }
    416 
    417 // Raises or lowers the version of all .exe and .dll files in |work_dir| as well
    418 // as the |work-dir|\Chrome-bin\w.x.y.z directory.  |original_version| and
    419 // |new_version|, when non-NULL, are given the original and new version numbers
    420 // on success.
    421 bool ApplyAlternateVersion(const base::FilePath& work_dir,
    422                            upgrade_test::Direction direction,
    423                            std::wstring* original_version,
    424                            std::wstring* new_version) {
    425   VisitResourceContext ctx;
    426   if (!GetSetupExeVersion(work_dir, &ctx.current_version)) {
    427     return false;
    428   }
    429   ctx.current_version_str = ctx.current_version.ToString();
    430 
    431   if (!IncrementNewVersion(direction, &ctx)) {
    432     return false;
    433   }
    434 
    435   // Modify all .dll and .exe files with the current version.
    436   bool doing_great = true;
    437   base::FileEnumerator all_files(work_dir, true, base::FileEnumerator::FILES);
    438   do {
    439     base::FilePath file = all_files.Next();
    440     if (file.empty()) {
    441       break;
    442     }
    443     std::wstring extension = file.Extension();
    444     if (extension == &kExtExe[0] || extension == &kExtDll[0]) {
    445       doing_great = UpdateVersionIfMatch(file, &ctx);
    446     }
    447   } while (doing_great);
    448 
    449   // Change the versioned directory.
    450   base::FilePath chrome_bin = work_dir.Append(&kChromeBin[0]);
    451   doing_great = base::Move(chrome_bin.Append(ctx.current_version_str),
    452                            chrome_bin.Append(ctx.new_version_str));
    453 
    454   if (doing_great) {
    455     // Report the version numbers if requested.
    456     if (original_version != NULL)
    457       original_version->assign(ctx.current_version_str);
    458     if (new_version != NULL)
    459       new_version->assign(ctx.new_version_str);
    460   }
    461 
    462   return doing_great;
    463 }
    464 
    465 // Returns the path to the directory holding the 7za executable.  By default, it
    466 // is assumed that the test resides in the tree's output directory, so the
    467 // relative path "..\..\third_party\lzma_sdk\Executable" is applied to the host
    468 // executable's directory.  This can be overridden with the --7za_path
    469 // command-line switch.
    470 base::FilePath Get7zaPath() {
    471   base::FilePath l7za_path =
    472       CommandLine::ForCurrentProcess()->GetSwitchValuePath(
    473           &kSwitch7zaPath[0]);
    474   if (l7za_path.empty()) {
    475     base::FilePath dir_exe;
    476     if (!PathService::Get(base::DIR_EXE, &dir_exe))
    477       LOG(DFATAL) << "Failed getting directory of host executable";
    478     l7za_path = dir_exe.Append(&k7zaPathRelative[0]);
    479   }
    480   return l7za_path;
    481 }
    482 
    483 bool CreateArchive(const base::FilePath& output_file,
    484                    const base::FilePath& input_path,
    485                    int compression_level) {
    486   DCHECK(compression_level == 0 ||
    487          compression_level >= 1 && compression_level <= 9 &&
    488          (compression_level & 0x01) != 0);
    489 
    490   std::wstring command_line(1, L'"');
    491   command_line
    492       .append(Get7zaPath().Append(&k7zaExe[0]).value())
    493       .append(L"\" a -bd -t7z \"")
    494       .append(output_file.value())
    495       .append(L"\" \"")
    496       .append(input_path.value())
    497       .append(L"\" -mx")
    498       .append(1, L'0' + compression_level);
    499   int exit_code;
    500   if (!RunProcessAndWait(NULL, command_line, &exit_code))
    501     return false;
    502   if (exit_code != 0) {
    503     LOG(DFATAL) << Get7zaPath().Append(&k7zaExe[0]).value()
    504                 << " exited with code " << exit_code
    505                 << " while creating " << output_file.value();
    506     return false;
    507   }
    508   return true;
    509 }
    510 
    511 }  // namespace
    512 
    513 namespace upgrade_test {
    514 
    515 bool GenerateAlternateVersion(const base::FilePath& original_installer_path,
    516                               const base::FilePath& target_path,
    517                               Direction direction,
    518                               std::wstring* original_version,
    519                               std::wstring* new_version) {
    520   // Create a temporary directory in which we'll do our work.
    521   ScopedTempDirectory work_dir;
    522   if (!work_dir.Initialize())
    523     return false;
    524 
    525   // Copy the original mini_installer.
    526   base::FilePath mini_installer =
    527       work_dir.directory().Append(original_installer_path.BaseName());
    528   if (!base::CopyFile(original_installer_path, mini_installer)) {
    529     LOG(DFATAL) << "Failed copying \"" << original_installer_path.value()
    530                 << "\" to \"" << mini_installer.value() << "\"";
    531     return false;
    532   }
    533 
    534   base::FilePath setup_ex_ = work_dir.directory().Append(&kSetupEx_[0]);
    535   base::FilePath chrome_packed_7z =
    536       work_dir.directory().Append(&kChromePacked7z[0]);
    537   // Load the original file and extract setup.ex_ and chrome.packed.7z
    538   {
    539     ResourceLoader resource_loader;
    540     std::pair<const uint8*, DWORD> resource_data;
    541 
    542     if (!resource_loader.Initialize(mini_installer))
    543       return false;
    544 
    545     // Write out setup.ex_
    546     if (!resource_loader.Load(&kSetupEx_[0], &kBl[0], &resource_data))
    547       return false;
    548     int written =
    549         file_util::WriteFile(setup_ex_,
    550                              reinterpret_cast<const char*>(resource_data.first),
    551                              static_cast<int>(resource_data.second));
    552     if (written != resource_data.second) {
    553       LOG(DFATAL) << "Failed writing \"" << setup_ex_.value() << "\"";
    554       return false;
    555     }
    556 
    557     // Write out chrome.packed.7z
    558     if (!resource_loader.Load(&kChromePacked7z[0], &kB7[0], &resource_data))
    559       return false;
    560     written =
    561         file_util::WriteFile(chrome_packed_7z,
    562                              reinterpret_cast<const char*>(resource_data.first),
    563                              static_cast<int>(resource_data.second));
    564     if (written != resource_data.second) {
    565       LOG(DFATAL) << "Failed writing \"" << chrome_packed_7z.value() << "\"";
    566       return false;
    567     }
    568   }
    569 
    570   // Expand setup.ex_
    571   base::FilePath setup_exe = setup_ex_.ReplaceExtension(&kExe[0]);
    572   std::wstring command_line;
    573   command_line.append(1, L'"')
    574     .append(&kExpandExe[0])
    575     .append(L"\" \"")
    576     .append(setup_ex_.value())
    577     .append(L"\" \"")
    578     .append(setup_exe.value())
    579     .append(1, L'\"');
    580   int exit_code;
    581   if (!RunProcessAndWait(NULL, command_line, &exit_code))
    582     return false;
    583   if (exit_code != 0) {
    584     LOG(DFATAL) << &kExpandExe[0] << " exited with code " << exit_code;
    585     return false;
    586   }
    587 
    588   // Unpack chrome.packed.7z
    589   std::wstring chrome_7z_name;
    590   if (LzmaUtil::UnPackArchive(chrome_packed_7z.value(),
    591                               work_dir.directory().value(),
    592                               &chrome_7z_name) != NO_ERROR) {
    593     LOG(DFATAL) << "Failed unpacking \"" << chrome_packed_7z.value() << "\"";
    594     return false;
    595   }
    596 
    597   // Unpack chrome.7z
    598   if (LzmaUtil::UnPackArchive(chrome_7z_name, work_dir.directory().value(),
    599                               NULL) != NO_ERROR) {
    600     LOG(DFATAL) << "Failed unpacking \"" << chrome_7z_name << "\"";
    601     return false;
    602   }
    603 
    604   // Get rid of intermediate files
    605   base::FilePath chrome_7z(chrome_7z_name);
    606   if (!base::DeleteFile(chrome_7z, false) ||
    607       !base::DeleteFile(chrome_packed_7z, false) ||
    608       !base::DeleteFile(setup_ex_, false)) {
    609     LOG(DFATAL) << "Failed deleting intermediate files";
    610     return false;
    611   }
    612 
    613   // Increment the version in all files.
    614   ApplyAlternateVersion(work_dir.directory(), direction, original_version,
    615                         new_version);
    616 
    617   // Pack up files into chrome.7z
    618   if (!CreateArchive(chrome_7z, work_dir.directory().Append(&kChromeBin[0]), 0))
    619     return false;
    620 
    621   // Compress chrome.7z into chrome.packed.7z
    622   if (!CreateArchive(chrome_packed_7z, chrome_7z, 9))
    623     return false;
    624 
    625   // Compress setup.exe into setup.ex_
    626   command_line.assign(1, L'"')
    627       .append(&kMakeCab[0])
    628       .append(L"\" /D CompressionType=LZX /L \"")
    629       .append(work_dir.directory().value())
    630       .append(L"\" \"")
    631       .append(setup_exe.value());
    632   if (!RunProcessAndWait(NULL, command_line, &exit_code))
    633     return false;
    634   if (exit_code != 0) {
    635     LOG(DFATAL) << &kMakeCab[0] << " exited with code " << exit_code;
    636     return false;
    637   }
    638 
    639   // Replace the mini_installer's setup.ex_ and chrome.packed.7z resources.
    640   ResourceUpdater updater;
    641   if (!updater.Initialize(mini_installer) ||
    642       !updater.Update(&kSetupEx_[0], &kBl[0],
    643                       MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
    644                       setup_ex_) ||
    645       !updater.Update(&kChromePacked7z[0], &kB7[0],
    646                       MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
    647                       chrome_packed_7z) ||
    648       !updater.Commit()) {
    649     return false;
    650   }
    651 
    652   // Finally, move the updated mini_installer into place.
    653   return base::Move(mini_installer, target_path);
    654 }
    655 
    656 bool GenerateAlternatePEFileVersion(const base::FilePath& original_file,
    657                                     const base::FilePath& target_file,
    658                                     Direction direction) {
    659   VisitResourceContext ctx;
    660   if (!GetFileVersion(original_file, &ctx.current_version)) {
    661     LOG(DFATAL) << "Failed reading version from \"" << original_file.value()
    662                 << "\"";
    663     return false;
    664   }
    665   ctx.current_version_str = ctx.current_version.ToString();
    666 
    667   if (!IncrementNewVersion(direction, &ctx)) {
    668     LOG(DFATAL) << "Failed to increment version from \""
    669                 << original_file.value() << "\"";
    670     return false;
    671   }
    672 
    673   Version new_version(WideToASCII(ctx.new_version_str));
    674   GenerateSpecificPEFileVersion(original_file, target_file, new_version);
    675 
    676   return true;
    677 }
    678 
    679 bool GenerateSpecificPEFileVersion(const base::FilePath& original_file,
    680                                    const base::FilePath& target_file,
    681                                    const Version& version) {
    682   // First copy original_file to target_file.
    683   if (!base::CopyFile(original_file, target_file)) {
    684     LOG(DFATAL) << "Failed copying \"" << original_file.value()
    685                 << "\" to \"" << target_file.value() << "\"";
    686     return false;
    687   }
    688 
    689   VisitResourceContext ctx;
    690   if (!GetFileVersion(target_file, &ctx.current_version)) {
    691     LOG(DFATAL) << "Failed reading version from \"" << target_file.value()
    692                 << "\"";
    693     return false;
    694   }
    695   ctx.current_version_str = ctx.current_version.ToString();
    696   ctx.new_version = ChromeVersion::FromString(version.GetString());
    697   ctx.new_version_str = ctx.new_version.ToString();
    698 
    699   return UpdateVersionIfMatch(target_file, &ctx);
    700 }
    701 
    702 }  // namespace upgrade_test
    703