456 lines
16 KiB
C++
456 lines
16 KiB
C++
#include "stdinc.h"
|
|
#include "st.h"
|
|
#include "msiinstall.h"
|
|
|
|
CDeque<MSIINSTALLTEST_THREAD_PROC_DATA, offsetof(MSIINSTALLTEST_THREAD_PROC_DATA, Linkage)> g_MSIInstallTest;
|
|
|
|
#define DATA_DIRECTORY_NAME L"msiinstall"
|
|
#define SLASH_DATA_DIRECTORY_SLASH_STAR L"\\" DATA_DIRECTORY_NAME L"\\*"
|
|
#define SLASH_DATA_DIRECTORY_SLASH L"\\" DATA_DIRECTORY_NAME L"\\"
|
|
#define INI_FILE L"msiinstall.ini"
|
|
#define SLASH_INI_FILE L"\\msiinstall.ini"
|
|
|
|
BOOL InitializeMSIInstallTest()
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
CFindFile hFind;
|
|
WIN32_FIND_DATAW wfd;
|
|
CStringBuffer TempDirectory;
|
|
CDequeIterator<MSIINSTALLTEST_THREAD_PROC_DATA, offsetof(MSIINSTALLTEST_THREAD_PROC_DATA, Linkage)> iter(&g_MSIInstallTest);
|
|
CStringBuffer IniFilePath;
|
|
WCHAR buf[MAX_PATH];
|
|
DWORD bufSize = NUMBER_OF(buf);
|
|
WCHAR FileKey[10]; // at most, there are 10^10 files in the assembly, including manifest
|
|
DWORD rSize;
|
|
STRINGBUFFER_LINKAGE * pTinyStringBuffer = NULL;
|
|
MSIINSTALLTEST_THREAD_PROC_DATA *pData = NULL;
|
|
|
|
|
|
if (!TempDirectory.Win32Assign(BaseDirectory))
|
|
goto Exit;
|
|
|
|
if (!TempDirectory.Win32AppendPathElement(DATA_DIRECTORY_NAME, NUMBER_OF(DATA_DIRECTORY_NAME) - 1))
|
|
goto Exit;
|
|
|
|
if ((wfd.dwFileAttributes = ::GetFileAttributesW(TempDirectory)) == 0xffffffff
|
|
&& (wfd.dwFileAttributes = ::FusionpGetLastWin32Error()) == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
printf("no %ls tests, skipping\n", DATA_DIRECTORY_NAME);
|
|
FN_SUCCESSFUL_EXIT();
|
|
}
|
|
|
|
if (!TempDirectory.Win32AppendPathElement(L"*", 1))
|
|
goto Exit;
|
|
|
|
hFind = ::FindFirstFileW(TempDirectory, &wfd);
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
::ReportFailure("Failed to find any files matching \"%ls\"\n", static_cast<PCWSTR>(TempDirectory));
|
|
goto Exit;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
CSmallStringBuffer TempString;
|
|
CSmallStringBuffer BaseDirectory2;
|
|
|
|
if (FusionpIsDotOrDotDot(wfd.cFileName))
|
|
goto Skip;
|
|
|
|
if (!BaseDirectory2.Win32Assign(BaseDirectory))
|
|
goto Exit;
|
|
|
|
if (!BaseDirectory2.Win32AppendPathElement(DATA_DIRECTORY_NAME, NUMBER_OF(DATA_DIRECTORY_NAME) - 1))
|
|
goto Exit;
|
|
|
|
if (!BaseDirectory2.Win32AppendPathElement(wfd.cFileName, wcslen(wfd.cFileName)))
|
|
goto Exit;
|
|
|
|
if ((pData = new MSIINSTALLTEST_THREAD_PROC_DATA) == NULL)
|
|
{
|
|
::FusionpSetLastWin32Error(ERROR_OUTOFMEMORY);
|
|
::ReportFailure("Failed to allocate MSIINSTALLTEST_THREAD_PROC_DATA\n");
|
|
goto Exit;
|
|
}
|
|
|
|
if (!IniFilePath.Win32Assign(BaseDirectory2))
|
|
goto Exit;
|
|
if (!IniFilePath.Win32AppendPathElement(INI_FILE, NUMBER_OF(INI_FILE) - 1))
|
|
goto Exit;
|
|
|
|
if (GetFileAttributesW(IniFilePath) == DWORD(-1))
|
|
{
|
|
::ReportFailure("Failed to find msiinstall.ini from %s.\n", IniFilePath);
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// get manifest filename
|
|
//
|
|
rSize = GetPrivateProfileStringW(L"general", L"manifest", L"", buf, bufSize, IniFilePath);
|
|
if ((rSize == bufSize - 1) || (rSize == 0))
|
|
{
|
|
::ReportFailure("manifest filename in %s is erroneous, either too long or empty\n", IniFilePath);
|
|
goto Exit;
|
|
}
|
|
IFW32FALSE_EXIT(pData->ManifestFileName.Win32Assign(buf, rSize));
|
|
|
|
//
|
|
// get AssemblyNamefromDarwin
|
|
//
|
|
rSize = GetPrivateProfileStringW(L"general", L"AssemblyNameFromDarwin", L"", buf, bufSize, IniFilePath);
|
|
if ((rSize == bufSize - 1) || (rSize == 0))
|
|
{
|
|
::ReportFailure("assemblyname from darwin in %s is erroneous, either too long or empty\n", IniFilePath);
|
|
goto Exit;
|
|
}
|
|
IFW32FALSE_EXIT(pData->AssemblyNameFromDarwin.Win32Assign(buf, rSize));
|
|
|
|
//
|
|
// get the SourceFile directory from ini or use the current directory
|
|
//
|
|
rSize = GetPrivateProfileStringW(L"general", L"AssemblySourceFileDirectory", L"", buf, bufSize, IniFilePath);
|
|
if (rSize == bufSize - 1)
|
|
goto Exit;
|
|
if (rSize != 0)
|
|
IFW32FALSE_EXIT(pData->AssemblySourceDirectory.Win32Assign(buf, rSize));
|
|
else
|
|
IFW32FALSE_EXIT(pData->AssemblySourceDirectory.Win32Assign(BaseDirectory2));
|
|
|
|
for (DWORD i=0;;i++)
|
|
{
|
|
swprintf(FileKey, L"%1d", i); // make FileKey in WSTR
|
|
rSize = GetPrivateProfileStringW(L"files", FileKey, L"", buf, bufSize, IniFilePath);
|
|
if (rSize == bufSize - 1)// the value string in .ini is too long
|
|
goto Exit;
|
|
if ( rSize == 0 ) // get all
|
|
break;
|
|
|
|
pTinyStringBuffer = new STRINGBUFFER_LINKAGE;
|
|
if (pTinyStringBuffer == NULL)
|
|
{
|
|
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
|
|
goto Exit;
|
|
}
|
|
|
|
IFW32FALSE_EXIT(pTinyStringBuffer->Str.Win32Assign(buf, rSize));
|
|
pData->FileNameOfAssemblyList.AddToTail(pTinyStringBuffer);
|
|
pTinyStringBuffer = NULL;
|
|
}
|
|
|
|
g_MSIInstallTest.AddToTail(pData);
|
|
pData = NULL;
|
|
|
|
Skip:
|
|
if (!::FindNextFileW(hFind, &wfd))
|
|
{
|
|
if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_FILES)
|
|
{
|
|
::ReportFailure("Error iterating over assemblies\n");
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (iter.Reset(); iter.More(); iter.Next())
|
|
{
|
|
if (!iter->Thread.Win32CreateThread(&MSIInstallTestThreadProc, iter.Current()))
|
|
{
|
|
::ReportFailure("Error launching install thread\n");
|
|
goto Exit;
|
|
}
|
|
TotalThreads += 1;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
if (pData)
|
|
delete pData;
|
|
|
|
if (pTinyStringBuffer != NULL)
|
|
delete pTinyStringBuffer;
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
void RequestShutdownMSIInstallTestThreads()
|
|
{
|
|
CDequeIterator<MSIINSTALLTEST_THREAD_PROC_DATA, offsetof(MSIINSTALLTEST_THREAD_PROC_DATA, Linkage)> iter(&g_MSIInstallTest);
|
|
|
|
for (iter.Reset(); iter.More(); iter.Next())
|
|
{
|
|
iter->Stop = true;
|
|
}
|
|
|
|
}
|
|
void WaitForMSIInstallTestThreads()
|
|
{
|
|
CDequeIterator<MSIINSTALLTEST_THREAD_PROC_DATA, offsetof(MSIINSTALLTEST_THREAD_PROC_DATA, Linkage)> iter(&g_MSIInstallTest);
|
|
|
|
for (iter.Reset(); iter.More(); iter.Next())
|
|
{
|
|
DWORD WaitResult = ::WaitForSingleObject(iter->Thread, INFINITE);
|
|
switch (WaitResult)
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
break;
|
|
case WAIT_FAILED:
|
|
::ReportFailure("MSIInstall :: Failed to WaitForSingleObject.\n");
|
|
break;
|
|
default:
|
|
::FusionpSetLastWin32Error(WaitResult);
|
|
::ReportFailure("MSIInstall :: Failed to WaitForSingleObject.\n");
|
|
break;
|
|
}
|
|
iter->Thread.Win32Close();
|
|
}
|
|
}
|
|
|
|
void CleanupMSIInstallTest()
|
|
{
|
|
g_MSIInstallTest.ClearAndDeleteAll();
|
|
}
|
|
|
|
HRESULT Helper_WriteStream(CSmallStringBuffer * pFileNameBuf,
|
|
IStream *pStream)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
LPBYTE pBuf[0x4000];
|
|
DWORD cbBuf = 0x4000;
|
|
DWORD dwWritten = 0;
|
|
DWORD cbRead = 0;
|
|
HANDLE hf = INVALID_HANDLE_VALUE;
|
|
|
|
hf = ::CreateFileW(static_cast<PCWSTR>(*pFileNameBuf), GENERIC_READ, FILE_SHARE_READ,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
if (hf == INVALID_HANDLE_VALUE){
|
|
hr = HRESULT_FROM_WIN32 (GetLastError());
|
|
goto Exit;
|
|
}
|
|
|
|
while (::ReadFile(hf, pBuf, cbBuf, &cbRead, NULL) && cbRead){
|
|
hr = pStream->Write(pBuf, cbRead, &dwWritten);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
}
|
|
|
|
if (! SUCCEEDED(hr = pStream->Commit(0)))
|
|
goto Exit;
|
|
|
|
CloseHandle(hf);
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
MSIInstallTestThreadProc(
|
|
LPVOID pvData
|
|
)
|
|
{
|
|
DWORD dwReturnValue = ERROR_INTERNAL_ERROR;
|
|
DWORD WaitResult = 0;
|
|
MSIINSTALLTEST_THREAD_PROC_DATA *pData = reinterpret_cast<MSIINSTALLTEST_THREAD_PROC_DATA*>(pvData);
|
|
IAssemblyCache * pAsmCache = NULL;
|
|
SIZE_T nPathLen = pData->AssemblySourceDirectory.Cch();
|
|
IAssemblyCacheItem * pCacheItem = NULL;
|
|
IStream * pStream = NULL;
|
|
IAssemblyName * pAssemblyName = NULL;
|
|
WCHAR buf[MAX_PATH];
|
|
DWORD bufSize = MAX_PATH;
|
|
|
|
|
|
HRESULT hr;
|
|
|
|
InterlockedIncrement(&ThreadsWaiting);
|
|
WaitResult = WaitForSingleObject(ResumeThreadsEvent, INFINITE);
|
|
switch (WaitResult)
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
break;
|
|
case WAIT_FAILED:
|
|
dwReturnValue = ::FusionpGetLastWin32Error();
|
|
::ReportFailure("Failed to WaitForSingleObject.\n");
|
|
goto Exit;
|
|
default:
|
|
dwReturnValue = WaitResult;
|
|
::FusionpSetLastWin32Error(WaitResult);
|
|
::ReportFailure("Failed to WaitForSingleObject.\n");
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
if (CreateAssemblyCache(&pAsmCache, 0) != S_OK)
|
|
{
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed CreateAssemblyCache with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
while (!pData->Stop)
|
|
{
|
|
//Create AssemblyCache and AssemblyCacheItem
|
|
if ((hr = pAsmCache->CreateAssemblyCacheItem(0, NULL, &pCacheItem, NULL)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed CreateAssemblyCacheItem with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// create manifests for assembly item
|
|
//
|
|
if ((hr = pCacheItem->CreateStream(0, pData->ManifestFileName, STREAM_FORMAT_WIN32_MANIFEST, 0, &pStream, NULL)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed CreateStream for manifest with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
|
|
}
|
|
pData->AssemblySourceDirectory.Left(nPathLen);
|
|
|
|
if (! pData->AssemblySourceDirectory.Win32AppendPathElement(pData->ManifestFileName))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if ( (hr = Helper_WriteStream(&pData->AssemblySourceDirectory, pStream)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed WriteStream for manifest with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
pStream->Release();
|
|
|
|
CDequeIterator<STRINGBUFFER_LINKAGE, offsetof(STRINGBUFFER_LINKAGE, Linkage)> iter(&pData->FileNameOfAssemblyList);
|
|
for (iter.Reset(); iter.More(); iter.Next())
|
|
{
|
|
if ((hr = pCacheItem->CreateStream(0, iter.Current()->Str, STREAM_FORMAT_WIN32_MODULE, 0, &pStream, NULL)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed CreateStream for module \"%ls\" with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
iter->Str,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
pData->AssemblySourceDirectory.Left(nPathLen);
|
|
if (! pData->AssemblySourceDirectory.Win32AppendPathElement(iter.Current()->Str))
|
|
goto Exit;
|
|
if ( (hr = Helper_WriteStream(&pData->AssemblySourceDirectory, pStream)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed CreateStream for module \"%ls\" with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
iter->Str,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
|
|
}
|
|
pStream->Release();
|
|
}
|
|
|
|
if ((hr = pCacheItem->Commit(0, NULL)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed Commit AssemblyCacheItem with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// uninstall the same assembly using CAssemblyName and CAssemblyCache->UninstallAssembly
|
|
|
|
if ( (hr = CreateAssemblyNameObject(&pAssemblyName, pData->AssemblyNameFromDarwin, CANOF_PARSE_DISPLAY_NAME,NULL)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed CreateAssemblyName for %s with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
pData->AssemblyNameFromDarwin,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
if ( (hr = pAssemblyName->GetDisplayName(buf, &bufSize, 0)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) failed IsAssemblyInstalled for %s with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
pData->AssemblyNameFromDarwin,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
if ( wcscmp(pData->AssemblyNameFromDarwin, buf) != 0)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
if ((hr = pAsmCache->UninstallAssembly(0, buf, NULL, NULL)) != S_OK)
|
|
{
|
|
::FusionpSetLastErrorFromHRESULT(hr);
|
|
::ReportFailure("[%lx.%lx] MSIInstall(\"%ls\", 0x%lx) Cache->UninstallAssemblyfailed for %s with gle %lu \n",
|
|
SxStressToolGetCurrentProcessId(), SxStressToolGetCurrentThreadId(),
|
|
pData->AssemblySourceDirectory,
|
|
pData->ManifestFileName,
|
|
buf,
|
|
::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
::WaitForSingleObject(StopEvent, pData->Sleep);
|
|
}
|
|
|
|
dwReturnValue = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
Exit:
|
|
dwReturnValue = ::FusionpGetLastWin32Error();
|
|
|
|
Cleanup:
|
|
if (pAsmCache)
|
|
pAsmCache->Release();
|
|
|
|
if (pCacheItem)
|
|
pCacheItem->Release();
|
|
|
|
if (pStream)
|
|
pStream->Release();
|
|
|
|
if (pAssemblyName)
|
|
pAssemblyName->Release();
|
|
|
|
return dwReturnValue;
|
|
}
|