You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
940 lines
26 KiB
940 lines
26 KiB
#include "nt.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "windows.h"
|
|
#include "stdio.h"
|
|
#include "stdlib.h"
|
|
#include "fcntl.h"
|
|
#include "sxsexpress.h"
|
|
#include "fci.h"
|
|
|
|
#define NUMBER_OF(q) (sizeof(q)/sizeof(*q))
|
|
|
|
#ifndef PAGE_SIZE
|
|
#define PAGE_SIZE (4*1024)
|
|
#endif
|
|
|
|
#define FILETYPE_CABINET (0x00000001)
|
|
#define FILETYPE_DISKFILE (0x00000002)
|
|
#define DEFAULT_RESERVE_SIZE (1024*1024*80) // By default, reserve 80mb
|
|
//#define DEFAULT_RESERVE_SIZE (1024*8) // By default, reserve 80mb
|
|
|
|
typedef struct _FILE_HANDLE {
|
|
DWORD dwFileType;
|
|
union {
|
|
HANDLE hDiskFile;
|
|
struct {
|
|
PVOID pvFileBase;
|
|
PVOID pvCursor;
|
|
SIZE_T cbReserved;
|
|
SIZE_T cbCommitted;
|
|
SIZE_T cbWritten;
|
|
} CabinetFile;
|
|
};
|
|
} FILE_HANDLE;
|
|
|
|
typedef struct _INSTALLED_FILE_NAME {
|
|
LIST_ENTRY Links;
|
|
PCWSTR pwszFileName;
|
|
PCWSTR pwszSubDir;
|
|
} INSTALLED_FILE;
|
|
|
|
LIST_ENTRY InstalledFiles;
|
|
|
|
typedef struct _FCI_CONTEXT_OBJECT
|
|
{
|
|
bool fWasError;
|
|
DWORD dwLastError;
|
|
FILE_HANDLE *CabinetFileHandle;
|
|
PCSTR pszCabFileName;
|
|
|
|
void ObtainErrorContext()
|
|
{
|
|
if (!fWasError)
|
|
{
|
|
fWasError = true;
|
|
dwLastError = ::GetLastError();
|
|
}
|
|
}
|
|
|
|
} FCI_CONTEXT_OBJECT;
|
|
|
|
BOOL CreateCabFromPath(PCWSTR pcwszBasePath, FILE_HANDLE *CreatedCabinet, PCWSTR pcwszInfName);
|
|
|
|
void
|
|
DoHelp(PCWSTR pcwszName)
|
|
{
|
|
static const WCHAR wchHelp[] =
|
|
L"SxsExpress Self-Extracting Creator Tool\r\n"
|
|
L"Copyright (c) 2001-2002 Microsoft Corporation, all \r\n"
|
|
L"\r\n"
|
|
L"Commandline:\r\n"
|
|
L"\r\n"
|
|
L" %ls [options] outputname\r\n"
|
|
L"\r\n"
|
|
L"Options:\r\n"
|
|
L"-sourcedir <dir> : Sets the base path to package up\r\n"
|
|
L"-sfxheader <file> : Self-extractor core (exe or dll) file\r\n"
|
|
L"-infname <file> : Pass in a pre-built downlevel installer .INF\r\n"
|
|
L" (If not specified, the tool will build an appropriate\r\n"
|
|
L" .INF that installs everything to system32)\r\n"
|
|
L"-overwrite : Forces the tool to output the built package even\r\n"
|
|
L" if the file <outputname> already exists\r\n";
|
|
|
|
wprintf(wchHelp, pcwszName);
|
|
}
|
|
|
|
|
|
PCWSTR
|
|
GenerateInfFromTo(
|
|
PCWSTR pcwszPackageDir
|
|
)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int __cdecl wmain(int argc, PCWSTR* argv)
|
|
{
|
|
PCWSTR pcwszHeaderName = NULL;
|
|
PCWSTR pcwszPackageDir = NULL;
|
|
PCWSTR pcwszInfName = NULL;
|
|
PCWSTR pcwszOutputName = NULL;
|
|
bool fAllocatedInfName = false;
|
|
bool fOverwrite = false;
|
|
FILE_HANDLE Created;
|
|
HANDLE hResource = INVALID_HANDLE_VALUE;;
|
|
int iResult = -1;
|
|
BOOL fOk = FALSE;
|
|
|
|
//
|
|
// Determine parameters
|
|
//
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if ((*argv[i] == L'-') || (*argv[i] == L'/'))
|
|
{
|
|
PCWSTR psz = argv[i] + 1;
|
|
|
|
if (lstrcmpiW(psz, L"sourcedir") == 0)
|
|
{
|
|
if (pcwszPackageDir != NULL)
|
|
{
|
|
DoHelp(argv[0]);
|
|
wprintf(L"Already specified a packaging directory.\r\n");
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
pcwszPackageDir = argv[++i];
|
|
}
|
|
}
|
|
else if (lstrcmpiW(psz, L"sfxheader") == 0)
|
|
{
|
|
if (pcwszHeaderName != NULL)
|
|
{
|
|
DoHelp(argv[0]);
|
|
wprintf(L"Already specified the SFX header.\r\n");
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
pcwszHeaderName = argv[++i];
|
|
}
|
|
}
|
|
else if (lstrcmpiW(psz, L"infname") == 0)
|
|
{
|
|
if (pcwszInfName != NULL)
|
|
{
|
|
DoHelp(argv[0]);
|
|
wprintf(L"Already specified an INF name, only one at a time.\r\n");
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
pcwszInfName = argv[++i];
|
|
}
|
|
}
|
|
else if (lstrcmpiW(psz, L"overwrite") == 0)
|
|
{
|
|
fOverwrite = true;
|
|
}
|
|
else
|
|
{
|
|
DoHelp(argv[0]);
|
|
wprintf(L"\r\nUnknown option %ls\r\n", psz);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pcwszOutputName != NULL)
|
|
{
|
|
DoHelp(argv[0]);
|
|
wprintf(L"\r\nToo many output names, just one please.\r\n");
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
pcwszOutputName = argv[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((pcwszHeaderName == NULL) || (pcwszPackageDir == NULL) || (pcwszOutputName == NULL))
|
|
{
|
|
DoHelp(argv[0]);
|
|
wprintf(L"\r\nMust specify at least the sfx header file, source directory, and output\r\n");
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Ok, let's generate the cabinet file from the source directory. When this is done,
|
|
// it'll be sitting in memory. We then have to copy the source header to the target
|
|
// filename and do some work to update its resources
|
|
//
|
|
if (!CreateCabFromPath(pcwszPackageDir, &Created, pcwszInfName))
|
|
{
|
|
wprintf(L"Error %ld creating cabinet from %ls\r\n", ::GetLastError(), pcwszPackageDir);
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Copy the file over
|
|
//
|
|
if (!CopyFileW(pcwszHeaderName, pcwszOutputName, fOverwrite ? FALSE : TRUE))
|
|
{
|
|
const DWORD dwError = ::GetLastError();
|
|
if (dwError == ERROR_FILE_EXISTS)
|
|
{
|
|
wprintf(L"Error, file %ls already exists. Move it or delete it, please.\r\n", pcwszOutputName);
|
|
}
|
|
else
|
|
{
|
|
wprintf(L"Error %ld copying SFX header %ls to output %ls\r\n", dwError, pcwszHeaderName, pcwszOutputName);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// The output contains the cabinet data above in its resources at a certain name.
|
|
// Do this by updating the resource table now.
|
|
//
|
|
hResource = BeginUpdateResourceW(pcwszOutputName, FALSE);
|
|
|
|
if ((hResource == NULL) || (hResource == INVALID_HANDLE_VALUE))
|
|
{
|
|
wprintf(L"Unable to open %ls for resource updating, error 0x%08lx\n", pcwszOutputName, GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
fOk = UpdateResourceW(hResource, SXSEXPRESS_RESOURCE_TYPE, SXSEXPRESS_RESOURCE_NAME, 0, Created.CabinetFile.pvFileBase, Created.CabinetFile.cbWritten);
|
|
|
|
if (!EndUpdateResourceW(hResource, !fOk))
|
|
{
|
|
wprintf(L"Failed updating resources, error code 0x%08lx\n", GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
iResult = 0;
|
|
Exit:
|
|
|
|
if (fAllocatedInfName && pcwszInfName)
|
|
HeapFree(GetProcessHeap(), 0, (PVOID)pcwszInfName);
|
|
|
|
return iResult;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
Our very own mini-cabarc code to create sxsexpress packages
|
|
|
|
*/
|
|
CHAR WorkingBufferOne[2048];
|
|
CHAR WorkingBufferTwo[2048];
|
|
|
|
|
|
|
|
FNFCIFILEPLACED(s_FilePlaced) { printf("Placed file %s (%d bytes)\n", pszFile, cbFile); return 1; }
|
|
FNFCIALLOC(s_Alloc) { return HeapAlloc(GetProcessHeap(), 0, cb); }
|
|
FNFCIFREE(s_Free) { HeapFree(GetProcessHeap(), 0, memory); }
|
|
|
|
FNFCIOPEN(s_OpenFile)
|
|
{
|
|
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
|
|
FILE_HANDLE *hHandle = NULL;
|
|
INT_PTR ipRetVal = -1;
|
|
int iConversion = 0, iActual = 0;
|
|
PWSTR pszConverted = NULL;
|
|
|
|
hHandle = (FILE_HANDLE*)s_Alloc(sizeof(FILE_HANDLE));
|
|
if (!hHandle) {
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
ZeroMemory(hHandle, sizeof(*hHandle));
|
|
|
|
//
|
|
// All strings that get here are in UTF-8. Convert them back.
|
|
//
|
|
iConversion = MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, NULL, 0);
|
|
if (iConversion == 0)
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
pszConverted = (PWSTR)s_Alloc(sizeof(WCHAR) * (iConversion + 1));
|
|
if (NULL == pszConverted)
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
iActual = MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, pszConverted, iConversion);
|
|
if ((iActual == 0) || (iActual > iConversion))
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
pszConverted[iConversion] = UNICODE_NULL;
|
|
}
|
|
|
|
|
|
if (oflag & _O_CREAT)
|
|
{
|
|
//
|
|
// The actual cabinet file becomes a large block of committed bytes
|
|
//
|
|
if (lstrcmpiA(pContext->pszCabFileName, pszFile) == 0)
|
|
{
|
|
if (pContext->CabinetFileHandle)
|
|
{
|
|
pContext->fWasError = true;
|
|
*err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
|
|
}
|
|
else
|
|
{
|
|
hHandle->dwFileType = FILETYPE_CABINET;
|
|
hHandle->CabinetFile.pvFileBase = VirtualAlloc(NULL, DEFAULT_RESERVE_SIZE, MEM_RESERVE, PAGE_READWRITE);
|
|
hHandle->CabinetFile.cbReserved = DEFAULT_RESERVE_SIZE;
|
|
hHandle->CabinetFile.cbCommitted = 0;
|
|
hHandle->CabinetFile.pvCursor = hHandle->CabinetFile.pvFileBase;
|
|
hHandle->CabinetFile.cbWritten = 0;
|
|
pContext->CabinetFileHandle = hHandle;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Open a real file on disk to write - probably a temp file
|
|
//
|
|
hHandle->dwFileType = FILETYPE_DISKFILE;
|
|
hHandle->hDiskFile = CreateFileW(pszConverted, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
|
|
if (hHandle->hDiskFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Read a file from disk
|
|
//
|
|
else
|
|
{
|
|
hHandle->dwFileType = FILETYPE_DISKFILE;
|
|
wprintf(L"Opening %ls\n", pszConverted);
|
|
hHandle->hDiskFile = CreateFileW(pszConverted, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
if (hHandle->hDiskFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
ipRetVal = (INT_PTR)hHandle;
|
|
hHandle = NULL;
|
|
Exit:
|
|
if (pszConverted)
|
|
s_Free(pszConverted);
|
|
|
|
if (hHandle)
|
|
{
|
|
s_Free(hHandle);
|
|
}
|
|
|
|
return ipRetVal;
|
|
}
|
|
|
|
FNFCIREAD(s_ReadFile)
|
|
{
|
|
FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
|
|
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
|
|
DWORD cbReadSize = 0;
|
|
UINT uiRetVal = -1;
|
|
|
|
if (pFile->dwFileType == FILETYPE_DISKFILE)
|
|
{
|
|
if (!ReadFile(pFile->hDiskFile, memory, cb, &cbReadSize, NULL))
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (pFile->dwFileType == FILETYPE_CABINET)
|
|
{
|
|
ULONG_PTR ulCursor = (ULONG_PTR)pFile->CabinetFile.pvCursor;
|
|
const ULONG_PTR ulTotalSize = (ULONG_PTR)pFile->CabinetFile.pvFileBase + pFile->CabinetFile.cbWritten;
|
|
|
|
if (ulCursor >= ulTotalSize)
|
|
{
|
|
cbReadSize = 0;
|
|
}
|
|
else
|
|
{
|
|
if (cb > (ulTotalSize - ulCursor))
|
|
{
|
|
cb = (ulTotalSize - ulCursor);
|
|
}
|
|
|
|
memcpy(memory, pFile->CabinetFile.pvCursor, cb);
|
|
pFile->CabinetFile.pvCursor = (PVOID)(ulCursor + cb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pContext->fWasError = true;
|
|
*err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
|
|
goto Exit;
|
|
}
|
|
|
|
uiRetVal = cbReadSize;
|
|
Exit:
|
|
return uiRetVal;
|
|
}
|
|
|
|
FNFCIWRITE(s_WriteFile)
|
|
{
|
|
FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
|
|
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
|
|
UINT uiRetVal = -1;
|
|
|
|
//
|
|
// Cabinet in-memory files are in large chunks. If the current cursor
|
|
// plus the requested write size goes beyond the commit limit, then
|
|
// reserve some more pages.
|
|
//
|
|
if (pFile->dwFileType == FILETYPE_CABINET)
|
|
{
|
|
ULONG_PTR ulpRequiredCommitted = cb;
|
|
|
|
ulpRequiredCommitted += ((ULONG_PTR)pFile->CabinetFile.pvCursor - (ULONG_PTR)pFile->CabinetFile.pvFileBase);
|
|
|
|
//
|
|
// If the cursor is NULL, or the cursor plus the write request goes beyond
|
|
// the committed range..
|
|
//
|
|
if (ulpRequiredCommitted > pFile->CabinetFile.cbCommitted)
|
|
{
|
|
PVOID pvReserveState;
|
|
|
|
//
|
|
// Just attempt to commit all the pages required
|
|
//
|
|
pvReserveState = VirtualAlloc(pFile->CabinetFile.pvFileBase, ulpRequiredCommitted, MEM_COMMIT, PAGE_READWRITE);
|
|
|
|
//
|
|
// If this fails, reserve another blob and schmooze the bytes over from the
|
|
// original allocation
|
|
//
|
|
if (pvReserveState == NULL)
|
|
{
|
|
const SIZE_T cbNewReserve = (ulpRequiredCommitted * 2) + (PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
|
|
|
// Reserve the new block
|
|
pvReserveState = VirtualAlloc(NULL, cbNewReserve, MEM_RESERVE, PAGE_READWRITE);
|
|
if (!pvReserveState)
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
// Commit what's necessary
|
|
if (!VirtualAlloc(pvReserveState, ulpRequiredCommitted, MEM_COMMIT, PAGE_READWRITE))
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
// Shoof data over
|
|
memcpy(pvReserveState, pFile->CabinetFile.pvFileBase, pFile->CabinetFile.cbWritten);
|
|
|
|
// Adjust file cursor, if necessary
|
|
if (pFile->CabinetFile.pvCursor == NULL)
|
|
pFile->CabinetFile.pvCursor = pvReserveState;
|
|
else
|
|
pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pvReserveState) + ((ULONG_PTR)pFile->CabinetFile.pvCursor - (ULONG_PTR)pFile->CabinetFile.pvFileBase));
|
|
|
|
// Free old reservation
|
|
VirtualFree(pFile->CabinetFile.pvFileBase, 0, MEM_RELEASE);
|
|
|
|
// Reset stuff
|
|
pFile->CabinetFile.pvFileBase = pvReserveState;
|
|
pFile->CabinetFile.cbReserved = cbNewReserve;
|
|
pFile->CabinetFile.cbCommitted = ulpRequiredCommitted;
|
|
}
|
|
else
|
|
{
|
|
pFile->CabinetFile.cbCommitted = ulpRequiredCommitted;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Okay, now just copy to the cursor and be done
|
|
//
|
|
memcpy(pFile->CabinetFile.pvCursor, memory, cb);
|
|
pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pFile->CabinetFile.pvCursor) + cb);
|
|
|
|
// If we wrote more than the high-water marker, reset it
|
|
if (ulpRequiredCommitted > pFile->CabinetFile.cbWritten)
|
|
{
|
|
pFile->CabinetFile.cbWritten = ulpRequiredCommitted;
|
|
}
|
|
|
|
uiRetVal = cb;
|
|
}
|
|
else if (pFile->dwFileType == FILETYPE_DISKFILE)
|
|
{
|
|
DWORD cbWritten;
|
|
|
|
if (!WriteFile(pFile->hDiskFile, memory, cb, &cbWritten, NULL))
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
uiRetVal = cbWritten;
|
|
}
|
|
else
|
|
{
|
|
pContext->fWasError = true;
|
|
*err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER);
|
|
uiRetVal = -1;
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
return uiRetVal;
|
|
}
|
|
|
|
FNFCICLOSE(s_Close)
|
|
{
|
|
FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
|
|
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
|
|
|
|
if (pFile->dwFileType == FILETYPE_DISKFILE)
|
|
{
|
|
CloseHandle(pFile->hDiskFile);
|
|
}
|
|
else if (pFile->dwFileType == FILETYPE_CABINET)
|
|
{
|
|
if (pFile == pContext->CabinetFileHandle)
|
|
pFile = NULL;
|
|
else
|
|
VirtualFree(pFile->CabinetFile.pvFileBase, 0, MEM_RELEASE);
|
|
}
|
|
|
|
if (pFile)
|
|
s_Free(pFile);
|
|
return 0;
|
|
}
|
|
|
|
FNFCISEEK(s_Seek)
|
|
{
|
|
FILE_HANDLE *pFile = (FILE_HANDLE*)hf;
|
|
long result;
|
|
|
|
if (pFile->dwFileType == FILETYPE_CABINET)
|
|
{
|
|
switch (seektype) {
|
|
|
|
case SEEK_SET:
|
|
if (dist < 0) dist = 0;
|
|
pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pFile->CabinetFile.pvFileBase) + dist);
|
|
break;
|
|
|
|
case SEEK_CUR:
|
|
pFile->CabinetFile.pvCursor = (PVOID)(((INT_PTR)pFile->CabinetFile.pvCursor) + dist);
|
|
break;
|
|
|
|
case SEEK_END:
|
|
pFile->CabinetFile.pvCursor = (PVOID)(((INT_PTR)pFile->CabinetFile.pvFileBase) + pFile->CabinetFile.cbWritten + dist);
|
|
break;
|
|
}
|
|
|
|
result = (long)((LONG_PTR)pFile->CabinetFile.pvCursor - (LONG_PTR)pFile->CabinetFile.pvFileBase);
|
|
}
|
|
else if (pFile->dwFileType == FILETYPE_DISKFILE)
|
|
{
|
|
result = (long)SetFilePointer(pFile->hDiskFile, dist, NULL, seektype);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
FNFCIDELETE(s_Delete)
|
|
{
|
|
FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv;
|
|
*err = 0;
|
|
|
|
if (lstrcmpiA(pContext->pszCabFileName, pszFile) == 0)
|
|
return 0;
|
|
|
|
if (!DeleteFileA(pszFile))
|
|
{
|
|
pContext->ObtainErrorContext();
|
|
*err = ::GetLastError();
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
FNFCIGETTEMPFILE(s_TempFile)
|
|
{
|
|
WCHAR chTemp[MAX_PATH];
|
|
WCHAR chTempOutput[MAX_PATH];
|
|
int i;
|
|
|
|
GetTempPathW(NUMBER_OF(chTemp), chTemp);
|
|
GetTempFileNameW(chTemp, L"SXP", 0, chTempOutput);
|
|
|
|
i = WideCharToMultiByte(CP_UTF8, 0, chTempOutput, -1, pszTempName, cbTempName, NULL, NULL);
|
|
return ((i > 0) && (i < cbTempName));
|
|
}
|
|
|
|
FNFCIGETNEXTCABINET(s_GetNextCabinet) { return FALSE; }
|
|
|
|
//
|
|
// All files are UTF-8 encoded
|
|
//
|
|
FNFCIGETOPENINFO(s_GetOpenInfo)
|
|
{
|
|
*pdate = *ptime = 0;
|
|
*pattribs = _A_NAME_IS_UTF;
|
|
return s_OpenFile(pszName, _O_BINARY|_O_RDONLY, _A_NAME_IS_UTF, err, pv);
|
|
}
|
|
|
|
FNFCISTATUS(s_StatusCallback)
|
|
{
|
|
switch (typeStatus)
|
|
{
|
|
case statusFile:
|
|
case statusFolder:
|
|
return 1;
|
|
break;
|
|
|
|
case statusCabinet:
|
|
return cb2;
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
BOOL
|
|
CabPathWorkerCallback(
|
|
HFCI hCabinet,
|
|
PWSTR pwszWorkingTemp,
|
|
SIZE_T cchTotalTemp,
|
|
PCWSTR pwszBaseInCabinet,
|
|
WIN32_FIND_DATAW *pFindData
|
|
)
|
|
{
|
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
|
BOOL fResult = FALSE;
|
|
SIZE_T ccOriginalLength = wcslen(pwszWorkingTemp);
|
|
SIZE_T cchBasePathLength;
|
|
|
|
if (pwszBaseInCabinet[0] == L'\\')
|
|
pwszBaseInCabinet++;
|
|
|
|
cchBasePathLength = wcslen(pwszBaseInCabinet);
|
|
|
|
//
|
|
// Ensure that the string is slash-terminated
|
|
//
|
|
if (pwszWorkingTemp[ccOriginalLength-1] != L'\\')
|
|
{
|
|
pwszWorkingTemp[ccOriginalLength++] = L'\\';
|
|
pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
|
|
}
|
|
|
|
wcscpy(pwszWorkingTemp + ccOriginalLength, L"*");
|
|
hFind = ::FindFirstFileW(pwszWorkingTemp, pFindData);
|
|
|
|
if (hFind != NULL) do
|
|
{
|
|
pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
|
|
|
|
if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
wcscat(pwszWorkingTemp, pFindData->cFileName);
|
|
|
|
if ((wcscmp(pFindData->cFileName, L".") == 0) ||
|
|
(wcscmp(pFindData->cFileName, L"..") == 0))
|
|
continue;
|
|
|
|
if (!CabPathWorkerCallback(hCabinet, pwszWorkingTemp, cchTotalTemp, pwszBaseInCabinet, pFindData))
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
INSTALLED_FILE *pFile = NULL;
|
|
PWSTR pwszCursor = NULL;
|
|
SIZE_T cchRequired;
|
|
|
|
cchRequired = (cchBasePathLength + 1) + (wcslen(pFindData->cFileName) + 1);
|
|
|
|
pFile = (INSTALLED_FILE*)s_Alloc(sizeof(INSTALLED_FILE) + (sizeof(WCHAR) * cchRequired));
|
|
|
|
if (pFile == NULL)
|
|
goto Exit;
|
|
|
|
pwszCursor = (PWSTR)(pFile + 1);
|
|
wcscpy(pwszCursor, pwszBaseInCabinet);
|
|
pFile->pwszSubDir = pwszCursor;
|
|
pwszCursor[cchBasePathLength] = UNICODE_NULL;
|
|
|
|
pwszCursor += cchBasePathLength + 1;
|
|
wcscpy(pwszCursor, pFindData->cFileName);
|
|
pFile->pwszFileName = pwszCursor;
|
|
|
|
InsertHeadList(&InstalledFiles, &pFile->Links);
|
|
|
|
wcscat(pwszWorkingTemp, pFindData->cFileName);
|
|
|
|
i = WideCharToMultiByte(
|
|
CP_UTF8,
|
|
0,
|
|
pwszBaseInCabinet,
|
|
-1,
|
|
WorkingBufferTwo,
|
|
sizeof(WorkingBufferTwo),
|
|
NULL,
|
|
NULL);
|
|
|
|
if ((i == 0) || (i > sizeof(WorkingBufferTwo)))
|
|
goto Exit;
|
|
|
|
i = WideCharToMultiByte(
|
|
CP_UTF8,
|
|
0,
|
|
pwszWorkingTemp,
|
|
-1,
|
|
WorkingBufferOne,
|
|
sizeof(WorkingBufferOne),
|
|
NULL,
|
|
NULL);
|
|
|
|
if ((i == 0) || (i > sizeof(WorkingBufferOne)))
|
|
goto Exit;
|
|
|
|
|
|
|
|
if (!FCIAddFile(
|
|
hCabinet,
|
|
WorkingBufferOne,
|
|
WorkingBufferTwo,
|
|
FALSE,
|
|
s_GetNextCabinet,
|
|
s_StatusCallback,
|
|
s_GetOpenInfo,
|
|
tcompTYPE_LZX | tcompLZX_WINDOW_HI))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
while (::FindNextFileW(hFind, pFindData));
|
|
|
|
if (::GetLastError() != ERROR_NO_MORE_FILES)
|
|
goto Exit;
|
|
|
|
|
|
fResult = TRUE;
|
|
Exit:
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
FindClose(hFind);
|
|
|
|
pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL;
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WriteFormatted(
|
|
INT_PTR File,
|
|
PVOID pvContext,
|
|
PCWSTR fmt,
|
|
...
|
|
)
|
|
{
|
|
static WCHAR wchDump[1024];
|
|
va_list va;
|
|
int iError;
|
|
int iResult;
|
|
|
|
va_start(va, fmt);
|
|
iResult = _vsnwprintf(wchDump, NUMBER_OF(wchDump), fmt, va);
|
|
va_end(va);
|
|
|
|
if ((iResult >= 0) && (iResult < NUMBER_OF(wchDump)))
|
|
wchDump[iResult] = UNICODE_NULL;
|
|
else
|
|
iResult = 0;
|
|
|
|
return (s_WriteFile(File, wchDump, iResult * sizeof(WCHAR), &iError, pvContext) != -1);
|
|
}
|
|
|
|
BOOL
|
|
CreateCabFromPath(
|
|
PCWSTR pcwszBasePath,
|
|
FILE_HANDLE *CreatedCabinet,
|
|
PCWSTR pcwszInfName
|
|
)
|
|
{
|
|
CHAR chCabName[] = "::::";
|
|
ERF erf;
|
|
FCI_CONTEXT_OBJECT Context = { false, 0, NULL, chCabName };
|
|
const ULONG CabMaxSize = 0x04000000;
|
|
CCAB CabInfo = { CabMaxSize, CabMaxSize };
|
|
HFCI hCabinet = NULL;
|
|
WIN32_FIND_DATAW FindData;
|
|
BOOL fResult = FALSE;
|
|
HANDLE hFindFile = INVALID_HANDLE_VALUE;
|
|
static WCHAR wchBasePath[2048];
|
|
int i = 0;
|
|
|
|
InitializeListHead(&InstalledFiles);
|
|
|
|
lstrcpyA(CabInfo.szDisk, "SxsExpressCompressedCabinet");
|
|
lstrcpyA(CabInfo.szCab, "::");
|
|
lstrcpyA(CabInfo.szCabPath, "::");
|
|
|
|
wcscpy(wchBasePath, pcwszBasePath);
|
|
|
|
//
|
|
// Start off by creating a compression object
|
|
//
|
|
hCabinet = FCICreate(
|
|
&erf,
|
|
s_FilePlaced,
|
|
s_Alloc,
|
|
s_Free,
|
|
s_OpenFile,
|
|
s_ReadFile,
|
|
s_WriteFile,
|
|
s_Close,
|
|
s_Seek,
|
|
s_Delete,
|
|
s_TempFile,
|
|
&CabInfo,
|
|
(void*)&Context);
|
|
|
|
fResult = CabPathWorkerCallback(hCabinet, wchBasePath, NUMBER_OF(wchBasePath), wchBasePath + wcslen(wchBasePath), &FindData);
|
|
|
|
if (!fResult)
|
|
{
|
|
printf("Error adding cabinet files, %d (code %d)\n", erf.erfOper, erf.erfType);
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// No name given? Cons one up from the files found
|
|
//
|
|
if (pcwszInfName == NULL)
|
|
{
|
|
CHAR chTempFile[MAX_PATH];
|
|
int iError;
|
|
INT_PTR p;
|
|
LIST_ENTRY *pLink;
|
|
s_TempFile(chTempFile, NUMBER_OF(chTempFile), &Context);
|
|
p = s_OpenFile(chTempFile, _O_CREAT, 0, &iError, &Context);
|
|
WORD wBom = 0xfeff;
|
|
|
|
//
|
|
// This is a UCS-2 file.
|
|
//
|
|
s_WriteFile(p, &wBom, sizeof(wBom), &iError, &Context);
|
|
|
|
//
|
|
// Spit out the source and target paths
|
|
//
|
|
WriteFormatted(p, &Context, L"[FileEntries]\r\n");
|
|
pLink = InstalledFiles.Flink;
|
|
while (pLink && (pLink != &InstalledFiles))
|
|
{
|
|
INSTALLED_FILE *pFile = CONTAINING_RECORD(pLink, INSTALLED_FILE, Links);
|
|
WriteFormatted(p, &Context, L"%ls\\%ls;<SysDir>\\%ls\r\n", pFile->pwszSubDir, pFile->pwszFileName, pFile->pwszFileName);
|
|
pLink = pLink->Flink;
|
|
}
|
|
|
|
s_Close(p, &iError, &Context);
|
|
|
|
strcpy(WorkingBufferOne, chTempFile);
|
|
}
|
|
else
|
|
{
|
|
i = WideCharToMultiByte(CP_UTF8, 0, pcwszInfName, -1, WorkingBufferOne, sizeof(WorkingBufferOne), NULL, NULL);
|
|
if ((i == 0) || (i > sizeof(WorkingBufferOne)))
|
|
{
|
|
wprintf(L"Error converting INF file %ls name to UTF-8\r\n", pcwszInfName);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
fResult = FCIAddFile(
|
|
hCabinet,
|
|
WorkingBufferOne,
|
|
INF_SPECIAL_NAME,
|
|
FALSE,
|
|
s_GetNextCabinet,
|
|
s_StatusCallback,
|
|
s_GetOpenInfo,
|
|
tcompTYPE_LZX | tcompLZX_WINDOW_HI);
|
|
|
|
FCIFlushFolder(hCabinet, s_GetNextCabinet, s_StatusCallback);
|
|
FCIFlushCabinet(hCabinet, FALSE, s_GetNextCabinet, s_StatusCallback);
|
|
|
|
fResult = TRUE;
|
|
Exit:
|
|
if (hCabinet != NULL)
|
|
FCIDestroy(hCabinet);
|
|
|
|
*CreatedCabinet = *Context.CabinetFileHandle;
|
|
|
|
return fResult;
|
|
}
|