Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / installer / mini_installer / decompress.cc
blob6440b214c548a04f775ed84794522413b38a98e8
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <windows.h> // NOLINT
6 #include <fcntl.h> // for _O_* constants
7 #include <fdi.h>
8 #include <stdlib.h>
10 #include "chrome/installer/mini_installer/decompress.h"
12 namespace {
14 FNALLOC(Alloc) {
15 return ::HeapAlloc(::GetProcessHeap(), 0, cb);
18 FNFREE(Free) {
19 ::HeapFree(::GetProcessHeap(), 0, pv);
22 // Converts a wide string to utf8. Set |len| to -1 if |str| is zero terminated
23 // and you want to convert the entire string.
24 // The returned string will have been allocated with Alloc(), so free it
25 // with a call to Free().
26 char* WideToUtf8(const wchar_t* str, int len) {
27 char* ret = NULL;
28 int size = WideCharToMultiByte(CP_UTF8, 0, str, len, NULL, 0, NULL, NULL);
29 if (size) {
30 if (len != -1)
31 ++size; // include space for the terminator.
32 ret = reinterpret_cast<char*>(Alloc(size * sizeof(ret[0])));
33 if (ret) {
34 WideCharToMultiByte(CP_UTF8, 0, str, len, ret, size, NULL, NULL);
35 if (len != -1)
36 ret[size - 1] = '\0'; // terminate the string
39 return ret;
42 wchar_t* Utf8ToWide(const char* str) {
43 wchar_t* ret = NULL;
44 int size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
45 if (size) {
46 ret = reinterpret_cast<wchar_t*>(Alloc(size * sizeof(ret[0])));
47 if (ret)
48 MultiByteToWideChar(CP_UTF8, 0, str, -1, ret, size);
50 return ret;
53 template <typename T>
54 class scoped_ptr {
55 public:
56 explicit scoped_ptr(T* a) : a_(a) {
58 ~scoped_ptr() {
59 if (a_)
60 Free(a_);
62 operator T*() {
63 return a_;
65 private:
66 T* a_;
69 FNOPEN(Open) {
70 DWORD access = 0;
71 DWORD disposition = 0;
73 if (oflag & _O_RDWR) {
74 access = GENERIC_READ | GENERIC_WRITE;
75 } else if (oflag & _O_WRONLY) {
76 access = GENERIC_WRITE;
77 } else {
78 access = GENERIC_READ;
81 if (oflag & _O_CREAT) {
82 disposition = CREATE_ALWAYS;
83 } else {
84 disposition = OPEN_EXISTING;
87 scoped_ptr<wchar_t> path(Utf8ToWide(pszFile));
88 HANDLE file = CreateFileW(path, access, FILE_SHARE_READ, NULL, disposition,
89 FILE_ATTRIBUTE_NORMAL, NULL);
90 return reinterpret_cast<INT_PTR>(file);
93 FNREAD(Read) {
94 DWORD read = 0;
95 if (!::ReadFile(reinterpret_cast<HANDLE>(hf), pv, cb, &read, NULL))
96 read = static_cast<DWORD>(-1L);
97 return read;
100 FNWRITE(Write) {
101 DWORD written = 0;
102 if (!::WriteFile(reinterpret_cast<HANDLE>(hf), pv, cb, &written, NULL))
103 written = static_cast<DWORD>(-1L);
104 return written;
107 FNCLOSE(Close) {
108 return ::CloseHandle(reinterpret_cast<HANDLE>(hf)) ? 0 : -1;
111 FNSEEK(Seek) {
112 return ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, seektype);
115 FNFDINOTIFY(Notify) {
116 INT_PTR result = 0;
118 // Since we will only ever be decompressing a single file at a time
119 // we take a shortcut and provide a pointer to the wide destination file
120 // of the file we want to write. This way we don't have to bother with
121 // utf8/wide conversion and concatenation of directory and file name.
122 const wchar_t* destination = reinterpret_cast<const wchar_t*>(pfdin->pv);
124 switch (fdint) {
125 case fdintCOPY_FILE: {
126 result = reinterpret_cast<INT_PTR>(::CreateFileW(destination,
127 GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
128 FILE_ATTRIBUTE_NORMAL, NULL));
129 break;
132 case fdintCLOSE_FILE_INFO: {
133 FILETIME file_time;
134 FILETIME local;
135 // Converts MS-DOS date and time values to a file time
136 if (DosDateTimeToFileTime(pfdin->date, pfdin->time, &file_time) &&
137 LocalFileTimeToFileTime(&file_time, &local)) {
138 SetFileTime(reinterpret_cast<HANDLE>(pfdin->hf), &local, NULL, NULL);
141 result = !Close(pfdin->hf);
142 pfdin->attribs &= FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
143 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE;
144 ::SetFileAttributes(destination, pfdin->attribs);
145 break;
148 case fdintCABINET_INFO:
149 case fdintENUMERATE:
150 // OK. continue as normal.
151 result = 0;
152 break;
154 case fdintPARTIAL_FILE:
155 case fdintNEXT_CABINET:
156 default:
157 // Error case.
158 result = -1;
159 break;
162 return result;
165 // Module handle of cabinet.dll
166 HMODULE g_fdi = NULL;
168 // API prototypes.
169 typedef HFDI (DIAMONDAPI* FDICreateFn)(PFNALLOC alloc, PFNFREE free,
170 PFNOPEN open, PFNREAD read,
171 PFNWRITE write, PFNCLOSE close,
172 PFNSEEK seek, int cpu_type, PERF perf);
173 typedef BOOL (DIAMONDAPI* FDIDestroyFn)(HFDI fdi);
174 typedef BOOL (DIAMONDAPI* FDICopyFn)(HFDI fdi, char* cab, char* cab_path,
175 int flags, PFNFDINOTIFY notify,
176 PFNFDIDECRYPT decrypt, void* context);
177 FDICreateFn g_FDICreate = NULL;
178 FDIDestroyFn g_FDIDestroy = NULL;
179 FDICopyFn g_FDICopy = NULL;
181 bool InitializeFdi() {
182 if (!g_fdi) {
183 // It has been observed that some users do not have the expected
184 // environment variables set, so we try a couple that *should* always be
185 // present and fallback to the default Windows install path if all else
186 // fails.
187 // The cabinet.dll should be available on all supported versions of Windows.
188 static const wchar_t* const candidate_paths[] = {
189 L"%WINDIR%\\system32\\cabinet.dll",
190 L"%SYSTEMROOT%\\system32\\cabinet.dll",
191 L"C:\\Windows\\system32\\cabinet.dll",
194 wchar_t path[MAX_PATH] = {0};
195 for (int i = 0; i < _countof(candidate_paths); ++i) {
196 path[0] = L'\0';
197 DWORD result = ::ExpandEnvironmentStringsW(candidate_paths[i],
198 path, _countof(path));
200 if (result > 0 && result <= _countof(path))
201 g_fdi = ::LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
203 if (g_fdi)
204 break;
208 if (g_fdi) {
209 g_FDICreate =
210 reinterpret_cast<FDICreateFn>(::GetProcAddress(g_fdi, "FDICreate"));
211 g_FDIDestroy =
212 reinterpret_cast<FDIDestroyFn>(::GetProcAddress(g_fdi, "FDIDestroy"));
213 g_FDICopy =
214 reinterpret_cast<FDICopyFn>(::GetProcAddress(g_fdi, "FDICopy"));
217 return g_FDICreate && g_FDIDestroy && g_FDICopy;
220 } // namespace
222 namespace mini_installer {
224 bool Expand(const wchar_t* source, const wchar_t* destination) {
225 if (!InitializeFdi())
226 return false;
228 // Start by splitting up the source path and convert to utf8 since the
229 // cabinet API doesn't support wide strings.
230 const wchar_t* source_name = source + lstrlenW(source);
231 while (source_name > source && *source_name != L'\\')
232 --source_name;
233 if (source_name == source)
234 return false;
236 // Convert the name to utf8.
237 source_name++;
238 scoped_ptr<char> source_name_utf8(WideToUtf8(source_name, -1));
239 // The directory part is assumed to have a trailing backslash.
240 scoped_ptr<char> source_path_utf8(WideToUtf8(source, source_name - source));
242 scoped_ptr<char> dest_utf8(WideToUtf8(destination, -1));
243 if (!dest_utf8 || !source_name_utf8 || !source_path_utf8)
244 return false;
246 bool success = false;
248 ERF erf = {0};
249 HFDI fdi = g_FDICreate(&Alloc, &Free, &Open, &Read, &Write, &Close, &Seek,
250 cpuUNKNOWN, &erf);
251 if (fdi) {
252 if (g_FDICopy(fdi, source_name_utf8, source_path_utf8, 0,
253 &Notify, NULL, const_cast<wchar_t*>(destination))) {
254 success = true;
256 g_FDIDestroy(fdi);
259 return success;
262 } // namespace mini_installer