/* Copyright (c) Microsoft Corporation */ #include "stdinc.h" #include #include "sxsp.h" #include "gsgenctx.h" #include "sxsexceptionhandling.h" typedef struct _CALLBACKDATA { union { GUID_SECTION_GENERATION_CONTEXT_CBDATA_GETDATASIZE GetDataSize; GUID_SECTION_GENERATION_CONTEXT_CBDATA_GETDATA GetData; GUID_SECTION_GENERATION_CONTEXT_CBDATA_ENTRYDELETED EntryDeleted; GUID_SECTION_GENERATION_CONTEXT_CBDATA_GETUSERDATASIZE GetUserDataSize; GUID_SECTION_GENERATION_CONTEXT_CBDATA_GETUSERDATA GetUserData; } u; } CALLBACKDATA, *PCALLBACKDATA; BOOL CGSGenCtx::Create( PGUID_SECTION_GENERATION_CONTEXT *GSGenContext, ULONG DataFormatVersion, GUID_SECTION_GENERATION_CONTEXT_CALLBACK_FUNCTION CallbackFunction, PVOID CallbackContext ) { FN_PROLOG_WIN32 CGSGenCtx *pGSGenCtx; if (GSGenContext != NULL) *GSGenContext = NULL; PARAMETER_CHECK(GSGenContext != NULL); PARAMETER_CHECK(CallbackFunction != NULL); IFALLOCFAILED_EXIT(pGSGenCtx = new CGSGenCtx); pGSGenCtx->m_CallbackFunction = CallbackFunction; pGSGenCtx->m_CallbackContext = CallbackContext; pGSGenCtx->m_DataFormatVersion = DataFormatVersion; *GSGenContext = (PGUID_SECTION_GENERATION_CONTEXT) pGSGenCtx; FN_EPILOG } CGSGenCtx::CGSGenCtx() { m_HashTableSize = 0; } CGSGenCtx::~CGSGenCtx() { CSxsPreserveLastError ple; CALLBACKDATA CBData; CGuidPtrTableIter iter(m_Table); for (iter.Reset(); iter.More(); iter.Next()) { CBData.u.EntryDeleted.DataContext = iter->m_DataContext; (*m_CallbackFunction)( m_CallbackContext, GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_ENTRYDELETED, &CBData); iter->m_DataContext = NULL; } ple.Restore(); } void CGSGenCtx::EntryGuidTableHelper::FinalizeValue( CGSGenCtx::Entry *&rpEntry ) { FUSION_DELETE_SINGLETON(rpEntry); rpEntry = NULL; } BOOL CGSGenCtx::Add( const GUID &rGuid, PVOID DataContext, ULONG AssemblyRosterIndex, DWORD DuplicateErrorCode ) { FN_PROLOG_WIN32 CSmartPtr pEntry; PARAMETER_CHECK(DuplicateErrorCode != ERROR_SUCCESS); // We'll assume that duplicates are rare, so we'll allocate the entry up front. IFW32FALSE_EXIT(pEntry.Win32Allocate(__FILE__, __LINE__)); IFW32FALSE_EXIT(pEntry->Initialize(DataContext, AssemblyRosterIndex)); IFW32FALSE_EXIT(m_Table.Insert(rGuid, pEntry)); pEntry.Detach(); FN_EPILOG } BOOL CGSGenCtx::Find( const GUID &rGuid, PVOID *DataContext ) { FN_PROLOG_WIN32 Entry *pEntry = NULL; if (DataContext != NULL) *DataContext = NULL; IFW32FALSE_EXIT(m_Table.Find(rGuid, pEntry)); if (pEntry == NULL) ORIGINATE_WIN32_FAILURE_AND_EXIT(GuidNotInSection, ERROR_SXS_KEY_NOT_FOUND); if (DataContext != NULL) *DataContext = pEntry->m_DataContext; FN_EPILOG } BOOL CGSGenCtx::GetSectionSize( PSIZE_T SizeOut ) { FN_PROLOG_WIN32 SIZE_T UserDataSize = 0; SIZE_T HeaderSize = 0; SIZE_T EntryListSize = 0; SIZE_T EntryDataSize = 0; CALLBACKDATA CBData; Entry *pEntry = NULL; CGuidPtrTableIter iter(m_Table); if (SizeOut != NULL) *SizeOut = 0; PARAMETER_CHECK(SizeOut != NULL); HeaderSize = sizeof(ACTIVATION_CONTEXT_GUID_SECTION_HEADER); CBData.u.GetUserDataSize.DataSize = 0; IFW32FALSE_EXIT((*m_CallbackFunction)(m_CallbackContext, GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETUSERDATASIZE, &CBData)); UserDataSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetUserDataSize.DataSize); for (iter.Reset(); iter.More(); iter.Next()) { EntryListSize += sizeof(ACTIVATION_CONTEXT_GUID_SECTION_ENTRY); CBData.u.GetDataSize.DataContext = iter->m_DataContext; CBData.u.GetDataSize.DataSize = 0; (*m_CallbackFunction)(m_CallbackContext, GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATASIZE, &CBData); EntryDataSize += ROUND_ACTCTXDATA_SIZE(CBData.u.GetDataSize.DataSize); } *SizeOut = HeaderSize + UserDataSize + EntryListSize + EntryDataSize; FN_EPILOG } BOOL CGSGenCtx::GetSectionData( SIZE_T BufferSize, PVOID Buffer, PSIZE_T BytesWritten ) { FN_PROLOG_WIN32 SIZE_T BytesSoFar = 0; SIZE_T BytesLeft = BufferSize; SIZE_T RoundedSize; PACTIVATION_CONTEXT_GUID_SECTION_HEADER Header; CALLBACKDATA CBData; PVOID Cursor = NULL; CGuidPtrTableIter iter(m_Table); SIZE_T EntryCount = m_Table.GetEntryCount(); if (BytesWritten != NULL) *BytesWritten = 0; if (BytesLeft < sizeof(ACTIVATION_CONTEXT_GUID_SECTION_HEADER)) ORIGINATE_WIN32_FAILURE_AND_EXIT(NoRoom, ERROR_INSUFFICIENT_BUFFER); Header = (PACTIVATION_CONTEXT_GUID_SECTION_HEADER) Buffer; Cursor = (PVOID) (Header + 1); Header->Magic = ACTIVATION_CONTEXT_GUID_SECTION_MAGIC; Header->HeaderSize = sizeof(ACTIVATION_CONTEXT_GUID_SECTION_HEADER); Header->FormatVersion = ACTIVATION_CONTEXT_GUID_SECTION_FORMAT_WHISTLER; Header->DataFormatVersion = m_DataFormatVersion; Header->Flags = 0; Header->ElementCount = static_cast(EntryCount); Header->ElementListOffset = 0; // filled in after we figure out the user data area Header->SearchStructureOffset = 0; Header->UserDataOffset = 0; // filled in below Header->UserDataSize = 0; BytesLeft -= sizeof(*Header); BytesSoFar += sizeof(*Header); CBData.u.GetUserDataSize.DataSize = 0; IFW32FALSE_EXIT( (*m_CallbackFunction)( m_CallbackContext, GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETUSERDATASIZE, &CBData)); RoundedSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetUserDataSize.DataSize); if (RoundedSize != 0) { CBData.u.GetUserData.SectionHeader = Header; CBData.u.GetUserData.BufferSize = RoundedSize; CBData.u.GetUserData.Buffer = Cursor; CBData.u.GetUserData.BytesWritten = 0; IFW32FALSE_EXIT( (*m_CallbackFunction)( m_CallbackContext, GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETUSERDATA, &CBData)); ASSERT(CBData.u.GetUserData.BytesWritten <= RoundedSize); if (CBData.u.GetUserData.BytesWritten != 0) { RoundedSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetUserData.BytesWritten); BytesLeft -= RoundedSize; BytesSoFar += RoundedSize; Header->UserDataSize = static_cast(CBData.u.GetUserData.BytesWritten); Header->UserDataOffset = static_cast(((LONG_PTR) Cursor) - ((LONG_PTR) Header)); Cursor = (PVOID) (((ULONG_PTR) Cursor) + RoundedSize); } } // Finally the array of entries... if (EntryCount != 0) { PVOID DataCursor; PACTIVATION_CONTEXT_GUID_SECTION_ENTRY DstEntry; PACTIVATION_CONTEXT_GUID_SECTION_ENTRY DstEntryArrayFirstElement; if (BytesLeft < (EntryCount * sizeof(ACTIVATION_CONTEXT_GUID_SECTION_ENTRY))) ORIGINATE_WIN32_FAILURE_AND_EXIT(NoRoom, ERROR_INSUFFICIENT_BUFFER); BytesLeft -= (EntryCount * sizeof(ACTIVATION_CONTEXT_GUID_SECTION_ENTRY)); BytesSoFar += (EntryCount * sizeof(ACTIVATION_CONTEXT_GUID_SECTION_ENTRY)); // // DstEntryArrayFirstElement actually points to the first thing that we'll // be writing out, as DstEntry is ++'d across each of the elements as we // zip through the output buffer. // DstEntryArrayFirstElement = (PACTIVATION_CONTEXT_GUID_SECTION_ENTRY) Cursor; DstEntry = DstEntryArrayFirstElement; Header->ElementListOffset = static_cast(((LONG_PTR) DstEntry) - ((LONG_PTR) Header)); DataCursor = (PVOID) (DstEntry + EntryCount); for (iter.Reset(); iter.More(); iter.Next()) { CBData.u.GetDataSize.DataContext = iter->m_DataContext; CBData.u.GetDataSize.DataSize = 0; IFW32FALSE_EXIT( (*m_CallbackFunction)( m_CallbackContext, GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATASIZE, &CBData)); RoundedSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetDataSize.DataSize); #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_INFO, "SXS.DLL: Guid section generation callback (_GETDATASIZE) returned data size of %d (rounded to %Id)\n", CBData.u.GetDataSize.DataSize, RoundedSize); #endif if (RoundedSize != 0) { if (BytesLeft < RoundedSize) ORIGINATE_WIN32_FAILURE_AND_EXIT(NoRoom, ERROR_INSUFFICIENT_BUFFER); CBData.u.GetData.SectionHeader = Header; CBData.u.GetData.DataContext = iter->m_DataContext; CBData.u.GetData.BufferSize = RoundedSize; CBData.u.GetData.Buffer = DataCursor; CBData.u.GetData.BytesWritten = 0; #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_INFO, "SXS.DLL: Calling guid section generation callback with reason GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATA\n" " .DataContext = %p\n" " .BufferSize = %d\n" " .Buffer = %p\n", CBData.u.GetData.DataContext, CBData.u.GetData.BufferSize, CBData.u.GetData.Buffer); #endif IFW32FALSE_EXIT( (*m_CallbackFunction)( m_CallbackContext, GUID_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATA, &CBData)); if (CBData.u.GetData.BytesWritten != 0) { ASSERT(CBData.u.GetData.BytesWritten <= RoundedSize); RoundedSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetData.BytesWritten); INTERNAL_ERROR_CHECK(CBData.u.GetData.BytesWritten <= RoundedSize); BytesLeft -= RoundedSize; BytesSoFar += RoundedSize; DstEntry->Offset = static_cast(((LONG_PTR) DataCursor) - ((LONG_PTR) Header)); } else DstEntry->Offset = 0; DstEntry->Length = static_cast(CBData.u.GetData.BytesWritten); DstEntry->AssemblyRosterIndex = iter->m_AssemblyRosterIndex; DataCursor = (PVOID) (((ULONG_PTR) DataCursor) + RoundedSize); } else { DstEntry->Offset = 0; DstEntry->Length = 0; DstEntry->AssemblyRosterIndex = 0; } DstEntry->Guid = iter.GetKey(); DstEntry++; } // // We compare the blobs via memcmp // if (m_HashTableSize == 0) { ::qsort(DstEntryArrayFirstElement, EntryCount, sizeof(*DstEntry), &CGSGenCtx::SortGuidSectionEntries); Header->Flags |= ACTIVATION_CONTEXT_GUID_SECTION_ENTRIES_IN_ORDER; } } if (BytesWritten != NULL) *BytesWritten = BytesSoFar; FN_EPILOG } int __cdecl CGSGenCtx::SortGuidSectionEntries( const void *elem1, const void *elem2 ) { // // The first thing in the structure is actually the GUID itself, but // we'll save problems later by casting and following the Guid // member. // const PACTIVATION_CONTEXT_GUID_SECTION_ENTRY pLeft = (const PACTIVATION_CONTEXT_GUID_SECTION_ENTRY)elem1; const PACTIVATION_CONTEXT_GUID_SECTION_ENTRY pRight = (const PACTIVATION_CONTEXT_GUID_SECTION_ENTRY)elem2; return memcmp( (const void*)&pLeft->Guid, (const void*)&pRight->Guid, sizeof(GUID) ); } BOOL CGSGenCtx::Entry::Initialize( PVOID DataContext, ULONG AssemblyRosterIndex ) { m_DataContext = DataContext; m_AssemblyRosterIndex = AssemblyRosterIndex; return TRUE; }