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
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);
|
|
}
|
|
|