| OLD | NEW |
| (Empty) | |
| 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. |
| 4 |
| 5 #include <windows.h> // NOLINT |
| 6 #include <fcntl.h> // for _O_* constants |
| 7 #include <fdi.h> |
| 8 |
| 9 #include "chrome/installer/mini_installer/decompress.h" |
| 10 |
| 11 namespace { |
| 12 |
| 13 FNALLOC(Alloc) { |
| 14 return ::HeapAlloc(::GetProcessHeap(), 0, cb); |
| 15 } |
| 16 |
| 17 FNFREE(Free) { |
| 18 ::HeapFree(::GetProcessHeap(), 0, pv); |
| 19 } |
| 20 |
| 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) { |
| 26 char* ret = NULL; |
| 27 int size = WideCharToMultiByte(CP_UTF8, 0, str, len, NULL, 0, NULL, NULL); |
| 28 if (size) { |
| 29 if (len != -1) |
| 30 ++size; // include space for the terminator. |
| 31 ret = reinterpret_cast<char*>(Alloc(size * sizeof(ret[0]))); |
| 32 if (ret) { |
| 33 WideCharToMultiByte(CP_UTF8, 0, str, len, ret, size, NULL, NULL); |
| 34 if (len != -1) |
| 35 ret[size - 1] = '\0'; // terminate the string |
| 36 } |
| 37 } |
| 38 return ret; |
| 39 } |
| 40 |
| 41 wchar_t* Utf8ToWide(const char* str) { |
| 42 wchar_t* ret = NULL; |
| 43 int size = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); |
| 44 if (size) { |
| 45 ret = reinterpret_cast<wchar_t*>(Alloc(size * sizeof(ret[0]))); |
| 46 if (ret) |
| 47 MultiByteToWideChar(CP_UTF8, 0, str, -1, ret, size); |
| 48 } |
| 49 return ret; |
| 50 } |
| 51 |
| 52 template <typename T> |
| 53 class scoped_ptr { |
| 54 public: |
| 55 explicit scoped_ptr(T* a) : a_(a) { |
| 56 } |
| 57 ~scoped_ptr() { |
| 58 if (a_) |
| 59 Free(a_); |
| 60 } |
| 61 operator T*() { |
| 62 return a_; |
| 63 } |
| 64 private: |
| 65 T* a_; |
| 66 }; |
| 67 |
| 68 FNOPEN(Open) { |
| 69 DWORD access = 0; |
| 70 DWORD disposition = 0; |
| 71 |
| 72 if (oflag & _O_RDWR) { |
| 73 access = GENERIC_READ | GENERIC_WRITE; |
| 74 } else if (oflag & _O_WRONLY) { |
| 75 access = GENERIC_WRITE; |
| 76 } else { |
| 77 access = GENERIC_READ; |
| 78 } |
| 79 |
| 80 if (oflag & _O_CREAT) { |
| 81 disposition = CREATE_ALWAYS; |
| 82 } else { |
| 83 disposition = OPEN_EXISTING; |
| 84 } |
| 85 |
| 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); |
| 90 } |
| 91 |
| 92 FNREAD(Read) { |
| 93 DWORD read = 0; |
| 94 if (!::ReadFile(reinterpret_cast<HANDLE>(hf), pv, cb, &read, NULL)) |
| 95 read = static_cast<DWORD>(-1L); |
| 96 return read; |
| 97 } |
| 98 |
| 99 FNWRITE(Write) { |
| 100 DWORD written = 0; |
| 101 if (!::WriteFile(reinterpret_cast<HANDLE>(hf), pv, cb, &written, NULL)) |
| 102 written = static_cast<DWORD>(-1L); |
| 103 return written; |
| 104 } |
| 105 |
| 106 FNCLOSE(Close) { |
| 107 return ::CloseHandle(reinterpret_cast<HANDLE>(hf)) ? 0 : -1; |
| 108 } |
| 109 |
| 110 FNSEEK(Seek) { |
| 111 return ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, seektype); |
| 112 } |
| 113 |
| 114 FNFDINOTIFY(Notify) { |
| 115 INT_PTR result = 0; |
| 116 |
| 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); |
| 122 |
| 123 switch (fdint) { |
| 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)); |
| 128 break; |
| 129 } |
| 130 |
| 131 case fdintCLOSE_FILE_INFO: { |
| 132 FILETIME file_time; |
| 133 FILETIME local; |
| 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); |
| 138 } |
| 139 |
| 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); |
| 144 break; |
| 145 } |
| 146 |
| 147 case fdintCABINET_INFO: |
| 148 case fdintENUMERATE: |
| 149 // OK. continue as normal. |
| 150 result = 0; |
| 151 break; |
| 152 |
| 153 case fdintPARTIAL_FILE: |
| 154 case fdintNEXT_CABINET: |
| 155 default: |
| 156 // Error case. |
| 157 result = -1; |
| 158 break; |
| 159 } |
| 160 |
| 161 return result; |
| 162 } |
| 163 |
| 164 // Module handle of cabinet.dll |
| 165 HMODULE g_fdi = NULL; |
| 166 |
| 167 // API prototypes. |
| 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; |
| 179 |
| 180 bool InitializeFdi() { |
| 181 if (!g_fdi) { |
| 182 wchar_t path[MAX_PATH] = {0}; |
| 183 ::ExpandEnvironmentStringsW(L"%WINDIR%\\system32\\cabinet.dll", path, |
| 184 MAX_PATH); |
| 185 // This DLL should be available on all supported versions of Windows. |
| 186 g_fdi = ::LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); |
| 187 g_FDICreate = |
| 188 reinterpret_cast<FDICreateFn>(::GetProcAddress(g_fdi, "FDICreate")); |
| 189 g_FDIDestroy = |
| 190 reinterpret_cast<FDIDestroyFn>(::GetProcAddress(g_fdi, "FDIDestroy")); |
| 191 g_FDICopy = |
| 192 reinterpret_cast<FDICopyFn>(::GetProcAddress(g_fdi, "FDICopy")); |
| 193 } |
| 194 |
| 195 return g_FDICreate && g_FDIDestroy && g_FDICopy; |
| 196 } |
| 197 |
| 198 } // namespace |
| 199 |
| 200 namespace mini_installer { |
| 201 |
| 202 bool Expand(const wchar_t* source, const wchar_t* destination) { |
| 203 if (!InitializeFdi()) |
| 204 return false; |
| 205 |
| 206 // Start by splitting up the source path and convert to utf8 since the |
| 207 // cabinet API doesn't support wide strings. |
| 208 const wchar_t* source_name = source + lstrlenW(source); |
| 209 while (source_name > source && *source_name != L'\\') |
| 210 --source_name; |
| 211 if (source_name == source) |
| 212 return false; |
| 213 |
| 214 // Convert the name to utf8. |
| 215 source_name++; |
| 216 scoped_ptr<char> source_name_utf8(WideToUtf8(source_name, -1)); |
| 217 // The directory part is assumed to have a trailing backslash. |
| 218 scoped_ptr<char> source_path_utf8(WideToUtf8(source, source_name - source)); |
| 219 |
| 220 scoped_ptr<char> dest_utf8(WideToUtf8(destination, -1)); |
| 221 if (!dest_utf8 || !source_name_utf8 || !source_path_utf8) |
| 222 return false; |
| 223 |
| 224 bool success = false; |
| 225 |
| 226 ERF erf = {0}; |
| 227 HFDI fdi = g_FDICreate(&Alloc, &Free, &Open, &Read, &Write, &Close, &Seek, |
| 228 cpuUNKNOWN, &erf); |
| 229 if (fdi) { |
| 230 if (g_FDICopy(fdi, source_name_utf8, source_path_utf8, 0, |
| 231 &Notify, NULL, const_cast<wchar_t*>(destination))) { |
| 232 success = true; |
| 233 } |
| 234 g_FDIDestroy(fdi); |
| 235 } |
| 236 |
| 237 return success; |
| 238 } |
| 239 |
| 240 } // namespace mini_installer |
| OLD | NEW |