Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1405 lines
45 KiB

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
cmmprops.cpp
Abstract:
This module contains the implementation of the property search class
Author:
Keith Lau ([email protected])
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);
}