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
9 #include "chrome/installer/mini_installer/decompress.h"
14 return ::HeapAlloc(::GetProcessHeap(), 0, cb
);
18 ::HeapFree(::GetProcessHeap(), 0, pv
);
21 // Converts a wide string to utf8. Set |len| to -1 if |str| is zero terminated
22 // and you want to convert the entire string.
23 // The returned string will have been allocated with Alloc(), so free it
24 // with a call to Free().
25 char* WideToUtf8(const wchar_t* str
, int len
) {
27 int size
= WideCharToMultiByte(CP_UTF8
, 0, str
, len
, NULL
, 0, NULL
, NULL
);
30 ++size
; // include space for the terminator.
31 ret
= reinterpret_cast<char*>(Alloc(size
* sizeof(ret
[0])));
33 WideCharToMultiByte(CP_UTF8
, 0, str
, len
, ret
, size
, NULL
, NULL
);
35 ret
[size
- 1] = '\0'; // terminate the string
41 wchar_t* Utf8ToWide(const char* str
) {
43 int size
= MultiByteToWideChar(CP_UTF8
, 0, str
, -1, NULL
, 0);
45 ret
= reinterpret_cast<wchar_t*>(Alloc(size
* sizeof(ret
[0])));
47 MultiByteToWideChar(CP_UTF8
, 0, str
, -1, ret
, size
);
55 explicit scoped_ptr(T
* a
) : a_(a
) {
70 DWORD disposition
= 0;
72 if (oflag
& _O_RDWR
) {
73 access
= GENERIC_READ
| GENERIC_WRITE
;
74 } else if (oflag
& _O_WRONLY
) {
75 access
= GENERIC_WRITE
;
77 access
= GENERIC_READ
;
80 if (oflag
& _O_CREAT
) {
81 disposition
= CREATE_ALWAYS
;
83 disposition
= OPEN_EXISTING
;
86 scoped_ptr
<wchar_t> path(Utf8ToWide(pszFile
));
87 HANDLE file
= CreateFileW(path
, access
, FILE_SHARE_READ
, NULL
, disposition
,
88 FILE_ATTRIBUTE_NORMAL
, NULL
);
89 return reinterpret_cast<INT_PTR
>(file
);
94 if (!::ReadFile(reinterpret_cast<HANDLE
>(hf
), pv
, cb
, &read
, NULL
))
95 read
= static_cast<DWORD
>(-1L);
101 if (!::WriteFile(reinterpret_cast<HANDLE
>(hf
), pv
, cb
, &written
, NULL
))
102 written
= static_cast<DWORD
>(-1L);
107 return ::CloseHandle(reinterpret_cast<HANDLE
>(hf
)) ? 0 : -1;
111 return ::SetFilePointer(reinterpret_cast<HANDLE
>(hf
), dist
, NULL
, seektype
);
114 FNFDINOTIFY(Notify
) {
117 // Since we will only ever be decompressing a single file at a time
118 // we take a shortcut and provide a pointer to the wide destination file
119 // of the file we want to write. This way we don't have to bother with
120 // utf8/wide conversion and concatenation of directory and file name.
121 const wchar_t* destination
= reinterpret_cast<const wchar_t*>(pfdin
->pv
);
124 case fdintCOPY_FILE
: {
125 result
= reinterpret_cast<INT_PTR
>(::CreateFileW(destination
,
126 GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_ALWAYS
,
127 FILE_ATTRIBUTE_NORMAL
, NULL
));
131 case fdintCLOSE_FILE_INFO
: {
134 // Converts MS-DOS date and time values to a file time
135 if (DosDateTimeToFileTime(pfdin
->date
, pfdin
->time
, &file_time
) &&
136 LocalFileTimeToFileTime(&file_time
, &local
)) {
137 SetFileTime(reinterpret_cast<HANDLE
>(pfdin
->hf
), &local
, NULL
, NULL
);
140 result
= !Close(pfdin
->hf
);
141 pfdin
->attribs
&= FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_HIDDEN
|
142 FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_ARCHIVE
;
143 ::SetFileAttributes(destination
, pfdin
->attribs
);
147 case fdintCABINET_INFO
:
149 // OK. continue as normal.
153 case fdintPARTIAL_FILE
:
154 case fdintNEXT_CABINET
:
164 // Module handle of cabinet.dll
165 HMODULE g_fdi
= NULL
;
168 typedef HFDI (DIAMONDAPI
* FDICreateFn
)(PFNALLOC alloc
, PFNFREE free
,
169 PFNOPEN open
, PFNREAD read
,
170 PFNWRITE write
, PFNCLOSE close
,
171 PFNSEEK seek
, int cpu_type
, PERF perf
);
172 typedef BOOL (DIAMONDAPI
* FDIDestroyFn
)(HFDI fdi
);
173 typedef BOOL (DIAMONDAPI
* FDICopyFn
)(HFDI fdi
, char* cab
, char* cab_path
,
174 int flags
, PFNFDINOTIFY notify
,
175 PFNFDIDECRYPT decrypt
, void* context
);
176 FDICreateFn g_FDICreate
= NULL
;
177 FDIDestroyFn g_FDIDestroy
= NULL
;
178 FDICopyFn g_FDICopy
= NULL
;
180 bool InitializeFdi() {
182 // It has been observed that some users do not have the expected
183 // environment variables set, so we try a couple that *should* always be
184 // present and fallback to the default Windows install path if all else
186 // The cabinet.dll should be available on all supported versions of Windows.
187 static const wchar_t* const candidate_paths
[] = {
188 L
"%WINDIR%\\system32\\cabinet.dll",
189 L
"%SYSTEMROOT%\\system32\\cabinet.dll",
190 L
"C:\\Windows\\system32\\cabinet.dll",
193 wchar_t path
[MAX_PATH
] = {0};
194 for (int i
= 0; i
< arraysize(candidate_paths
); ++i
) {
196 DWORD result
= ::ExpandEnvironmentStringsW(candidate_paths
[i
],
197 path
, arraysize(path
));
199 if (result
> 0 && result
<= arraysize(path
))
200 g_fdi
= ::LoadLibraryExW(path
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
209 reinterpret_cast<FDICreateFn
>(::GetProcAddress(g_fdi
, "FDICreate"));
211 reinterpret_cast<FDIDestroyFn
>(::GetProcAddress(g_fdi
, "FDIDestroy"));
213 reinterpret_cast<FDICopyFn
>(::GetProcAddress(g_fdi
, "FDICopy"));
216 return g_FDICreate
&& g_FDIDestroy
&& g_FDICopy
;
221 namespace mini_installer
{
223 bool Expand(const wchar_t* source
, const wchar_t* destination
) {
224 if (!InitializeFdi())
227 // Start by splitting up the source path and convert to utf8 since the
228 // cabinet API doesn't support wide strings.
229 const wchar_t* source_name
= source
+ lstrlenW(source
);
230 while (source_name
> source
&& *source_name
!= L
'\\')
232 if (source_name
== source
)
235 // Convert the name to utf8.
237 scoped_ptr
<char> source_name_utf8(WideToUtf8(source_name
, -1));
238 // The directory part is assumed to have a trailing backslash.
239 scoped_ptr
<char> source_path_utf8(WideToUtf8(source
, source_name
- source
));
241 scoped_ptr
<char> dest_utf8(WideToUtf8(destination
, -1));
242 if (!dest_utf8
|| !source_name_utf8
|| !source_path_utf8
)
245 bool success
= false;
248 HFDI fdi
= g_FDICreate(&Alloc
, &Free
, &Open
, &Read
, &Write
, &Close
, &Seek
,
251 if (g_FDICopy(fdi
, source_name_utf8
, source_path_utf8
, 0,
252 &Notify
, NULL
, const_cast<wchar_t*>(destination
))) {
261 } // namespace mini_installer