summaryrefslogtreecommitdiffstats
path: root/chrome/installer
diff options
context:
space:
mode:
authortommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-16 21:32:06 +0000
committertommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-16 21:32:06 +0000
commit25f8a30ee91813f9216c6e9b23eaf760a3bd02a8 (patch)
tree43988f648eb9413871969f1f420f9ad944c6c0b2 /chrome/installer
parentf50a65764158f8bb3954dd3677c0f21628ef6f71 (diff)
downloadchromium_src-25f8a30ee91813f9216c6e9b23eaf760a3bd02a8.zip
chromium_src-25f8a30ee91813f9216c6e9b23eaf760a3bd02a8.tar.gz
chromium_src-25f8a30ee91813f9216c6e9b23eaf760a3bd02a8.tar.bz2
Switch out the setup api for using the cabinet API directly.
The setup API and tools such as expand.exe and makecab.exe all use the cabinet API. However, the setup API has additional dependencies that we don't need or want in the mini installer. So, this is a part of a pet pieve effort of mine to remove these dependencies and hopefully reduce the number of installation failures. TEST=Run accompanied unit test and also make sure that the mini installer works as before. BUG=none Review URL: http://codereview.chromium.org/6693015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78434 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r--chrome/installer/mini_installer.gyp4
-rw-r--r--chrome/installer/mini_installer/decompress.cc240
-rw-r--r--chrome/installer/mini_installer/decompress.h19
-rw-r--r--chrome/installer/mini_installer/decompress_test.cc44
-rw-r--r--chrome/installer/mini_installer/mini_installer.cc23
-rw-r--r--chrome/installer/test/data/SETUP.EX_bin0 -> 908964 bytes
6 files changed, 316 insertions, 14 deletions
diff --git a/chrome/installer/mini_installer.gyp b/chrome/installer/mini_installer.gyp
index 7ab32c6..84a2f70 100644
--- a/chrome/installer/mini_installer.gyp
+++ b/chrome/installer/mini_installer.gyp
@@ -25,6 +25,8 @@
],
'sources': [
'mini_installer/appid.h',
+ 'mini_installer/decompress.cc',
+ 'mini_installer/decompress.h',
'mini_installer/mini_installer.cc',
'mini_installer/mini_installer.h',
'mini_installer/mini_installer.ico',
@@ -67,13 +69,11 @@
'crt\\src\\intel\\mt_lib\\memset.obj"',
'"$(VCInstallDir)..\\..\\Microsoft Visual Studio 9.0\\VC\\'
'crt\\src\\intel\\mt_lib\\P4_memset.obj"',
- 'setupapi.lib',
],
},{
'AdditionalDependencies': [
'"$(VCInstallDir)crt\\src\\intel\\mt_lib\\memset.obj"',
'"$(VCInstallDir)crt\\src\\intel\\mt_lib\\P4_memset.obj"',
- 'setupapi.lib',
],
}],
],
diff --git a/chrome/installer/mini_installer/decompress.cc b/chrome/installer/mini_installer/decompress.cc
new file mode 100644
index 0000000..ced226d
--- /dev/null
+++ b/chrome/installer/mini_installer/decompress.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h> // NOLINT
+#include <fcntl.h> // for _O_* constants
+#include <fdi.h>
+
+#include "chrome/installer/mini_installer/decompress.h"
+
+namespace {
+
+FNALLOC(Alloc) {
+ return ::HeapAlloc(::GetProcessHeap(), 0, cb);
+}
+
+FNFREE(Free) {
+ ::HeapFree(::GetProcessHeap(), 0, pv);
+}
+
+// Converts a wide string to utf8. Set |len| to -1 if |str| is zero terminated
+// and you want to convert the entire string.
+// The returned string will have been allocated with Alloc(), so free it
+// with a call to Free().
+char* WideToUtf8(const wchar_t* str, int len) {
+ char* ret = NULL;
+ int size = WideCharToMultiByte(CP_UTF8, 0, str, len, NULL, 0, NULL, NULL);
+ if (size) {
+ if (len != -1)
+ ++size; // include space for the terminator.
+ ret = reinterpret_cast<char*>(Alloc(size * sizeof(ret[0])));
+ if (ret) {
+ WideCharToMultiByte(CP_UTF8, 0, str, len, ret, size, NULL, NULL);
+ if (len != -1)
+ ret[size - 1] = '\0'; // terminate the string
+ }
+ }
+ return ret;
+}
+
+wchar_t* Utf8ToWide(const char* str) {
+ wchar_t* ret = NULL;
+ int size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
+ if (size) {
+ ret = reinterpret_cast<wchar_t*>(Alloc(size * sizeof(ret[0])));
+ if (ret)
+ MultiByteToWideChar(CP_UTF8, 0, str, -1, ret, size);
+ }
+ return ret;
+}
+
+template <typename T>
+class scoped_ptr {
+ public:
+ explicit scoped_ptr(T* a) : a_(a) {
+ }
+ ~scoped_ptr() {
+ if (a_)
+ Free(a_);
+ }
+ operator T*() {
+ return a_;
+ }
+ private:
+ T* a_;
+};
+
+FNOPEN(Open) {
+ DWORD access = 0;
+ DWORD disposition = 0;
+
+ if (oflag & _O_RDWR) {
+ access = GENERIC_READ | GENERIC_WRITE;
+ } else if (oflag & _O_WRONLY) {
+ access = GENERIC_WRITE;
+ } else {
+ access = GENERIC_READ;
+ }
+
+ if (oflag & _O_CREAT) {
+ disposition = CREATE_ALWAYS;
+ } else {
+ disposition = OPEN_EXISTING;
+ }
+
+ scoped_ptr<wchar_t> path(Utf8ToWide(pszFile));
+ HANDLE file = CreateFileW(path, access, FILE_SHARE_READ, NULL, disposition,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ return reinterpret_cast<INT_PTR>(file);
+}
+
+FNREAD(Read) {
+ DWORD read = 0;
+ if (!::ReadFile(reinterpret_cast<HANDLE>(hf), pv, cb, &read, NULL))
+ read = static_cast<DWORD>(-1L);
+ return read;
+}
+
+FNWRITE(Write) {
+ DWORD written = 0;
+ if (!::WriteFile(reinterpret_cast<HANDLE>(hf), pv, cb, &written, NULL))
+ written = static_cast<DWORD>(-1L);
+ return written;
+}
+
+FNCLOSE(Close) {
+ return ::CloseHandle(reinterpret_cast<HANDLE>(hf)) ? 0 : -1;
+}
+
+FNSEEK(Seek) {
+ return ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, seektype);
+}
+
+FNFDINOTIFY(Notify) {
+ INT_PTR result = 0;
+
+ // Since we will only ever be decompressing a single file at a time
+ // we take a shortcut and provide a pointer to the wide destination file
+ // of the file we want to write. This way we don't have to bother with
+ // utf8/wide conversion and concatenation of directory and file name.
+ const wchar_t* destination = reinterpret_cast<const wchar_t*>(pfdin->pv);
+
+ switch (fdint) {
+ case fdintCOPY_FILE: {
+ result = reinterpret_cast<INT_PTR>(::CreateFileW(destination,
+ GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL));
+ break;
+ }
+
+ case fdintCLOSE_FILE_INFO: {
+ FILETIME file_time;
+ FILETIME local;
+ // Converts MS-DOS date and time values to a file time
+ if (DosDateTimeToFileTime(pfdin->date, pfdin->time, &file_time) &&
+ LocalFileTimeToFileTime(&file_time, &local)) {
+ SetFileTime(reinterpret_cast<HANDLE>(pfdin->hf), &local, NULL, NULL);
+ }
+
+ result = !Close(pfdin->hf);
+ pfdin->attribs &= FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE;
+ ::SetFileAttributes(destination, pfdin->attribs);
+ break;
+ }
+
+ case fdintCABINET_INFO:
+ case fdintENUMERATE:
+ // OK. continue as normal.
+ result = 0;
+ break;
+
+ case fdintPARTIAL_FILE:
+ case fdintNEXT_CABINET:
+ default:
+ // Error case.
+ result = -1;
+ break;
+ }
+
+ return result;
+}
+
+// Module handle of cabinet.dll
+HMODULE g_fdi = NULL;
+
+// API prototypes.
+typedef HFDI (DIAMONDAPI* FDICreateFn)(PFNALLOC alloc, PFNFREE free,
+ PFNOPEN open, PFNREAD read,
+ PFNWRITE write, PFNCLOSE close,
+ PFNSEEK seek, int cpu_type, PERF perf);
+typedef BOOL (DIAMONDAPI* FDIDestroyFn)(HFDI fdi);
+typedef BOOL (DIAMONDAPI* FDICopyFn)(HFDI fdi, char* cab, char* cab_path,
+ int flags, PFNFDINOTIFY notify,
+ PFNFDIDECRYPT decrypt, void* context);
+FDICreateFn g_FDICreate = NULL;
+FDIDestroyFn g_FDIDestroy = NULL;
+FDICopyFn g_FDICopy = NULL;
+
+bool InitializeFdi() {
+ if (!g_fdi) {
+ wchar_t path[MAX_PATH] = {0};
+ ::ExpandEnvironmentStringsW(L"%WINDIR%\\system32\\cabinet.dll", path,
+ MAX_PATH);
+ // This DLL should be available on all supported versions of Windows.
+ g_fdi = ::LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+ g_FDICreate =
+ reinterpret_cast<FDICreateFn>(::GetProcAddress(g_fdi, "FDICreate"));
+ g_FDIDestroy =
+ reinterpret_cast<FDIDestroyFn>(::GetProcAddress(g_fdi, "FDIDestroy"));
+ g_FDICopy =
+ reinterpret_cast<FDICopyFn>(::GetProcAddress(g_fdi, "FDICopy"));
+ }
+
+ return g_FDICreate && g_FDIDestroy && g_FDICopy;
+}
+
+} // namespace
+
+namespace mini_installer {
+
+bool Expand(const wchar_t* source, const wchar_t* destination) {
+ if (!InitializeFdi())
+ return false;
+
+ // Start by splitting up the source path and convert to utf8 since the
+ // cabinet API doesn't support wide strings.
+ const wchar_t* source_name = source + lstrlenW(source);
+ while (source_name > source && *source_name != L'\\')
+ --source_name;
+ if (source_name == source)
+ return false;
+
+ // Convert the name to utf8.
+ source_name++;
+ scoped_ptr<char> source_name_utf8(WideToUtf8(source_name, -1));
+ // The directory part is assumed to have a trailing backslash.
+ scoped_ptr<char> source_path_utf8(WideToUtf8(source, source_name - source));
+
+ scoped_ptr<char> dest_utf8(WideToUtf8(destination, -1));
+ if (!dest_utf8 || !source_name_utf8 || !source_path_utf8)
+ return false;
+
+ bool success = false;
+
+ ERF erf = {0};
+ HFDI fdi = g_FDICreate(&Alloc, &Free, &Open, &Read, &Write, &Close, &Seek,
+ cpuUNKNOWN, &erf);
+ if (fdi) {
+ if (g_FDICopy(fdi, source_name_utf8, source_path_utf8, 0,
+ &Notify, NULL, const_cast<wchar_t*>(destination))) {
+ success = true;
+ }
+ g_FDIDestroy(fdi);
+ }
+
+ return success;
+}
+
+} // namespace mini_installer
diff --git a/chrome/installer/mini_installer/decompress.h b/chrome/installer/mini_installer/decompress.h
new file mode 100644
index 0000000..8e093aa
--- /dev/null
+++ b/chrome/installer/mini_installer/decompress.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_INSTALLER_MINI_INSTALLER_DECOMPRESS_H_
+#define CHROME_INSTALLER_MINI_INSTALLER_DECOMPRESS_H_
+#pragma once
+
+namespace mini_installer {
+
+// Same as the tool, expand.exe. Decompresses a file that was compressed
+// using Microsoft's MSCF compression algorithm.
+// |source| is the full path of the file to decompress and |destination|
+// is the full path of the target file.
+bool Expand(const wchar_t* source, const wchar_t* destination);
+
+} // namespace mini_installer
+
+#endif // CHROME_INSTALLER_MINI_INSTALLER_DECOMPRESS_H_
diff --git a/chrome/installer/mini_installer/decompress_test.cc b/chrome/installer/mini_installer/decompress_test.cc
new file mode 100644
index 0000000..9258412
--- /dev/null
+++ b/chrome/installer/mini_installer/decompress_test.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/scoped_temp_dir.h"
+#include "chrome/installer/mini_installer/decompress.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class MiniDecompressTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ }
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(MiniDecompressTest, ExpandTest) {
+ FilePath source_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &source_path);
+ source_path = source_path.Append(FILE_PATH_LITERAL("chrome"))
+ .Append(FILE_PATH_LITERAL("installer"))
+ .Append(FILE_PATH_LITERAL("test"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .Append(FILE_PATH_LITERAL("SETUP.EX_"));
+
+ // Prepare a temp folder that will be automatically deleted along with
+ // our temporary test data.
+ ScopedTempDir temp_dir;
+ EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath dest_path(temp_dir.path().Append(FILE_PATH_LITERAL("setup.exe")));
+
+ // Decompress our test file.
+ EXPECT_TRUE(mini_installer::Expand(source_path.value().c_str(),
+ dest_path.value().c_str()));
+
+ // Check if the expanded file is a valid executable.
+ DWORD type = static_cast<DWORD>(-1);
+ EXPECT_TRUE(GetBinaryType(dest_path.value().c_str(), &type));
+ EXPECT_EQ(SCS_32BIT_BINARY, type);
+}
diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc
index a06fa9a..d9ed606 100644
--- a/chrome/installer/mini_installer/mini_installer.cc
+++ b/chrome/installer/mini_installer/mini_installer.cc
@@ -33,6 +33,7 @@
#include <shellapi.h>
#include "chrome/installer/mini_installer/appid.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"
@@ -427,20 +428,18 @@ bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path,
if (setup_path->length() > 0) {
// Uncompress LZ compressed resource. Setup is packed with 'MSCF'
- // as opposed to old DOS way of 'SZDD'. Hence use SetupInstallFile
- // instead of LZCopy.
- // Note that the API will automatically delete the original file
- // if the extraction was successful.
- // TODO(tommi): Use the cabinet API directly.
- if (!SetupInstallFile(NULL, NULL, setup_path->get(), NULL,
- setup_dest_path.get(),
- SP_COPY_DELETESOURCE | SP_COPY_SOURCE_ABSOLUTE,
- NULL, NULL)) {
- DeleteFile(setup_path->get());
- return false;
+ // as opposed to old DOS way of 'SZDD'. Hence we don't use LZCopy.
+ 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 setup_path->assign(setup_dest_path.get());
+ return success;
}
// setup.exe still not found. So finally check if it was sent as 'BN'
diff --git a/chrome/installer/test/data/SETUP.EX_ b/chrome/installer/test/data/SETUP.EX_
new file mode 100644
index 0000000..976ffcf
--- /dev/null
+++ b/chrome/installer/test/data/SETUP.EX_
Binary files differ