ReactOS  0.4.15-dev-1636-gf634010
cabinet.cpp
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS Applications Manager
3  * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE: Cabinet extraction using FDI API
5  * COPYRIGHT: Copyright 2018 Alexander Shaposhnikov (sanchaez@reactos.org)
6  */
7 #include "rapps.h"
8 #include <debug.h>
9 
10 #include <fdi.h>
11 #include <fcntl.h>
12 
13 /*
14  * HACK: treat any input strings as Unicode (UTF-8)
15  * cabinet.dll lacks any sort of a Unicode API, but FCI/FDI
16  * provide an ability to use user-defined callbacks for any file or memory
17  * operations. This flexibility and the magic power of C/C++ casting allows
18  * us to treat input as we please.
19  * This is by far the best way to extract .cab using Unicode paths.
20  */
21 
22 /* String conversion helper functions */
23 
24 // converts CStringW to CStringA using a given codepage
26  CStringA& szDest,
27  UINT Codepage)
28 {
29  // determine the needed size
30  INT sz = WideCharToMultiByte(Codepage,
31  0,
32  szSource,
33  -1,
34  NULL,
35  NULL,
36  NULL,
37  NULL);
38  if (!sz)
39  return FALSE;
40 
41  // do the actual conversion
42  sz = WideCharToMultiByte(Codepage,
43  0,
44  szSource,
45  -1,
46  szDest.GetBuffer(sz),
47  sz,
48  NULL,
49  NULL);
50 
51  szDest.ReleaseBuffer();
52  return sz != 0;
53 }
54 
55 // converts CStringA to CStringW using a given codepage
57  CStringW& szDest,
58  UINT Codepage)
59 {
60  // determine the needed size
61  INT sz = MultiByteToWideChar(Codepage,
62  0,
63  szSource,
64  -1,
65  NULL,
66  NULL);
67  if (!sz)
68  return FALSE;
69 
70  // do the actual conversion
72  0,
73  szSource,
74  -1,
75  szDest.GetBuffer(sz),
76  sz);
77 
78  szDest.ReleaseBuffer();
79  return sz != 0;
80 }
81 
82 /* FDICreate callbacks */
83 
84 FNALLOC(fnMemAlloc)
85 {
86  return HeapAlloc(GetProcessHeap(), NULL, cb);
87 }
88 
89 FNFREE(fnMemFree)
90 {
92 }
93 
94 FNOPEN(fnFileOpen)
95 {
96  HANDLE hFile = NULL;
97  DWORD dwDesiredAccess = 0;
98  DWORD dwCreationDisposition = 0;
99  ATL::CStringW szFileName;
100 
101  UNREFERENCED_PARAMETER(pmode);
102 
103  if (oflag & _O_RDWR)
104  {
105  dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
106  }
107  else if (oflag & _O_WRONLY)
108  {
109  dwDesiredAccess = GENERIC_WRITE;
110  }
111  else
112  {
113  dwDesiredAccess = GENERIC_READ;
114  }
115 
116  if (oflag & _O_CREAT)
117  {
118  dwCreationDisposition = CREATE_ALWAYS;
119  }
120  else
121  {
122  dwCreationDisposition = OPEN_EXISTING;
123  }
124 
125  MultiByteToWide(pszFile, szFileName, CP_UTF8);
126 
127  hFile = CreateFileW(szFileName,
128  dwDesiredAccess,
130  NULL,
131  dwCreationDisposition,
133  NULL);
134 
135  return (INT_PTR) hFile;
136 }
137 
138 FNREAD(fnFileRead)
139 {
140  DWORD dwBytesRead = 0;
141 
142  if (ReadFile((HANDLE) hf, pv, cb, &dwBytesRead, NULL) == FALSE)
143  {
144  dwBytesRead = (DWORD) -1L;
145  }
146 
147  return dwBytesRead;
148 }
149 
150 FNWRITE(fnFileWrite)
151 {
152  DWORD dwBytesWritten = 0;
153 
154  if (WriteFile((HANDLE) hf, pv, cb, &dwBytesWritten, NULL) == FALSE)
155  {
156  dwBytesWritten = (DWORD) -1;
157  }
158 
159  return dwBytesWritten;
160 }
161 
162 FNCLOSE(fnFileClose)
163 {
164  return (CloseHandle((HANDLE) hf) != FALSE) ? 0 : -1;
165 }
166 
167 FNSEEK(fnFileSeek)
168 {
169  return SetFilePointer((HANDLE) hf, dist, NULL, seektype);
170 }
171 
172 /* FDICopy callbacks */
173 
174 FNFDINOTIFY(fnNotify)
175 {
176  INT_PTR iResult = 0;
177 
178  switch (fdint)
179  {
180  case fdintCOPY_FILE:
181  {
182  CStringW szExtractDir, szCabFileName;
183 
184  // Append the destination directory to the file name.
185  MultiByteToWide((LPCSTR) pfdin->pv, szExtractDir, CP_UTF8);
186  MultiByteToWide(pfdin->psz1, szCabFileName, CP_ACP);
187 
188  if (szCabFileName.Find('\\') >= 0)
189  {
190  CStringW szNewDirName = szExtractDir;
191  int nTokenPos = 0;
192  // We do not want to interpret the filename as directory,
193  // so bail out before the last token!
194  while (szCabFileName.Find('\\', nTokenPos) >= 0)
195  {
196  CStringW token = szCabFileName.Tokenize(L"\\", nTokenPos);
197  if (token.IsEmpty())
198  break;
199 
200  szNewDirName += L"\\" + token;
201  if (!CreateDirectoryW(szNewDirName, NULL))
202  {
205  {
206  DPRINT1("ERROR: Unable to create directory %S (err %lu)\n", szNewDirName.GetString(), dwErr);
207  }
208  }
209  }
210  }
211 
212  CStringW szNewFileName = szExtractDir + L"\\" + szCabFileName;
213 
214  CStringA szFilePathUTF8;
215  WideToMultiByte(szNewFileName, szFilePathUTF8, CP_UTF8);
216 
217  // Open the file
218  iResult = fnFileOpen((LPSTR) szFilePathUTF8.GetString(),
220  0);
221  }
222  break;
223 
225  iResult = !fnFileClose(pfdin->hf);
226  break;
227 
228  case fdintNEXT_CABINET:
229  if (pfdin->fdie != FDIERROR_NONE)
230  {
231  iResult = -1;
232  }
233  break;
234 
235  case fdintPARTIAL_FILE:
236  iResult = 0;
237  break;
238 
239  case fdintCABINET_INFO:
240  iResult = 0;
241  break;
242 
243  case fdintENUMERATE:
244  iResult = 0;
245  break;
246 
247  default:
248  iResult = -1;
249  break;
250  }
251 
252  return iResult;
253 }
254 
255 /* cabinet.dll FDI function pointers */
256 
258  PFNFREE,
259  PFNOPEN,
260  PFNREAD,
261  PFNWRITE,
262  PFNCLOSE,
263  PFNSEEK,
264  int,
265  PERF);
266 
267 typedef BOOL(*fnFDICopy)(HFDI,
268  LPSTR,
269  LPSTR,
270  INT,
271  PFNFDINOTIFY,
273  void FAR *pvUser);
274 
275 typedef BOOL(*fnFDIDestroy)(HFDI);
276 
277 /*
278  * Extraction function
279  * TODO: require only a full path to the cab as an argument
280  */
282  const ATL::CStringW& szCabDir,
283  const ATL::CStringW& szOutputDir)
284 {
285  HINSTANCE hCabinetDll;
286  HFDI ExtractHandler;
287  ERF ExtractErrors;
288  ATL::CStringA szCabNameUTF8, szCabDirUTF8, szOutputDirUTF8;
289  fnFDICreate pfnFDICreate;
290  fnFDICopy pfnFDICopy;
291  fnFDIDestroy pfnFDIDestroy;
292  BOOL bResult;
293 
294  // Load cabinet.dll and extract needed functions
295  hCabinetDll = LoadLibraryW(L"cabinet.dll");
296 
297  if (!hCabinetDll)
298  {
299  return FALSE;
300  }
301 
302  pfnFDICreate = (fnFDICreate) GetProcAddress(hCabinetDll, "FDICreate");
303  pfnFDICopy = (fnFDICopy) GetProcAddress(hCabinetDll, "FDICopy");
304  pfnFDIDestroy = (fnFDIDestroy) GetProcAddress(hCabinetDll, "FDIDestroy");
305 
306  if (!pfnFDICreate || !pfnFDICopy || !pfnFDIDestroy)
307  {
308  FreeLibrary(hCabinetDll);
309  return FALSE;
310  }
311 
312  // Create FDI context
313  ExtractHandler = pfnFDICreate(fnMemAlloc,
314  fnMemFree,
315  fnFileOpen,
316  fnFileRead,
317  fnFileWrite,
318  fnFileClose,
319  fnFileSeek,
320  cpuUNKNOWN,
321  &ExtractErrors);
322 
323  if (!ExtractHandler)
324  {
325  FreeLibrary(hCabinetDll);
326  return FALSE;
327  }
328 
329  // Create output dir
330  bResult = CreateDirectoryW(szOutputDir, NULL);
331 
332  if (bResult || GetLastError() == ERROR_ALREADY_EXISTS)
333  {
334  // Convert wide strings to UTF-8
335  bResult = WideToMultiByte(szCabName, szCabNameUTF8, CP_UTF8);
336  bResult &= WideToMultiByte(szCabDir, szCabDirUTF8, CP_UTF8);
337  bResult &= WideToMultiByte(szOutputDir, szOutputDirUTF8, CP_UTF8);
338  }
339 
340  // Perform extraction
341  if (bResult)
342  {
343  // Add a slash to cab name as required by the api
344  szCabNameUTF8 = "\\" + szCabNameUTF8;
345 
346  bResult = pfnFDICopy(ExtractHandler,
347  (LPSTR) szCabNameUTF8.GetString(),
348  (LPSTR) szCabDirUTF8.GetString(),
349  0,
350  fnNotify,
351  NULL,
352  (void FAR *) szOutputDirUTF8.GetString());
353  }
354 
355  pfnFDIDestroy(ExtractHandler);
356  FreeLibrary(hCabinetDll);
357  return bResult;
358 }
INT_PTR(__cdecl * PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
Definition: fdi.h:255
BOOL WINAPI CreateDirectoryW(IN LPCWSTR lpPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
Definition: dir.c:90
FNREAD(fnFileRead)
Definition: cabinet.cpp:138
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
BOOL ExtractFilesFromCab(const ATL::CStringW &szCabName, const ATL::CStringW &szCabDir, const ATL::CStringW &szOutputDir)
Definition: cabinet.cpp:281
#define CloseHandle
Definition: compat.h:598
#define WideCharToMultiByte
Definition: compat.h:111
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
BOOL(* fnFDIDestroy)(HFDI)
Definition: cabinet.cpp:275
#define CP_ACP
Definition: compat.h:109
int(__cdecl * PFNFDIDECRYPT)(PFDIDECRYPT pfdid)
Definition: fdi.h:223
#define _O_CREAT
Definition: cabinet.h:46
#define INT
Definition: polytest.cpp:20
void(__cdecl * PFNFREE)(void *pv)
Definition: fdi.h:205
FNOPEN(fnFileOpen)
Definition: cabinet.cpp:94
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1044
int32_t INT_PTR
Definition: typedefs.h:64
char * LPSTR
Definition: xmlstorage.h:182
#define BOOL
Definition: nt_native.h:43
#define DWORD
Definition: nt_native.h:44
int32_t INT
Definition: typedefs.h:58
#define FILE_SHARE_READ
Definition: compat.h:136
BOOL MultiByteToWide(const CStringA &szSource, CStringW &szDest, UINT Codepage)
Definition: cabinet.cpp:56
#define _O_RDWR
Definition: cabinet.h:39
void ReleaseBuffer(_In_ int nNewLength=-1)
Definition: atlsimpstr.h:372
#define cpuUNKNOWN
Definition: fdi.h:269
#define CP_UTF8
Definition: nls.h:20
#define FALSE
Definition: types.h:117
FNALLOC(fnMemAlloc)
Definition: cabinet.cpp:84
unsigned int BOOL
Definition: ntddk_ex.h:94
HFDI(* fnFDICreate)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF)
Definition: cabinet.cpp:257
Definition: fci.h:44
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat token
Definition: glfuncs.h:210
#define GENERIC_WRITE
Definition: nt_native.h:90
int(__cdecl * PFNCLOSE)(INT_PTR hf)
Definition: fdi.h:217
#define LoadLibraryW(x)
Definition: compat.h:606
const char * LPCSTR
Definition: xmlstorage.h:183
INT_PTR(__cdecl * PFNOPEN)(char *pszFile, int oflag, int pmode)
Definition: fdi.h:208
#define OPEN_EXISTING
Definition: compat.h:634
BOOL WideToMultiByte(const CStringW &szSource, CStringA &szDest, UINT Codepage)
Definition: cabinet.cpp:25
UINT(__cdecl * PFNREAD)(INT_PTR hf, void *pv, UINT cb)
Definition: fdi.h:211
DWORD dwErr
Definition: service.c:36
struct ERF * PERF
#define FreeLibrary(x)
Definition: compat.h:607
#define GetProcessHeap()
Definition: compat.h:595
PVOID WINAPI HeapAlloc(HANDLE, DWORD, SIZE_T)
CStringT Tokenize(_In_z_ PCXSTR pszTokens, _Inout_ int &iStart) const
Definition: cstringt.h:835
PCXSTR GetString() const
Definition: atlsimpstr.h:361
unsigned long DWORD
Definition: ntddk_ex.h:95
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:137
static const WCHAR L[]
Definition: oid.c:1250
void *(__cdecl * PFNALLOC)(ULONG cb)
Definition: fdi.h:202
static const WCHAR szSource[]
Definition: automation.c:501
LONG(__cdecl * PFNSEEK)(INT_PTR hf, LONG dist, int seektype)
Definition: fdi.h:220
void * HFDI
Definition: fdi.h:141
#define GENERIC_READ
Definition: compat.h:135
UINT(__cdecl * PFNWRITE)(INT_PTR hf, void *pv, UINT cb)
Definition: fdi.h:214
_In_ HANDLE hFile
Definition: mswsock.h:90
#define _O_WRONLY
Definition: cabinet.h:38
#define CREATE_ALWAYS
Definition: disk.h:72
FNSEEK(fnFileSeek)
Definition: cabinet.cpp:167
#define ReadFile(a, b, c, d, e)
Definition: compat.h:601
unsigned int UINT
Definition: ndis.h:50
#define NULL
Definition: types.h:112
#define MultiByteToWideChar
Definition: compat.h:110
#define DPRINT1
Definition: precomp.h:8
int Find(_In_ PCXSTR pszSub, _In_opt_ int iStart=0) const
Definition: cstringt.h:611
#define CreateFileW
Definition: compat.h:600
static HMODULE MODULEINFO DWORD cb
Definition: module.c:32
BOOL(* fnFDICopy)(HFDI, LPSTR, LPSTR, INT, PFNFDINOTIFY, PFNFDIDECRYPT, void FAR *pvUser)
Definition: cabinet.cpp:267
FNCLOSE(fnFileClose)
Definition: cabinet.cpp:162
#define GetProcAddress(x, y)
Definition: compat.h:612
#define ERROR_ALREADY_EXISTS
Definition: disk.h:80
FNWRITE(fnFileWrite)
Definition: cabinet.cpp:150
#define FAR
Definition: zlib.h:34
FNFDINOTIFY(fnNotify)
Definition: cabinet.cpp:174
FNFREE(fnMemFree)
Definition: cabinet.cpp:89
#define HeapFree(x, y, z)
Definition: compat.h:594
#define SetFilePointer
Definition: compat.h:602
unsigned int(__cdecl typeof(jpeg_read_scanlines))(struct jpeg_decompress_struct *
Definition: typeof.h:31