#include "stdinc.h" #include "st.h" #include "msiinstall.h" CDeque 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 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(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 iter(&g_MSIInstallTest); for (iter.Reset(); iter.More(); iter.Next()) { iter->Stop = true; } } void WaitForMSIInstallTestThreads() { CDequeIterator 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(*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(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 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; }