diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/installer/mini_installer/mini_installer.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer/mini_installer/mini_installer.cc')
-rw-r--r-- | chrome/installer/mini_installer/mini_installer.cc | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc new file mode 100644 index 0000000..7c1650e --- /dev/null +++ b/chrome/installer/mini_installer/mini_installer.cc @@ -0,0 +1,392 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// mini_installer.exe is the first exe that is run when chrome is being +// installed or upgraded. It is designed to be extremely small (~5KB with no +// extra resources linked) and it has two main jobs: +// 1) unpack the resources (possibly decompressing some) +// 2) run the real installer (setup.exe) with appropiate flags. +// +// In order to be really small we don't link against the CRT and we define the +// following compiler/linker flags: +// EnableIntrinsicFunctions="true" compiler: /Oi +// BasicRuntimeChecks="0" +// BufferSecurityCheck="false" compiler: /GS- +// EntryPointSymbol="MainEntryPoint" linker: /ENTRY +// IgnoreAllDefaultLibraries="true" linker: /NODEFAULTLIB +// OptimizeForWindows98="1" liker: /OPT:NOWIN98 +// linker: /SAFESEH:NO +// Also some built-in code that the compiler relies on is not defined so we +// are forced to manually link against it. It comes in the form of two +// object files that exist in $(VCInstallDir)\crt\src which are memset.obj and +// P4_memset.obj. These two object files rely on the existence of a static +// variable named __sse2_available which indicates the presence of intel sse2 +// extensions. We define it to false which causes a slower but safe code for +// memcpy and memset intrinsics. + +// having the linker merge the sections is saving us ~500 bytes. +#pragma comment(linker, "/MERGE:.rdata=.text") + +#include <windows.h> +#include <shlwapi.h> +#include <stdlib.h> + +#include "chrome/installer/mini_installer/mini_installer.h" +#include "chrome/installer/mini_installer/pe_resource.h" + +// Required linker symbol. See remarks above. +extern "C" unsigned int __sse2_available = 0; + +namespace mini_installer { + +// This structure passes data back and forth for the processing +// of resource callbacks. +struct Context { + // We really have a single string that is used for all operations. + // We keep two pointers to it as follows: + // [uncompress command]' '[path to exe]\[name of file] + // | | + // Context->full_path Context->name + wchar_t* full_path; // input to the call back method + wchar_t* name; // input/output from the call back method (contains file name) + size_t max_name_size; // input (contains the size of buffer "name") +}; + + +// Takes the path to file and returns a pointer to the filename component. For +// exmaple for input of c:\full\path\to\file.ext it returns pointer to file.ext. +// It also checks that there is an extension (of length 3) in file name and +// the size of path is at least 7. It returns NULL if extension or path +// separator is not found. +wchar_t* GetNameFromPathExt(wchar_t* path, size_t size) { + const int kMinPath = 7; + const int kExtSize = 4; + if ((size < kMinPath) || (path[size - kExtSize] != L'.')) { + return NULL; + } + wchar_t* current = &path[size - kExtSize]; + while (L'\\' != *current) { + --current; + if (current == path) { + return NULL; + } + } + return (current + 1); +} + + +// Simple replacement for CRT string copy method that does not overflow. +// Returns true if the source was copied successfully otherwise returns false. +// Parameter src is assumed to be NULL terminated and the NULL character is +// copied over to string dest. +bool SafeStrCopy(wchar_t* dest, const wchar_t* src, size_t dest_size) { + for (size_t length = 0; length < dest_size; ++dest, ++src, ++length) { + *dest = *src; + if (L'\0' == *src) { + return true; + } + } + return false; +} + + +// Helper function to read a value from registry. Returns true if value +// is read successfully and stored in parameter value. Returns false otherwise. +bool ReadValueFromRegistry(HKEY root_key, const wchar_t *sub_key, + const wchar_t *value_name, wchar_t *value, + size_t size) { + HKEY key; + + if ((::RegOpenKeyEx(root_key, sub_key, NULL, + KEY_READ, &key) == ERROR_SUCCESS) && + (::RegQueryValueEx(key, value_name, NULL, NULL, + reinterpret_cast<LPBYTE>(value), + reinterpret_cast<LPDWORD>(&size)) == ERROR_SUCCESS)) { + ::RegCloseKey(key); + return true; + } + return false; +} + + +// Gets the setup.exe path from Registry by looking the value of Uninstall +// string, strips the arguments for uninstall and returns only the full path +// to setup.exe. +bool GetSetupExePathFromRegistry(wchar_t *path, size_t size) { + if (!ReadValueFromRegistry(HKEY_CURRENT_USER, kUninstallRegistryKey, + kUninstallRegistryValueName, path, size)) { + if (!ReadValueFromRegistry(HKEY_LOCAL_MACHINE, kUninstallRegistryKey, + kUninstallRegistryValueName, path, size)) { + return false; + } + } + wchar_t *tmp = StrStr(path, L" --"); + if (tmp) { + *tmp = L'\0'; + } else { + return false; + } + + return true; +} + + +// Calls CreateProcess with good default parameters and waits for the process +// to terminate returning the process exit code. +bool RunProcessAndWait(const wchar_t* exe_path, wchar_t* cmdline, + int* exit_code) { + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi = {0}; + if (!::CreateProcessW(exe_path, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, + NULL, NULL, &si, &pi)) { + return false; + } + DWORD wr = ::WaitForSingleObject(pi.hProcess, INFINITE); + if (WAIT_OBJECT_0 != wr) { + return false; + } + + bool ret = true; + if (exit_code) { + if (!::GetExitCodeProcess(pi.hProcess, + reinterpret_cast<DWORD*>(exit_code))) { + ret = false; + } + } + ::CloseHandle(pi.hProcess); + ::CloseHandle(pi.hThread); + return ret; +} + + +// Windows defined callback used in the EnumResourceNamesW call. For each +// matching resource found, the callback is invoked and at this point we write +// it to disk and possibly uncompress it. Resources of type BL +// are assumed to be LZ compressed and are uncompressed using 'expand.exe'. +BOOL CALLBACK OnResourceFound(HMODULE module, const wchar_t* type, + wchar_t* name, LONG_PTR context) { + if (NULL == context) { + return FALSE; + } + Context* ctx = reinterpret_cast<Context*>(context); + + PEResource resource(name, type, module); + if ((!resource.IsValid()) || + (resource.Size() < 1 ) || + (resource.Size() > kMaxResourceSize)) { + return FALSE; + } + + // Note that the copy operation actually replaces the file name part of + // ctx->full_path + if ((!SafeStrCopy(ctx->name, name, ctx->max_name_size)) || + (!resource.WriteToDisk(ctx->full_path))) { + return FALSE; + } + + // If this is LZ compressed resource, uncompress it using the existing + // program in the system32 folder named 'expand.exe'. + if (0 == ::lstrcmpiW(type, kLZCResourceType)) { + wchar_t expand_cmd[MAX_PATH * 2] = UNCOMPRESS_CMD; + ::lstrcatW(expand_cmd, L"\""); + ::lstrcatW(expand_cmd, ctx->full_path); + ::lstrcatW(expand_cmd, L"\""); + if (!RunProcessAndWait(NULL, expand_cmd, NULL)) { + // Somehow we failed to uncompress the file. Exit now and leave the file + // behind for postmortem analysis. + return FALSE; + } + // Uncompression was successful, delete the source but it is not critical + // if that fails. + ::DeleteFileW(ctx->full_path); + } + return TRUE; +} + + +// Finds and writes to disk all resources of various types. Returns false +// if there is a problem in writing any resource to disk. +bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path, + bool* unpacked_setup, wchar_t* archive_name) { + *unpacked_setup = false; + + // Prepare the input to OnResourceFound method that needs a location where + // it will write all the resources. + wchar_t module_path[MAX_PATH]; + if (!SafeStrCopy(module_path, base_path, _countof(module_path))) { + return false; + } + size_t length = ::lstrlen(module_path); + wchar_t* name = (wchar_t *) module_path + length; + Context context = {module_path, name, MAX_PATH - length}; + + // Get the resources of type 'B7' + if (!::EnumResourceNamesW(module, kLZMAResourceType, OnResourceFound, + LONG_PTR(&context))) { + // We need a compressed archive to do the installation. So if there + // is a problem in fetching B7 resource, just return error. + return false; + } else { + ::lstrcpy(archive_name, name); + } + + // Get the resources of type 'BL'. setup.exe can be sent as 'BL' or 'BN'. + // So if we get 'resource not found' error just ignore it. + if (!::EnumResourceNamesW(module, kLZCResourceType, OnResourceFound, + LONG_PTR(&context))) { + if (::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND) { + return false; + } + } else if (0 == ::lstrcmpiW(name, kSetupLZName)) { + *unpacked_setup = true; + } + + if (!::EnumResourceNamesW(module, kBinResourceType, OnResourceFound, + LONG_PTR(&context))) { + if (::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND) { + return false; + } + } else if (0 == ::lstrcmpiW(name, kSetupName)) { + *unpacked_setup = true; + } + + return true; +} + + +// Executes setup.exe, waits for it to finish and returns the exit code. +bool RunSetup(bool have_upacked_setup, const wchar_t* base_path, + const wchar_t* archive_name, int* exit_code) { + wchar_t cmd_line[MAX_PATH * 2]; + wchar_t cmd_args[MAX_PATH * 2]; + + if (!SafeStrCopy(cmd_args, L" --install-archive=\"", _countof(cmd_args)) || + !::lstrcat(cmd_args, base_path) || + !::lstrcat(cmd_args, archive_name) || + !::lstrcat(cmd_args, L"\"")) { + return false; + } + + if (have_upacked_setup) { + if (!SafeStrCopy(cmd_line, L"\"", _countof(cmd_line)) || + !::lstrcat(cmd_line, base_path) || + !::lstrcat(cmd_line, kSetupName) || + !::lstrcat(cmd_line, L"\"")) { + return false; + } + } else { + if (!GetSetupExePathFromRegistry(cmd_line, sizeof(cmd_line))) { + return false; + } + } + + return (::lstrcat(cmd_line, cmd_args) && + RunProcessAndWait(NULL, cmd_line, exit_code)); +} + + +void DeleteExtractedFiles(const wchar_t* base_path, + const wchar_t* archive_name) { + wchar_t file_path[MAX_PATH]; + // Delete setup.exe. + SafeStrCopy(file_path, base_path, MAX_PATH); + ::lstrcat(file_path, kSetupName); + ::DeleteFile(file_path); + // Delete chrome archive file. + SafeStrCopy(file_path, base_path, MAX_PATH); + ::lstrcat(file_path, archive_name); + ::DeleteFile(file_path); + // Delete the temp dir (if it is empty, otherwise fail). + ::RemoveDirectory(base_path); +} + +// Creates and returns a temporary directory that can be used to extract +// mini_installer payload. +bool GetWorkDir(HMODULE module, wchar_t* work_dir) { + wchar_t base_path[MAX_PATH]; + DWORD len = ::GetTempPath(MAX_PATH, base_path); + if (len >= MAX_PATH || len <= 0) { + // Problem in getting TEMP path so just use current directory as base path + len = ::GetModuleFileNameW(module, base_path, MAX_PATH); + if (len >= MAX_PATH || len <= 0) + return false; // Can't even get current directory? Return with error. + wchar_t* name = GetNameFromPathExt(base_path, len); + *name = L'\0'; + } + + wchar_t temp_name[MAX_PATH + 1]; + if (!GetTempFileName(base_path, L"CR_", 0, temp_name)) + return false; // Didn't get any temp name to use. Return error. + len = GetLongPathName(temp_name, work_dir, MAX_PATH); + if (len > MAX_PATH + 1 || len == 0) + return false; // Couldn't get full path to temp dir. Return error. + + // GetTempFileName creates the file as well so delete it before creating + // the directory in its place. + if (!::DeleteFile(work_dir) || !::CreateDirectory(work_dir, NULL)) + return false; // What's the use of temp dir if we can not create it? + ::lstrcat(work_dir, L"\\"); + return true; +} + +int WMain(HMODULE module) { + // First get a path where we can extract payload + wchar_t base_path[MAX_PATH]; + if (!GetWorkDir(module, base_path)) + return 1; + + wchar_t archive_name[MAX_PATH]; + bool have_upacked_setup; + if (!UnpackBinaryResources(module, base_path, + &have_upacked_setup, archive_name)) { + return 1; + } + + int setup_exit_code = 0; + if (!RunSetup(have_upacked_setup, base_path, + archive_name, &setup_exit_code)) { + return 2; + } + + wchar_t value[4]; + if ((!ReadValueFromRegistry(HKEY_CURRENT_USER, kCleanupRegistryKey, + kCleanupRegistryValueName, value, 4)) || + (value[0] != L'0')) { + DeleteExtractedFiles(base_path, archive_name); + } + + return setup_exit_code; +} +} // namespace mini_installer + + +int MainEntryPoint() { + int result = mini_installer::WMain(::GetModuleHandle(NULL)); + ::ExitProcess(result); +} |