486 lines
12 KiB
C
486 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
expandit.c
|
|
|
|
Abstract:
|
|
|
|
Expandit provides routines to expand a given file as much as possible.
|
|
It is useful for expanding cabinet files and executable files that may
|
|
themselves contain cabinet files.
|
|
|
|
Author:
|
|
|
|
Marc R. Whitten (marcw) 30-Jul-1998
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#include "migutilp.h"
|
|
|
|
UINT g_DirSequencer = 0;
|
|
|
|
BOOL
|
|
ExpandAllFilesW (
|
|
IN PCWSTR FileDir,
|
|
IN PCWSTR TempDir
|
|
);
|
|
|
|
BOOL
|
|
ExpandFileW (
|
|
IN PCWSTR FullPath,
|
|
IN PCWSTR TempDir
|
|
);
|
|
|
|
PWSTR
|
|
pGetExpandName (
|
|
VOID
|
|
)
|
|
{
|
|
static WCHAR rName[MAX_WCHAR_PATH];
|
|
|
|
g_DirSequencer++;
|
|
swprintf (rName, L"EXP%04x", g_DirSequencer);
|
|
|
|
return rName;
|
|
}
|
|
|
|
BOOL
|
|
pIsFileType (
|
|
IN PCWSTR FullPath,
|
|
IN PCWSTR TypePattern
|
|
)
|
|
{
|
|
PCWSTR p;
|
|
|
|
p = GetFileExtensionFromPathW (FullPath);
|
|
|
|
return p && IsPatternMatchW (TypePattern, p);
|
|
}
|
|
|
|
|
|
BOOL CALLBACK
|
|
pResNameCallback (
|
|
IN HANDLE hModule, // module handle
|
|
IN LPCWSTR lpszType, // pointer to resource type
|
|
IN LPWSTR lpszName, // pointer to resource name
|
|
IN LONG lParam // application-defined parameter
|
|
)
|
|
{
|
|
HRSRC hResource;
|
|
DWORD size;
|
|
HGLOBAL hGlobal;
|
|
HANDLE hFile;
|
|
PWSTR fileName;
|
|
PWSTR dirName;
|
|
PVOID srcBytes;
|
|
UINT dontCare;
|
|
BOOL rSuccess=TRUE;
|
|
|
|
hResource = FindResourceW (hModule, lpszName, lpszType);
|
|
|
|
if (hResource) {
|
|
|
|
size = SizeofResource (hModule, hResource);
|
|
|
|
if (size) {
|
|
|
|
hGlobal = LoadResource (hModule, hResource);
|
|
|
|
if (hGlobal) {
|
|
|
|
srcBytes = LockResource (hGlobal);
|
|
if (srcBytes) {
|
|
|
|
//
|
|
// Ok, lets see if this is a cabinet file..
|
|
//
|
|
if (size < 4 || *((PDWORD)srcBytes) != 0x4643534D) {
|
|
//
|
|
// Not a cabinet file.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
dirName = JoinPathsW ((PWSTR) lParam, pGetExpandName ());
|
|
fileName = JoinPathsW (dirName, L"temp.cab");
|
|
|
|
hFile = CreateFileW (
|
|
fileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DEBUGMSGW ((DBG_ERROR, "Unable to create file %s.", fileName));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!WriteFile (hFile, srcBytes, size, &dontCare, NULL)) {
|
|
DEBUGMSGW ((DBG_ERROR, "Cannot write to file %s", fileName));
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle (hFile);
|
|
|
|
//
|
|
// Expand this file.
|
|
//
|
|
rSuccess = ExpandFileW (fileName, dirName);
|
|
FreePathStringW (dirName);
|
|
FreePathStringW (fileName);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rSuccess;
|
|
}
|
|
|
|
|
|
BOOL CALLBACK
|
|
pResTypeCallback (
|
|
IN HANDLE hModule, // resource-module handle
|
|
IN PWSTR lpszType, // pointer to resource type
|
|
IN LONG lParam // application-defined parameter
|
|
)
|
|
{
|
|
|
|
//
|
|
// Oh what a pain. All of the RT types are #defined without A/Ws.
|
|
// We need to converte the string to ansi, just to do this comparison.
|
|
//
|
|
|
|
PCSTR aString = ConvertWtoA (lpszType);
|
|
|
|
|
|
if ((aString != RT_ACCELERATOR ) &&
|
|
(aString != RT_ANICURSOR ) &&
|
|
(aString != RT_ANIICON ) &&
|
|
(aString != RT_BITMAP ) &&
|
|
(aString != RT_CURSOR ) &&
|
|
(aString != RT_DIALOG ) &&
|
|
(aString != RT_FONT ) &&
|
|
(aString != RT_FONTDIR ) &&
|
|
(aString != RT_GROUP_CURSOR ) &&
|
|
(aString != RT_GROUP_ICON ) &&
|
|
(aString != RT_HTML ) &&
|
|
(aString != RT_ICON ) &&
|
|
(aString != RT_MENU ) &&
|
|
(aString != RT_MESSAGETABLE ) &&
|
|
(aString != RT_PLUGPLAY ) &&
|
|
(aString != RT_STRING ) &&
|
|
(aString != RT_VERSION ) &&
|
|
(aString != RT_VXD ) &&
|
|
(aString != RT_HTML )
|
|
) {
|
|
|
|
//
|
|
// Unknown type. We assume it is a cabinet file and try to extract it.
|
|
// Since it may not be a cabinet file, we eat the error.
|
|
//
|
|
if (!EnumResourceNamesW (hModule, lpszType, pResNameCallback, lParam)) {
|
|
DEBUGMSGW ((DBG_ERROR, "Error enumerating resource names."));
|
|
}
|
|
}
|
|
|
|
FreeConvertedStr (aString);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
UINT CALLBACK
|
|
pCabFileCallback (
|
|
IN PVOID Context, //context used by the callback routine
|
|
IN UINT Notification, //notification sent to callback routine
|
|
IN UINT Param1, //additional notification information
|
|
IN UINT Param2 //additional notification information
|
|
)
|
|
{
|
|
PCWSTR tempDir = Context;
|
|
PCWSTR fileName = (PCWSTR)Param2 ;
|
|
PFILE_IN_CABINET_INFO_W fileInfo = (PFILE_IN_CABINET_INFO_W)Param1;
|
|
PCWSTR fromPtr, toPtr;
|
|
WCHAR tempStr [MEMDB_MAX];
|
|
|
|
if (Notification == SPFILENOTIFY_FILEINCABINET) {
|
|
|
|
if (toPtr = wcschr (fileInfo->NameInCabinet, L'\\')) {
|
|
|
|
StringCopyW (fileInfo->FullTargetName, tempDir);
|
|
fromPtr = fileInfo->NameInCabinet;
|
|
|
|
while (toPtr) {
|
|
|
|
StringCopyABW (tempStr, fromPtr, toPtr);
|
|
StringCatW (fileInfo->FullTargetName, L"\\");
|
|
StringCatW (fileInfo->FullTargetName, tempStr);
|
|
CreateDirectoryW (fileInfo->FullTargetName, NULL);
|
|
toPtr++;
|
|
fromPtr = toPtr;
|
|
toPtr = wcschr (toPtr, L'\\');
|
|
}
|
|
}
|
|
|
|
swprintf (fileInfo->FullTargetName, L"%ws\\%ws", tempDir, fileInfo->NameInCabinet);
|
|
return FILEOP_DOIT;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
BOOL
|
|
ExpandFileW (
|
|
IN PCWSTR FullPath,
|
|
IN PCWSTR TempDir
|
|
)
|
|
{
|
|
BOOL rSuccess = TRUE;
|
|
PWSTR dirName = NULL;
|
|
PWSTR fileName = NULL;
|
|
HANDLE exeModule = NULL;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
CHAR aName[MAX_MBCHAR_PATH];
|
|
PCWSTR uName = NULL;
|
|
UINT bytesRead = 0;
|
|
|
|
|
|
if (pIsFileType (FullPath, L"CAB")) {
|
|
//
|
|
// Expand this cabinet file and recursively handle all of its files.
|
|
//
|
|
dirName = JoinPathsW (TempDir, pGetExpandName());
|
|
__try {
|
|
|
|
if (!CreateDirectoryW (dirName, NULL)) {
|
|
DEBUGMSGW ((DBG_ERROR, "ExpandFile: Cannot create directory %s", dirName));
|
|
rSuccess = FALSE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Expand the cabinet file into the temporary directory.
|
|
//
|
|
SetLastError (ERROR_SUCCESS);
|
|
if (!SetupIterateCabinetW (FullPath, 0, pCabFileCallback, dirName)) {
|
|
DEBUGMSGW ((DBG_ERROR, "ExpandFile: SetupIterateCabinet failed for file %s.", FullPath));
|
|
rSuccess = FALSE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Now, make sure all of the files in this SubDirectory are expanded.
|
|
//
|
|
rSuccess = ExpandAllFilesW (dirName, dirName);
|
|
}
|
|
__finally {
|
|
|
|
FreePathStringW (dirName);
|
|
}
|
|
|
|
} else if (pIsFileType (FullPath, L"EXE")) {
|
|
|
|
//
|
|
// This is an executable file. Check to make sure that they aren't any cabinet files hanging out
|
|
// inside of it.
|
|
//
|
|
exeModule = LoadLibraryExW (FullPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
|
|
if (!exeModule) {
|
|
DEBUGMSGW ((DBG_ERROR, "ExpandFile: LoadLibraryEx failed for %s.", FullPath));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!EnumResourceTypesW (exeModule, pResTypeCallback, (LONG) TempDir)) {
|
|
DEBUGMSGW ((DBG_ERROR, "ExpandFile: EnumResourceTypes failed for %s.", FullPath));
|
|
FreeLibrary (exeModule);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
} else if (pIsFileType (FullPath, L"*_")) {
|
|
|
|
//
|
|
// Compressed file. Decompress it.
|
|
//
|
|
hFile = CreateFileW (
|
|
FullPath,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DEBUGMSGW ((DBG_ERROR, "ExpandFile: Unable to open file %s.", FullPath));
|
|
return FALSE;
|
|
}
|
|
|
|
__try {
|
|
|
|
//
|
|
// The real file name is stored at 0x3c.
|
|
//
|
|
if (0xffffffff == SetFilePointer (hFile, 0x3c, NULL, FILE_BEGIN)) {
|
|
DEBUGMSGW ((DBG_ERROR, "ExpandFile: Cannot get real file name for compressed file %s.", FullPath));
|
|
rSuccess = FALSE;
|
|
__leave;
|
|
}
|
|
|
|
if (!ReadFile (hFile, aName, sizeof(aName), &bytesRead, NULL)) {
|
|
DEBUGMSG ((DBG_ERROR, "ExpandFile: Cannot read real file name for compressed file %s.", FullPath));
|
|
rSuccess = FALSE;
|
|
__leave;
|
|
}
|
|
|
|
if (bytesRead >= ByteCountW (FullPath)) {
|
|
|
|
uName = ConvertAtoW (aName);
|
|
|
|
if (StringIMatchTcharCountW (FullPath, uName, TcharCountW (FullPath) - 1)) {
|
|
|
|
dirName = JoinPathsW (TempDir, pGetExpandName());
|
|
fileName = JoinPathsW (dirName, uName);
|
|
|
|
//
|
|
// Create directory for this file and decompress it.
|
|
//
|
|
if (!CreateDirectoryW (dirName, NULL)) {
|
|
DEBUGMSGW ((DBG_ERROR, "ExpandFile: Cannot create directory %s", dirName));
|
|
rSuccess = FALSE;
|
|
__leave;
|
|
}
|
|
|
|
if (SetupDecompressOrCopyFileW (FullPath, fileName, NULL) != ERROR_SUCCESS) {
|
|
DEBUGMSGW ((DBG_ERROR, "ExpandFile: Cannot decompreses %s => %s.", FullPath, fileName));
|
|
rSuccess = FALSE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Now, run expand recursively on the decompressed file. Could be a CAB or an EXE.
|
|
//
|
|
rSuccess = ExpandFileW (fileName, dirName);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
__finally {
|
|
|
|
CloseHandle (hFile);
|
|
FreePathStringW (dirName);
|
|
FreePathStringW (fileName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rSuccess;
|
|
}
|
|
|
|
BOOL
|
|
ExpandAllFilesW (
|
|
IN PCWSTR FileDir,
|
|
IN PCWSTR TempDir
|
|
)
|
|
{
|
|
BOOL rSuccess = TRUE;
|
|
TREE_ENUMW e;
|
|
|
|
if (EnumFirstFileInTreeW (&e, FileDir, L"*", FALSE)) {
|
|
do {
|
|
|
|
if (!e.Directory) {
|
|
rSuccess &= ExpandFileW (e.FullPath, TempDir);
|
|
}
|
|
|
|
} while (EnumNextFileInTreeW (&e));
|
|
}
|
|
|
|
DEBUGMSGW_IF ((!rSuccess,DBG_ERROR, "ExpandAllFilesW: One or more errors occurred while expanding all files in %s.", FileDir));
|
|
|
|
return rSuccess;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ExpandFileA (
|
|
IN PCSTR FullPath,
|
|
IN PCSTR TempDir
|
|
)
|
|
{
|
|
|
|
PCWSTR wFullPath = NULL;
|
|
PCWSTR wTempDir = NULL;
|
|
BOOL rSuccess = TRUE;
|
|
|
|
MYASSERT(FullPath && TempDir);
|
|
|
|
//
|
|
// Convert args and call W version.
|
|
//
|
|
|
|
wFullPath = ConvertAtoW (FullPath);
|
|
wTempDir = ConvertAtoW (TempDir);
|
|
|
|
MYASSERT (wFullPath && wTempDir);
|
|
|
|
rSuccess = ExpandFileW (wFullPath, wTempDir);
|
|
|
|
FreeConvertedStr (wFullPath);
|
|
FreeConvertedStr (wTempDir);
|
|
|
|
return rSuccess;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ExpandAllFilesA (
|
|
IN PCSTR FileDir,
|
|
IN PCSTR TempDir
|
|
)
|
|
{
|
|
|
|
PCWSTR wFileDir = NULL;
|
|
PCWSTR wTempDir = NULL;
|
|
BOOL rSuccess = TRUE;
|
|
|
|
MYASSERT(FileDir && TempDir);
|
|
|
|
//
|
|
// Convert args and call W version.
|
|
//
|
|
|
|
wFileDir = ConvertAtoW (FileDir);
|
|
wTempDir = ConvertAtoW (TempDir);
|
|
|
|
MYASSERT (wFileDir && wTempDir);
|
|
|
|
rSuccess = ExpandAllFilesW (wFileDir, wTempDir);
|
|
|
|
FreeConvertedStr (wFileDir);
|
|
FreeConvertedStr (wTempDir);
|
|
|
|
return rSuccess;
|
|
}
|