summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/chrome_installer.gypi3
-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
7 files changed, 319 insertions, 14 deletions
diff --git a/chrome/chrome_installer.gypi b/chrome/chrome_installer.gypi
index 1e41e30..05f287f 100644
--- a/chrome/chrome_installer.gypi
+++ b/chrome/chrome_installer.gypi
@@ -341,6 +341,9 @@
# below into a separate lib and then link both setup.exe and
# setup_unittests.exe against that.
'sources': [
+ 'installer/mini_installer/decompress.cc',
+ 'installer/mini_installer/decompress.h',
+ 'installer/mini_installer/decompress_test.cc',
'installer/mini_installer/mini_string.cc',
'installer/mini_installer/mini_string.h',
'installer/mini_installer/mini_string_test.cc',
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