This source file includes following definitions.
- is_valid
- Open
- ReadValue
- WriteValue
- Close
- ReadValueFromRegistry
- OpenClientStateKey
- SetInstallerFlags
- GetSetupExePathForGuidFromRegistry
- GetSetupExePathFromRegistry
- RunProcessAndWait
- AppendCommandLineFlags
- OnResourceFound
- UnpackBinaryResources
- RunSetup
- DeleteExtractedFiles
- CreateWorkDir
- GetWorkDir
- IsCurrentOrParentDirectory
- RecursivelyDeleteDirectory
- DeleteDirectoriesWithPrefix
- DeleteOldChromeTempDirectories
- ProcessNonInstallOperations
- ShouldDeleteExtractedFiles
- WMain
- MainEntryPoint
- memset
#pragma comment(linker, "/MERGE:.rdata=.text")
#include <windows.h>
#include <shellapi.h>
#include "chrome/installer/mini_installer/appid.h"
#include "chrome/installer/mini_installer/configuration.h"
#include "chrome/installer/mini_installer/decompress.h"
#include "chrome/installer/mini_installer/mini_installer.h"
#include "chrome/installer/mini_installer/mini_string.h"
#include "chrome/installer/mini_installer/pe_resource.h"
namespace mini_installer {
typedef StackString<MAX_PATH> PathString;
typedef StackString<MAX_PATH * 4> CommandString;
struct Context {
const wchar_t* base_path;
PathString* chrome_resource_path;
PathString* setup_resource_path;
};
class RegKey {
public:
RegKey() : key_(NULL) { }
~RegKey() { Close(); }
LONG Open(HKEY key, const wchar_t* sub_key, REGSAM access);
bool is_valid() const { return key_ != NULL; }
LONG ReadValue(const wchar_t* value_name,
wchar_t* value,
size_t value_size) const;
LONG WriteValue(const wchar_t* value_name, const wchar_t* value);
void Close();
private:
RegKey(const RegKey&);
RegKey& operator=(const RegKey&);
HKEY key_;
};
LONG RegKey::Open(HKEY key, const wchar_t* sub_key, REGSAM access) {
Close();
return ::RegOpenKeyEx(key, sub_key, NULL, access, &key_);
}
LONG RegKey::ReadValue(const wchar_t* value_name,
wchar_t* value,
size_t value_size) const {
DWORD type;
DWORD byte_length = static_cast<DWORD>(value_size * sizeof(wchar_t));
LONG result = ::RegQueryValueEx(key_, value_name, NULL, &type,
reinterpret_cast<BYTE*>(value),
&byte_length);
if (result == ERROR_SUCCESS) {
if (type != REG_SZ) {
result = ERROR_NOT_SUPPORTED;
} else if (byte_length == 0) {
*value = L'\0';
} else if (value[byte_length/sizeof(wchar_t) - 1] != L'\0') {
if ((byte_length / sizeof(wchar_t)) < value_size)
value[byte_length / sizeof(wchar_t)] = L'\0';
else
result = ERROR_MORE_DATA;
}
}
return result;
}
LONG RegKey::WriteValue(const wchar_t* value_name, const wchar_t* value) {
return ::RegSetValueEx(key_, value_name, 0, REG_SZ,
reinterpret_cast<const BYTE*>(value),
(lstrlen(value) + 1) * sizeof(wchar_t));
}
void RegKey::Close() {
if (key_ != NULL) {
::RegCloseKey(key_);
key_ = NULL;
}
}
bool ReadValueFromRegistry(HKEY root_key, const wchar_t *sub_key,
const wchar_t *value_name, wchar_t *value,
size_t size) {
RegKey key;
if (key.Open(root_key, sub_key, KEY_QUERY_VALUE) == ERROR_SUCCESS &&
key.ReadValue(value_name, value, size) == ERROR_SUCCESS) {
return true;
}
return false;
}
bool OpenClientStateKey(HKEY root_key, const wchar_t* app_guid, REGSAM access,
RegKey* key) {
PathString client_state_key;
return client_state_key.assign(kApRegistryKeyBase) &&
client_state_key.append(app_guid) &&
(key->Open(root_key, client_state_key.get(), access) == ERROR_SUCCESS);
}
void SetInstallerFlags(const Configuration& configuration) {
RegKey key;
const REGSAM key_access = KEY_QUERY_VALUE | KEY_SET_VALUE;
const HKEY root_key =
configuration.is_system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
const wchar_t* app_guid =
configuration.has_chrome_frame() ?
google_update::kChromeFrameAppGuid :
configuration.chrome_app_guid();
StackString<128> value;
LONG ret;
if (configuration.is_multi_install()) {
if (OpenClientStateKey(root_key, app_guid, key_access, &key)) {
ret = key.ReadValue(kApRegistryValueName, value.get(), value.capacity());
if (ret != ERROR_FILE_NOT_FOUND &&
(ret != ERROR_SUCCESS ||
FindTagInStr(value.get(), kMultiInstallTag, NULL))) {
key.Close();
app_guid = google_update::kMultiInstallAppGuid;
}
} else {
key.Close();
app_guid = google_update::kMultiInstallAppGuid;
}
}
if (!key.is_valid()) {
if (!OpenClientStateKey(root_key, app_guid, key_access, &key))
return;
value.clear();
ret = key.ReadValue(kApRegistryValueName, value.get(), value.capacity());
}
if ((ret == ERROR_SUCCESS) || (ret == ERROR_FILE_NOT_FOUND)) {
if (ret == ERROR_FILE_NOT_FOUND)
value.clear();
if (!StrEndsWith(value.get(), kFullInstallerSuffix) &&
value.append(kFullInstallerSuffix)) {
key.WriteValue(kApRegistryValueName, value.get());
}
}
}
bool GetSetupExePathForGuidFromRegistry(bool system_level,
const wchar_t* app_guid,
wchar_t* path,
size_t size) {
const HKEY root_key = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
RegKey key;
return OpenClientStateKey(root_key, app_guid, KEY_QUERY_VALUE, &key) &&
(key.ReadValue(kUninstallRegistryValueName, path, size) == ERROR_SUCCESS);
}
bool GetSetupExePathFromRegistry(const Configuration& configuration,
wchar_t* path,
size_t size) {
bool system_level = configuration.is_system_level();
if (configuration.is_multi_install() && GetSetupExePathForGuidFromRegistry(
system_level, google_update::kMultiInstallAppGuid, path, size)) {
return true;
}
if (configuration.has_chrome_frame() && GetSetupExePathForGuidFromRegistry(
system_level, google_update::kChromeFrameAppGuid, path, size)) {
return true;
}
if (GetSetupExePathForGuidFromRegistry(
system_level, configuration.chrome_app_guid(), path, size)) {
return true;
}
if (configuration.has_app_host() && GetSetupExePathForGuidFromRegistry(
system_level, google_update::kChromeAppHostAppGuid, path, size)) {
return true;
}
return false;
}
bool RunProcessAndWait(const wchar_t* exe_path, wchar_t* cmdline,
int* exit_code) {
STARTUPINFOW si = {sizeof(si)};
PROCESS_INFORMATION pi = {0};
if (!::CreateProcess(exe_path, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW,
NULL, NULL, &si, &pi)) {
return false;
}
::CloseHandle(pi.hThread);
bool ret = true;
DWORD wr = ::WaitForSingleObject(pi.hProcess, INFINITE);
if (WAIT_OBJECT_0 != wr) {
ret = false;
} else if (exit_code) {
if (!::GetExitCodeProcess(pi.hProcess,
reinterpret_cast<DWORD*>(exit_code))) {
ret = false;
}
}
::CloseHandle(pi.hProcess);
return ret;
}
void AppendCommandLineFlags(const Configuration& configuration,
CommandString* buffer) {
PathString full_exe_path;
size_t len = ::GetModuleFileName(NULL, full_exe_path.get(),
full_exe_path.capacity());
if (!len || len >= full_exe_path.capacity())
return;
const wchar_t* exe_name = GetNameFromPathExt(full_exe_path.get(), len);
if (exe_name == NULL)
return;
const wchar_t* cmd_to_append = L"";
if (!StrEndsWith(configuration.program(), exe_name)) {
cmd_to_append = configuration.command_line();
} else if (configuration.argument_count() > 1) {
const wchar_t* tmp = SearchStringI(configuration.command_line(), exe_name);
tmp = SearchStringI(tmp, L" ");
cmd_to_append = tmp;
}
buffer->append(cmd_to_append);
}
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;
}
PathString full_path;
if (!full_path.assign(ctx->base_path) ||
!full_path.append(name) ||
!resource.WriteToDisk(full_path.get()))
return FALSE;
if (StrStartsWith(name, kChromePrefix)) {
if (!ctx->chrome_resource_path->assign(full_path.get()))
return FALSE;
} else if (StrStartsWith(name, kSetupPrefix)) {
if (!ctx->setup_resource_path->assign(full_path.get()))
return FALSE;
} else {
return FALSE;
}
return TRUE;
}
bool UnpackBinaryResources(const Configuration& configuration, HMODULE module,
const wchar_t* base_path, PathString* archive_path,
PathString* setup_path) {
PathString setup_dest_path;
if (!setup_dest_path.assign(base_path) ||
!setup_dest_path.append(kSetupName))
return false;
Context context = {
base_path,
archive_path,
setup_path,
};
if (!::EnumResourceNames(module, kLZMAResourceType, OnResourceFound,
reinterpret_cast<LONG_PTR>(&context)) ||
archive_path->length() == 0)
return false;
if (setup_path->length() > 0) {
CommandString cmd_line;
bool success = true;
if (!GetSetupExePathFromRegistry(configuration, cmd_line.get(),
cmd_line.capacity()) ||
!cmd_line.append(kCmdUpdateSetupExe) ||
!cmd_line.append(L"=\"") ||
!cmd_line.append(setup_path->get()) ||
!cmd_line.append(L"\"") ||
!cmd_line.append(kCmdNewSetupExe) ||
!cmd_line.append(L"=\"") ||
!cmd_line.append(setup_dest_path.get()) ||
!cmd_line.append(L"\"")) {
success = false;
}
AppendCommandLineFlags(configuration, &cmd_line);
int exit_code = 0;
if (success &&
(!RunProcessAndWait(NULL, cmd_line.get(), &exit_code) ||
exit_code != ERROR_SUCCESS)) {
success = false;
}
if (!success)
DeleteFile(setup_path->get());
return success && setup_path->assign(setup_dest_path.get());
}
if (!::EnumResourceNames(module, kLZCResourceType, OnResourceFound,
reinterpret_cast<LONG_PTR>(&context)) &&
::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND)
return false;
if (setup_path->length() > 0) {
bool success = mini_installer::Expand(setup_path->get(),
setup_dest_path.get());
::DeleteFile(setup_path->get());
if (success) {
if (!setup_path->assign(setup_dest_path.get())) {
::DeleteFile(setup_dest_path.get());
success = false;
}
}
return success;
}
if (!::EnumResourceNames(module, kBinResourceType, OnResourceFound,
reinterpret_cast<LONG_PTR>(&context)) &&
::GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND)
return false;
if (setup_path->length() > 0) {
if (setup_path->comparei(setup_dest_path.get()) != 0) {
if (!::MoveFileEx(setup_path->get(), setup_dest_path.get(),
MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) {
::DeleteFile(setup_path->get());
setup_path->clear();
} else if (!setup_path->assign(setup_dest_path.get())) {
::DeleteFile(setup_dest_path.get());
}
}
}
return setup_path->length() > 0;
}
bool RunSetup(const Configuration& configuration, const wchar_t* archive_path,
const wchar_t* setup_path, int* exit_code) {
CommandString cmd_line;
if (::lstrlen(setup_path) > 0) {
if (!cmd_line.assign(L"\"") ||
!cmd_line.append(setup_path) ||
!cmd_line.append(L"\""))
return false;
} else if (!GetSetupExePathFromRegistry(configuration, cmd_line.get(),
cmd_line.capacity())) {
return false;
}
if (!cmd_line.append(kCmdInstallArchive) ||
!cmd_line.append(L"=\"") ||
!cmd_line.append(archive_path) ||
!cmd_line.append(L"\""))
return false;
AppendCommandLineFlags(configuration, &cmd_line);
return RunProcessAndWait(NULL, cmd_line.get(), exit_code);
}
void DeleteExtractedFiles(const wchar_t* base_path,
const wchar_t* archive_path,
const wchar_t* setup_path) {
::DeleteFile(archive_path);
::DeleteFile(setup_path);
::RemoveDirectory(base_path);
}
bool CreateWorkDir(const wchar_t* base_path, PathString* work_dir) {
if (!work_dir->assign(base_path) || !work_dir->append(kTempPrefix))
return false;
size_t end = work_dir->length();
if ((work_dir->capacity() - end) < (arraysize("fffff.tmp") + 1))
return false;
DWORD id = ::GetTickCount();
id ^= (id >> 12);
int max_attempts = 10;
while (max_attempts--) {
if (!HexEncode(&id, sizeof(id), work_dir->get() + end,
work_dir->capacity() - end)) {
return false;
}
work_dir->truncate_at(end + 5);
work_dir->append(L".tmp");
if (::CreateDirectory(work_dir->get(), NULL)) {
return work_dir->append(L"\\");
}
++id;
}
return false;
}
bool GetWorkDir(HMODULE module, PathString* work_dir) {
PathString base_path;
DWORD len = ::GetTempPath(base_path.capacity(), base_path.get());
if (!len || len >= base_path.capacity() ||
!CreateWorkDir(base_path.get(), work_dir)) {
len = ::GetModuleFileName(module, base_path.get(), base_path.capacity());
if (len >= base_path.capacity() || !len)
return false;
wchar_t* name = GetNameFromPathExt(base_path.get(), len);
if (!name)
return false;
*name = L'\0';
return CreateWorkDir(base_path.get(), work_dir);
}
return true;
}
bool IsCurrentOrParentDirectory(const wchar_t* dir) {
return dir &&
dir[0] == L'.' &&
(dir[1] == L'\0' || (dir[1] == L'.' && dir[2] == L'\0'));
}
void RecursivelyDeleteDirectory(PathString* path) {
size_t end = path->length();
if (!path->append(L"\\*.*"))
return;
WIN32_FIND_DATA find_data = {0};
HANDLE find = ::FindFirstFile(path->get(), &find_data);
if (find != INVALID_HANDLE_VALUE) {
do {
const wchar_t* name = find_data.cAlternateFileName[0] ?
find_data.cAlternateFileName : find_data.cFileName;
if (IsCurrentOrParentDirectory(name))
continue;
path->truncate_at(end + 1);
if (!path->append(name))
continue;
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
RecursivelyDeleteDirectory(path);
} else {
::DeleteFile(path->get());
}
} while (::FindNextFile(find, &find_data));
::FindClose(find);
}
path->truncate_at(end);
::RemoveDirectory(path->get());
}
void DeleteDirectoriesWithPrefix(const wchar_t* parent_dir,
const wchar_t* prefix) {
PathString spec;
if (!spec.assign(parent_dir) || !spec.append(prefix) || !spec.append(L"*.*"))
return;
WIN32_FIND_DATA find_data = {0};
HANDLE find = ::FindFirstFileEx(spec.get(), FindExInfoStandard, &find_data,
FindExSearchLimitToDirectories, NULL, 0);
if (find == INVALID_HANDLE_VALUE)
return;
PathString path;
do {
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
const wchar_t* name = find_data.cAlternateFileName[0] ?
find_data.cAlternateFileName : find_data.cFileName;
if (IsCurrentOrParentDirectory(name))
continue;
if (path.assign(parent_dir) && path.append(name))
RecursivelyDeleteDirectory(&path);
}
} while (::FindNextFile(find, &find_data));
::FindClose(find);
}
void DeleteOldChromeTempDirectories() {
static const wchar_t* const kDirectoryPrefixes[] = {
kTempPrefix,
L"chrome_"
};
PathString temp;
DWORD len = ::GetTempPath(temp.capacity(), temp.get());
if (!len || len >= temp.capacity())
return;
for (int i = 0; i < arraysize(kDirectoryPrefixes); ++i) {
DeleteDirectoriesWithPrefix(temp.get(), kDirectoryPrefixes[i]);
}
}
bool ProcessNonInstallOperations(const Configuration& configuration,
int* exit_code) {
bool ret = false;
switch (configuration.operation()) {
case Configuration::CLEANUP:
*exit_code = 0;
ret = true;
break;
default: break;
}
return ret;
}
bool ShouldDeleteExtractedFiles() {
wchar_t value[2] = {0};
if (ReadValueFromRegistry(HKEY_CURRENT_USER, kCleanupRegistryKey,
kCleanupRegistryValueName, value,
arraysize(value)) &&
value[0] == L'0') {
return false;
}
return true;
}
int WMain(HMODULE module) {
#if defined(COMPONENT_BUILD)
static const wchar_t kComponentBuildIncompatibleMessage[] =
L"mini_installer.exe is incompatible with the component build, please run"
L" setup.exe with the same command line instead. See"
L" http://crbug.com/127233#c17 for details.";
::MessageBox(NULL, kComponentBuildIncompatibleMessage, NULL, MB_ICONERROR);
return 1;
#endif
DeleteOldChromeTempDirectories();
int exit_code = 101;
Configuration configuration;
if (!configuration.Initialize())
return exit_code;
if (ProcessNonInstallOperations(configuration, &exit_code))
return exit_code;
PathString base_path;
if (!GetWorkDir(module, &base_path))
return 101;
#if defined(GOOGLE_CHROME_BUILD)
SetInstallerFlags(configuration);
#endif
PathString archive_path;
PathString setup_path;
if (!UnpackBinaryResources(configuration, module, base_path.get(),
&archive_path, &setup_path)) {
exit_code = 102;
} else {
::SetProcessWorkingSetSize(::GetCurrentProcess(), -1, -1);
if (!RunSetup(configuration, archive_path.get(), setup_path.get(),
&exit_code)) {
exit_code = 103;
}
}
if (ShouldDeleteExtractedFiles())
DeleteExtractedFiles(base_path.get(), archive_path.get(), setup_path.get());
return exit_code;
}
}
int MainEntryPoint() {
int result = mini_installer::WMain(::GetModuleHandle(NULL));
::ExitProcess(result);
}
extern "C" {
#pragma function(memset)
void* memset(void* dest, int c, size_t count) {
size_t adjcount = count >> 2;
UINT32 fill = (c << 24 | c << 16 | c << 8 | c);
UINT32* dest32 = reinterpret_cast<UINT32*>(dest);
UINT8* dest8 = reinterpret_cast<UINT8*>(dest);
switch (count - (adjcount << 2)) {
case 3:
dest8[count - 3] = c;
case 2:
dest8[count - 2] = c;
case 1:
dest8[count - 1] = c;
}
while (adjcount-- > 0)
*(dest32++) = fill;
return dest;
}
}