• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FNALLOC(Alloc)13 FNALLOC(Alloc) {
14   return ::HeapAlloc(::GetProcessHeap(), 0, cb);
15 }
16 
FNFREE(Free)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().
WideToUtf8(const wchar_t * str,int len)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 
Utf8ToWide(const char * str)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:
scoped_ptr(T * a)55   explicit scoped_ptr(T* a) : a_(a) {
56   }
~scoped_ptr()57   ~scoped_ptr() {
58     if (a_)
59       Free(a_);
60   }
operator T*()61   operator T*() {
62     return a_;
63   }
64  private:
65   T* a_;
66 };
67 
FNOPEN(Open)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 
FNREAD(Read)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 
FNWRITE(Write)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 
FNCLOSE(Close)106 FNCLOSE(Close) {
107   return ::CloseHandle(reinterpret_cast<HANDLE>(hf)) ? 0 : -1;
108 }
109 
FNSEEK(Seek)110 FNSEEK(Seek) {
111   return ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, seektype);
112 }
113 
FNFDINOTIFY(Notify)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 
InitializeFdi()180 bool InitializeFdi() {
181   if (!g_fdi) {
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
185     // fails.
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",
191     };
192 
193     wchar_t path[MAX_PATH] = {0};
194     for (int i = 0; i < arraysize(candidate_paths); ++i) {
195       path[0] = L'\0';
196       DWORD result = ::ExpandEnvironmentStringsW(candidate_paths[i],
197                                                  path, arraysize(path));
198 
199       if (result > 0 && result <= arraysize(path))
200         g_fdi = ::LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
201 
202       if (g_fdi)
203         break;
204     }
205   }
206 
207   if (g_fdi) {
208     g_FDICreate =
209         reinterpret_cast<FDICreateFn>(::GetProcAddress(g_fdi, "FDICreate"));
210     g_FDIDestroy =
211         reinterpret_cast<FDIDestroyFn>(::GetProcAddress(g_fdi, "FDIDestroy"));
212     g_FDICopy =
213         reinterpret_cast<FDICopyFn>(::GetProcAddress(g_fdi, "FDICopy"));
214   }
215 
216   return g_FDICreate && g_FDIDestroy && g_FDICopy;
217 }
218 
219 }  // namespace
220 
221 namespace mini_installer {
222 
Expand(const wchar_t * source,const wchar_t * destination)223 bool Expand(const wchar_t* source, const wchar_t* destination) {
224   if (!InitializeFdi())
225     return false;
226 
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'\\')
231     --source_name;
232   if (source_name == source)
233     return false;
234 
235   // Convert the name to utf8.
236   source_name++;
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));
240 
241   scoped_ptr<char> dest_utf8(WideToUtf8(destination, -1));
242   if (!dest_utf8 || !source_name_utf8 || !source_path_utf8)
243     return false;
244 
245   bool success = false;
246 
247   ERF erf = {0};
248   HFDI fdi = g_FDICreate(&Alloc, &Free, &Open, &Read, &Write, &Close, &Seek,
249                          cpuUNKNOWN, &erf);
250   if (fdi) {
251     if (g_FDICopy(fdi, source_name_utf8, source_path_utf8, 0,
252                   &Notify, NULL, const_cast<wchar_t*>(destination))) {
253       success = true;
254     }
255     g_FDIDestroy(fdi);
256   }
257 
258   return success;
259 }
260 
261 }  // namespace mini_installer
262