|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
cmmprops.cpp
Abstract:
This module contains the implementation of the property search class
Author:
Keith Lau (keithlau@microsoft.com)
Revision History:
keithlau 03/05/98 created
--*/
#include <windows.h>
#include <malloc.h>
#include <stdlib.h>
#include <search.h>
#include "dbgtrace.h"
#include "signatur.h"
#include "cmmprops.h"
#include "cmmtypes.h"
#include "stddef.h"
long g_cCPropertyTableCreations = 0; long g_cCPropertyTableSearchs = 0;
extern DWORD g_fValidateSignatures;
// =================================================================
// Implementation of CPropertyTableItem
//
CPropertyTableItem::CPropertyTableItem( CBlockManager *pBlockManager, LPPROPERTY_TABLE_INSTANCE pInstanceInfo ) { _ASSERT(pInstanceInfo); _ASSERT(pBlockManager);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::CPropertyTableItem");
m_pInstanceInfo = pInstanceInfo; m_pBlockManager = pBlockManager; m_fLoaded = FALSE;
m_dwCurrentFragment = 0; m_faOffsetToFragment = 0; m_dwCurrentItem = 0; m_dwCurrentItemInFragment = 0; m_faOffsetToCurrentItem = INVALID_FLAT_ADDRESS;
TraceFunctLeaveEx((LPARAM)this); }
CPropertyTableItem::~CPropertyTableItem() { _ASSERT(m_pInstanceInfo); _ASSERT(m_pBlockManager);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::~CPropertyTableItem");
m_pInstanceInfo = NULL; m_pBlockManager = NULL; m_fLoaded = FALSE; m_dwCurrentFragment = 0; m_faOffsetToFragment = 0; m_dwCurrentItem = 0; m_dwCurrentItemInFragment = 0; m_faOffsetToCurrentItem = INVALID_FLAT_ADDRESS;
TraceFunctLeaveEx((LPARAM)this); }
HRESULT CPropertyTableItem::AddItem( LPPROPERTY_ITEM pItem, DWORD *pdwIndex, FLAT_ADDRESS *pfaOffsetToItem ) { HRESULT hrRes = S_OK; DWORD cNumSleeps = 0; const DWORD MAX_ADDITEM_SLEEPS = 5; DWORD dwPropertyId; DWORD dwFragmentNumber; DWORD dwItemsInFragment; DWORD dwItemInFragment; DWORD dwSize; FLAT_ADDRESS faOffset;
_ASSERT(pdwIndex); _ASSERT(m_pInstanceInfo); _ASSERT(m_pBlockManager);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::AddItem");
for (;;) { // OK, first we determine if we need to create a new fragment
// before we move on ...
dwPropertyId = m_pInstanceInfo->dwProperties;
// Find out about our fragment type
dwItemsInFragment = 1 << m_pInstanceInfo->dwItemBits; dwItemInFragment = dwPropertyId & (dwItemsInFragment - 1); dwFragmentNumber = dwPropertyId >> m_pInstanceInfo->dwItemBits; dwSize = m_pInstanceInfo->dwItemSize;
// See if we already have the desired fragment
hrRes = ReadFragmentFromFragmentNumber(dwFragmentNumber); if (!SUCCEEDED(hrRes)) { // It's some other error, return failure ...
if (hrRes != STG_E_PATHNOTFOUND) { ErrorTrace((LPARAM)this, "Unable to ReadFragmentFromFragmentNumber"); TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
if (dwFragmentNumber && (m_dwCurrentFragment < dwFragmentNumber)) { // This is really embarassing, we are on a new fragment
// but the fragment(s) before that are still not created
// yet, we got to retry at this point ...
continue; }
// OK, so the fragment is not created yet, see if we need to
// create it. THe first entry in a new fragment is responsible
// for creating the fragment
if (!dwItemInFragment) { // Build a new fragment structure ...
DWORD dwOffset; FLAT_ADDRESS faOffsetSlot; FLAT_ADDRESS *pfaOffset; PROPERTY_TABLE_FRAGMENT ifFragment; ifFragment.dwSignature = PROPERTY_FRAGMENT_SIGNATURE_VALID; ifFragment.faNextFragment = INVALID_FLAT_ADDRESS;
// The next ten or so lines of code is very tricky.
// If we are at the first fragment (i.e. no fragments
// have been created yet), then we would actually have to
// fill in the offset of the allocated block into the
// m_pInstanceInfo->faFirstFragment variable. Note that
// pfaOffset is passed into AtomicAllocWriteAndIncrement
// and the value is assigned INSIDE the locked region,
// which makes this assignment thread-safe.
//
// For the other case, we need to fill in the parent's
// faNextFragment member to link to the newly allocated
// block. Now, since ReadFragmentFromFragmentNumber must
// have failed beforehand at the node right before us.
// m_faOffsetToFragment actually points to our parent's
// fragment. So we pass in the offset of our parent's
// faNextFragment value so the atomic operation can
// fill it in for us.
if (!dwFragmentNumber) { // Hook up the first fragment
// _ASSERT(m_pInstanceInfo->faFirstFragment ==
// INVALID_FLAT_ADDRESS);
pfaOffset = &(m_pInstanceInfo->faFirstFragment); faOffsetSlot = INVALID_FLAT_ADDRESS; } else { // Hook up subsequent fragments to its parent
//_ASSERT(m_Fragment.faNextFragment == INVALID_FLAT_ADDRESS);
//_ASSERT(m_dwCurrentFragment == dwFragmentNumber);
pfaOffset = &faOffset; faOffsetSlot = m_faOffsetToFragment + offsetof(PROPERTY_TABLE_FRAGMENT, faNextFragment); }
// Attempt to create the fragment, add the item to
// the beginning of the new fragment, and increment the
// property count in one atomic shot
dwOffset = (dwItemInFragment * dwSize) + sizeof(PROPERTY_TABLE_FRAGMENT); hrRes = m_pBlockManager->AtomicAllocWriteAndIncrement( m_pInstanceInfo->dwFragmentSize, pfaOffset, faOffsetSlot, INVALID_FLAT_ADDRESS, (LPBYTE)&ifFragment, sizeof(PROPERTY_TABLE_FRAGMENT), (LPBYTE)pItem, dwOffset, dwSize, &(m_pInstanceInfo->dwProperties), dwPropertyId, 1, &m_bcContext ); if (pfaOffsetToItem) *pfaOffsetToItem = *pfaOffset + dwOffset; if (!SUCCEEDED(hrRes)) { // We can fail for 2 reasons: Error or Retry; we bail
// out if it's an error.
if (hrRes != HRESULT_FROM_WIN32(ERROR_RETRY)) { // Bail out!
ErrorTrace((LPARAM)this, "Failed to AtomicAllocWriteAndIncrement (%08x)", hrRes); break; } } else { // Success
DebugTrace((LPARAM)this, "Succeeded to AtomicAllocWriteAndIncrement!");
// We might want to update some internal members
// First, hook up the previous fragment to this new
// fragment.
_ASSERT(*pfaOffset != INVALID_FLAT_ADDRESS); CopyMemory(&m_Fragment, &ifFragment, sizeof(PROPERTY_TABLE_FRAGMENT)); m_dwCurrentFragment = dwFragmentNumber; m_faOffsetToFragment = *pfaOffset; m_dwCurrentItem = dwPropertyId; m_dwCurrentItemInFragment = dwItemInFragment; m_fLoaded = TRUE; break; }
// Oooops, someone beat us in using this property ID,
// we must retry immediately. Note since the state already
// changed we would not be required to wait.
continue; }
// This is the most expensive case, basically, there is nothing
// we can do but give up the time slice, I think besides changing
// algorithm, this is the bast since I'd rather context switch
// right away than switch after exhausting the time quanta
Sleep(0);
//
// If we keep on doing this, then it is likely that there is
// some problem... that our conditions will never be met.
//
if (cNumSleeps > MAX_ADDITEM_SLEEPS) { FatalTrace((LPARAM) this, "Looping in AddItem...potential corrupt P1 - bailing"); hrRes = E_FAIL; _ASSERT(0 && "Potential loop condition (corrupt msg) detected - contact SMTP dev"); break; } cNumSleeps++; ErrorTrace((LPARAM) this, "Looping for the %d time in AddItem", cNumSleeps); continue; }
// This is the simplest case where we don't have to create a new
// fragment so all we do is attempt an atomic write and increment
// Still, there will be a window where some other thread might
// beat us in using this property ID. In that case, we will retry
// immediately.
faOffset = m_faOffsetToFragment + sizeof(PROPERTY_TABLE_FRAGMENT) + (dwItemInFragment * dwSize); hrRes = m_pBlockManager->AtomicWriteAndIncrement( (LPBYTE)pItem, faOffset, dwSize, &(m_pInstanceInfo->dwProperties), dwPropertyId, 1, &m_bcContext ); if (pfaOffsetToItem) *pfaOffsetToItem = faOffset; if (!SUCCEEDED(hrRes)) { // We can fail for 2 reasons: Error or Retry; we bail
// out if it's an error.
if (hrRes != HRESULT_FROM_WIN32(ERROR_RETRY)) { // Bail out!
ErrorTrace((LPARAM)this, "Failed to AtomicWriteAndIncrement (%08x)", hrRes); break; } } else { // Success
DebugTrace((LPARAM)this, "Succeeded to AtomicWriteAndIncrement!"); break; }
// Retry scenario ...
} // for (;;)
// Fill in info ...
if (SUCCEEDED(hrRes)) { *pdwIndex = dwPropertyId; } TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
HRESULT CPropertyTableItem::UpdateItem( DWORD dwIndex, LPPROPERTY_ITEM pItem, FLAT_ADDRESS *pfaOffsetToItem ) { HRESULT hrRes = S_OK;
_ASSERT(m_pInstanceInfo); _ASSERT(m_pBlockManager);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::UpdateItem");
// Atomically set the item
m_fLoaded = FALSE; m_dwCurrentItem = dwIndex; hrRes = GetOrSetNextExistingItem(pItem, PIO_ATOMIC_WRITE_ITEM, pfaOffsetToItem); TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
HRESULT CPropertyTableItem::GetItemAtIndex( DWORD dwIndex, LPPROPERTY_ITEM pItem, LPFLAT_ADDRESS pfaOffset ) { HRESULT hrRes; _ASSERT(m_pInstanceInfo);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::GetItemAtIndex");
// Just pre-set to what we want, and call GetOrSetNextExistingItem ...
m_fLoaded = FALSE; m_dwCurrentItem = dwIndex; hrRes = GetOrSetNextExistingItem(pItem, PIO_READ_ITEM, pfaOffset);
TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
HRESULT CPropertyTableItem::GetNextItem( LPPROPERTY_ITEM pItem ) { HRESULT hrRes; _ASSERT(m_pInstanceInfo);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::GetNextItem");
// Just call GetOrSetNextExistingItem ...
hrRes = GetOrSetNextExistingItem(pItem, PIO_READ_ITEM);
TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
HRESULT CPropertyTableItem::GetOrSetNextExistingItem( // This looks at m_dwCurrentItem for index
LPPROPERTY_ITEM pItem, DWORD dwOperation, LPFLAT_ADDRESS pfaOffset ) { HRESULT hrRes = S_OK; DWORD dwCurrentItem; _ASSERT(m_pInstanceInfo);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::GetOrSetNextExistingItem");
// See if we are still in range
dwCurrentItem = m_dwCurrentItem; if (m_fLoaded) dwCurrentItem++;
// If we are at the end, respond so.
if (dwCurrentItem == m_pInstanceInfo->dwProperties) { m_fLoaded = FALSE; return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)); }
// We are blatantly out-of-range!
if (dwCurrentItem > m_pInstanceInfo->dwProperties) { m_fLoaded = FALSE; return(STG_E_INVALIDPARAMETER); }
m_dwCurrentItem = dwCurrentItem;
// See if we are still in the current fragment
if (!m_fLoaded || (++m_dwCurrentItemInFragment >= (DWORD)(1 << m_pInstanceInfo->dwItemBits))) { FLAT_ADDRESS faOffsetOfFragment; // We need to load a fragment
if (!m_fLoaded) { DWORD dwWhichFragment = m_dwCurrentItem >> m_pInstanceInfo->dwItemBits;
// Get the offset to the current fragment
hrRes = ReadFragmentFromFragmentNumber(dwWhichFragment); if (!SUCCEEDED(hrRes)) return(hrRes); _ASSERT(SUCCEEDED(hrRes)); // Calculate the current item w.r.t the fragment
m_dwCurrentItemInFragment = m_dwCurrentItem & ((1 << m_pInstanceInfo->dwItemBits) - 1); } else { // Walk to next node
faOffsetOfFragment = m_Fragment.faNextFragment; hrRes = ReadFragment(faOffsetOfFragment); if (!SUCCEEDED(hrRes)) { ErrorTrace((LPARAM)this, "Unable to load fragmentat offset %u", (DWORD)faOffsetOfFragment); TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
// Okay, reset the current item
m_dwCurrentFragment++; m_dwCurrentItemInFragment = 0; } }
// Make sure what we have makes sense
_ASSERT(m_dwCurrentItemInFragment < (DWORD)(1 << m_pInstanceInfo->dwItemBits));
FLAT_ADDRESS faOperateOffset = m_faOffsetToFragment + sizeof(PROPERTY_TABLE_FRAGMENT) + (m_dwCurrentItemInFragment * m_pInstanceInfo->dwItemSize);
switch (dwOperation) { case PIO_READ_ITEM:
// OK, Issue a read to get the item entry.
DebugTrace((LPARAM)this, "Reading item"); hrRes = ReadItem(faOperateOffset, pItem); if (SUCCEEDED(hrRes)) m_faOffsetToCurrentItem = faOperateOffset; break; case PIO_WRITE_ITEM: case PIO_ATOMIC_WRITE_ITEM:
// OK, Issue a write to set the item entry.
DebugTrace((LPARAM)this, "Writing item%s", (dwOperation == PIO_ATOMIC_WRITE_ITEM)?" atomically":""); hrRes = WriteItem(faOperateOffset, pItem, (dwOperation == PIO_ATOMIC_WRITE_ITEM)); if (SUCCEEDED(hrRes)) m_faOffsetToCurrentItem = faOperateOffset; break;
default: _ASSERT(FALSE); ErrorTrace((LPARAM)this, "Invalid operation %u", dwOperation); hrRes = STG_E_INVALIDFUNCTION; }
if (SUCCEEDED(hrRes) && pfaOffset) *pfaOffset = faOperateOffset;
TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
inline HRESULT CPropertyTableItem::ReadFragmentFromFragmentNumber( DWORD dwFragmentNumber ) { HRESULT hrRes; FLAT_ADDRESS faOffsetOfFragment; _ASSERT(m_pInstanceInfo);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::ReadFragmentFromFragmentNumber");
// Note this is strictly internal so we don't do much checking
// Initially point to sentinel
m_fLoaded = FALSE; m_dwCurrentFragment = 0; faOffsetOfFragment = m_pInstanceInfo->faFirstFragment; do { // Now if we are only one away from the desired node, but the
// fragment does not exist, we will return a special code to
// indicate that
if (faOffsetOfFragment == INVALID_FLAT_ADDRESS) { DebugTrace((LPARAM)this, "Unable to load fragment at offset %u (INVALID_FLAT_ADDRESS)", (DWORD)faOffsetOfFragment); hrRes = STG_E_PATHNOTFOUND; break; }
hrRes = ReadFragment(faOffsetOfFragment); if (!SUCCEEDED(hrRes)) { ErrorTrace((LPARAM)this, "Unable to load fragment %u at offset %u", dwFragmentNumber, (DWORD)faOffsetOfFragment); break; }
// Walk to next node
m_dwCurrentFragment++; faOffsetOfFragment = m_Fragment.faNextFragment;
} while (dwFragmentNumber--);
TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
inline HRESULT CPropertyTableItem::ReadFragment( FLAT_ADDRESS faOffset ) { HRESULT hrRes; DWORD dwSize;
_ASSERT(m_pBlockManager);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::ReadFragment");
// Is the fragment correct?
if (faOffset == INVALID_FLAT_ADDRESS) return(STG_E_INVALIDPARAMETER);
// Load up the minimal fragment header
hrRes = m_pBlockManager->ReadMemory( (LPBYTE)&m_Fragment, faOffset, sizeof(PROPERTY_TABLE_FRAGMENT), &dwSize, &m_bcContext); if (SUCCEEDED(hrRes)) { if(g_fValidateSignatures && m_Fragment.dwSignature != PROPERTY_FRAGMENT_SIGNATURE_VALID) ForceCrashIfNeeded();
m_fLoaded = TRUE; m_faOffsetToFragment = faOffset; }
TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
inline HRESULT CPropertyTableItem::ReadItem( FLAT_ADDRESS faOffset, LPPROPERTY_ITEM pItem ) { HRESULT hrRes; DWORD dwSize;
_ASSERT(m_pBlockManager);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::ReadItem");
hrRes = m_pBlockManager->ReadMemory( (LPBYTE)pItem, faOffset, m_pInstanceInfo->dwItemSize, &dwSize, &m_bcContext);
DebugTrace((LPARAM)this, "Loaded item from offset %u, HRESULT = %08x", (DWORD)faOffset, hrRes);
TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
inline HRESULT CPropertyTableItem::WriteItem( FLAT_ADDRESS faOffset, LPPROPERTY_ITEM pItem, BOOL fAtomic ) { HRESULT hrRes; DWORD dwSize;
_ASSERT(m_pBlockManager);
TraceFunctEnterEx((LPARAM)this, "CPropertyTableItem::WriteItem");
if (fAtomic) { hrRes = m_pBlockManager->AtomicWriteAndIncrement( (LPBYTE)pItem, faOffset, m_pInstanceInfo->dwItemSize, NULL, // No increment value, just a write
0, 0, &m_bcContext); } else { hrRes = m_pBlockManager->WriteMemory( (LPBYTE)pItem, faOffset, m_pInstanceInfo->dwItemSize, &dwSize, &m_bcContext); }
DebugTrace((LPARAM)this, "Written item to offset %u, HRESULT = %08x", (DWORD)faOffset, hrRes);
TraceFunctLeaveEx((LPARAM)this); return(hrRes); }
// =================================================================
// Implementation of CPropertyTable
//
CPropertyTable::CPropertyTable( PROPERTY_TABLE_TYPES pttTableType, DWORD dwValidSignature, CBlockManager *pBlockManager, LPPROPERTY_TABLE_INSTANCE pInstanceInfo, LPPROPERTY_COMPARE_FUNCTION pfnCompare, const LPINTERNAL_PROPERTY_ITEM pInternalProperties, DWORD dwInternalProperties ) { _ASSERT(pBlockManager); _ASSERT(pInstanceInfo); _ASSERT(pfnCompare);
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::CPropertyTable");
// Invalidate before initialization
m_dwSignature = CPROPERTY_TABLE_SIGNATURE_INVALID;
if (pttTableType == PTT_PROPERTY_TABLE) { // Enforce very strict checking of consistency
if (!pInternalProperties) { _ASSERT(!dwInternalProperties); } else { _ASSERT(dwInternalProperties); } } else { // These parameters must not be set if the table is other
// than a property table
_ASSERT(!pInternalProperties); _ASSERT(!dwInternalProperties); }
// Initialize internals
m_dwTableType = pttTableType; m_pBlockManager = pBlockManager; m_pfnCompare = pfnCompare; m_pInstanceInfo = pInstanceInfo; m_pInternalProperties = pInternalProperties; m_dwInternalProperties = dwInternalProperties; m_dwValidInstanceSignature = dwValidSignature;
// Validate the instance info structure
/*
_ASSERT(IsInstanceInfoValid()); _ASSERT(m_pInstanceInfo->dwFragmentSize == ((m_pInstanceInfo->dwItemSize << m_pInstanceInfo->dwItemBits) + sizeof(PROPERTY_TABLE_FRAGMENT))); */
// figure out what sort of mailmsg property table we are creating.
// if its the global property table then setup member variables to
// do property caching.
//
// There is no reason to cache recipient property offsets at this
// time since the recipient property table is instantiated, used
// once, then thrown away. we'd spend more time making the cache
// then the linear search in SearchForProperty costs
if (m_dwValidInstanceSignature == GLOBAL_PTABLE_INSTANCE_SIGNATURE_VALID) { m_iCachedPropsBase = IMMPID_MP_BEFORE__+1; m_cCachedProps = IMMPID_MP_AFTER__ - m_iCachedPropsBase; } else { m_iCachedPropsBase = 0xffffffff; m_cCachedProps = 0; }
// this is allocated and filled in lazily in InitializePropCache()
m_rgCachedProps = NULL;
// Validate the property table object
m_dwSignature = CPROPERTY_TABLE_SIGNATURE_VALID;
TraceFunctLeaveEx((LPARAM)this); }
CPropertyTable::~CPropertyTable() { _ASSERT(IsValid());
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::~CPropertyTable");
// Invalidate!
m_dwSignature = CPROPERTY_TABLE_SIGNATURE_INVALID;
// free memory
if (m_rgCachedProps) { _ASSERT(m_cCachedProps != 0); CMemoryAccess::FreeBlock(m_rgCachedProps); m_rgCachedProps = NULL; m_iCachedPropsBase = 0xffffffff; m_cCachedProps = 0; }
// Wipe out all info so we make sure we AV if we access this
// afterwards
// Initialize internals
m_dwTableType = PTT_INVALID_TYPE; m_pBlockManager = NULL; m_pfnCompare = NULL; m_pInstanceInfo = NULL; m_pInternalProperties = NULL; m_dwInternalProperties = 0;
TraceFunctLeaveEx((LPARAM)this); }
BOOL CPropertyTable::IsValid() { return((m_dwSignature == CPROPERTY_TABLE_SIGNATURE_VALID)); }
BOOL CPropertyTable::IsInstanceInfoValid() { BOOL fRet = FALSE;
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::IsInstanceInfoValid");
if (m_pInstanceInfo && m_pInstanceInfo->dwSignature == m_dwValidInstanceSignature) { fRet = TRUE; } else { FatalTrace((LPARAM)this, "Invalid signature"); }
TraceFunctLeaveEx((LPARAM)this); return(fRet); }
HRESULT CPropertyTable::GetCount( DWORD *pdwCount ) { _ASSERT(IsInstanceInfoValid()); _ASSERT(pdwCount);
if (!IsInstanceInfoValid()) return(STG_E_INVALIDHEADER);
*pdwCount = m_pInstanceInfo->dwProperties; return(S_OK); }
HRESULT CPropertyTable::GetPropertyItem( LPVOID pvPropKey, LPPROPERTY_ITEM pItem ) { HRESULT hrRes = S_OK; DWORD dwCurrentItem = 0;
_ASSERT(IsInstanceInfoValid()); _ASSERT(m_pfnCompare); _ASSERT(pvPropKey); _ASSERT(pItem);
TraceFunctEnter("CPropertyTable::GetPropertyItem");
hrRes = SearchForProperty(pvPropKey, pItem, NULL, NULL); TraceFunctLeave(); return(hrRes); }
HRESULT CPropertyTable::GetPropertyItemAndValue( LPVOID pvPropKey, LPPROPERTY_ITEM pItem, DWORD dwLength, DWORD *pdwLengthRead, LPBYTE pbValue ) { HRESULT hrRes; FLAT_ADDRESS faItemOffset;
_ASSERT(IsInstanceInfoValid()); _ASSERT(m_pBlockManager); _ASSERT(pvPropKey); _ASSERT(pdwLengthRead); _ASSERT(pItem); _ASSERT(pbValue);
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::GetPropertyItemAndValue");
// First, find the property
hrRes = SearchForProperty(pvPropKey, pItem, NULL, &faItemOffset); if (SUCCEEDED(hrRes)) { // OK, the item is found. Since the offset and length fields could
// have changed between SearchForProperty and now, we need a protected
// call to make sure we read the most up to date info as well as no
// other thread can change it while we are reading.
hrRes = m_pBlockManager->AtomicDereferenceAndRead( pbValue, &dwLength, (LPBYTE)pItem, faItemOffset, m_pInstanceInfo->dwItemSize, offsetof(PROPERTY_ITEM, faOffset), offsetof(PROPERTY_ITEM, dwSize), NULL);
*pdwLengthRead = dwLength;
DebugTrace((LPARAM)this, "AtomicDereferenceAndRead: offset %u, size %u, HRESULT = %08x", (DWORD)pItem->faOffset, pItem->dwSize, hrRes); }
TraceFunctLeave(); return(hrRes); }
HRESULT CPropertyTable::GetPropertyItemAndValueUsingIndex( DWORD dwIndex, LPPROPERTY_ITEM pItem, DWORD dwLength, DWORD *pdwLengthRead, LPBYTE pbValue ) { HRESULT hrRes; FLAT_ADDRESS faItemOffset;
_ASSERT(IsInstanceInfoValid()); _ASSERT(m_pBlockManager); _ASSERT(pdwLengthRead); _ASSERT(pItem); _ASSERT(pbValue);
// We've read nothing so far
*pdwLengthRead = 0;
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::GetPropertyItemAndValueUsingIndex");
CPropertyTableItem ptiItem(m_pBlockManager, m_pInstanceInfo);
// First, load the specified property
hrRes = ptiItem.GetItemAtIndex(dwIndex, pItem, &faItemOffset); if (SUCCEEDED(hrRes)) { // OK, the item is found. Since the offset and length fields could
// have changed between SearchForProperty and now, we need a protected
// call to make sure we read the most up to date info as well as no
// other thread can change it while we are reading.
hrRes = m_pBlockManager->AtomicDereferenceAndRead( pbValue, &dwLength, (LPBYTE)pItem, faItemOffset, m_pInstanceInfo->dwItemSize, offsetof(PROPERTY_ITEM, faOffset), offsetof(PROPERTY_ITEM, dwSize), NULL);
// Set the length read if we succeeded
if (SUCCEEDED(hrRes)) *pdwLengthRead = pItem->dwSize;
DebugTrace((LPARAM)this, "AtomicDereferenceAndRead: offset %u, size %u, HRESULT = %08x", (DWORD)pItem->faOffset, pItem->dwSize, hrRes); }
TraceFunctLeave(); return(hrRes); }
HRESULT CPropertyTable::PutProperty( LPVOID pvPropKey, LPPROPERTY_ITEM pItem, DWORD dwSize, LPBYTE pbValue ) { HRESULT hrRes; FLAT_ADDRESS faItemOffset; DWORD dwIndex; BOOL fGrow = FALSE; BOOL fCreate = FALSE; MAX_PROPERTY_ITEM MaxItem; PROPERTY_ITEM *pItemCopy = (PROPERTY_ITEM *) &MaxItem;
CBlockContext bcContext;
_ASSERT(IsInstanceInfoValid()); _ASSERT(m_pBlockManager); _ASSERT(pvPropKey); _ASSERT(pItem); _ASSERT(fMaxPropertyItemSizeValid()); // pbValue can be NULL
TraceFunctEnterEx((LPARAM)this, "CPropertyTable::PutProperty");
if (!pbValue && dwSize) { hrRes = E_POINTER; goto Exit; }
//
// OK, since the search will destroy the extra property info,
// we must save it somewhere. If the size is larger than our
// max size, then we need to bail.
//
if (!fPropertyItemSizeValid(m_pInstanceInfo->dwItemSize)) { FatalTrace((LPARAM) this, "Message propety items size %d is invalid... assuming P1 corrupt", m_pInstanceInfo->dwItemSize); hrRes = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); goto Exit; }
MoveMemory((LPVOID)pItemCopy, (LPVOID)pItem, m_pInstanceInfo->dwItemSize);
// First, see if the property exists
hrRes = SearchForProperty(pvPropKey, pItem, &dwIndex, &faItemOffset); if (SUCCEEDED(hrRes)) { // If we don't need to specify the value, we can skip this junk
if (pbValue) { if (pItem->dwMaxSize >= dwSize) { // Best scenario: these's enough space for the new value
DebugTrace((LPARAM)this, "Replacing property %u at offset %u, %u bytes", dwIndex, (DWORD)pItem->faOffset, dwSize);
// Update pItem
pItem->dwSize = dwSize; } else { // We must grow the property, then
DebugTrace((LPARAM)this, "Growing property %u at offset %u, from %u to %u bytes", dwIndex, (DWORD)pItem->faOffset, pItem->dwSize, dwSize); fGrow = TRUE; } } } else { // See if the property is not found ...
if (hrRes != STG_E_UNKNOWN) { // Nope, this is a genuine error!
ErrorTrace((LPARAM)this, "Error searching property: HRESULT = %08x", hrRes); goto Exit; }
// Create a new property
DebugTrace((LPARAM)this, "Creating new property, %u bytes", dwSize); fCreate = TRUE; }
// See if we need any new space ...
if (pbValue) { if (fCreate || fGrow) { FLAT_ADDRESS faOffset; DWORD dwAllocSize;
// Allocate some new memory
DebugTrace((LPARAM)this, "Allocating %u bytes", dwSize);
hrRes = m_pBlockManager->AllocateMemory( dwSize, &faOffset, &dwAllocSize, &bcContext); if (!SUCCEEDED(hrRes)) { DebugTrace((LPARAM)this, "Allocating failed: HRESULT = %08x", hrRes); goto Exit; }
// Update pItem
pItem->faOffset = faOffset; pItem->dwSize = dwSize; pItem->dwMaxSize = dwAllocSize; }
// Atomically write the value
hrRes = m_pBlockManager->AtomicWriteAndIncrement( pbValue, pItem->faOffset, pItem->dwSize, NULL, 0, 0, &bcContext); }
if (SUCCEEDED(hrRes)) { CPropertyTableItem ptiItem( m_pBlockManager, m_pInstanceInfo); FLAT_ADDRESS faOffsetToItem;
if (fCreate) { // Atomically create the record
MoveMemory((LPVOID)pItemCopy, (LPVOID)pItem, sizeof(PROPERTY_ITEM)); hrRes = ptiItem.AddItem(pItemCopy, &dwIndex, &faOffsetToItem); DebugTrace((LPARAM)this, "AddItem: HRESULT = %08x, new index = %u", hrRes, dwIndex); } else { // Atomically update the item record
hrRes = ptiItem.UpdateItem(dwIndex, pItem, &faOffsetToItem); DebugTrace((LPARAM)this, "UpdateItem: HRESULT = %08x, index = %u", hrRes, dwIndex); }
if (m_rgCachedProps && SUCCEEDED(hrRes)) { _ASSERT(faOffsetToItem != INVALID_FLAT_ADDRESS); UpdatePropCache(pItem, faOffsetToItem, dwIndex); } }
if (SUCCEEDED(hrRes) && fCreate) hrRes = S_FALSE;
Exit: TraceFunctLeave(); return(hrRes); }
int __cdecl CompareInternalProperties(const void *pElem1, const void *pElem2) { if (((LPINTERNAL_PROPERTY_ITEM)pElem1)->idProp == ((LPINTERNAL_PROPERTY_ITEM)pElem2)->idProp) return(0); else { if (((LPINTERNAL_PROPERTY_ITEM)pElem1)->idProp > ((LPINTERNAL_PROPERTY_ITEM)pElem2)->idProp) return(1); } return(-1); }
//
// This function allocates and fills in m_rgCachedProps
//
void CPropertyTable::InitializePropCache() { TraceFunctEnterEx((LPARAM) this, "CPropertyTable::InitializePropCache"); // it should only be called when there are properties to cache
_ASSERT(m_cCachedProps);
//
// Previously, this was a dynamic allocation based on
// the property stream read in. However, we do not
// return an error in this function... bailing without
// initializing the cache will cause the calling code to
// fall back on not using the cache. A later check of
// the item size will return ERROR_FILE_CORRUPT
//
if (!fPropertyItemSizeValid(m_pInstanceInfo->dwItemSize)) { _ASSERT(fMaxPropertyItemSizeValid()); _ASSERT(0 && "Invalid property item size"); FatalTrace((LPARAM) this, "Message propety item size %d is invalid... assuming P1 corrupt", m_pInstanceInfo->dwItemSize); goto Exit; }
// its okay if this allocation failed. in that case we won't have
// the m_rgCachedProps array and will do linear lookups
if (FAILED(CMemoryAccess::AllocBlock((void **) &m_rgCachedProps, sizeof(PROPCACHEITEM) * m_cCachedProps))) { m_rgCachedProps = NULL; } else { InterlockedIncrement(&g_cCPropertyTableCreations);
// invalidate all items in the cache
for (DWORD i = 0; i < m_cCachedProps; i++) { m_rgCachedProps[i].fa = INVALID_FLAT_ADDRESS; }
// update the cache from what is already in the table
FLAT_ADDRESS fa; DWORD dwCurrentItem = 0; MAX_PROPERTY_ITEM MaxItem; PROPERTY_ITEM *pItem = (PROPERTY_ITEM *) &MaxItem;
CPropertyTableItem ptiItem(m_pBlockManager, m_pInstanceInfo); HRESULT hrRes = ptiItem.GetItemAtIndex(dwCurrentItem, pItem, &fa); while (SUCCEEDED(hrRes)) { // put every item that we come across into the cache
UpdatePropCache(pItem, fa, dwCurrentItem);
// Get the next one. We can do this because the item object
// is single-threaded
hrRes = ptiItem.GetNextItem(pItem); if (SUCCEEDED(hrRes)) ptiItem.GetOffsetToCurrentItem(&fa); dwCurrentItem++; } _ASSERT(hrRes == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)); }
Exit: TraceFunctLeave(); }
//
// set an item in the property cache. to invalidate an item pass in
// INVALID_FLAT_ADDRESS for fa.
//
void CPropertyTable::UpdatePropCache(LPPROPERTY_ITEM pItem, FLAT_ADDRESS fa, DWORD dwIndex) { TraceFunctEnter("CPropertyTable::UpdatePropCache"); int iCachedProp; if (m_dwValidInstanceSignature == GLOBAL_PTABLE_INSTANCE_SIGNATURE_VALID) { GLOBAL_PROPERTY_ITEM *pGlobalItem = (GLOBAL_PROPERTY_ITEM *) pItem; iCachedProp = MapCachedProp(pGlobalItem->idProp); } else { iCachedProp = -1; } if (iCachedProp != -1) { DebugTrace((LPARAM) this, "iCachedProp = 0x%x fa = 0x%x dwIndex = 0x%x m_rgCachedProps = 0x%x", iCachedProp, fa, dwIndex, m_rgCachedProps); m_rgCachedProps[iCachedProp].fa = fa; m_rgCachedProps[iCachedProp].dwIndex = dwIndex; }
TraceFunctLeave(); }
HRESULT CPropertyTable::SearchForProperty( LPVOID pvPropKey, LPPROPERTY_ITEM pItem, DWORD *pdwIndexToItem, FLAT_ADDRESS *pfaOffsetToItem ) { HRESULT hrRes = S_OK; DWORD dwCurrentItem = 0; PROP_ID idProp;
InterlockedIncrement(&g_cCPropertyTableSearchs);
_ASSERT(IsInstanceInfoValid()); _ASSERT(m_pBlockManager); _ASSERT(m_pfnCompare); _ASSERT(pvPropKey); _ASSERT(pItem);
TraceFunctEnter("CPropertyTable::SearchForProperty");
// Create an instance of the item object
CPropertyTableItem ptiItem( m_pBlockManager, m_pInstanceInfo);
idProp = *(PROP_ID *) pvPropKey;
// First, search the well-known properties
if (m_dwInternalProperties && m_dwTableType == PTT_PROPERTY_TABLE) { LPINTERNAL_PROPERTY_ITEM pInternalItem = NULL; INTERNAL_PROPERTY_ITEM KeyItem;
// Bsearch
KeyItem.idProp = idProp; pInternalItem = (LPINTERNAL_PROPERTY_ITEM)bsearch( &KeyItem, m_pInternalProperties, m_dwInternalProperties, sizeof(INTERNAL_PROPERTY_ITEM), CompareInternalProperties);
if (pInternalItem) { hrRes = ptiItem.GetItemAtIndex(pInternalItem->dwIndex, pItem); ptiItem.GetOffsetToCurrentItem(pfaOffsetToItem); if (pdwIndexToItem) *pdwIndexToItem = pInternalItem->dwIndex; return(hrRes); }
// This is not a well-known property
dwCurrentItem = m_dwInternalProperties; }
DebugTrace((LPARAM)this, "Scanning Property table");
//
// see if its in the property cache
//
// get an index into the cache array
int iCachedProp = MapCachedProp(idProp);
// we lazily initialize the property cache the first time that we need it
if (iCachedProp != -1 && !m_rgCachedProps) InitializePropCache();
// if the cache is initialize and this should be in the case then
// search for it
if (iCachedProp != -1 && m_rgCachedProps) { // see if this cache item is valid, and verify that it points to
// the item that the user wanted
if ((pItem != NULL) && (m_rgCachedProps[iCachedProp].fa != INVALID_FLAT_ADDRESS) && SUCCEEDED(ptiItem.ReadItem(m_rgCachedProps[iCachedProp].fa, pItem)) && SUCCEEDED(m_pfnCompare(pvPropKey, pItem))) { // we've got a winner!
*pfaOffsetToItem = m_rgCachedProps[iCachedProp].fa; if (pdwIndexToItem) *pdwIndexToItem = m_rgCachedProps[iCachedProp].dwIndex; return S_OK; } } else if (iCachedProp != -1) { // this case can be hit if we couldn't allocate memory for the
// property cache. we just need to set iCachedProp back to -1
// so that we do a linear search
iCachedProp = -1; } hrRes = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
//
// Linear Search
//
#ifdef DEBUG
//
// In debug builds we do the linear search if we couldn't find it
// in the cache. we then make sure that the linear search failed
// as well.
//
if (1) { #else
//
// in retail builds we only do this when the data wasn't in the cache
//
if (iCachedProp == -1) { #endif
// Linear search
FLAT_ADDRESS fa; MAX_PROPERTY_ITEM MaxItem;
// we don't want to walk with pItem because if we don't find the
// item then we will trash whatever the user had placed in pItem
PROPERTY_ITEM *pThisItem = NULL;
//
// Sanity check size of property item (firewall corrupt messages)
//
if (!fPropertyItemSizeValid(m_pInstanceInfo->dwItemSize)) { hrRes = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); FatalTrace((LPARAM) this, "Property Items size %d to large... message corrupt", m_pInstanceInfo->dwItemSize); } else {
pThisItem = (PROPERTY_ITEM *) &MaxItem;
hrRes = ptiItem.GetItemAtIndex(dwCurrentItem, pThisItem, &fa); while (SUCCEEDED(hrRes)) { // Call the user-supplied compare function
hrRes = m_pfnCompare(pvPropKey, pThisItem); if (SUCCEEDED(hrRes)) break;
// Get the next one. We can do this because the item object
// is single-threaded
hrRes = ptiItem.GetNextItem(pThisItem); dwCurrentItem++; } } #ifdef DEBUG
// if the item was found here, but not found in the cache,
// then there is an inconsistency that needs to be debugged.
if (iCachedProp != -1 && SUCCEEDED(hrRes)) { DebugTrace(0, "iCachedProp = %i", iCachedProp); _ASSERT(FALSE); // we dont' want debug builds to behave differently then
// retail builds, so force it to fail
hrRes = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS); } #endif
// if we found the item then copy it from pThisItem to pItem
if (SUCCEEDED(hrRes)) { memcpy(pItem, pThisItem, m_pInstanceInfo->dwItemSize); } }
// OKay, if we have no more items, then we cannot find the item,
// otherwise, we let the error code percolate up
if (!SUCCEEDED(hrRes) && hrRes == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) { // Property not found
hrRes = STG_E_UNKNOWN; } else { // Fill in the offset
ptiItem.GetOffsetToCurrentItem(pfaOffsetToItem); if (pdwIndexToItem) *pdwIndexToItem = dwCurrentItem; } TraceFunctLeave(); return(hrRes); }
|