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