ReactOS 0.4.15-dev-7994-gb388cb6
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
25inline BOOL
26WideToMultiByte(const CStringW &szSource, CStringA &szDest, UINT Codepage)
27{
28 // determine the needed size
29 INT sz = WideCharToMultiByte(Codepage, 0, szSource, -1, NULL, NULL, NULL, NULL);
30 if (!sz)
31 return FALSE;
32
33 // do the actual conversion
34 sz = WideCharToMultiByte(Codepage, 0, szSource, -1, szDest.GetBuffer(sz), sz, NULL, NULL);
35
36 szDest.ReleaseBuffer();
37 return sz != 0;
38}
39
40// converts CStringA to CStringW using a given codepage
41inline BOOL
42MultiByteToWide(const CStringA &szSource, CStringW &szDest, UINT Codepage)
43{
44 // determine the needed size
45 INT sz = MultiByteToWideChar(Codepage, 0, szSource, -1, NULL, NULL);
46 if (!sz)
47 return FALSE;
48
49 // do the actual conversion
50 sz = MultiByteToWideChar(CP_UTF8, 0, szSource, -1, szDest.GetBuffer(sz), sz);
51
52 szDest.ReleaseBuffer();
53 return sz != 0;
54}
55
56/* FDICreate callbacks */
57
58FNALLOC(fnMemAlloc)
59{
60 return HeapAlloc(GetProcessHeap(), NULL, cb);
61}
62
63FNFREE(fnMemFree)
64{
66}
67
68FNOPEN(fnFileOpen)
69{
71 DWORD dwDesiredAccess = 0;
72 DWORD dwCreationDisposition = 0;
73 CStringW szFileName;
74
76
77 if (oflag & _O_RDWR)
78 {
79 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
80 }
81 else if (oflag & _O_WRONLY)
82 {
83 dwDesiredAccess = GENERIC_WRITE;
84 }
85 else
86 {
87 dwDesiredAccess = GENERIC_READ;
88 }
89
90 if (oflag & _O_CREAT)
91 {
92 dwCreationDisposition = CREATE_ALWAYS;
93 }
94 else
95 {
96 dwCreationDisposition = OPEN_EXISTING;
97 }
98
99 MultiByteToWide(pszFile, szFileName, CP_UTF8);
100
102 szFileName, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
103
104 return (INT_PTR)hFile;
105}
106
107FNREAD(fnFileRead)
108{
109 DWORD dwBytesRead = 0;
110
111 if (ReadFile((HANDLE)hf, pv, cb, &dwBytesRead, NULL) == FALSE)
112 {
113 dwBytesRead = (DWORD)-1L;
114 }
115
116 return dwBytesRead;
117}
118
119FNWRITE(fnFileWrite)
120{
121 DWORD dwBytesWritten = 0;
122
123 if (WriteFile((HANDLE)hf, pv, cb, &dwBytesWritten, NULL) == FALSE)
124 {
125 dwBytesWritten = (DWORD)-1;
126 }
127
128 return dwBytesWritten;
129}
130
131FNCLOSE(fnFileClose)
132{
133 return (CloseHandle((HANDLE)hf) != FALSE) ? 0 : -1;
134}
135
136FNSEEK(fnFileSeek)
137{
138 return SetFilePointer((HANDLE)hf, dist, NULL, seektype);
139}
140
141/* FDICopy callbacks */
142
143FNFDINOTIFY(fnNotify)
144{
145 INT_PTR iResult = 0;
146
147 switch (fdint)
148 {
149 case fdintCOPY_FILE:
150 {
151 CStringW szExtractDir, szCabFileName;
152
153 // Append the destination directory to the file name.
154 MultiByteToWide((LPCSTR)pfdin->pv, szExtractDir, CP_UTF8);
155 MultiByteToWide(pfdin->psz1, szCabFileName, CP_ACP);
156
157 if (szCabFileName.Find('\\') >= 0)
158 {
159 CStringW szNewDirName = szExtractDir;
160 int nTokenPos = 0;
161 // We do not want to interpret the filename as directory,
162 // so bail out before the last token!
163 while (szCabFileName.Find('\\', nTokenPos) >= 0)
164 {
165 CStringW token = szCabFileName.Tokenize(L"\\", nTokenPos);
166 if (token.IsEmpty())
167 break;
168
169 szNewDirName += L"\\" + token;
170 if (!CreateDirectoryW(szNewDirName, NULL))
171 {
174 {
175 DPRINT1(
176 "ERROR: Unable to create directory %S (err %lu)\n", szNewDirName.GetString(), dwErr);
177 }
178 }
179 }
180 }
181
182 CStringW szNewFileName = szExtractDir + L"\\" + szCabFileName;
183
184 CStringA szFilePathUTF8;
185 WideToMultiByte(szNewFileName, szFilePathUTF8, CP_UTF8);
186
187 // Open the file
188 iResult = fnFileOpen((LPSTR)szFilePathUTF8.GetString(), _O_WRONLY | _O_CREAT, 0);
189 }
190 break;
191
193 iResult = !fnFileClose(pfdin->hf);
194 break;
195
197 if (pfdin->fdie != FDIERROR_NONE)
198 {
199 iResult = -1;
200 }
201 break;
202
204 iResult = 0;
205 break;
206
208 iResult = 0;
209 break;
210
211 case fdintENUMERATE:
212 iResult = 0;
213 break;
214
215 default:
216 iResult = -1;
217 break;
218 }
219
220 return iResult;
221}
222
223/* cabinet.dll FDI function pointers */
224
226
228
230
231/*
232 * Extraction function
233 * TODO: require only a full path to the cab as an argument
234 */
235BOOL
236ExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const CStringW &szOutputDir)
237{
238 HINSTANCE hCabinetDll;
239 HFDI ExtractHandler;
240 ERF ExtractErrors;
241 ATL::CStringA szCabNameUTF8, szCabDirUTF8, szOutputDirUTF8;
242 fnFDICreate pfnFDICreate;
243 fnFDICopy pfnFDICopy;
244 fnFDIDestroy pfnFDIDestroy;
245 BOOL bResult;
246
247 // Load cabinet.dll and extract needed functions
248 hCabinetDll = LoadLibraryW(L"cabinet.dll");
249
250 if (!hCabinetDll)
251 {
252 return FALSE;
253 }
254
255 pfnFDICreate = (fnFDICreate)GetProcAddress(hCabinetDll, "FDICreate");
256 pfnFDICopy = (fnFDICopy)GetProcAddress(hCabinetDll, "FDICopy");
257 pfnFDIDestroy = (fnFDIDestroy)GetProcAddress(hCabinetDll, "FDIDestroy");
258
259 if (!pfnFDICreate || !pfnFDICopy || !pfnFDIDestroy)
260 {
261 FreeLibrary(hCabinetDll);
262 return FALSE;
263 }
264
265 // Create FDI context
266 ExtractHandler = pfnFDICreate(
267 fnMemAlloc, fnMemFree, fnFileOpen, fnFileRead, fnFileWrite, fnFileClose, fnFileSeek, cpuUNKNOWN,
268 &ExtractErrors);
269
270 if (!ExtractHandler)
271 {
272 FreeLibrary(hCabinetDll);
273 return FALSE;
274 }
275
276 // Create output dir
277 bResult = CreateDirectoryW(szOutputDir, NULL);
278
279 if (bResult || GetLastError() == ERROR_ALREADY_EXISTS)
280 {
281 // Convert wide strings to UTF-8
282 bResult = WideToMultiByte(szCabName, szCabNameUTF8, CP_UTF8);
283 bResult &= WideToMultiByte(szCabDir, szCabDirUTF8, CP_UTF8);
284 bResult &= WideToMultiByte(szOutputDir, szOutputDirUTF8, CP_UTF8);
285 }
286
287 // Perform extraction
288 if (bResult)
289 {
290 // Add a slash to cab name as required by the api
291 szCabNameUTF8 = "\\" + szCabNameUTF8;
292
293 bResult = pfnFDICopy(
294 ExtractHandler, (LPSTR)szCabNameUTF8.GetString(), (LPSTR)szCabDirUTF8.GetString(), 0, fnNotify, NULL,
295 (void FAR *)szOutputDirUTF8.GetString());
296 }
297
298 pfnFDIDestroy(ExtractHandler);
299 FreeLibrary(hCabinetDll);
300 return bResult;
301}
#define DPRINT1
Definition: precomp.h:8
DWORD dwErr
Definition: service.c:36
BOOL ExtractFilesFromCab(const CStringW &szCabName, const CStringW &szCabDir, const CStringW &szOutputDir)
Definition: cabinet.cpp:236
BOOL MultiByteToWide(const CStringA &szSource, CStringW &szDest, UINT Codepage)
Definition: cabinet.cpp:42
BOOL WideToMultiByte(const CStringW &szSource, CStringA &szDest, UINT Codepage)
Definition: cabinet.cpp:26
HFDI(* fnFDICreate)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF)
Definition: cabinet.cpp:225
BOOL(* fnFDICopy)(HFDI, LPSTR, LPSTR, INT, PFNFDINOTIFY, PFNFDIDECRYPT, void FAR *pvUser)
Definition: cabinet.cpp:227
BOOL(* fnFDIDestroy)(HFDI)
Definition: cabinet.cpp:229
void ReleaseBuffer(_In_ int nNewLength=-1)
Definition: atlsimpstr.h:387
PXSTR GetString() noexcept
Definition: atlsimpstr.h:367
CStringT Tokenize(_In_z_ PCXSTR pszTokens, _Inout_ int &iStart) const
Definition: cstringt.h:947
int Find(_In_ PCXSTR pszSub, _In_opt_ int iStart=0) const noexcept
Definition: cstringt.h:696
#define NULL
Definition: types.h:112
#define FALSE
Definition: types.h:117
#define _O_RDWR
Definition: cabinet.h:39
#define _O_CREAT
Definition: cabinet.h:46
#define _O_WRONLY
Definition: cabinet.h:38
#define CloseHandle
Definition: compat.h:739
#define GetProcessHeap()
Definition: compat.h:736
#define CP_ACP
Definition: compat.h:109
#define OPEN_EXISTING
Definition: compat.h:775
#define ReadFile(a, b, c, d, e)
Definition: compat.h:742
#define SetFilePointer
Definition: compat.h:743
#define GetProcAddress(x, y)
Definition: compat.h:753
#define HeapAlloc
Definition: compat.h:733
#define FreeLibrary(x)
Definition: compat.h:748
#define GENERIC_READ
Definition: compat.h:135
#define HeapFree(x, y, z)
Definition: compat.h:735
#define CreateFileW
Definition: compat.h:741
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:137
#define WideCharToMultiByte
Definition: compat.h:111
#define MultiByteToWideChar
Definition: compat.h:110
#define LoadLibraryW(x)
Definition: compat.h:747
#define FILE_SHARE_READ
Definition: compat.h:136
#define FAR
Definition: zlib.h:34
BOOL WINAPI CreateDirectoryW(IN LPCWSTR lpPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
Definition: dir.c:90
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
unsigned int(__cdecl typeof(jpeg_read_scanlines))(struct jpeg_decompress_struct *
Definition: typeof.h:31
struct ERF * PERF
#define FNSEEK(fn)
Definition: fdi.h:221
@ FDIERROR_NONE
Definition: fdi.h:115
#define FNALLOC(fn)
Definition: fdi.h:203
UINT(__cdecl * PFNWRITE)(INT_PTR hf, void *pv, UINT cb)
Definition: fdi.h:214
int(__cdecl * PFNCLOSE)(INT_PTR hf)
Definition: fdi.h:217
#define FNOPEN(fn)
Definition: fdi.h:209
@ fdintCABINET_INFO
Definition: fdi.h:247
@ fdintCOPY_FILE
Definition: fdi.h:249
@ fdintPARTIAL_FILE
Definition: fdi.h:248
@ fdintCLOSE_FILE_INFO
Definition: fdi.h:250
@ fdintNEXT_CABINET
Definition: fdi.h:251
@ fdintENUMERATE
Definition: fdi.h:252
#define FNFREE(fn)
Definition: fdi.h:206
#define FNCLOSE(fn)
Definition: fdi.h:218
INT_PTR(__cdecl * PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
Definition: fdi.h:255
UINT(__cdecl * PFNREAD)(INT_PTR hf, void *pv, UINT cb)
Definition: fdi.h:211
void(__cdecl * PFNFREE)(void *pv)
Definition: fdi.h:205
void *(__cdecl * PFNALLOC)(ULONG cb)
Definition: fdi.h:202
#define FNREAD(fn)
Definition: fdi.h:212
INT_PTR(__cdecl * PFNOPEN)(char *pszFile, int oflag, int pmode)
Definition: fdi.h:208
LONG(__cdecl * PFNSEEK)(INT_PTR hf, LONG dist, int seektype)
Definition: fdi.h:220
#define FNFDINOTIFY(fn)
Definition: fdi.h:257
#define FNWRITE(fn)
Definition: fdi.h:215
void * HFDI
Definition: fdi.h:141
int(__cdecl * PFNFDIDECRYPT)(PFDIDECRYPT pfdid)
Definition: fdi.h:223
#define cpuUNKNOWN
Definition: fdi.h:269
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
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 CREATE_ALWAYS
Definition: disk.h:72
#define ERROR_ALREADY_EXISTS
Definition: disk.h:80
static HMODULE MODULEINFO DWORD cb
Definition: module.c:33
_In_ HANDLE hFile
Definition: mswsock.h:90
unsigned int UINT
Definition: ndis.h:50
#define BOOL
Definition: nt_native.h:43
#define DWORD
Definition: nt_native.h:44
#define GENERIC_WRITE
Definition: nt_native.h:90
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
#define L(x)
Definition: ntvdm.h:50
#define INT
Definition: polytest.cpp:20
#define CP_UTF8
Definition: nls.h:20
Definition: fci.h:44
int32_t INT_PTR
Definition: typedefs.h:64
int32_t INT
Definition: typedefs.h:58
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
const char * LPCSTR
Definition: xmlstorage.h:183
char * LPSTR
Definition: xmlstorage.h:182