|
|
/*
Copyright (c) Microsoft Corporation */ /*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell Some general issues in this file:
We should stop using slists.
Shouldn't wfp/sfc work even under stress? Should we preallocate memory during boot? */ #include "stdinc.h"
#include "util.h"
#include "xmlparser.hxx"
#include "fusioneventlog.h"
#include "hashfile.h"
#include "recover.h"
#include "filestream.h"
#include "cassemblyrecoveryinfo.h"
#include "sxsprotect.h"
#include "strongname.h"
#include "sxsexceptionhandling.h"
#include "sxssfcscan.h"
#define MANIFEST_FILE_EXTENSION (L".manifest")
#define FILE_ELEMENT_NAME (L"file")
#define HASH_ATTRIB_NAME (L"hash")
#define FILE_ATTRIB_NAME (L"name")
#define HASHALG_ATTRIB_NAME (L"hashalg")
class CProtectionRequestList; CProtectionRequestList* g_ProtectionRequestList; HANDLE g_hSxsLoginEvent; BOOL s_fIsSfcAcceptingNotifications = TRUE;
BOOL CRecoveryJobTableEntry::Initialize() { FN_PROLOG_WIN32
//
// Creates an event that other callers should wait on - manual reset, not
// currently signalled.
//
m_Subscriber = 0; m_fSuccessValue = FALSE; IFW32NULL_EXIT(m_EventInstallingAssemblyComplete = ::CreateEventW(NULL, TRUE, FALSE, NULL));
FN_EPILOG }
BOOL CRecoveryJobTableEntry::StartInstallation() { FN_PROLOG_WIN32
//
// Clear the event (if it wasn't already cleared.
//
IFW32FALSE_ORIGINATE_AND_EXIT(::ResetEvent(m_EventInstallingAssemblyComplete));
FN_EPILOG }
BOOL SxspEnsureCatalogStillPresentForManifest( IN const CBaseStringBuffer& buffManifestPath, OUT BOOL &rfStillPresent ) { FN_PROLOG_WIN32
rfStillPresent = FALSE;
CStringBuffer buffCatalogPath; bool fExist = false;
IFW32FALSE_EXIT(buffCatalogPath.Win32Assign(buffManifestPath));
IFW32FALSE_EXIT( buffCatalogPath.Win32ChangePathExtension( FILE_EXTENSION_CATALOG, FILE_EXTENSION_CATALOG_CCH, eAddIfNoExtension)); IFW32FALSE_EXIT(::SxspDoesFileExist(SXSP_DOES_FILE_EXIST_FLAG_CHECK_FILE_ONLY | SXSP_DOES_FILE_EXIST_FLAG_INCLUDE_NETWORK_ERRORS, buffCatalogPath, fExist)); if (fExist) { rfStillPresent = TRUE; } FN_EPILOG }
BOOL CRecoveryJobTableEntry::InstallationComplete( BOOL bDoneOk, SxsRecoveryResult Result, DWORD dwRecoveryLastError ) { FN_PROLOG_WIN32
m_Result = Result; m_fSuccessValue = bDoneOk; m_dwLastError = dwRecoveryLastError;
//
// This will tell all the people waiting that we're done and that they
// should capture the exit code and exit out.
//
IFW32FALSE_ORIGINATE_AND_EXIT(::SetEvent(m_EventInstallingAssemblyComplete));
//
// We wait for all our subscribers to go away (ie: for them to capture an
// install code and success value.)
//
while (m_Subscriber) { Sleep(50); }
FN_EPILOG }
BOOL CRecoveryJobTableEntry::WaitUntilCompleted( SxsRecoveryResult &rResult, BOOL &rfSucceededValue, DWORD &rdwErrorResult ) { FN_PROLOG_WIN32
DWORD WaitResult = 0;
rfSucceededValue = FALSE;
//
// Here we join up to the existing installation routine. We up the number
// of people waiting before entering the wait. I wish there was a better
// way of doing this, something like a built-in kernel object that we can
// raise a count on (like a semaphore), and have another thread lower a
// count on, and someone can wait on the internal count being zero. Yes,
// I could implement this by hand using something with another event or
// two, but that's not the point.
//
::SxspInterlockedIncrement(&m_Subscriber);
//
// Hang about forever until another thread is done installing
//
IFW32FALSE_ORIGINATE_AND_EXIT((WaitResult = ::WaitForSingleObject(m_EventInstallingAssemblyComplete, INFINITE)) != WAIT_FAILED);
//
// Capture values once the installation is done, return them to the caller.
//
rResult = m_Result; rdwErrorResult = m_dwLastError; rfSucceededValue = m_fSuccessValue;
//
// And indicate that we're complete.
//
::SxspInterlockedDecrement(&m_Subscriber);
FN_EPILOG }
CRecoveryJobTableEntry::~CRecoveryJobTableEntry() { //
// We're done with the event, so close it (release refcount, we don't want lots of these
// just sitting around.)
//
if ((m_EventInstallingAssemblyComplete != NULL) && (m_EventInstallingAssemblyComplete != INVALID_HANDLE_VALUE)) { ::CloseHandle(m_EventInstallingAssemblyComplete); m_EventInstallingAssemblyComplete = INVALID_HANDLE_VALUE; } }
//
// This is our holy grail of sxs protection lists. Don't fidget with
// this listing. Note that we've also got only one. This is because
// right now, there's only one entry to be had (a), and (b) because we
// will fill in the sxs directory on the fly at runtime.
//
SXS_PROTECT_DIRECTORY s_SxsProtectionList[] = { { {0}, 0, SXS_PROTECT_RECURSIVE, SXS_PROTECT_FILTER_DEFAULT } };
const SIZE_T s_SxsProtectionListCount = NUMBER_OF(s_SxsProtectionList);
BOOL SxspConstructProtectionList();
BOOL WINAPI SxsProtectionGatherEntriesW( PCSXS_PROTECT_DIRECTORY *prgpProtectListing, SIZE_T *pcProtectEntries ) /*++
This is called by Sfc to gather the entries that we want them to watch. At the moment, it's a 'proprietary' listing of all the directories and flags we want them to set on their call to the directory-change watcher. Mayhaps we should work with them to find out exactly what they want us to pass (probably the name plus a PVOID, which is fine) and then go from there. --*/ { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess);
CStringBuffer sbTemp;
if (prgpProtectListing) *prgpProtectListing = NULL;
if (pcProtectEntries) *pcProtectEntries = 0;
PARAMETER_CHECK(prgpProtectListing); PARAMETER_CHECK(pcProtectEntries);
//
// This is try/caught because we don't want something here to
// cause WinLogon to bugcheck the system when it AV's.
//
IFW32FALSE_EXIT(::SxspConstructProtectionList());
//
// We really have only one entry, so let's go and edit the first entry to
// be the main sxs store directory.
//
IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(sbTemp)); /*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell truncating string copy, need growing buffer */ wcsncpy( s_SxsProtectionList[0].pwszDirectory, sbTemp, NUMBER_OF(s_SxsProtectionList[0].pwszDirectory)); //
// Zero out the last character, just in case wcsncpy didn't do it for us
//
s_SxsProtectionList[0].pwszDirectory[NUMBER_OF(s_SxsProtectionList[0].pwszDirectory) - 1] = L'\0';
//
// Shh, don't tell anyone, but the cookie is actually this structure!
//
for (DWORD dw = 0; dw < s_SxsProtectionListCount; dw++) { s_SxsProtectionList[dw].pvCookie = &(s_SxsProtectionList[dw]); }
*prgpProtectListing = s_SxsProtectionList; *pcProtectEntries = s_SxsProtectionListCount;
fSuccess = TRUE; Exit: return fSuccess; }
BOOL SxspExpandLongPath( IN OUT CBaseStringBuffer &rbuffPathToLong ) /*++
Takes in a short path (c:\foo\bar\bloben~1.zot) and sends back out the full path (c:\foo\bar\blobenheisen.zotamax) if possible. Returns FALSE if the path could not be expanded (most likely because the path on-disk is no longer available.)
--*/ { FN_PROLOG_WIN32
CStringBuffer buffPathName; CStringBufferAccessor buffAccess; SIZE_T cchNeededChars = 0;
IFW32ZERO_EXIT( cchNeededChars = ::GetLongPathNameW( static_cast<PCWSTR>(rbuffPathToLong), NULL, 0));
IFW32FALSE_EXIT(buffPathName.Win32ResizeBuffer(cchNeededChars, eDoNotPreserveBufferContents)); buffAccess.Attach(&buffPathName);
IFW32ZERO_EXIT( cchNeededChars = ::GetLongPathNameW( rbuffPathToLong, buffAccess, static_cast<DWORD>(buffAccess.GetBufferCch())));
/*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell this can fail in reality; it is not an internal error; this is because the general try, grow buffer, try again, contains a race condition, the second try will not necessarily work */ INTERNAL_ERROR_CHECK(cchNeededChars <= buffAccess.GetBufferCch());
IFW32FALSE_EXIT(rbuffPathToLong.Win32Assign(buffPathName));
FN_EPILOG }
BOOL SxspResolveAssemblyManifestPath( const CBaseStringBuffer &rbuffAssemblyDirectoryName, CBaseStringBuffer &rbuffManifestPath ) { FN_PROLOG_WIN32 CStringBuffer buffAssemblyRootDir; BOOL fLooksLikeAssemblyName = FALSE;
rbuffManifestPath.Clear();
//
// If the string doesn't look like an assembly name, then it's an
// invalid parameter. This is somewhat heavy-handed, as the caller(s)
// to this function will be entirely hosed if they haven't checked to
// see if the string is really an assembly name. Make sure that all
// clients of this,
//
IFW32FALSE_EXIT(::SxspLooksLikeAssemblyDirectoryName(rbuffAssemblyDirectoryName, fLooksLikeAssemblyName)); PARAMETER_CHECK(fLooksLikeAssemblyName);
IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffAssemblyRootDir)); IFW32FALSE_EXIT(rbuffManifestPath.Win32Format( L"%ls\\Manifests\\%ls.%ls", static_cast<PCWSTR>(buffAssemblyRootDir), static_cast<PCWSTR>(rbuffAssemblyDirectoryName), FILE_EXTENSION_MANIFEST));
FN_EPILOG }
CProtectionRequestRecord::CProtectionRequestRecord() : m_dwAction(0), m_pvProtection(NULL), m_ulInRecoveryMode(0), m_pParent(NULL), m_bIsManPathResolved(FALSE), m_bInitialized(FALSE) { }
BOOL CProtectionRequestRecord::GetManifestPath( CBaseStringBuffer &rsbManPath ) { BOOL bOk = FALSE; FN_TRACE_WIN32(bOk);
rsbManPath.Clear();
if (!m_bIsManPathResolved) { m_bIsManPathResolved = ::SxspResolveAssemblyManifestPath( m_sbAssemblyDirectoryName, m_sbManifestPath); }
if (m_bIsManPathResolved) { IFW32FALSE_EXIT(rsbManPath.Win32Assign(m_sbManifestPath)); } else { goto Exit; }
bOk = TRUE; Exit: return bOk; }
//
// Close down this request record.
//
CProtectionRequestRecord::~CProtectionRequestRecord() { if (m_bInitialized) { ClearList(); m_bInitialized = FALSE; } }
VOID CProtectionRequestRecord::ClearList() /*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell stop using slists */ { CStringListEntry *pTop;
while (pTop = (CStringListEntry*)::SxspInterlockedPopEntrySList(&m_ListHeader)) { FUSION_DELETE_SINGLETON(pTop); } }
BOOL CProtectionRequestRecord::AddSubFile( const CBaseStringBuffer &rbuffRelChange ) { CStringListEntry *pairing = NULL; BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
if (!::SxspInterlockedCompareExchange(&m_ulInRecoveryMode, 0, 0)) { IFALLOCFAILED_EXIT(pairing = new CStringListEntry); IFW32FALSE_EXIT(pairing->m_sbText.Win32Assign(rbuffRelChange));
//
// Add it to the list (atomically, to boot!)
//
::SxspInterlockedPushEntrySList(&m_ListHeader, pairing); pairing = NULL; }
fSuccess = TRUE; Exit: if (pairing) { //
// The setup or something like that failed - release it here.
//
FUSION_DELETE_SINGLETON(pairing); } return fSuccess; }
BOOL CProtectionRequestRecord::Initialize( const CBaseStringBuffer &rsbAssemblyDirectoryName, const CBaseStringBuffer &rsbKeyString, CProtectionRequestList* ParentList, PVOID pvRequestRecord, DWORD dwAction ) { BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
m_sbAssemblyDirectoryName.Clear(); m_sbKeyValue.Clear(); m_sbManifestPath.Clear(); ::SxspInitializeSListHead(&m_ListHeader);
PARAMETER_CHECK(ParentList != NULL); PARAMETER_CHECK(pvRequestRecord != NULL);
m_pParent = ParentList; m_dwAction = dwAction; m_pvProtection = (PSXS_PROTECT_DIRECTORY)pvRequestRecord;
IFW32FALSE_EXIT(m_sbAssemblyStore.Win32Assign(m_pvProtection->pwszDirectory, (m_pvProtection->pwszDirectory != NULL) ? ::wcslen(m_pvProtection->pwszDirectory) : 0)); IFW32FALSE_EXIT(m_sbAssemblyDirectoryName.Win32Assign(rsbAssemblyDirectoryName)); IFW32FALSE_EXIT(m_sbKeyValue.Win32Assign(rsbKeyString));
fSuccess = TRUE; Exit: return fSuccess; }
//
// Bad form: This returns a BOOL to indicate whether or not this class
// was able to pop something off the list. It's no good to ask "is the
// list empty," since that's not provided with this list class.
//
BOOL CProtectionRequestRecord::PopNextFileChange(CBaseStringBuffer &Dest) { BOOL fFound = FALSE;
Dest.Clear(); CStringListEntry *pPairing = reinterpret_cast<CStringListEntry*>(::SxspInterlockedPopEntrySList(&m_ListHeader));
if (pPairing != NULL) { //
// NTRAID#NTBUG9-591177-2002/03/31-JayKrell
// missing error check
//
Dest.Win32Assign(pPairing->m_sbText); FUSION_DELETE_SINGLETON(pPairing); fFound = TRUE; }
return fFound; }
// NTRAID#NTBUG9-591177-2002/03/31-JayKrell
// "If a thread dies in winlogon, and no kernel debugger is attached, does it
// bugcheck the system?" - From 'The Zen of Dodgy Code'
//
DWORD CProtectionRequestList::ProtectionNormalThreadProc(PVOID pvParam) { BOOL fSuccess = FALSE; CProtectionRequestRecord *pRequestRecord = NULL; CProtectionRequestList *pThis = NULL;
pRequestRecord = static_cast<CProtectionRequestRecord*>(pvParam); if (pRequestRecord) { pThis = pRequestRecord->GetParent(); } if (pThis) { fSuccess = pThis->ProtectionNormalThreadProcWrapped(pRequestRecord); }
return static_cast<DWORD>(fSuccess); }
BOOL CProtectionRequestList::Initialize() { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess);
ASSERT(m_pInternalList == NULL);
IFW32FALSE_EXIT(::FusionpInitializeCriticalSectionAndSpinCount(&m_cSection, INITIALIZE_CRITICAL_SECTION_AND_SPIN_COUNT_ALLOCATE_NOW)); IFW32FALSE_EXIT(::FusionpInitializeCriticalSectionAndSpinCount(&m_cInstallerCriticalSection, INITIALIZE_CRITICAL_SECTION_AND_SPIN_COUNT_ALLOCATE_NOW)); IFW32FALSE_EXIT(::SxspAtExit(this));
IFALLOCFAILED_EXIT(m_pInternalList = new COurInternalTable); IFW32FALSE_EXIT(m_pInternalList->Initialize());
IFALLOCFAILED_EXIT(m_pInstallsTable = new CInstallsInProgressTable); IFW32FALSE_EXIT(m_pInstallsTable->Initialize());
//
// Manifest protection stuff
//
::SxspInitializeSListHead(&m_ManifestEditList);
/*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell This should be IF32NULL_EXIT(::CreateEventW(NULL, TRUE, FALSE, NULL)); */ m_hManifestEditHappened = ::CreateEventW(NULL, TRUE, FALSE, NULL); if (m_hManifestEditHappened == NULL) { TRACE_WIN32_FAILURE_ORIGINATION(CreateEventW); goto Exit; }
ASSERT(m_pInternalList != NULL); ASSERT(m_pInstallsTable != NULL);
fSuccess = TRUE; Exit: return fSuccess; }
CProtectionRequestList::CProtectionRequestList() : m_pInternalList(NULL), m_pInstallsTable(NULL), m_hManifestEditHappened(INVALID_HANDLE_VALUE), m_ulIsAThreadServicingManifests(0)
{ }
CProtectionRequestList::~CProtectionRequestList() { ::DeleteCriticalSection(&m_cSection); ::DeleteCriticalSection(&m_cInstallerCriticalSection);
COurInternalTable *pTempListing = m_pInternalList; CInstallsInProgressTable *pInstalls = m_pInstallsTable;
m_pInternalList = NULL; m_pInternalList = NULL;
if (pTempListing != NULL) { pTempListing->Clear(this, &CProtectionRequestList::ClearProtectionItems); FUSION_DELETE_SINGLETON(pTempListing); }
if (pInstalls != NULL) { pInstalls->ClearNoCallback(); FUSION_DELETE_SINGLETON(pInstalls); }
}
BOOL CProtectionRequestList::IsSfcIgnoredStoreSubdir(PCWSTR wsz) { FN_TRACE();
ASSERT(m_arrIgnorableSubdirs);
for (SIZE_T i = 0; i < m_cIgnorableSubdirs; i++) { if (::FusionpEqualStringsI(m_arrIgnorableSubdirs[i], wsz)) { return TRUE; } }
return FALSE; }
BOOL CProtectionRequestList::AttemptRemoveItem(CProtectionRequestRecord *AttemptRemoval) { //
// This quickly indicates that the progress is complete and just returns to
// the caller.
//
const CBaseStringBuffer &sbKey = AttemptRemoval->GetChangeBasePath(); BOOL fSuccess = FALSE; CSxsLockCriticalSection lock(m_cSection);
FN_TRACE_WIN32(fSuccess); PARAMETER_CHECK(AttemptRemoval != NULL);
IFW32FALSE_EXIT(lock.Lock()); //
// This item is no longer in service. Please check the item and
// try your call again. The nice thing is that Remove on CStringPtrTable
// knows to delete the value lickety-split before returning. This isn't
// such a bad thing, but it's ... different.
//
m_pInternalList->Remove(sbKey, NULL);
fSuccess = TRUE; Exit: return fSuccess; }
BOOL CProtectionRequestList::AddRequest( PSXS_PROTECT_DIRECTORY pProtect, PCWSTR pcwszDirName, SIZE_T cchName, DWORD dwAction ) { BOOL fSuccess = FALSE; bool fIsManifestEdit = false; BOOL fIsIgnorable = FALSE; BOOL fNewAddition = FALSE; CSmallStringBuffer sbTemp; CSmallStringBuffer sbAssemblyDirectoryName; CSmallStringBuffer sbRequestText; CSmallStringBuffer buffManifestsDirectoryName; CSmallStringBuffer buffManifestsShortDirectoryName; CProtectionRequestRecord *pRecord = NULL; CProtectionRequestRecord *ppFoundInTable = NULL; CSxsLockCriticalSection lock(m_cSection);
FN_TRACE_WIN32(fSuccess);
//
// The key here is the first characters (up to the first slash) in the
// notification name. If there's no slash in the notification name, then
// we can ignore this change request, since nothing important happened.
//
IFW32FALSE_EXIT(sbTemp.Win32Assign(pProtect->pwszDirectory, (pProtect->pwszDirectory != NULL) ? ::wcslen(pProtect->pwszDirectory) : 0)); IFW32FALSE_EXIT(sbRequestText.Win32Assign(pcwszDirName, cchName)); IFW32FALSE_EXIT(sbRequestText.Win32GetFirstPathElement(sbAssemblyDirectoryName));
fIsIgnorable = IsSfcIgnoredStoreSubdir(sbAssemblyDirectoryName);
fIsManifestEdit = !::FusionpStrCmpI(sbAssemblyDirectoryName, MANIFEST_ROOT_DIRECTORY_NAME);
if (!fIsManifestEdit) { DWORD dwTemp = 0; CStringBufferAccessor acc;
IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffManifestsDirectoryName)); IFW32FALSE_EXIT(buffManifestsDirectoryName.Win32AppendPathElement(MANIFEST_ROOT_DIRECTORY_NAME, NUMBER_OF(MANIFEST_ROOT_DIRECTORY_NAME) - 1));
acc.Attach(&buffManifestsShortDirectoryName); IFW32ZERO_ORIGINATE_AND_EXIT(dwTemp = ::GetShortPathNameW(buffManifestsDirectoryName, acc.GetBufferPtr(), acc.GetBufferCchAsDWORD())); /*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell possibility of infinite loop; "only try twice". */ while (dwTemp >= acc.GetBufferCchAsDWORD()) { acc.Detach(); IFW32FALSE_EXIT(buffManifestsShortDirectoryName.Win32ResizeBuffer(dwTemp, eDoNotPreserveBufferContents)); acc.Attach(&buffManifestsShortDirectoryName); IFW32ZERO_ORIGINATE_AND_EXIT(dwTemp = ::GetShortPathNameW(buffManifestsDirectoryName, acc.GetBufferPtr(), acc.GetBufferCchAsDWORD())); }
acc.Detach();
// Ok all that work and now we finally have the manifests directory name as a short string. Let's abuse buffManifestsShortDirectoryName
// to just hold the short name of the manifests directory.
IFW32FALSE_EXIT(buffManifestsShortDirectoryName.Win32GetLastPathElement(buffManifestsDirectoryName));
if (::FusionpCompareStrings( buffManifestsDirectoryName, sbAssemblyDirectoryName, true) == 0) { // Convert the directory name to its proper long form
IFW32FALSE_EXIT(sbAssemblyDirectoryName.Win32Assign(MANIFEST_ROOT_DIRECTORY_NAME, NUMBER_OF(MANIFEST_ROOT_DIRECTORY_NAME) - 1)); fIsManifestEdit = true; } }
if ((fIsIgnorable) && (!fIsManifestEdit)) { #if DBG
//
// We get a lot of these.
//
if (::FusionpStrCmpI(sbAssemblyDirectoryName, L"InstallTemp") != 0) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - %ls is ignorable (%d)\n", __FUNCTION__, static_cast<PCWSTR>(sbAssemblyDirectoryName), fIsIgnorable ); } #endif
fSuccess = TRUE; goto Exit; }
//
// The "key" value here is the full path to the assembly that we're protecting.
// This is what we'll store in the table.
//
IFW32FALSE_EXIT(sbTemp.Win32AppendPathElement(sbAssemblyDirectoryName));
if (fIsManifestEdit) { CStringListEntry *pEntry = NULL; ULONG ulWasSomeoneServicing = 0;
//
// Create a new manifest edit slot, add it to the list of items that are being
// serviced.
//
IFALLOCFAILED_EXIT(pEntry = new CStringListEntry); if (!pEntry->m_sbText.Win32Assign(sbRequestText)) { TRACE_WIN32_FAILURE(m_sbText.Win32Assign); FUSION_DELETE_SINGLETON(pEntry); pEntry = NULL; goto Exit; } ::SxspInterlockedPushEntrySList(&m_ManifestEditList, pEntry); pEntry = NULL;
//
// Tell anyone that's listening that we have a new manifest edit here
//
SetEvent(m_hManifestEditHappened);
//
// See if someone is servicing the queue at the moment
//
ulWasSomeoneServicing = ::SxspInterlockedCompareExchange(&m_ulIsAThreadServicingManifests, 1, 0);
if (!ulWasSomeoneServicing) { // Missing error checking!
QueueUserWorkItem(ProtectionManifestThreadProc, (PVOID)this, WT_EXECUTEDEFAULT); }
fSuccess = TRUE; goto Exit; }
//
// At this point, we need to see if the chunk that we identified is currently
// in the list of things to be validated. If not, it gets added and a thread
// is spun off to work on it. Otherwise, an entry may already exist in a
// thread that's being serviced, and so it needs to be deleted.
//
IFW32FALSE_EXIT(lock.Lock()); m_pInternalList->Find(sbTemp, ppFoundInTable);
if (!ppFoundInTable) { IFALLOCFAILED_EXIT(pRecord = new CProtectionRequestRecord); #if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - Creating protection record for %ls:\n" "\tKey = %ls\n" "\tManifest? = %d\n" "\tProtectionRecord = %p\n" "\tAction = %d\n", __FUNCTION__, static_cast<PCWSTR>(sbAssemblyDirectoryName), static_cast<PCWSTR>(sbTemp), fIsManifestEdit, pProtect, dwAction); #endif
IFW32FALSE_EXIT(pRecord->Initialize( sbAssemblyDirectoryName, sbTemp, this, pProtect, dwAction));
//
// Add this first request to be serviced, then spin a thread to start it.
//
m_pInternalList->Insert(sbTemp, pRecord); fNewAddition = TRUE;
//
// A little bookkeeping ... so we don't accidentally use it later.
//
ppFoundInTable = pRecord; pRecord = NULL; }
//
// If we actually got something into the table...
//
if (ppFoundInTable) { ppFoundInTable->AddSubFile(sbRequestText);
//
// If this is a new thing in the table (ie: we inserted it ourselves)
// then we should go and spin up a thread for it.
//
if (fNewAddition) { /*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell We should check for an error from QueueUserWorkItem, like out of memory. */ QueueUserWorkItem(ProtectionNormalThreadProc, (PVOID)ppFoundInTable, WT_EXECUTEDEFAULT); } }
fSuccess = TRUE;
Exit: DWORD dwLastError = ::FusionpGetLastWin32Error(); if (pRecord) { //
// If this is still set, something bad happened in the process of trying to
// create/find this object. Delete it here.
//
FUSION_DELETE_SINGLETON(pRecord); pRecord = NULL; } ::FusionpSetLastWin32Error(dwLastError); return fSuccess; }
static BYTE p_bProtectionListBuffer[ sizeof(CProtectionRequestList) * 2 ]; PCWSTR CProtectionRequestList::m_arrIgnorableSubdirs[] = { ASSEMBLY_INSTALL_TEMP_DIR_NAME, POLICY_ROOT_DIRECTORY_NAME, REGISTRY_BACKUP_ROOT_DIRECTORY_NAME }; SIZE_T CProtectionRequestList::m_cIgnorableSubdirs = NUMBER_OF(CProtectionRequestList::m_arrIgnorableSubdirs);
BOOL SxspIsSfcIgnoredStoreSubdir(PCWSTR pwsz) { return CProtectionRequestList::IsSfcIgnoredStoreSubdir(pwsz); }
BOOL SxspConstructProtectionList() { CProtectionRequestList *pTemp = NULL; BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
//
// This only gets called once, if they know what's good for them.
//
ASSERT(!g_ProtectionRequestList);
/*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell The comments here are bogus. Placement new actually cannot fail due to out of memory, only the constructor throwing an exception. */ //
// Construct - this should never fail, but if it does, there's trouble.
//
pTemp = new (&p_bProtectionListBuffer) CProtectionRequestList; if (pTemp == NULL) { ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS: %s() - Failed placement new of CProtectionRequestList????\n", __FUNCTION__); ::FusionpSetLastWin32Error(FUSION_WIN32_ALLOCFAILED_ERROR); TRACE_WIN32_FAILURE_ORIGINATION(new(CProtectionRequestList)); goto Exit; } IFW32FALSE_EXIT(pTemp->Initialize());
g_ProtectionRequestList = pTemp; pTemp = NULL;
//
// Create our logon event.
//
IFW32NULL_EXIT(g_hSxsLoginEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL));
fSuccess = TRUE; Exit:
if (pTemp) { //
// If this is still set, then something failed somewhere in the construction
// code for the protection system. We don't want to delete it, per-se, but
// we need to just null out everything.
//
g_ProtectionRequestList = NULL; pTemp = NULL; g_hSxsLoginEvent = NULL; }
return fSuccess; }
BOOL WINAPI SxsProtectionNotifyW( PVOID pvCookie, PCWSTR wsChangeText, SIZE_T cchChangeText, DWORD dwChangeAction ) { #if YOU_ARE_HAVING_ANY_WIERDNESS_WITH_SFC_AND_SXS
return TRUE; #else
BOOL fSuccess = FALSE;
if (::FusionpDbgWouldPrintAtFilterLevel(FUSION_DBG_LEVEL_FILECHANGENOT)) { const USHORT Length = (cchChangeText > UNICODE_STRING_MAX_CHARS) ? UNICODE_STRING_MAX_BYTES : ((USHORT) (cchChangeText * sizeof(WCHAR))); const UNICODE_STRING u = { Length, Length, const_cast<PWSTR>(wsChangeText) };
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_FILECHANGENOT, "[%lx.%lx: %wZ] SXS FCN (cookie, action, text): %p, %lu, \"%wZ\"\n", HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess), HandleToULong(NtCurrentTeb()->ClientId.UniqueThread), &NtCurrentPeb()->ProcessParameters->ImagePathName, pvCookie, dwChangeAction, &u); }
//
// If we're not accepting notifications, then quit out immediately.
//
if (!s_fIsSfcAcceptingNotifications) { fSuccess = TRUE; goto Exit; }
//
// Having done this in the wrong order is also a Bad Bad Thing
//
ASSERT2_NTC(g_ProtectionRequestList != NULL, "SXS.DLL: Protection - Check order of operations, g_ProtectionRequestList is invalid!!\n");
fSuccess = g_ProtectionRequestList->AddRequest( (PSXS_PROTECT_DIRECTORY)pvCookie, wsChangeText, cchChangeText, dwChangeAction);
fSuccess = TRUE; Exit: return fSuccess; #endif
}
BOOL CProtectionRequestList::ProtectionManifestThreadProcNoSEH(LPVOID pvParam) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess);
CProtectionRequestList *pThis = NULL;
PARAMETER_CHECK(pvParam != NULL);
pThis = reinterpret_cast<CProtectionRequestList*>(pvParam); IFW32FALSE_EXIT(pThis->ProtectionManifestThreadProcWrapped()); fSuccess = TRUE; Exit: return fSuccess; }
DWORD CProtectionRequestList::ProtectionManifestThreadProc(LPVOID pvParam) { BOOL fSuccess = FALSE; fSuccess = ProtectionManifestThreadProcNoSEH(pvParam); return static_cast<DWORD>(fSuccess); }
class CProtectionManifestSingleManifestWorkerLocals { public: CProtectionManifestSingleManifestWorkerLocals() { } ~CProtectionManifestSingleManifestWorkerLocals() { }
CStringBuffer sbAssemblyDirectoryName; CStringBuffer sbManifestPath; CAssemblyRecoveryInfo RecoverInfo; };
BOOL CProtectionRequestList::ProtectionManifestSingleManifestWorker( const CStringListEntry *pEntry ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess);
SxsRecoveryResult RecoverResult = Recover_Unknown; HashValidateResult HashValidResult = HashValidate_OtherProblems; BOOL fCatalogPresent = FALSE; bool fNoAssembly = false;
CSmartPtr<CProtectionManifestSingleManifestWorkerLocals> Locals; IFW32FALSE_EXIT(Locals.Win32Allocate(__FILE__, __LINE__)); CStringBuffer &sbAssemblyDirectoryName = Locals->sbAssemblyDirectoryName; CStringBuffer &sbManifestPath = Locals->sbManifestPath; CAssemblyRecoveryInfo &RecoverInfo = Locals->RecoverInfo;
PARAMETER_CHECK(pEntry);
//
// Calculate the name of the assembly based on the middling part of
// the string
//
IFW32FALSE_EXIT(sbAssemblyDirectoryName.Win32Assign(pEntry->m_sbText)); IFW32FALSE_EXIT(sbAssemblyDirectoryName.Win32RemoveFirstPathElement()); IFW32FALSE_EXIT(sbAssemblyDirectoryName.Win32ClearPathExtension());
if (sbAssemblyDirectoryName.Cch() == 0) FN_SUCCESSFUL_EXIT();
::Sleep(5000); // wait 5 seconds for the offender to probably let go of their handle on the file
//
// Try mashing this into an assembly name/recovery info
//
IFW32FALSE_EXIT(RecoverInfo.AssociateWithAssembly(sbAssemblyDirectoryName, fNoAssembly));
// If we couldn't figure out what this was for, we have to ignore it.
if (fNoAssembly) { #if DBG
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - File \"%ls\" in the manifest directory modified, but could not be mapped to an assembly. IGNORING.\n", __FUNCTION__, static_cast<PCWSTR>(pEntry->m_sbText)); #endif
FN_SUCCESSFUL_EXIT(); }
//
// Now that we have the recovery info..
//
if (!RecoverInfo.GetHasCatalog()) { #if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - Assembly %ls was in the registry, but without a catalog so we aren't protecting it\n", __FUNCTION__, static_cast<PCWSTR>(sbAssemblyDirectoryName)); #endif
FN_SUCCESSFUL_EXIT(); }
//
// Resolve the manifest path, then validate
//
IFW32FALSE_EXIT(::SxspResolveAssemblyManifestPath(sbAssemblyDirectoryName, sbManifestPath));
IFW32FALSE_EXIT( ::SxspVerifyFileHash( SVFH_RETRY_LOGIC_SIMPLE, sbManifestPath, RecoverInfo.GetSecurityInformation().GetManifestHash(), CALG_SHA1, HashValidResult)); IFW32FALSE_EXIT(::SxspEnsureCatalogStillPresentForManifest(sbManifestPath, fCatalogPresent));
//
// Reinstall needed?
//
if ((HashValidResult != HashValidate_Matches) || !fCatalogPresent) IFW32FALSE_EXIT(this->PerformRecoveryOfAssembly(RecoverInfo, RecoverResult));
fSuccess = TRUE; Exit: return fSuccess; }
BOOL CProtectionRequestList::ProtectionManifestThreadProcWrapped() { BOOL fSuccess = FALSE; BOOL bFoundItemsThisTimeAround; CStringListEntry *pNextItem = NULL; DWORD dwWaitResult = 0;
FN_TRACE_WIN32(fSuccess);
bFoundItemsThisTimeAround = FALSE;
do { //
// Yes, mother, we hear you.
//
::ResetEvent(m_hManifestEditHappened);
//
// Pull the next thing off the list and service it.
//
while (pNextItem = (CStringListEntry*)::SxspInterlockedPopEntrySList(&m_ManifestEditList)) { bFoundItemsThisTimeAround = TRUE;
if (!this->ProtectionManifestSingleManifestWorker(pNextItem)) ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - Processing work item %p failed\n", __FUNCTION__, pNextItem);
FUSION_DELETE_SINGLETON(pNextItem); }
//
// Loaf about for a bit and see if anyone else has stuff for us to do
//
dwWaitResult = ::WaitForSingleObject(m_hManifestEditHappened, 3000); if (dwWaitResult == WAIT_TIMEOUT) { ::SxspInterlockedExchange(&m_ulIsAThreadServicingManifests, 0); break; } else if (dwWaitResult == WAIT_OBJECT_0) { continue; } else { TRACE_WIN32_FAILURE_ORIGINATION(WaitForSingleObject); goto Exit; }
} while (true);
fSuccess = TRUE; Exit: return fSuccess; }
class CProtectionRequestListProtectionNormalThreadProcWrappedLocals { public: CProtectionRequestListProtectionNormalThreadProcWrappedLocals() { } ~CProtectionRequestListProtectionNormalThreadProcWrappedLocals() { }
CSmallStringBuffer buffFullPathOfChange; CSmallStringBuffer buffAssemblyStore; CSmallStringBuffer buffAssemblyRelativeChange; CStringBuffer rbuffAssemblyDirectoryName; };
BOOL CProtectionRequestList::ProtectionNormalThreadProcWrapped( CProtectionRequestRecord *pRequestRecord ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess);
CProtectionRequestRecord &rRequest = *pRequestRecord; CProtectionRequestList *pRequestList = rRequest.GetParent();
BOOL fNeedsReinstall = FALSE; BOOL fAssemblyNotFound = FALSE;
DWORD dwAsmPathAttribs = 0;
SxsRecoveryResult RecoverResult = Recover_Unknown;
bool fNoAssembly = false; bool fExist = false;
//
// The request's key value contains the full path of the assembly that
// is being modified, in theory. So, we can just use it locally.
//
const CBaseStringBuffer &rbuffAssemblyPath = rRequest.GetChangeBasePath(); CAssemblyRecoveryInfo &rRecoveryInfo = rRequest.GetRecoveryInfo(); CSecurityMetaData &rSecurityMetaData = rRecoveryInfo.GetSecurityInformation();
CSmartPtr<CProtectionRequestListProtectionNormalThreadProcWrappedLocals> Locals; IFW32FALSE_EXIT(Locals.Win32Allocate(__FILE__, __LINE__)); CSmallStringBuffer &buffFullPathOfChange = Locals->buffFullPathOfChange; CSmallStringBuffer &buffAssemblyStore = Locals->buffAssemblyStore; CSmallStringBuffer &buffAssemblyRelativeChange = Locals->buffAssemblyRelativeChange; CStringBuffer &rbuffAssemblyDirectoryName = Locals->rbuffAssemblyDirectoryName;
::Sleep(5000); // wait 5 seconds for the offender to let go of the file handle
//
// this name could be changes because the assemblyName in the request could be a short name,
// in this case, we need reset the AssemblyName in rRequest
//
IFW32FALSE_EXIT(rbuffAssemblyDirectoryName.Win32Assign(rRequest.GetAssemblyDirectoryName())); //
// This value should not change at all during this function. Save it here.
//
IFW32FALSE_EXIT(rRequest.GetAssemblyStore(buffAssemblyStore));
//
// The big question of the day - find out the recovery information for this
// assembly. See if there was a catalog at installation time (a) or find out
// whether or not there is a catalog for it right now.
//
IFW32FALSE_EXIT(rRecoveryInfo.AssociateWithAssembly(rbuffAssemblyDirectoryName, fNoAssembly));
// If we couldn't figure out what assembly this was for, we ignore it.
if (fNoAssembly) FN_SUCCESSFUL_EXIT();
//
// if rbuffAssemblyName is different from the assemblyname from rRequest,
// it must be a short name, we must reset the AssemblyName in rRequest
//
StringComparisonResult scr = eEquals; IFW32FALSE_EXIT(rbuffAssemblyDirectoryName.Win32Compare(rRequest.GetAssemblyDirectoryName(), rRequest.GetAssemblyDirectoryName().Cch(), scr, NORM_IGNORECASE)); if (scr != eEquals) IFW32FALSE_EXIT(rRequest.SetAssemblyDirectoryName(rbuffAssemblyDirectoryName));
if (rRecoveryInfo.GetInfoPrepared() == FALSE) ORIGINATE_WIN32_FAILURE_AND_EXIT(RecoveryInfoCouldNotBePrepared, ERROR_PATH_NOT_FOUND);
if (!rRecoveryInfo.GetHasCatalog()) { #if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_INFO, "SXS.DLL: %s - Assembly %ls not registered with catalog, WFP ignoring it.\n", __FUNCTION__, static_cast<PCWSTR>(rbuffAssemblyDirectoryName)); #endif
fSuccess = TRUE; goto Exit; }
//
// See if it still exists...
//
IFW32FALSE_EXIT(::SxspDoesFileExist(SXSP_DOES_FILE_EXIST_FLAG_CHECK_DIRECTORY_ONLY, rbuffAssemblyPath, fExist)); if (!fExist) // the directory does not exist
{ ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: WFP reinstalling assembly because GetFileAttributesW(\"%ls\") failed with win32 error %ld\n", static_cast<PCWSTR>(rbuffAssemblyPath), ::FusionpGetLastWin32Error());
fNeedsReinstall = TRUE; goto DoReinstall; }
#if 0
// "if 0" is added by xiaoyuw-2002.05.01,
// the reason is that if the file is not a directory, we still need do reinstall, instead of SUCCESSFUL_EXIT
//
//
// Otherwise, is it maybe not a directory for one reason or another?
//
else if (!(dwAsmPathAttribs & FILE_ATTRIBUTE_DIRECTORY)) { #if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL:%s - %ws isn't a directory, we should attempt to remove it?\n", __FUNCTION__, static_cast<PCWSTR>(rbuffAssemblyPath)); #endif
FN_SUCCESSFUL_EXIT(); } #endif
//
// Find out whether or not the file is still OK by checking against the manifest
//
{ HashValidateResult HashValid = HashValidate_OtherProblems; CStringBuffer buffManifestFullPath; BOOL fPresent = FALSE;
IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory( buffFullPathOfChange ) ); IFW32FALSE_EXIT(::SxspCreateManifestFileNameFromTextualString( 0, SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST, buffFullPathOfChange, rSecurityMetaData.GetTextualIdentity(), buffManifestFullPath) );
#if DBG
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_WFP, "SXS.DLL:%s - Checking to see if manifest %ls is OK\n", __FUNCTION__, static_cast<PCWSTR>(buffManifestFullPath)); #endif
IFW32FALSE_EXIT(::SxspVerifyFileHash( SVFH_RETRY_LOGIC_SIMPLE, buffManifestFullPath, rSecurityMetaData.GetManifestHash(), CALG_SHA1, HashValid));
#if DBG
FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL:%s - Manifest %ls checks %ls\n", __FUNCTION__, static_cast<PCWSTR>(buffManifestFullPath), SxspHashValidateResultToString(HashValid) ); #endif
if ( HashValid != HashValidate_Matches ) { fNeedsReinstall = TRUE; goto DoReinstall; }
//
// Let's just ensure the catalog is there - it's not necessary anymore
// for the protection pass, but it should be there if someone wants to
// repackage the assembly for distribution.
//
IFW32FALSE_EXIT(::SxspEnsureCatalogStillPresentForManifest(buffManifestFullPath, fPresent)); if ( !fPresent ) { fNeedsReinstall = TRUE; goto DoReinstall; } }
//
// Now we can loop through the items in our list of things to be evaluated and
// see if any of them are bad (or missing, or whatever.)
//
// Start out by touching the thing that indicates the last time we spun through
// here and looked at the file list.
//
while (!fNeedsReinstall) { const CMetaDataFileElement* pFileDataElement = NULL; HashValidateResult Valid = HashValidate_OtherProblems; BOOL fFileNotFound;
if (!rRequest.PopNextFileChange(buffAssemblyRelativeChange)) { break; }
IFW32FALSE_EXIT(buffAssemblyRelativeChange.Win32RemoveFirstPathElement());
//
// The change here is really to the top level directory - don't
// bother doing anything in this case. Maybe we should catch
// this beforehand so we don't do the work of parsing?
//
if (buffAssemblyRelativeChange.Cch() == 0) { continue; }
//
// Acquire the security data
//
IFW32FALSE_EXIT( rSecurityMetaData.GetFileMetaData( buffAssemblyRelativeChange, pFileDataElement ) );
//
// There wasn't any data for this file? Means we don't know about the file, so we
// probably should do /something/ about it. For now, however, with the agreeance of
// the backup team, we let sleeping files lie.
if ( pFileDataElement == NULL ) { //
// because short-filename is not stored in registry, so for a filename, which might be long-pathname
// or a short pathname, if we try out all entries in the Registry and still can not find it,
// we assume it is a short filename. In this case, we would verify the assembly, if it is not intact,
// do reinstall for the assembly....
//
DWORD dwResult = 0;
IFW32FALSE_EXIT(::SxspValidateEntireAssembly( SXS_VALIDATE_ASM_FLAG_CHECK_EVERYTHING, rRecoveryInfo, dwResult));
fNeedsReinstall = ( dwResult != SXS_VALIDATE_ASM_FLAG_VALID_PERFECT ); goto DoReinstall; }
//
// And build the full path of the change via:
//
// sbAssemblyPath + \ + buffAssemblyRelativeChange
//
IFW32FALSE_EXIT(buffFullPathOfChange.Win32Assign(rbuffAssemblyPath)); IFW32FALSE_EXIT(buffFullPathOfChange.Win32AppendPathElement(buffAssemblyRelativeChange));
//
// We really should check the return value here, but the
// function is smart enough to set Valid to something useful
// before returning. A failure here should NOT be an IFW32FALSE_EXIT
// call, mostly because we don't want to stop protecting this
// assembly just because it failed with a FILE_NOT_FOUND or other
// such.
//
IFW32FALSE_EXIT_UNLESS(::SxspValidateAllFileHashes( *pFileDataElement, buffFullPathOfChange, Valid ), FILE_OR_PATH_NOT_FOUND(::FusionpGetLastWin32Error()), fFileNotFound );
if ( ( Valid != HashValidate_Matches ) || fFileNotFound ) { fNeedsReinstall = TRUE; goto DoReinstall; }
} /* while */
DoReinstall: //
// If somewhere along the line we were supposed to reinstall, then we
// do so.
//
if (fNeedsReinstall) { //
// We have to indicate that all changes from point A to point B need
// to be ignored.
//
rRequest.MarkInRecoveryMode(TRUE); PerformRecoveryOfAssembly(rRecoveryInfo, RecoverResult); rRequest.ClearList(); rRequest.MarkInRecoveryMode(FALSE);
/*
NTRAID#NTBUG9-591177-2002/03/31-JayKrell see following comment */ //
// HACKHACK jonwis 1/20/2001 - Stop failing assertions because lasterror
// is set wrong by one of the above.
//
::FusionpSetLastWin32Error(0); }
fSuccess = TRUE; Exit: const DWORD dwLastErrorSaved = ::FusionpGetLastWin32Error();
//
// We are done - this always succeeds. The explanation is hairy.
//
if (pRequestList->AttemptRemoveItem(&rRequest)) { ::FusionpSetLastWin32Error(dwLastErrorSaved); } else { if (!fSuccess) { // This seems bad that we're losing the original failure; let's at least spew it.
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS.DLL: %s() losing original win32 error code of %d; replaced with %d from CProtectionRequestList::AttemptRemoveItem() call.\n", __FUNCTION__, dwLastErrorSaved, ::FusionpGetLastWin32Error()); }
fSuccess = FALSE; }
return fSuccess; }
BOOL WINAPI SxsProtectionUserLogonEvent() { return SetEvent(g_hSxsLoginEvent); }
BOOL WINAPI SxsProtectionUserLogoffEvent() { return ResetEvent(g_hSxsLoginEvent); }
BOOL CProtectionRequestList::PerformRecoveryOfAssembly( const CAssemblyRecoveryInfo &RecoverInfo, SxsRecoveryResult &ResultOut ) { FN_PROLOG_WIN32
BOOL fFound = FALSE; CRecoveryJobTableEntry *pNewEntry, **pExistingEntry; DWORD dwRecoveryLastError = ERROR_SUCCESS; CSxsLockCriticalSection lock(m_cInstallerCriticalSection);
IFALLOCFAILED_EXIT(pNewEntry = new CRecoveryJobTableEntry); IFW32FALSE_EXIT(pNewEntry->Initialize());
IFW32FALSE_EXIT(lock.Lock()); IFW32FALSE_EXIT( m_pInstallsTable->FindOrInsertIfNotPresent( RecoverInfo.GetAssemblyDirectoryName(), pNewEntry, &pExistingEntry, &fFound)); lock.Unlock();
//
// Great, it was either inserted or it was already there - if not already there,
// then we'll take care if it.
//
if (!fFound) { BOOL fSuccess = FALSE;
IFW32FALSE_EXIT(pNewEntry->StartInstallation());
//
// Perform the recovery.
//
fSuccess = ::SxspRecoverAssembly(RecoverInfo, ResultOut);
if (!fSuccess) dwRecoveryLastError = ::FusionpGetLastWin32Error();
#if DBG
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_WFP, "SXS: %s() - RecoverAssembly returned Result = %ls, fSuccess = %s, LastError = 0x%08x\n", __FUNCTION__, ::SxspRecoveryResultToString(ResultOut), fSuccess ? "true" : "false", dwRecoveryLastError); #endif
//
// Tell this entry that it's all done. This releases the other people
// that were waiting on the event to get done as well.
//
IFW32FALSE_EXIT(pNewEntry->InstallationComplete(fSuccess, ResultOut, dwRecoveryLastError));
//
// And now delete the item from the list.
//
IFW32FALSE_EXIT(lock.Lock()); IFW32FALSE_EXIT(m_pInstallsTable->Remove(RecoverInfo.GetAssemblyDirectoryName())); lock.Unlock(); } else { DWORD dwLastError; BOOL fSuccess = FALSE; IFW32FALSE_EXIT((*pExistingEntry)->WaitUntilCompleted(ResultOut, fSuccess, dwLastError));
#if DBG
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_WFP, "SXS: %s() - WaitUntilCompleted returned Result = %ls, fInstalledOk = %s, LastError = 0x%08x\n", __FUNCTION__, ::SxspRecoveryResultToString(ResultOut), fSuccess ? "true" : "false", dwLastError); #endif
dwRecoveryLastError = dwLastError; }
if (dwRecoveryLastError != ERROR_SUCCESS) ORIGINATE_WIN32_FAILURE_AND_EXIT(RecoveryFailed, dwRecoveryLastError);
FN_EPILOG }
VOID WINAPI SxsProtectionEnableProcessing(BOOL fActivityEnabled) { s_fIsSfcAcceptingNotifications = fActivityEnabled; }
|