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
10 #include "chrome/installer/mini_installer/decompress.h"
15 return ::HeapAlloc(::GetProcessHeap(), 0, cb
);
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
) {
28 int size
= WideCharToMultiByte(CP_UTF8
, 0, str
, len
, NULL
, 0, NULL
, NULL
);
31 ++size
; // include space for the terminator.
32 ret
= reinterpret_cast<char*>(Alloc(size
* sizeof(ret
[0])));
34 WideCharToMultiByte(CP_UTF8
, 0, str
, len
, ret
, size
, NULL
, NULL
);
36 ret
[size
- 1] = '\0'; // terminate the string
42 wchar_t* Utf8ToWide(const char* str
) {
44 int size
= MultiByteToWideChar(CP_UTF8
, 0, str
, -1, NULL
, 0);
46 ret
= reinterpret_cast<wchar_t*>(Alloc(size
* sizeof(ret
[0])));
48 MultiByteToWideChar(CP_UTF8
, 0, str
, -1, ret
, size
);
56 explicit scoped_ptr(T
* a
) : a_(a
) {
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
;
78 access
= GENERIC_READ
;
81 if (oflag
& _O_CREAT
) {
82 disposition
= CREATE_ALWAYS
;
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
);
95 if (!::ReadFile(reinterpret_cast<HANDLE
>(hf
), pv
, cb
, &read
, NULL
))
96 read
= static_cast<DWORD
>(-1L);
102 if (!::WriteFile(reinterpret_cast<HANDLE
>(hf
), pv
, cb
, &written
, NULL
))
103 written
= static_cast<DWORD
>(-1L);
108 return ::CloseHandle(reinterpret_cast<HANDLE
>(hf
)) ? 0 : -1;
112 return ::SetFilePointer(reinterpret_cast<HANDLE
>(hf
), dist
, NULL
, seektype
);
115 FNFDINOTIFY(Notify
) {
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
);
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
));
132 case fdintCLOSE_FILE_INFO
: {
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
);
148 case fdintCABINET_INFO
:
150 // OK. continue as normal.
154 case fdintPARTIAL_FILE
:
155 case fdintNEXT_CABINET
:
165 // Module handle of cabinet.dll
166 HMODULE g_fdi
= NULL
;
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() {
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
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
) {
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
);
210 reinterpret_cast<FDICreateFn
>(::GetProcAddress(g_fdi
, "FDICreate"));
212 reinterpret_cast<FDIDestroyFn
>(::GetProcAddress(g_fdi
, "FDIDestroy"));
214 reinterpret_cast<FDICopyFn
>(::GetProcAddress(g_fdi
, "FDICopy"));
217 return g_FDICreate
&& g_FDIDestroy
&& g_FDICopy
;
222 namespace mini_installer
{
224 bool Expand(const wchar_t* source
, const wchar_t* destination
) {
225 if (!InitializeFdi())
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
'\\')
233 if (source_name
== source
)
236 // Convert the name to utf8.
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
)
246 bool success
= false;
249 HFDI fdi
= g_FDICreate(&Alloc
, &Free
, &Open
, &Read
, &Write
, &Close
, &Seek
,
252 if (g_FDICopy(fdi
, source_name_utf8
, source_path_utf8
, 0,
253 &Notify
, NULL
, const_cast<wchar_t*>(destination
))) {
262 } // namespace mini_installer