| Index: mi_exe_stub/mi.cc
|
| diff --git a/mi_exe_stub/mi.cc b/mi_exe_stub/mi.cc
|
| deleted file mode 100644
|
| index 474cb99b4437078ff8cd46d41c413006648b0aee..0000000000000000000000000000000000000000
|
| --- a/mi_exe_stub/mi.cc
|
| +++ /dev/null
|
| @@ -1,501 +0,0 @@
|
| -// Copyright 2006-2009 Google Inc.
|
| -//
|
| -// Licensed under the Apache License, Version 2.0 (the "License");
|
| -// you may not use this file except in compliance with the License.
|
| -// You may obtain a copy of the License at
|
| -//
|
| -// http://www.apache.org/licenses/LICENSE-2.0
|
| -//
|
| -// Unless required by applicable law or agreed to in writing, software
|
| -// distributed under the License is distributed on an "AS IS" BASIS,
|
| -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -// See the License for the specific language governing permissions and
|
| -// limitations under the License.
|
| -// ========================================================================
|
| -//
|
| -// Implementation of the metainstaller logic.
|
| -// Untars a tarball and executes the extracted executable.
|
| -// If no command line is specified, "/install" is passed to the executable
|
| -// along with a .gup file if one is extracted.
|
| -// If found, the contents of the signature tag are also passed to the
|
| -// executable unmodified.
|
| -
|
| -#include <tchar.h>
|
| -#include <atlsimpcoll.h>
|
| -#include <atlstr.h>
|
| -#include <shellapi.h>
|
| -#include <shlwapi.h>
|
| -#include <windows.h>
|
| -
|
| -#pragma warning(push)
|
| -// C4310: cast truncates constant value
|
| -#pragma warning(disable : 4310)
|
| -#include "base/basictypes.h"
|
| -#pragma warning(pop)
|
| -#include "base/scoped_ptr.h"
|
| -#include "omaha/base/constants.h"
|
| -#include "omaha/base/error.h"
|
| -#include "omaha/base/extractor.h"
|
| -#include "omaha/base/scoped_any.h"
|
| -#include "omaha/base/system_info.h"
|
| -#include "omaha/common/const_cmd_line.h"
|
| -#include "omaha/mi_exe_stub/process.h"
|
| -#include "omaha/mi_exe_stub/mi.grh"
|
| -#include "omaha/mi_exe_stub/tar.h"
|
| -extern "C" {
|
| -#include "third_party/lzma/v4_65/files/C/Bcj2.h"
|
| -#include "third_party/lzma/v4_65/files/C/LzmaDec.h"
|
| -}
|
| -
|
| -namespace omaha {
|
| -
|
| -// Resource ID of the goopdate payload inside the meta-installer.
|
| -#define IDR_PAYLOAD 102
|
| -
|
| -namespace {
|
| -
|
| -HRESULT HandleError(HRESULT hr);
|
| -
|
| -// The function assumes that the extractor has already been opened.
|
| -// The buffer must be deleted by the caller.
|
| -char* ReadTag(TagExtractor* extractor) {
|
| - const int kMaxTagLength = 0x10000; // 64KB
|
| -
|
| - int tag_buffer_size = 0;
|
| - if (!extractor->ExtractTag(NULL, &tag_buffer_size)) {
|
| - return NULL;
|
| - }
|
| - if (!tag_buffer_size || (tag_buffer_size >= kMaxTagLength)) {
|
| - return NULL;
|
| - }
|
| -
|
| - scoped_array<char> tag_buffer(new char[tag_buffer_size]);
|
| - if (!tag_buffer.get()) {
|
| - return NULL;
|
| - }
|
| -
|
| - if (!extractor->ExtractTag(tag_buffer.get(), &tag_buffer_size)) {
|
| - _ASSERTE(false);
|
| - return NULL;
|
| - }
|
| -
|
| - // Do a sanity check of the tag string. The double quote '"'
|
| - // is a special character that should not be included in the tag string.
|
| - for (const char* tag_char = tag_buffer.get(); *tag_char; ++tag_char) {
|
| - if (*tag_char == '"') {
|
| - _ASSERTE(false);
|
| - return NULL;
|
| - }
|
| - }
|
| -
|
| - return tag_buffer.release();
|
| -}
|
| -
|
| -// Extract the tag containing the extra information written by the server.
|
| -// The memory returned by the function will have to be freed using delete[]
|
| -// operator.
|
| -char* ExtractTag(const TCHAR* module_file_name) {
|
| - if (!module_file_name) {
|
| - return NULL;
|
| - }
|
| -
|
| - TagExtractor extractor;
|
| - if (!extractor.OpenFile(module_file_name)) {
|
| - return NULL;
|
| - }
|
| - char* ret = ReadTag(&extractor);
|
| - extractor.CloseFile();
|
| -
|
| - return ret;
|
| -}
|
| -
|
| -class MetaInstaller {
|
| - public:
|
| - MetaInstaller(HINSTANCE instance, LPCSTR cmd_line)
|
| - : instance_(instance),
|
| - cmd_line_(cmd_line),
|
| - exit_code_(0) {
|
| - }
|
| -
|
| - ~MetaInstaller() {
|
| - // When a crash happens while running GoogleUpdate and breakpad gets it
|
| - // GooogleUpdate.exe is started with the /report to report the crash.
|
| - // In a crash, the temp directory and the contained files can't be deleted.
|
| - if (exit_code_ != GOOPDATE_E_CRASH) {
|
| - CleanUpTempDirectory();
|
| - }
|
| - }
|
| -
|
| - int ExtractAndRun() {
|
| - if (CreateUniqueTempDirectory() != 0) {
|
| - return -1;
|
| - }
|
| - scoped_hfile tarball_file(ExtractTarballToTempLocation());
|
| - if (!valid(tarball_file)) {
|
| - return -1;
|
| - }
|
| -
|
| - // Extract files from the archive and run the first EXE we find in it.
|
| - Tar tar(temp_dir_, get(tarball_file), true);
|
| - tar.SetCallback(TarFileCallback, this);
|
| - if (!tar.ExtractToDir()) {
|
| - return -1;
|
| - }
|
| -
|
| - exit_code_ = ULONG_MAX;
|
| - if (!exe_path_.IsEmpty()) {
|
| - // Build the command line. There are three scenarios we consider:
|
| - // 1. Run by the user, in which case the MI does not receive any
|
| - // argument on its command line. In this case the command line
|
| - // to run is: "exe_path" /install [["]manifest["]]
|
| - // 2. Run with command line arguments. The tag, if present, will be
|
| - // appended to the command line.
|
| - // The command line is: "exe_path" args <tag>
|
| - // For example, pass "/silent /install" to the metainstaller to
|
| - // initiate a silent install using the extra args in the tag.
|
| - // If a command line does not take a tag or a custom tag is needed,
|
| - // use an untagged file.
|
| - CString command_line(exe_path_);
|
| - ::PathQuoteSpaces(CStrBuf(command_line, MAX_PATH));
|
| -
|
| - scoped_array<char> tag(GetTag());
|
| - if (cmd_line_.IsEmpty()) {
|
| - // Run-by-user case.
|
| - if (!tag.get()) {
|
| - _ASSERTE(!_T("Must provide arguments with untagged metainstaller."));
|
| - HRESULT hr = GOOPDATE_E_UNTAGGED_METAINSTALLER;
|
| - HandleError(hr);
|
| - return hr;
|
| - }
|
| - command_line.AppendFormat(_T(" /%s %s /%s"),
|
| - kCmdLineInstallSource,
|
| - kCmdLineInstallSource_TaggedMetainstaller,
|
| - kCmdLineInstall);
|
| - } else {
|
| - command_line.AppendFormat(_T(" %s"), cmd_line_);
|
| -
|
| - CheckAndHandleRecoveryCase(&command_line);
|
| - }
|
| -
|
| - if (tag.get()) {
|
| - command_line.AppendFormat(_T(" \"%s\""), CString(tag.get()));
|
| - }
|
| -
|
| - RunAndWait(command_line, &exit_code_);
|
| - }
|
| - // Propagate up the exit code of the program we have run.
|
| - return exit_code_;
|
| - }
|
| -
|
| - private:
|
| - void CleanUpTempDirectory() {
|
| - // Delete our temp directory and its contents.
|
| - for (int i = 0; i != files_to_delete_.GetSize(); ++i) {
|
| - DeleteFile(files_to_delete_[i]);
|
| - }
|
| - files_to_delete_.RemoveAll();
|
| -
|
| - ::RemoveDirectory(temp_dir_);
|
| - temp_dir_.Empty();
|
| - }
|
| -
|
| - // Determines whether this is a silent install.
|
| - bool IsSilentInstall() {
|
| - CString silent_argument;
|
| - silent_argument.Format(_T("/%s"), kCmdLineSilent);
|
| -
|
| - return silent_argument == cmd_line_;
|
| - }
|
| -
|
| - // Determines whether the MI is being invoked for recovery purposes, and,
|
| - // if so, appends the MI's full path to the command line.
|
| - // cmd_line_ must begin with "/recover" in order for the recovery case to be
|
| - // detected.
|
| - void CheckAndHandleRecoveryCase(CString* command_line) {
|
| - _ASSERTE(command_line);
|
| -
|
| - CString recover_argument;
|
| - recover_argument.Format(_T("/%s"), kCmdLineRecover);
|
| -
|
| - if (cmd_line_.Left(recover_argument.GetLength()) == recover_argument) {
|
| - TCHAR current_path[MAX_PATH] = {};
|
| - if (::GetModuleFileName(NULL, current_path, arraysize(current_path))) {
|
| - command_line->AppendFormat(_T(" \"%s\""), current_path);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Create a temp directory to hold the embedded setup files.
|
| - // This is a bit of a hack: we ask the system to create a temporary
|
| - // filename for us, and instead we use that name for a subdirectory name.
|
| - int CreateUniqueTempDirectory() {
|
| - ::GetTempPath(MAX_PATH, CStrBuf(temp_root_dir_, MAX_PATH));
|
| - if (::CreateDirectory(temp_root_dir_, NULL) != 0 ||
|
| - ::GetLastError() == ERROR_ALREADY_EXISTS) {
|
| - if (!::GetTempFileName(temp_root_dir_,
|
| - _T("GUM"),
|
| - 0, // form a unique filename
|
| - CStrBuf(temp_dir_, MAX_PATH))) {
|
| - return -1;
|
| - }
|
| - // GetTempFileName() actually creates the temp file, so delete it.
|
| - ::DeleteFile(temp_dir_);
|
| - ::CreateDirectory(temp_dir_, NULL);
|
| - } else {
|
| - return -1;
|
| - }
|
| - return 0;
|
| - }
|
| -
|
| - HANDLE ExtractTarballToTempLocation() {
|
| - HANDLE tarball_file = INVALID_HANDLE_VALUE;
|
| - TCHAR tarball_filename[MAX_PATH] = {0};
|
| - if (::GetTempFileName(temp_root_dir_,
|
| - _T("GUT"),
|
| - 0, // form a unique filename
|
| - tarball_filename)) {
|
| - files_to_delete_.Add(tarball_filename);
|
| - HRSRC res_info = ::FindResource(NULL,
|
| - MAKEINTRESOURCE(IDR_PAYLOAD),
|
| - _T("B"));
|
| - if (NULL != res_info) {
|
| - HGLOBAL resource = ::LoadResource(NULL, res_info);
|
| - if (NULL != resource) {
|
| - LPVOID resource_pointer = ::LockResource(resource);
|
| - if (NULL != resource_pointer) {
|
| - tarball_file = ::CreateFile(tarball_filename,
|
| - GENERIC_READ | GENERIC_WRITE,
|
| - 0,
|
| - NULL,
|
| - OPEN_ALWAYS,
|
| - 0,
|
| - NULL);
|
| - if (INVALID_HANDLE_VALUE != tarball_file) {
|
| - LARGE_INTEGER file_position = {};
|
| - if (0 != DecompressBufferToFile(
|
| - static_cast<const uint8*>(resource_pointer),
|
| - ::SizeofResource(NULL, res_info),
|
| - tarball_file) ||
|
| - !::SetFilePointerEx(tarball_file, file_position, NULL,
|
| - FILE_BEGIN)) {
|
| - ::CloseHandle(tarball_file);
|
| - tarball_file = INVALID_HANDLE_VALUE;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return tarball_file;
|
| - }
|
| -
|
| - char* GetTag() const {
|
| - // Get this module file name.
|
| - TCHAR module_file_name[MAX_PATH] = {};
|
| - if (!::GetModuleFileName(instance_, module_file_name,
|
| - arraysize(module_file_name))) {
|
| - _ASSERTE(false);
|
| - return NULL;
|
| - }
|
| -
|
| - return ExtractTag(module_file_name);
|
| - }
|
| -
|
| - static CString GetFilespec(const CString& path) {
|
| - int pos = path.ReverseFind('\\');
|
| - if (pos >= 0) {
|
| - return path.Mid(pos + 1);
|
| - }
|
| - return path;
|
| - }
|
| -
|
| - void HandleTarFile(const TCHAR* filename) {
|
| - CString new_filename(filename);
|
| - files_to_delete_.Add(new_filename);
|
| - CString filespec(GetFilespec(new_filename));
|
| - filespec.MakeLower();
|
| -
|
| - if (filespec.GetLength() > 4) {
|
| - CString extension(filespec.Mid(filespec.GetLength() - 4));
|
| -
|
| - if (extension == _T(".exe")) {
|
| - // We're interested in remembering only the first exe in the tarball.
|
| - if (exe_path_.IsEmpty()) {
|
| - exe_path_ = new_filename;
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - static void TarFileCallback(void* context, const TCHAR* filename) {
|
| - MetaInstaller* mi = reinterpret_cast<MetaInstaller*>(context);
|
| - mi->HandleTarFile(filename);
|
| - }
|
| -
|
| - // TODO(omaha): reimplement the relevant files in the LZMA SDK to optimize
|
| - // for size. We'll have to release the modifications (LZMA SDK is CDDL/CDL),
|
| - // which shouldn't be a problem.
|
| - static void* MyAlloc(void* p, size_t size) {
|
| - UNREFERENCED_PARAMETER(p);
|
| - return new uint8[size];
|
| - }
|
| -
|
| - static void MyFree(void* p, void* address) {
|
| - UNREFERENCED_PARAMETER(p);
|
| - delete[] address;
|
| - }
|
| -
|
| - // Decompress the content of the memory buffer into the file
|
| - static int DecompressBufferToFile(const uint8* packed_buffer,
|
| - size_t packed_size,
|
| - HANDLE file) {
|
| - // need header and len minimally
|
| - if (packed_size < LZMA_PROPS_SIZE + 8) {
|
| - return -1;
|
| - }
|
| -
|
| - // Note this code won't properly handle decoding large files, since uint32
|
| - // is used in several places to count size.
|
| - ISzAlloc allocators = { &MyAlloc, &MyFree };
|
| - CLzmaDec lzma_state;
|
| - LzmaDec_Construct(&lzma_state);
|
| - LzmaDec_Allocate(&lzma_state, packed_buffer, LZMA_PROPS_SIZE, &allocators);
|
| - LzmaDec_Init(&lzma_state);
|
| - packed_buffer += LZMA_PROPS_SIZE;
|
| - packed_size -= LZMA_PROPS_SIZE;
|
| -
|
| - // TODO(omaha): make this independent of endianness.
|
| - uint64 unpacked_size_64 = *reinterpret_cast<const uint64*>(packed_buffer);
|
| - size_t unpacked_size = static_cast<size_t>(unpacked_size_64);
|
| - packed_buffer += sizeof(unpacked_size_64);
|
| - packed_size -= sizeof(unpacked_size_64);
|
| -
|
| - scoped_array<uint8> unpacked_buffer(new uint8[unpacked_size]);
|
| -
|
| - ELzmaStatus status = static_cast<ELzmaStatus>(0);
|
| - SRes result = LzmaDec_DecodeToBuf(
|
| - &lzma_state,
|
| - unpacked_buffer.get(),
|
| - &unpacked_size,
|
| - packed_buffer,
|
| - &packed_size,
|
| - LZMA_FINISH_END,
|
| - &status);
|
| - LzmaDec_Free(&lzma_state, &allocators);
|
| - if (SZ_OK != result) {
|
| - return -1;
|
| - }
|
| -
|
| -#if 0
|
| - // Reverse BCJ coding.
|
| - uint32 x86_conversion_state;
|
| - x86_Convert_Init(x86_conversion_state);
|
| - x86_Convert(unpacked_buffer.get(), unpacked_size, 0, &x86_conversion_state,
|
| - 0 /* decoding */);
|
| -#else
|
| - // Reverse BCJ2 coding.
|
| - const uint8* p = unpacked_buffer.get();
|
| - uint32 original_size = *reinterpret_cast<const uint32*>(p);
|
| - p += sizeof(uint32); // NOLINT
|
| - uint32 stream0_size = *reinterpret_cast<const uint32*>(p);
|
| - p += sizeof(uint32); // NOLINT
|
| - uint32 stream1_size = *reinterpret_cast<const uint32*>(p);
|
| - p += sizeof(uint32); // NOLINT
|
| - uint32 stream2_size = *reinterpret_cast<const uint32*>(p);
|
| - p += sizeof(uint32); // NOLINT
|
| - uint32 stream3_size = *reinterpret_cast<const uint32*>(p);
|
| - p += sizeof(uint32); // NOLINT
|
| -
|
| - scoped_array<uint8> output_buffer(new uint8[original_size]);
|
| - if (SZ_OK != Bcj2_Decode(p,
|
| - stream0_size,
|
| - p + stream0_size,
|
| - stream1_size,
|
| - p + stream0_size + stream1_size,
|
| - stream2_size,
|
| - p + stream0_size + stream1_size + stream2_size,
|
| - stream3_size,
|
| - output_buffer.get(), original_size)) {
|
| - return 1;
|
| - }
|
| -#endif
|
| -
|
| - DWORD written;
|
| - if (!::WriteFile(file, output_buffer.get(), original_size, &written,
|
| - NULL) ||
|
| - written != original_size) {
|
| - return -1;
|
| - }
|
| -
|
| - return 0;
|
| - }
|
| -
|
| - HINSTANCE instance_;
|
| - CString cmd_line_;
|
| - CString exe_path_;
|
| - DWORD exit_code_;
|
| - CSimpleArray<CString> files_to_delete_;
|
| - CString temp_dir_;
|
| - CString temp_root_dir_;
|
| -};
|
| -
|
| -HRESULT CheckOSRequirements() {
|
| - return SystemInfo::OSWin2KSP4OrLater() ? S_OK :
|
| - GOOPDATE_E_RUNNING_INFERIOR_WINDOWS;
|
| -}
|
| -
|
| -CString GetCompanyDisplayName() {
|
| - CString company_name;
|
| - company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME);
|
| - _ASSERTE(!company_name.IsEmpty());
|
| - return company_name;
|
| -}
|
| -
|
| -CString GetUiTitle() {
|
| - CString title;
|
| - title.FormatMessage(IDS_INSTALLER_DISPLAY_NAME, GetCompanyDisplayName());
|
| - return title;
|
| -}
|
| -
|
| -HRESULT HandleError(HRESULT result) {
|
| - _ASSERTE(FAILED(result));
|
| - CString msg_box_text;
|
| -
|
| - switch (result) {
|
| - case GOOPDATE_E_RUNNING_INFERIOR_WINDOWS:
|
| - msg_box_text.FormatMessage(IDS_RUNNING_INFERIOR_WINDOWS,
|
| - GetCompanyDisplayName());
|
| - break;
|
| -
|
| - case GOOPDATE_E_UNTAGGED_METAINSTALLER:
|
| - default:
|
| - msg_box_text.LoadString(IDS_GENERIC_ERROR);
|
| - _ASSERTE(!msg_box_text.IsEmpty());
|
| - break;
|
| - }
|
| -
|
| - ::MessageBox(NULL, msg_box_text, GetUiTitle(), MB_OK);
|
| - return result;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -} // namespace omaha
|
| -
|
| -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int) {
|
| - scoped_co_init init_com_apt;
|
| - HRESULT hr(init_com_apt.hresult());
|
| - if (FAILED(hr)) {
|
| - return omaha::HandleError(hr);
|
| - }
|
| -
|
| - hr = omaha::CheckOSRequirements();
|
| - if (FAILED(hr)) {
|
| - return omaha::HandleError(hr);
|
| - }
|
| -
|
| - omaha::MetaInstaller mi(hInstance, lpCmdLine);
|
| - int result = mi.ExtractAndRun();
|
| - return result;
|
| -}
|
| -
|
|
|