Do not announce robot account token before account ID is available
[chromium-blink-merge.git] / chrome / installer / test / alternate_version_generator.cc
blob98c72cf77e743d09116e60efcfa9bdee4ea531b6
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.
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.
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.
24 #include "chrome/installer/test/alternate_version_generator.h"
26 #include <windows.h>
28 #include <algorithm>
29 #include <limits>
30 #include <sstream>
31 #include <utility>
32 #include <vector>
34 #include "base/basictypes.h"
35 #include "base/command_line.h"
36 #include "base/files/file.h"
37 #include "base/files/file_enumerator.h"
38 #include "base/files/file_path.h"
39 #include "base/files/file_util.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"
54 namespace {
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";
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() << "\"";
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;
92 return true;
94 const base::FilePath& directory() const {
95 DCHECK(!directory_.empty());
96 return directory_;
99 private:
100 base::FilePath directory_;
101 DISALLOW_COPY_AND_ASSIGN(ScopedTempDirectory);
102 }; // class ScopedTempDirectory
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));
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]));
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
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);
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_; }
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
167 MappedFile::~MappedFile() {
168 if (view_ != NULL) {
169 if (UnmapViewOfFile(view_) == 0) {
170 PLOG(DFATAL) << "MappedFile failed to unmap view.";
173 if (mapping_ != NULL) {
174 if (CloseHandle(mapping_) == 0) {
175 PLOG(DFATAL) << "Could not close file mapping handle.";
180 bool MappedFile::Initialize(base::File file) {
181 DCHECK(mapping_ == NULL);
182 bool result = false;
183 base::File::Info file_info;
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";
198 } else {
199 PLOG(DFATAL) << "CreateFileMapping failed";
201 } else {
202 LOG(DFATAL) << "Files larger than " << std::numeric_limits<DWORD>::max()
203 << " are not supported.";
205 } else {
206 PLOG(DFATAL) << "file.GetInfo failed";
208 file_ = file.Pass();
209 return result;
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::LaunchOptions options;
218 options.wait = true;
219 options.start_hidden = true;
220 base::Process process = base::LaunchProcess(cmdline, options);
221 if (process.IsValid()) {
222 if (exit_code) {
223 if (!GetExitCodeProcess(process.Handle(),
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);
232 } else {
233 result = false;
236 return result;
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;
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";
265 return result;
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);
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;
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;
296 dest_first += (src_last - src_first);
297 } while (true);
299 if (replacements_made != NULL) {
300 *replacements_made = changed;
303 return result;
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
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);
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;
338 VersionPair cur_ver = {
339 ctx.current_version.high(), ctx.current_version.low()
341 VersionPair new_ver = {
342 ctx.new_version.high(), ctx.new_version.low()
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);
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;
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);
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;
388 } else {
389 PLOG(DFATAL) << "Failed to open \"" << image_file.value() << "\"";
391 return result;
394 bool IncrementNewVersion(upgrade_test::Direction direction,
395 VisitResourceContext* ctx) {
396 DCHECK(ctx);
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);
402 do {
403 if (incrementer == 0) {
404 LOG(DFATAL) << "Improbable version at the cusp of complete rollover";
405 return false;
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());
412 return true;
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;
427 ctx.current_version_str = ctx.current_version.ToString();
429 if (!IncrementNewVersion(direction, &ctx)) {
430 return false;
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;
441 std::wstring extension = file.Extension();
442 if (extension == &kExtExe[0] || extension == &kExtDll[0]) {
443 doing_great = UpdateVersionIfMatch(file, &ctx);
445 } while (doing_great);
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));
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);
460 return doing_great;
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 base::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]);
478 return l7za_path;
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);
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;
506 return true;
509 } // namespace
511 namespace upgrade_test {
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;
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;
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
537 ResourceLoader resource_loader;
538 std::pair<const uint8*, DWORD> resource_data;
540 if (!resource_loader.Initialize(mini_installer))
541 return false;
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;
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;
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;
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;
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;
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;
611 // Increment the version in all files.
612 ApplyAlternateVersion(work_dir.directory(), direction, original_version,
613 new_version);
615 // Pack up files into chrome.7z
616 if (!CreateArchive(chrome_7z, work_dir.directory().Append(&kChromeBin[0]), 0))
617 return false;
619 // Compress chrome.7z into chrome.packed.7z
620 if (!CreateArchive(chrome_packed_7z, chrome_7z, 9))
621 return false;
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;
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;
650 // Finally, move the updated mini_installer into place.
651 return base::Move(mini_installer, target_path);
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;
663 ctx.current_version_str = ctx.current_version.ToString();
665 if (!IncrementNewVersion(direction, &ctx)) {
666 LOG(DFATAL) << "Failed to increment version from \""
667 << original_file.value() << "\"";
668 return false;
671 Version new_version(base::UTF16ToASCII(ctx.new_version_str));
672 GenerateSpecificPEFileVersion(original_file, target_file, new_version);
674 return true;
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;
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;
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();
697 return UpdateVersionIfMatch(target_file, &ctx);
700 } // namespace upgrade_test